寫代碼:java
1,明確需求。我要作什麼?linux
2,分析思路。我要怎麼作?1,2,3。程序員
3,肯定步驟。每個思路部分用到哪些語句,方法,和對象。web
4,代碼實現。用具體的java語言代碼把思路體現出來。面試
學習新技術的四點:正則表達式
1,該技術是什麼?算法
2,該技術有什麼特色(使用注意):express
3,該技術怎麼使用。demo編程
4,該技術何時用?test。小程序
-----------------------------------------------------------------------------------------------
一:java概述:
1991 年Sun公司的James Gosling等人開始開發名稱爲 Oak 的語言,但願用於控制嵌入在有線電視交換盒、PDA等的微處理器;
1994年將Oak語言改名爲Java;
Java的三種技術架構:
JAVAEE:Java Platform Enterprise Edition,開發企業環境下的應用程序,主要針對web程序開發;
JAVASE:Java Platform Standard Edition,完成桌面應用程序的開發,是其它二者的基礎;
JAVAME:Java Platform Micro Edition,開發電子消費產品和嵌入式設備,如手機中的程序;
1,JDK:Java Development Kit,java的開發和運行環境,java的開發工具和jre。
2,JRE:Java Runtime Environment,java程序的運行環境,java運行的所需的類庫+JVM(java虛擬機)。
3,配置環境變量:讓java jdk\bin目錄下的工具,能夠在任意目錄下運行,緣由是,將該工具所在目錄告訴了系統,當使用該工具時,由系統幫咱們去找指定的目錄。
環境變量的配置:
1):永久配置方式:
1)JAVA_HOME(新建) D:\jdk1.7
2)classpath(新建) .;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar
3)path(已存在+) %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin
2):臨時配置方式:set path=%path%;C:\Program Files\Java\jdk\bin
特色:系統默認先去當前路徑下找要執行的程序,若是沒有,再去path中設置的路徑下找。
classpath的配置:
1):永久配置方式:classpath=.;c:\;e:\
2):臨時配置方式:set classpath=.;c:\;e:\
注意:在定義classpath環境變量時,須要注意的狀況
若是沒有定義環境變量classpath,java啓動jvm後,會在當前目錄下查找要運行的類文件;
若是指定了classpath,那麼會在指定的目錄下查找要運行的類文件。
還會在當前目錄找嗎?兩種狀況:
1):若是classpath的值結尾處有分號,在具體路徑中沒有找到運行的類,會默認在當前目錄再找一次。
2):若是classpath的值結果出沒有分號,在具體的路徑中沒有找到運行的類,不會再當前目錄找。
通常不指定分號,若是沒有在指定目錄下找到要運行的類文件,就報錯,這樣能夠調試程序。
4,javac命令和java命令作什麼事情呢?
要知道java是分兩部分的:一個是編譯,一個是運行。
javac:負責的是編譯的部分,當執行javac時,會啓動java的編譯器程序。對指定擴展名的.java文件進行編譯。 生成了jvm能夠識別的字節碼文件。也就是class文件,也就是java的運行程序。
java:負責運行的部分.會啓動jvm.加載運行時所需的類庫,並對class文件進行執行.
一個文件要被執行,必需要有一個執行的起始點,這個起始點就是main函數.
----------------------------------------------------------------------------------------------
二:java語法基礎:
1,關鍵字:其實就是某種語言賦予了特殊含義的單詞。
保留字:其實就是尚未賦予特殊含義,可是準備往後要使用過的單詞。
2,標示符:其實就是在程序中自定義的名詞。好比類名,變量名,函數名。包含 0-九、a-z、$、_ ;
注意:
1),數字不能夠開頭。
2),不可使用關鍵字。
3,常量:是在程序中的不會變化的數據。
4,變量:其實就是內存中的一個存儲空間,用於存儲常量數據。
做用:方便於運算。由於有些數據不肯定。因此肯定該數據的名詞和存儲空間。
特色:變量空間能夠重複使用。
何時定義變量?只要是數據不肯定的時候,就定義變量。
變量空間的開闢須要什麼要素呢?
1,這個空間要存儲什麼數據?數據類型。
2,這個空間叫什麼名字啊?變量名稱。
3,這個空間的第一次的數據是什麼? 變量的初始化值。
變量的做用域和生存期:
變量的做用域:
做用域從變量定義的位置開始,到該變量所在的那對大括號結束;
生命週期:
變量從定義的位置開始就在內存中活了;
變量到達它所在的做用域的時候就在內存中消失了;
數據類型:
1):基本數據類型:byte、short、int、long、float、double、char、boolean
2):引用數據類型: 數組、類、接口。
級別從低到高爲:byte,char,short(這三個平級)-->int-->float-->long-->double
自動類型轉換:從低級別到高級別,系統自動轉的;
強制類型轉換:什麼狀況下使用?把一個高級別的數賦給一個別該數的級別低的變量;
運算符號:
1)、算術運算符。
+ - * / % %:任何整數模2不是0就是1,因此只要改變被模數就能夠實現開關運算。
+:鏈接符。
++,--
2)、賦值運算符。
= += -= *= /= %=
3)、比較運算符。
特色:該運算符的特色是:運算完的結果,要麼是true,要麼是false。
4)、邏輯運算符。
& | ^ ! && ||
邏輯運算符除了 ! 外都是用於鏈接兩個boolean類型表達式。
&: 只有兩邊都爲true結果是true。不然就是false。
|:只要兩邊都爲false結果是false,不然就是true
^:異或:和或有點不同。
兩邊結果同樣,就爲false。
兩邊結果不同,就爲true.
&和 &&區別: & :不管左邊結果是什麼,右邊都參與運算。
&&:短路與,若是左邊爲false,那麼右邊不參數與運算。
| 和|| 區別:|:兩邊都運算。
||:短路或,若是左邊爲true,那麼右邊不參與運算。
5)、位運算符:用於操做二進制位的運算符。
& | ^
<< >> >>>(無符號右移)
練習:對兩個變量的數據進行互換。不須要第三方變量。
int a = 3,b = 5;-->b = 3,a = 5;
a = a + b; a = 8;
b = a - b; b = 3;
a = a - b; a = 5;
a = a ^ b;//
b = a ^ b;//b = a ^ b ^ b = a
a = a ^ b;//a = a ^ b ^ a = b;
練習:高效的算出 2*8 = 2<<3;
5,語句。
If switch do while while for
這些語句何時用?
1)、當判斷固定個數的值的時候,可使用if,也可使用switch。
可是建議使用switch,效率相對較高。
switch(變量){
case 值:要執行的語句;break;
…
default:要執行的語句;
}
工做原理:用小括號中的變量的值依次和case後面的值進行對比,和哪一個case後面的值相同了
就執行哪一個case後面的語句,若是沒有相同的則執行default後面的語句;
細節:1):break是能夠省略的,若是省略了就一直執行到遇到break爲止;
2):switch 後面的小括號中的變量應該是byte,char,short,int四種類型中的一種;
3):default能夠寫在switch結構中的任意位置;若是將default語句放在了第一行,則無論expression與case中的value是否匹配,程序會從default開始執行直到第一個break出現。
2)、當判斷數據範圍,獲取判斷運算結果boolean類型時,須要使用if。
3)、當某些語句須要執行不少次時,就用循環結構。
while和for能夠進行互換。
區別在於:若是須要定義變量控制循環次數。建議使用for。由於for循環完畢,變量在內存中釋放。
break:做用於switch ,和循環語句,用於跳出,或者稱爲結束。
break語句單獨存在時,下面不要定義其餘語句,由於執行不到,編譯會失敗。當循環嵌套時,break只跳出當前所在循環。要跳出嵌套中的外部循環,只要給循環起名字便可,這個名字稱之爲標號。
continue:只做用於循環結構,繼續循環用的。
做用:結束本次循環,繼續下次循環。該語句單獨存在時,下面不能夠定義語句,執行不到。
6,函 數:爲了提升代碼的複用性,能夠將其定義成一個單獨的功能,該功能的體現就是java中的函數。函數就是體現之一。
java中的函數的定義格式:
修飾符 返回值類型 函數名(參數類型 形式參數1,參數類型 形式參數1,…){
執行語句;
return 返回值;
}
當函數沒有具體的返回值時,返回的返回值類型用void關鍵字表示。
若是函數的返回值類型是void時,return語句能夠省略不寫的,系統會幫你自動加上。
return的做用:結束函數。結束功能。
如何定義一個函數?
函數其實就是一個功能,定義函數就是實現功能,經過兩個明確來完成:
1)、明確該功能的運算完的結果,實際上是在明確這個函數的返回值類型。
2)、在實現該功能的過程當中是否有未知內容參與了運算,其實就是在明確這個函數的參數列表(參數類型&參數個數)。
函數的做用:
1)、用於定義功能。
2)、用於封裝代碼提升代碼的複用性。
注意:函數中只能調用函數,不能定義函數。
主函數:
1)、保證該類的獨立運行。
2)、由於它是程序的入口。
3)、由於它在被jvm調用。
函數定義名稱是爲何呢?
答:1)、爲了對該功能進行標示,方便於調用。
2)、爲了經過名稱就能夠明確函數的功能,爲了增長代碼的閱讀性。
重載的定義是:在一個類中,若是出現了兩個或者兩個以上的同名函數,只要它們的參數的個數,或者參數的類型不一樣,便可稱之爲該函數重載了。
如何區分重載:當函數同名時,只看參數列表。和返回值類型不要緊。
7,數 組:用於存儲同一類型數據的一個容器。好處:能夠對該容器中的數據進行編號,從0開始。數組用於封裝數據,就是一個具體的實體。
如何在java中表現一個數組呢?兩種表現形式。
1)、元素類型[] 變量名 = new 元素類型[元素的個數];
2)、元素類型[] 變量名 = {元素1,元素2...};
元素類型[] 變量名 = new 元素類型[]{元素1,元素2...};
---------------------------------------------------------
//二分查找法。必須有前提:數組中的元素要有序。
public static int halfSeach_2(int[] arr,int key){
int min,max,mid;
min = 0;
max = arr.length-1;
mid = (max+min)>>1; //(max+min)/2;
while(arr[mid]!=key){
if(key>arr[mid]){
min = mid + 1;
}
else if(key<arr[mid])
max = mid - 1;
if(max<min)
return -1;
mid = (max+min)>>1;
}
return mid;
}
---------------------------------------------------------
java分了5片內存。
1:寄存器。2:本地方法區。3:方法區。4:棧。5:堆。
棧:存儲的都是局部變量 ( 函數中定義的變量,函數上的參數,語句中的變量 );
只要數據運算完成所在的區域結束,該數據就會被釋放。
堆:用於存儲數組和對象,也就是實體。啥是實體啊?就是用於封裝多個數據的。
1:每個實體都有內存首地址值。
2:堆內存中的變量都有默認初始化值。由於數據類型不一樣,值也不同。
3:垃圾回收機制。
----------------------------------------------------------------------------------------------
三:面向對象:★★★★★
特色:1:將複雜的事情簡單化。
2:面向對象將之前的過程當中的執行者,變成了指揮者。
3:面向對象這種思想是符合如今人們思考習慣的一種思想。
過程和對象在咱們的程序中是如何體現的呢?過程其實就是函數;對象是將函數等一些內容進行了封裝。
匿名對象使用場景:
1:當對方法只進行一次調用的時候,可使用匿名對象。
2:當對象對成員進行屢次調用時,不能使用匿名對象。必須給對象起名字。
在類中定義其實都稱之爲成員。成員有兩種:
1:成員變量:其實對應的就是事物的屬性。
2:成員函數:其實對應的就是事物的行爲。
因此,其實定義類,就是在定義成員變量和成員函數。可是在定義前,必須先要對事物進行屬性和行爲的分析,才能夠用代碼來體現。
private int age;//私有的訪問權限最低,只有在本類中的訪問有效。
注意:私有僅僅是封裝的一種體現形式而已。
私有的成員:其餘類不能直接建立對象訪問,因此只有經過本類對外提供具體的訪問方式來完成對私有的訪問,能夠經過對外提供函數的形式對其進行訪問。
好處:能夠在函數中加入邏輯判斷等操做,對數據進行判斷等操做。
總結:開發時,記住,屬性是用於存儲數據的,直接被訪問,容易出現安全隱患,因此,類中的屬性一般被私有化,並對外提供公共的訪問方法。
這個方法通常有兩個,規範寫法:對於屬性 xxx,可使用setXXX(),getXXX()對其進行操做。
類中怎麼沒有定義主函數呢?
注意:主函數的存在,僅爲該類是否須要獨立運行,若是不須要,主函數是不用定義的。
主函數的解釋:保證所在類的獨立運行,是程序的入口,被jvm調用。
成員變量和局部變量的區別:
1:成員變量直接定義在類中。
局部變量定義在方法中,參數上,語句中。
2:成員變量在這個類中有效。
局部變量只在本身所屬的大括號內有效,大括號結束,局部變量失去做用域。
3:成員變量存在於堆內存中,隨着對象的產生而存在,消失而消失。
局部變量存在於棧內存中,隨着所屬區域的運行而存在,結束而釋放。
構造函數:用於給對象進行初始化,是給與之對應的對象進行初始化,它具備針對性,函數中的一種。
特色:
1:該函數的名稱和所在類的名稱相同。
2:不須要定義返回值類型。
3:該函數沒有具體的返回值。
記住:全部對象建立時,都須要初始化纔可使用。
注意事項:一個類在定義時,若是沒有定義過構造函數,那麼該類中會自動生成一個空參數的構造函數,爲了方便該類建立對象,完成初始化。若是在類中自定義了構造函數,那麼默認的構造函數就沒有了。
一個類中,能夠有多個構造函數,由於它們的函數名稱都相同,因此只能經過參數列表來區分。因此,一個類中若是出現多個構造函數。它們的存在是以重載體現的。
構造函數和通常函數有什麼區別呢?
1:兩個函數定義格式不一樣。
2:構造函數是在對象建立時,就被調用,用於初始化,並且初始化動做只執行一次。
通常函數,是對象建立後,須要調用才執行,能夠被調用屢次。
何時使用構造函數呢?
分析事物時,發現具體事物一出現,就具有了一些特徵,那就將這些特徵定義到構造函數內。
構造代碼塊和構造函數有什麼區別?
構造代碼塊:是給全部的對象進行初始化,也就是說,全部的對象都會調用一個代碼塊。只要對象一創建。就會調用這個代碼塊。
構造函數:是給與之對應的對象進行初始化。它具備針對性。
Person p = new Person();
建立一個對象都在內存中作了什麼事情?
1:先將硬盤上指定位置的Person.class文件加載進內存。
2:執行main方法時,在棧內存中開闢了main方法的空間(壓棧-進棧),而後在main方法的棧區分配了一個變量p。
3:在堆內存中開闢一個實體空間,分配了一個內存首地址值。new
4:在該實體空間中進行屬性的空間分配,並進行了默認初始化。
5:對空間中的屬性進行顯示初始化。
6:進行實體的構造代碼塊初始化。
7:調用該實體對應的構造函數,進行構造函數初始化。()
8:將首地址賦值給p ,p變量就引用了該實體。(指向了該對象)
--------------------------------------------------------------------------------------------
封 裝(面向對象特徵之一):是指隱藏對象的屬性和實現細節,僅對外提供公共訪問方式。
好處:將變化隔離;便於使用;提升重用性;安全性。
封裝原則:將不須要對外提供的內容都隱藏起來,把屬性都隱藏,提供公共方法對其訪問。
this:表明對象。就是所在函數所屬對象的引用。
this到底表明什麼呢?哪一個對象調用了this所在的函數,this就表明哪一個對象,就是哪一個對象的引用。
開發時,何時使用this呢?
在定義功能時,若是該功能內部使用到了調用該功能的對象,這時就用this來表示這個對象。
this 還能夠用於構造函數間的調用。
調用格式:this(實際參數);
this對象後面跟上 . 調用的是成員屬性和成員方法(通常方法);
this對象後面跟上 () 調用的是本類中的對應參數的構造函數。
注意:用this調用構造函數,必須定義在構造函數的第一行。由於構造函數是用於初始化的,因此初始化動做必定要執行。不然編譯失敗。
static:★★★ 關鍵字,是一個修飾符,用於修飾成員(成員變量和成員函數)。
特色:
1,想要實現對象中的共性數據的對象共享。能夠將這個數據進行靜態修飾。
2,被靜態修飾的成員,能夠直接被類名所調用。也就是說,靜態的成員多了一種調用方式。類名.靜態方式。
3,靜態隨着類的加載而加載。並且優先於對象存在。
弊端:
1,有些數據是對象特有的數據,是不能夠被靜態修飾的。由於那樣的話,特有數據會變成對象的共享數據。這樣對事物的描述就出了問題。因此,在定義靜態時,必需要明確,這個數據是不是被對象所共享的。
2,靜態方法只能訪問靜態成員,不能夠訪問非靜態成員。
由於靜態方法加載時,優先於對象存在,因此沒有辦法訪問對象中的成員。
3,靜態方法中不能使用this,super關鍵字。
由於this表明對象,而靜態在時,有可能沒有對象,因此this沒法使用。
4,主函數是靜態的。
何時定義靜態成員呢?或者說:定義成員時,到底需不須要被靜態修飾呢?
成員分兩種:
1,成員變量。(數據共享時靜態化)
該成員變量的數據是不是全部對象都同樣:
若是是,那麼該變量須要被靜態修飾,由於是共享的數據。
若是不是,那麼就說這是對象的特有數據,要存儲到對象中。
2,成員函數。(方法中沒有調用特有數據時就定義成靜態)
若是判斷成員函數是否須要被靜態修飾呢?
只要參考,該函數內是否訪問了對象中的特有數據:
若是有訪問特有數據,那方法不能被靜態修飾。
若是沒有訪問過特有數據,那麼這個方法須要被靜態修飾。
成員變量和靜態變量的區別:
1,成員變量所屬於對象。因此也稱爲實例變量。
靜態變量所屬於類。因此也稱爲類變量。
2,成員變量存在於堆內存中。
靜態變量存在於方法區中。
3,成員變量隨着對象建立而存在。隨着對象被回收而消失。
靜態變量隨着類的加載而存在。隨着類的消失而消失。
4,成員變量只能被對象所調用 。
靜態變量能夠被對象調用,也能夠被類名調用。
因此,成員變量能夠稱爲對象的特有數據,靜態變量稱爲對象的共享數據。
靜態的注意:靜態的生命週期很長。
靜態代碼塊:就是一個有靜態關鍵字標示的一個代碼塊區域。定義在類中。
做用:能夠完成類的初始化。靜態代碼塊隨着類的加載而執行,並且只執行一次(new 多個對象就只執行一次)。若是和主函數在同一類中,優先於主函數執行。
Public:訪問權限最大。
static:不須要對象,直接類名便可。
void:主函數沒有返回值。
Main:主函數特定的名稱。
(String[] args):主函數的參數,是一個字符串數組類型的參數,jvm調用main方法時,傳遞的實際參數是 new String[0]。
jvm默認傳遞的是長度爲0的字符串數組,咱們在運行該類時,也能夠指定具體的參數進行傳遞。能夠在控制檯,運行該類時,在後面加入參數。參數之間經過空格隔開。jvm會自動將這些字符串參數做爲args數組中的元素,進行存儲。
靜態代碼塊、構造代碼塊、構造函數同時存在時的執行順序:靜態代碼塊 à構造代碼塊 à構造函數;
生成Java幫助文檔:命令格式:javadoc –d 文件夾名 –auther –version *.java
/** //格式
*類描述
*@author 做者名
*@version 版本號
*/
/**
*方法描述
*@param 參數描述
*@return 返回值描述
*/
---------------------------------------------------------------------------------------------
設計模式:解決問題最行之有效的思想。是一套被反覆使用、多數人知曉的、通過分類編目的、代碼設計經驗的總結。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。
java中有23種設計模式:
單例設計模式:★★★★★
解決的問題:保證一個類在內存中的對象惟一性。
好比:多程序讀取一個配置文件時,建議配置文件封裝成對象。會方便操做其中數據,又要保證多個程序讀到的是同一個配置文件對象,就須要該配置文件對象在內存中是惟一的。
Runtime()方法就是單例設計模式進行設計的。
如何保證對象惟一性呢?
思想:
1,不讓其餘程序建立該類對象。
2,在本類中建立一個本類對象。
3,對外提供方法,讓其餘程序獲取這個對象。
步驟:
1,由於建立對象都須要構造函數初始化,只要將本類中的構造函數私有化,其餘程序就沒法再建立該類對象;
2,就在類中建立一個本類的對象;
3,定義一個方法,返回該對象,讓其餘程序能夠經過方法就獲得本類對象。(做用:可控)
代碼體現:
1,私有化構造函數;
2,建立私有並靜態的本類對象;
3,定義公有並靜態的方法,返回該對象。
---------------------------------------------
//餓漢式
class Single{
private Single(){} //私有化構造函數。
private static Single s = new Single(); //建立私有並靜態的本類對象。
public static Single getInstance(){ //定義公有並靜態的方法,返回該對象。
return s;
}
}
---------------------------------------------
//懶漢式:延遲加載方式。
class Single2{
private Single2(){}
private static Single2 s = null;
public static Single2 getInstance(){
if(s==null)
s = new Single2();
return s;
}
}
-------------------------------------------------------------------------------------------------
繼 承(面向對象特徵之一)
好處:
1:提升了代碼的複用性。
2:讓類與類之間產生了關係,提供了另外一個特徵多態的前提。
父類的由來:實際上是由多個類不斷向上抽取共性內容而來的。
java中對於繼承,java只支持單繼承。java雖然不直接支持多繼承,可是保留了這種多繼承機制,進行改良。
單繼承:一個類只能有一個父類。
多繼承:一個類能夠有多個父類。
爲何不支持多繼承呢?
由於當一個類同時繼承兩個父類時,兩個父類中有相同的功能,那麼子類對象調用該功能時,運行哪個呢?由於父類中的方法中存在方法體。
可是java支持多重繼承。A繼承B B繼承C C繼承D。
多重繼承的出現,就有了繼承體系。體系中的頂層父類是經過不斷向上抽取而來的。它裏面定義的該體系最基本最共性內容的功能。
因此,一個體系要想被使用,直接查閱該系統中的父類的功能便可知道該體系的基本用法。那麼想要使用一個體系時,須要創建對象。建議創建最子類對象,由於最子類不只可使用父類中的功能。還可使用子類特有的一些功能。
簡單說:對於一個繼承體系的使用,查閱頂層父類中的內容,建立最底層子類的對象。
子父類出現後,類中的成員都有了哪些特色:
1:成員變量。
當子父類中出現同樣的屬性時,子類類型的對象,調用該屬性,值是子類的屬性值。
若是想要調用父類中的屬性值,須要使用一個關鍵字:super
This:表明是本類類型的對象引用。
Super:表明是子類所屬的父類中的內存空間引用。
注意:子父類中一般是不會出現同名成員變量的,由於父類中只要定義了,子類就不用在定義了,直接繼承過來用就能夠了。
2:成員函數。
當子父類中出現瞭如出一轍的方法時,創建子類對象會運行子類中的方法。好像父類中的方法被覆蓋掉同樣。因此這種狀況,是函數的另外一個特性:覆蓋(複寫,重寫)
何時使用覆蓋呢?當一個類的功能內容須要修改時,能夠經過覆蓋來實現。
3:構造函數。
發現子類構造函數運行時,先運行了父類的構造函數。爲何呢?
緣由:子類的全部構造函數中的第一行,其實都有一條隱身的語句super();
super(): 表示父類的構造函數,並會調用於參數相對應的父類中的構造函數。而super():是在調用父類中空參數的構造函數。
爲何子類對象初始化時,都須要調用父類中的函數?(爲何要在子類構造函數的第一行加入這個super()?)
由於子類繼承父類,會繼承到父類中的數據,因此必需要看父類是如何對本身的數據進行初始化的。因此子類在進行對象初始化時,先調用父類的構造函數,這就是子類的實例化過程。
注意:子類中全部的構造函數都會默認訪問父類中的空參數的構造函數,由於每個子類構造內第一行都有默認的語句super();
若是父類中沒有空參數的構造函數,那麼子類的構造函數內,必須經過super語句指定要訪問的父類中的構造函數。
若是子類構造函數中用this來指定調用子類本身的構造函數,那麼被調用的構造函數也同樣會訪問父類中的構造函數。
問題:super()和this()是否能夠同時出現的構造函數中。
兩個語句只能有一個定義在第一行,因此只能出現其中一個。
super()或者this():爲何必定要定義在第一行?
由於super()或者this()都是調用構造函數,構造函數用於初始化,因此初始化的動做要先完成。
繼承的細節:
何時使用繼承呢?
當類與類之間存在着所屬關係時,才具有了繼承的前提。a是b中的一種。a繼承b。狼是犬科中的一種。
英文書中,所屬關係:" is a "
注意:不要僅僅爲了獲取其餘類中的已有成員進行繼承。
因此判斷所屬關係,能夠簡單看,若是繼承後,被繼承的類中的功能,均可以被該子類所具有,那麼繼承成立。若是不是,不能夠繼承。
細節二:
在方法覆蓋時,注意兩點:
1:子類覆蓋父類時,必需要保證,子類方法的權限必須大於等於父類方法權限能夠實現繼承。不然,編譯失敗。
2:覆蓋時,要麼都靜態,要麼都不靜態。 (靜態只能覆蓋靜態,或者被靜態覆蓋)
繼承的一個弊端:打破了封裝性。對於一些類,或者類中功能,是須要被繼承,或者複寫的。
這時如何解決問題呢?介紹一個關鍵字,final:最終。
final特色:
1:這個關鍵字是一個修飾符,能夠修飾類,方法,變量。
2:被final修飾的類是一個最終類,不能夠被繼承。
3:被final修飾的方法是一個最終方法,不能夠被覆蓋。
4:被final修飾的變量是一個常量,只能賦值一次。
其實這樣的緣由的就是給一些固定的數據起個閱讀性較強的名稱。
不加final修飾不是也可使用嗎?那麼這個值是一個變量,是能夠更改的。加了final,程序更爲嚴謹。常量名稱定義時,有規範,全部字母都大寫,若是由多個單詞組成,中間用 _ 鏈接。
抽象類:abstract
抽象:不具體,看不明白。抽象類表象體現。
在不斷抽取過程當中,將共性內容中的方法聲明抽取,可是方法不同,沒有抽取,這時抽取到的方法,並不具體,須要被指定關鍵字abstract所標示,聲明爲抽象方法。
抽象方法所在類必定要標示爲抽象類,也就是說該類須要被abstract關鍵字所修飾。
抽象類的特色:
1:抽象方法只能定義在抽象類中,抽象類和抽象方法必須由abstract關鍵字修飾(能夠描述類和方法,不能夠描述變量)。
2:抽象方法只定義方法聲明,並不定義方法實現。
3:抽象類不能夠被建立對象(實例化)。
4:只有經過子類繼承抽象類並覆蓋了抽象類中的全部抽象方法後,該子類才能夠實例化。不然,該子類仍是一個抽象類。
抽象類的細節:
1:抽象類中是否有構造函數?有,用於給子類對象進行初始化。
2:抽象類中是否能夠定義非抽象方法?
能夠。其實,抽象類和通常類沒有太大的區別,都是在描述事物,只不過抽象類在描述事物時,有些功能不具體。因此抽象類和通常類在定義上,都是須要定義屬性和行爲的。只不過,比通常類多了一個抽象函數。並且比通常類少了一個建立對象的部分。
3:抽象關鍵字abstract和哪些不能夠共存?final , private , static
4:抽象類中可不能夠不定義抽象方法?能夠。抽象方法目的僅僅爲了避免讓該類建立對象。
-----------------------------------------------------------------------------------------------
模板方法設計模式:
解決的問題:當功能內部一部分實現時肯定,一部分實現是不肯定的。這時能夠把不肯定的部分暴露出去,讓子類去實現。
abstract class GetTime{
public final void getTime(){ //此功能若是不須要複寫,可加final限定
long start = System.currentTimeMillis();
code(); //不肯定的功能部分,提取出來,經過抽象方法實現
long end = System.currentTimeMillis();
System.out.println("毫秒是:"+(end-start));
}
public abstract void code(); //抽象不肯定的功能,讓子類複寫實現
}
class SubDemo extends GetTime{
public void code(){ //子類複寫功能方法
for(int y=0; y<1000; y++){
System.out.println("y");
}
}
}
---------------------------------------------------------------------------------------------
接 口:★★★★★
1:是用關鍵字interface定義的。
2:接口中包含的成員,最多見的有全局常量、抽象方法。
注意:接口中的成員都有固定的修飾符。
成員變量:public static final
成員方法:public abstract
interface Inter{
public static final int x = 3;
public abstract void show();
}
3:接口中有抽象方法,說明接口不能夠實例化。接口的子類必須實現了接口中全部的抽象方法後,該子類才能夠實例化。不然,該子類仍是一個抽象類。
4:類與類之間存在着繼承關係,類與接口中間存在的是實現關係。
繼承用extends ;實現用implements ;
5:接口和類不同的地方,就是,接口能夠被多實現,這就是多繼承改良後的結果。java將多繼承機制經過多現實來體現。
6:一個類在繼承另外一個類的同時,還能夠實現多個接口。因此接口的出現避免了單繼承的侷限性。還能夠將類進行功能的擴展。
7:其實java中是有多繼承的。接口與接口之間存在着繼承關係,接口能夠多繼承接口。
接口都用於設計上,設計上的特色:(能夠理解主板上提供的接口)
1:接口是對外提供的規則。
2:接口是功能的擴展。
3:接口的出現下降了耦合性。
抽象類與接口:
抽象類:通常用於描述一個體系單元,將一組共性內容進行抽取,特色:能夠在類中定義抽象內容讓子類實現,能夠定義非抽象內容讓子類直接使用。它裏面定義的都是一些體系中的基本內容。
接口:通常用於定義對象的擴展功能,是在繼承以外還需這個對象具有的一些功能。
抽象類和接口的共性:都是不斷向上抽取的結果。
抽象類和接口的區別:
1:抽象類只能被繼承,並且只能單繼承。
接口須要被實現,並且能夠多實現。
2:抽象類中能夠定義非抽象方法,子類能夠直接繼承使用。
接口中都有抽象方法,須要子類去實現。
3:抽象類使用的是 is a 關係。
接口使用的 like a 關係。
4:抽象類的成員修飾符能夠自定義。
接口中的成員修飾符是固定的。全都是public的。
在開發以前,先定義規則,A和B分別開發,A負責實現這個規則,B負責使用這個規則。至於A是如何對規則具體實現的,B是不須要知道的。這樣這個接口的出現就下降了A和B直接耦合性。
------------------------------------------------------------------------------------------------
多 態★★★★★(面向對象特徵之一):函數自己就具有多態性,某一種事物有不一樣的具體的體現。
體現:父類引用或者接口的引用指向了本身的子類對象。//Animal a = new Cat();
多態的好處:提升了程序的擴展性。
多態的弊端:當父類引用指向子類對象時,雖然提升了擴展性,可是隻能訪問父類中具有的方法,不能夠訪問子類中特有的方法。(前期不能使用後期產生的功能,即訪問的侷限性)
多態的前提:
1:必需要有關係,好比繼承、或者實現。
2:一般會有覆蓋操做。
多態的出現思想上也作着變化:之前是建立對象並指揮對象作事情。有了多態之後,咱們能夠找到對象的共性類型,直接操做共性類型作事情便可,這樣能夠指揮一批對象作事情,即經過操做父類或接口實現。
--------------------------------------------------------------
class 畢姥爺{
void 講課(){
System.out.println("企業管理");
}
void 釣魚(){
System.out.println("釣魚");
}
}
class 畢老師 extends 畢姥爺{
void 講課(){
System.out.println("JAVA");
}
void 看電影(){
System.out.println("看電影");
}
}
class {
public static void main(String[] args) {
畢姥爺 x = new 畢老師(); //畢老師對象被提高爲了畢姥爺類型。
// x.講課();
// x.看電影(); //錯誤.
畢老師 y = (畢老師)x; //將畢姥爺類型強制轉換成畢老師類型。
y.看電影();//在多態中,自始自終都是子類對象在作着類型的變化。
}
}
---------------------------------------------------------------
若是想用子類對象的特有方法,如何判斷對象是哪一個具體的子類類型呢?
能夠能夠經過一個關鍵字 instanceof ;//判斷對象是否實現了指定的接口或繼承了指定的類
格式:<對象 instanceof 類型> ,判斷一個對象是否所屬於指定的類型。
Student instanceof Person = true;//student繼承了person類
多態在子父類中的成員上的體現的特色:
1,成員變量:在多態中,子父類成員變量同名。
在編譯時期:參考的是引用型變量所屬的類中是否有調用的成員。(編譯時不產生對象,只檢查語法錯誤)
運行時期:也是參考引用型變量所屬的類中是否有調用的成員。
簡單一句話:不管編譯和運行,成員變量參考的都是引用變量所屬的類中的成員變量。
再說的更容易記憶一些:成員變量 --- 編譯運行都看 = 左邊。
2,成員函數。
編譯時期:參考引用型變量所屬的類中是否有調用的方法。
運行事情:參考的是對象所屬的類中是否有調用的方法。
爲何是這樣的呢?由於在子父類中,對於如出一轍的成員函數,有一個特性:覆蓋。
簡單一句:成員函數,編譯看引用型變量所屬的類,運行看對象所屬的類。
更簡單:成員函數 --- 編譯看 = 左邊,運行看 = 右邊。
3,靜態函數。
編譯時期:參考的是引用型變量所屬的類中是否有調用的成員。
運行時期:也是參考引用型變量所屬的類中是否有調用的成員。
爲何是這樣的呢?由於靜態方法,其實不所屬於對象,而是所屬於該方法所在的類。
調用靜態的方法引用是哪一個類的引用調用的就是哪一個類中的靜態方法。
簡單說:靜態函數 --- 編譯運行都看 = 左邊。
-----------------------------------------------------------------------------------------------
------java.lang.Object
Object:全部類的直接或者間接父類,Java認爲全部的對象都具有一些基本的共性內容,這些內容能夠不斷的向上抽取,最終就抽取到了一個最頂層的類中的,該類中定義的就是全部對象都具有的功能。
具體方法:
1,boolean equals(Object obj):用於比較兩個對象是否相等,其實內部比較的就是兩個對象地址。
而根據對象的屬性不一樣,判斷對象是否相同的具體內容也不同。因此在定義類時,通常都會複寫equals方法,創建本類特有的判斷對象是否相同的依據。
public boolean equals(Object obj){
if(!(obj instanceof Person))
return false;
Person p = (Person)obj;
return this.age == p.age;
}
2,String toString():將對象變成字符串;默認返回的格式:類名@哈希值 = getClass().getName() + '@' + Integer.toHexString(hashCode())
爲了對象對應的字符串內容有意義,能夠經過複寫,創建該類對象本身特有的字符串表現形式。
public String toString(){
return "person : "+age;
}
3,Class getClass():獲取任意對象運行時的所屬字節碼文件對象。
4,int hashCode():返回該對象的哈希碼值。支持此方法是爲了提升哈希表的性能。
一般equals,toString,hashCode,在應用中都會被複寫,創建具體對象的特有的內容。
------------------------------------------------------------------------------------------------
內部類:若是A類須要直接訪問B類中的成員,而B類又須要創建A類的對象。這時,爲了方便設計和訪問,直接將A類定義在B類中。就能夠了。A類就稱爲內部類。內部類能夠直接訪問外部類中的成員。而外部類想要訪問內部類,必需要創建內部類的對象。
-----------------------------------------------------
class Outer{
int num = 4;
class Inner {
void show(){
System.out.println("inner show run "+num);
}
}
public void method(){
Inner in = new Inner();//建立內部類的對象。
in.show();//調用內部類的方法。
}
}
-------------------------------------------------------
當內部類定義在外部類中的成員位置上,可使用一些成員修飾符修飾 private、static。
1:默認修飾符。
直接訪問內部類格式:外部類名.內部類名 變量名 = 外部類對象.內部類對象;
Outer.Inner in = new Outer.new Inner();//這種形式不多用。
可是這種應用很少見,由於內部類之因此定義在內部就是爲了封裝。想要獲取內部類對象一般都經過外部類的方法來獲取。這樣能夠對內部類對象進行控制。
2:私有修飾符。
一般內部類被封裝,都會被私有化,由於封裝性不讓其餘程序直接訪問。
3:靜態修飾符。
若是內部類被靜態修飾,至關於外部類,會出現訪問侷限性,只能訪問外部類中的靜態成員。
注意;若是內部類中定義了靜態成員,那麼該內部類必須是靜態的。
內部類編譯後的文件名爲:「外部類名$內部類名.java」;
爲何內部類能夠直接訪問外部類中的成員呢?
那是由於內部中都持有一個外部類的引用。這個是引用是 外部類名.this
內部類能夠定義在外部類中的成員位置上,也能夠定義在外部類中的局部位置上。
當內部類被定義在局部位置上,只能訪問局部中被final修飾的局部變量。
匿名內部類:沒有名字的內部類。就是內部類的簡化形式。通常只用一次就能夠用這種形式。匿名內部類其實就是一個匿名子類對象。想要定義匿名內部類:須要前提,內部類必須繼承一個類或者實現接口。
匿名內部類的格式:new 父類名&接口名(){ 定義子類成員或者覆蓋父類方法 }.方法。
匿名內部類的使用場景:
當函數的參數是接口類型引用時,若是接口中的方法不超過3個。能夠經過匿名內部類來完成參數的傳遞。
其實就是在建立匿名內部類時,該類中的封裝的方法不要過多,最好兩個或者兩個之內。
--------------------------------------------------------
//面試
//1
new Object(){
void show(){
System.out.println("show run");
}
}.show();
//2
Object obj = new Object(){
void show(){
System.out.println("show run");
}
};
obj.show();
1和2的寫法正確嗎?有區別嗎?說出緣由。
寫法是正確,1和2都是在經過匿名內部類創建一個Object類的子類對象。
區別:
第一個但是編譯經過,並運行。
第二個編譯失敗,由於匿名內部類是一個子類對象,當用Object的obj引用指向時,就被提高爲了
Object類型,而編譯時檢查Object類中是否有show方法,因此編譯失敗。
-------------------------------------------------------
class InnerClassDemo6 {
+(static)class Inner{
void show(){}
}
public void method(){
this.new Inner().show();//能夠
}
public static void main(String[] args) {//static不容許this
This.new Inner().show();//錯誤,Inner類須要定義成static
}
}
------------------------------------------------------
interface Inter{
void show();
}
class Outer{//經過匿名內部類補足Outer類中的代碼。
public static Inter method(){
return new Inter(){
public void show(){}
};
}
}
class InnerClassDemo7 {
public static void main(String[] args) {
Outer.method().show();
/*
Outer.method():意思是:Outer中有一個名稱爲method的方法,並且這個方法是靜態的。
Outer.method().show():當Outer類調用靜態的method方法運算結束後的結果又調用了show方法,意味着:method()方法運算完一個是對象,並且這個對象是Inter類型的。
*/
function (new Inter(){
public void show(){}
}); //匿名內部類做爲方法的參數進行傳遞。
}
public static void function(Inter in){
in.show();
}
}
------------------------------------------------------------------------------------------------
異 常:★★★★
異常:就是不正常。程序在運行時出現的不正常狀況。其實就是程序中出現的問題。這個問題按照面向對象思想進行描述,並封裝成了對象。由於問題的產生有產生的緣由、有問題的名稱、有問題的描述等多個屬性信息存在。當出現多屬性信息最方便的方式就是將這些信息進行封裝。異常就是java按照面向對象的思想將問題進行對象封裝。這樣就方便於操做問題以及處理問題。
出現的問題有不少種,好比角標越界,空指針等都是。就對這些問題進行分類。並且這些問題都有共性內容好比:每個問題都有名稱,同時還有問題描述的信息,問題出現的位置,因此能夠不斷的向上抽取。造成了異常體系。
--------java.lang.Throwable:
Throwable:可拋出的。
|--Error:錯誤,通常狀況下,不編寫針對性的代碼進行處理,一般是jvm發生的,須要對程序進行修正。
|--Exception:異常,能夠有針對性的處理方式
不管是錯誤仍是異常,它們都有具體的子類體現每個問題,它們的子類都有一個共性,就是都以父類名才做爲子類的後綴名。
這個體系中的全部類和對象都具有一個獨有的特色;就是可拋性。
可拋性的體現:就是這個體系中的類和對象均可以被throws和throw兩個關鍵字所操做。
------------------------------------------------------
class ExceptionDemo{
public static void main(String[] args) {
// byte[] buf = new byte[1024*1024*700];//java.lang.OutOfMemoryError內存溢出錯誤
}
}
------------------------------------------------------
在開發時,若是定義功能時,發現該功能會出現一些問題,應該將問題在定義功能時標示出來,這樣調用者就能夠在使用這個功能的時候,預先給出處理方式。
如何標示呢?經過throws關鍵字完成,格式:throws 異常類名,異常類名...
這樣標示後,調用者,在使用該功能時,就必需要處理,不然編譯失敗。
處理方式有兩種:一、捕捉;二、拋出。
對於捕捉:java有針對性的語句塊進行處理。
try {
須要被檢測的代碼;
}
catch(異常類 變量名){
異常處理代碼;
}
fianlly{
必定會執行的代碼;
}
--------------------------------------------------------
catch (Exception e) { //e用於接收try檢測到的異常對象。
System.out.println("message:"+e.getMessage());//獲取的是異常的信息。
System.out.println("toString:"+e.toString());//獲取的是異常的名字+異常的信息。
e.printStackTrace();//打印異常在堆棧中信息;異常名稱+異常信息+異常的位置。
}
---------------------------------------------------------
異常處理原則:功能拋出幾個異常,功能調用若是進行try處理,須要與之對應的catch處理代碼塊,這樣的處理有針對性,拋幾個就處理幾個。
特殊狀況:try對應多個catch時,若是有父類的catch語句塊,必定要放在下面。
throw 和throws關鍵字的區別:
throw用於拋出異常對象,後面跟的是異常對象;throw用在函數內。
throws用於拋出異常類,後面跟的異常類名,能夠跟多個,用逗號隔開。throws用在函數上。
一般狀況:函數內容若是有throw,拋出異常對象,並無進行處理,那麼函數上必定要聲明,不然編譯失敗。可是也有特殊狀況。
異常分兩種:
1:編譯時被檢查的異常,只要是Exception及其子類都是編譯時被檢測的異常。
2:運行時異常,其中Exception有一個特殊的子類RuntimeException,以及RuntimeException的子類是運行異常,也就說這個異常是編譯時不被檢查的異常。
編譯時被檢查的異常和運行時異常的區別:
編譯被檢查的異常在函數內被拋出,函數必需要聲明,否編譯失敗。
聲明的緣由:是須要調用者對該異常進行處理。
運行時異常若是在函數內被拋出,在函數上不須要聲明。
不聲明的緣由:不須要調用者處理,運行時異常發生,已經沒法再讓程序繼續運行,因此,不讓調用處理的,直接讓程序中止,由調用者對代碼進行修正。
定義異常處理時,何時定義try,何時定義throws呢?
功能內部若是出現異常,若是內部能夠處理,就用try;
若是功能內部處理不了,就必須聲明出來,讓調用者處理。
自定義異常:當開發時,項目中出現了java中沒有定義過的問題時,這時就須要咱們按照java異常創建思想,將項目的中的特有問題也進行對象的封裝。這個異常,稱爲自定義異常。
對於除法運算,0做爲除數是不能夠的。java中對這種問題用ArithmeticException類進行描述。對於這個功能,在咱們項目中,除數除了不能夠爲0外,還不能夠爲負數。但是負數的部分java並無針對描述。因此咱們就須要自定義這個異常。
自定義異常的步驟:
1:定義一個子類繼承Exception或RuntimeException,讓該類具有可拋性。
2:經過throw 或者throws進行操做。
異常的轉換思想:當出現的異常是調用者處理不了的,就須要將此異常轉換爲一個調用者能夠處理的異常拋出。
try catch finally的幾種結合方式:
|
|
1,
try
catch
finally
這種狀況,若是出現異常,並不處理,可是資源必定關閉,因此try finally集合只爲關閉資源。
記住:finally頗有用,主要用戶關閉資源。不管是否發生異常,資源都必須進行關閉。
System.exit(0); //退出jvm,只有這種狀況finally不執行。
當異常出現後,在子父類進行覆蓋時,有了一些新的特色:
1:當子類覆蓋父類的方法時,若是父類的方法拋出了異常,那麼子類的方法要麼不拋出異常要麼拋出父類異常或者該異常的子類,不能拋出其餘異常。
2:若是父類拋出了多個異常,那麼子類在覆蓋時只能拋出父類的異常的子集。
注意:
若是父類或者接口中的方法沒有拋出過異常,那麼子類是不能夠拋出異常的,若是子類的覆蓋的方法中出現了異常,只能try不能throws。
若是這個異常子類沒法處理,已經影響了子類方法的具體運算,這時能夠在子類方法中,經過throw拋出RuntimeException異常或者其子類,這樣,子類的方法上是不須要throws聲明的。
常見異常:
一、腳標越界異常(IndexOutOfBoundsException)包括數組、字符串;
空指針異常(NullPointerException)
二、類型轉換異常:ClassCastException
三、沒有這個元素異常:NullPointerException
四、不支持操做異常;
異常要儘可能避免,若是避免不了,須要預先給出處理方式。好比家庭備藥,好比滅火器。
-----------------------------------------------------------------------------------------------
包:定義包用package關鍵字。
1:對類文件進行分類管理。
2:給類文件提供多層名稱空間。
若是生成的包不在當前目錄下,須要最好執行classpath,將包所在父目錄定義到classpath變量中便可。
通常在定義包名時,由於包的出現是爲了區分重名的類。因此包名要儘可能惟一。怎麼保證惟一性呢?可使用url域名來進行包名稱的定義。
package pack;//定義了一個包,名稱爲pack。 注意:包名的寫法規範:全部字母都小寫。
//package cn.itcast.pack.demo;
類的全名稱是 包名.類名
編譯命令:javac –d 位置(.當前路徑) java源文件 (就能夠自動生成包)
包是一種封裝形式,用於封裝類,想要被包之外的程序訪問,該類必須public;
類中的成員,若是被包之外訪問,也必須public;
包與包之間訪問可使用的權限有兩種:
1:public
2:protected:只能是不一樣包中的子類可使用的權限。
總結java中的四種權限:
範圍 public protected default private
同一個類中 ok ok ok ok
同一包中 ok ok ok
子類 ok
不一樣包中 ok
-----------------------------------------------------------------------------------------------
Import - 導入:類名稱變長,寫起來很麻煩。爲了簡化,使用了一個關鍵字:import,可使用這個關鍵字導入指定包中的類。記住:實際開發時,到的哪一個類就導入哪一個類,不建議使用*.
import packa.*;//這個僅僅是導入了packa當前目錄下的全部的類。不包含子包。
import packa.abc.*;//導入了packa包中的子包abc下的當前的全部類。
若是導入的兩個包中存在着相同名稱的類。這時若是用到該類,必須在代碼中指定包名。
常見的軟件包:
java.lang : language java的核心包,Object System String Throwable jdk1.2版本後,該包中的類自動被導入。
java.awt : 定義的都是用於java圖形界面開發的對象。
javax.swing: 提供全部的windows桌面應用程序包括的控件,好比:Frame , Dialog, Table, List 等等,就是java的圖形界面庫。
java.net : 用於java網絡編程方面的對象都在該包中。
java.io : input output 用於操做設備上數據的對象都在該包中。好比:讀取硬盤數據,往硬盤寫入數據。
java.util : java的工具包,時間對象,集合框架。
java.applet: application+let 客戶端java小程序。server+let --> servlet 服務端java小程序。
jar :java的壓縮包,主要用於存儲類文件,或者配置文件等。
命令格式:jar –cf 包名.jar 包目錄
解壓縮:jar –xvf 包名.jar
將jar包目錄列表重定向到一個文件中:jar –tf 包名.jar> c:\1.txt
-----------------------------------------------------------------------------------------------
多線程:★★★★
進程:正在進行中的程序。其實進程就是一個應用程序運行時的內存分配空間。
線程:其實就是進程中一個程序執行控制單元,一條執行路徑。進程負責的是應用程序的空間的標示。線程負責的是應用程序的執行順序。
一個進程至少有一個線程在運行,當一個進程中出現多個線程時,就稱這個應用程序是多線程應用程序,每一個線程在棧區中都有本身的執行空間,本身的方法區、本身的變量。
jvm在啓動的時,首先有一個主線程,負責程序的執行,調用的是main函數。主線程執行的代碼都在main方法中。
當產生垃圾時,收垃圾的動做,是不須要主線程來完成,由於這樣,會出現主線程中的代碼執行會中止,會去運行垃圾回收器代碼,效率較低,因此由單獨一個線程來負責垃圾回收。
隨機性的原理:由於cpu的快速切換形成,哪一個線程獲取到了cpu的執行權,哪一個線程就執行。
返回當前線程的名稱:Thread.currentThread().getName()
線程的名稱是由:Thread-編號定義的。編號從0開始。
線程要運行的代碼都統一存放在了run方法中。
線程要運行必需要經過類中指定的方法開啓。start方法。(啓動後,就多了一條執行路徑)
start方法:1)、啓動了線程;2)、讓jvm調用了run方法。
建立線程的第一種方式:繼承Thread ,由子類複寫run方法。
步驟:
1,定義類繼承Thread類;
2,目的是複寫run方法,將要讓線程運行的代碼都存儲到run方法中;
3,經過建立Thread類的子類對象,建立線程對象;
4,調用線程的start方法,開啓線程,並執行run方法。
線程狀態:
被建立:start()
運行:具有執行資格,同時具有執行權;
凍結:sleep(time),wait()—notify()喚醒;線程釋放了執行權,同時釋放執行資格;
臨時阻塞狀態:線程具有cpu的執行資格,沒有cpu的執行權;
消亡:stop()
建立線程的第二種方式:實現一個接口Runnable。
步驟:
1,定義類實現Runnable接口。
2,覆蓋接口中的run方法(用於封裝線程要運行的代碼)。
3,經過Thread類建立線程對象;
4,將實現了Runnable接口的子類對象做爲實際參數傳遞給Thread類中的構造函數。
爲何要傳遞呢?由於要讓線程對象明確要運行的run方法所屬的對象。
5,調用Thread對象的start方法。開啓線程,並運行Runnable接口子類中的run方法。
Ticket t = new Ticket();
/*
直接建立Ticket對象,並非建立線程對象。
由於建立對象只能經過new Thread類,或者new Thread類的子類才能夠。
因此最終想要建立線程。既然沒有了Thread類的子類,就只能用Thread類。
*/
Thread t1 = new Thread(t); //建立線程。
/*
只要將t做爲Thread類的構造函數的實際參數傳入便可完成線程對象和t之間的關聯
爲何要將t傳給Thread類的構造函數呢?其實就是爲了明確線程要運行的代碼run方法。
*/
t1.start();
爲何要有Runnable接口的出現?
1:經過繼承Thread類的方式,能夠完成多線程的創建。可是這種方式有一個侷限性,若是一個類已經有了本身的父類,就不能夠繼承Thread類,由於java單繼承的侷限性。
但是該類中的還有部分代碼須要被多個線程同時執行。這時怎麼辦呢?
只有對該類進行額外的功能擴展,java就提供了一個接口Runnable。這個接口中定義了run方法,其實run方法的定義就是爲了存儲多線程要運行的代碼。
因此,一般建立線程都用第二種方式。
由於實現Runnable接口能夠避免單繼承的侷限性。
2:實際上是將不一樣類中須要被多線程執行的代碼進行抽取。將多線程要運行的代碼的位置單獨定義到接口中。爲其餘類進行功能擴展提供了前提。
因此Thread類在描述線程時,內部定義的run方法,也來自於Runnable接口。
實現Runnable接口能夠避免單繼承的侷限性。並且,繼承Thread,是能夠對Thread類中的方法,進行子類複寫的。可是不須要作這個複寫動做的話,只爲定義線程代碼存放位置,實現Runnable接口更方便一些。因此Runnable接口將線程要執行的任務封裝成了對象。
-------------------------------------------------------
//面試
new Thread(new Runnable(){ //匿名
public void run(){
System.out.println("runnable run");
}
})
{
public void run(){
System.out.println("subthread run");
}
}.start(); //結果:subthread run
---------------------------------------------------------
Try {
Thread.sleep(10);
}catch(InterruptedException e){}// 當刻意讓線程稍微停一下,模擬cpu 切換狀況。
多線程安全問題的緣由:
經過圖解:發現一個線程在執行多條語句時,並運算同一個數據時,在執行過程當中,其餘線程參與進來,並操做了這個數據。致使到了錯誤數據的產生。
涉及到兩個因素:
1,多個線程在操做共享數據。
2,有多條語句對共享數據進行運算。
緣由:這多條語句,在某一個時刻被一個線程執行時,尚未執行完,就被其餘線程執行了。
解決安全問題的原理:
只要將操做共享數據的語句在某一時段讓一個線程執行完,在執行過程當中,其餘線程不能進來執行就能夠解決這個問題。
如何進行多句操做共享數據代碼的封裝呢?
java中提供了一個解決方式:就是同步代碼塊。
格式:
synchronized(對象) { // 任意對象均可以。這個對象就是鎖。
須要被同步的代碼;
}
---------------------------------------------------------------
同步:★★★★★
好處:解決了線程安全問題。
弊端:相對下降性能,由於判斷鎖須要消耗資源,產生了死鎖。
定義同步是有前提的:
1,必需要有兩個或者兩個以上的線程,才須要同步。
2,多個線程必須保證使用的是同一個鎖。
同步的第二種表現形式:
同步函數:其實就是將同步關鍵字定義在函數上,讓函數具有了同步性。
同步函數是用的哪一個鎖呢?
經過驗證,函數都有本身所屬的對象this,因此同步函數所使用的鎖就是this鎖。
當同步函數被static修飾時,這時的同步用的是哪一個鎖呢?
靜態函數在加載時所屬於類,這時有可能尚未該類產生的對象,可是該類的字節碼文件加載進內存就已經被封裝成了對象,這個對象就是該類的字節碼文件對象。
因此靜態加載時,只有一個對象存在,那麼靜態同步函數就使用的這個對象。
這個對象就是 類名.class
同步代碼塊和同步函數的區別?
同步代碼塊使用的鎖能夠是任意對象。
同步函數使用的鎖是this,靜態同步函數的鎖是該類的字節碼文件對象。
在一個類中只有一個同步,可使用同步函數。若是有多同步,必須使用同步代碼塊,來肯定不一樣的鎖。因此同步代碼塊相對靈活一些。
-------------------------------------------------------
★考點問題:請寫一個延遲加載的單例模式?寫懶漢式;當出現多線程訪問時怎麼解決?加同步,解決安全問題;效率高嗎?不高;怎樣解決?經過雙重判斷的形式解決。
//懶漢式:延遲加載方式。
當多線程訪問懶漢式時,由於懶漢式的方法內對共性數據進行多條語句的操做。因此容易出現線程安全問題。爲了解決,加入同步機制,解決安全問題。可是卻帶來了效率下降。
爲了效率問題,經過雙重判斷的形式解決。
class Single{
private static Single s = null;
private Single(){}
public static Single getInstance(){ //鎖是誰?字節碼文件對象;
if(s == null){
synchronized(Single.class){
if(s == null)
s = new Single();
}
}
return s;
}
}
---------------------------------------------------------
同步死鎖:一般只要將同步進行嵌套,就能夠看到現象。同步函數中有同步代碼塊,同步代碼塊中還有同步函數。
線程間通訊:思路:多個線程在操做同一個資源,可是操做的動做卻不同。
1:將資源封裝成對象。
2:將線程執行的任務(任務其實就是run方法。)也封裝成對象。
等待喚醒機制:涉及的方法:
wait:將同步中的線程處於凍結狀態。釋放了執行權,釋放了資格。同時將線程對象存儲到線程池中。
notify:喚醒線程池中某一個等待線程。
notifyAll:喚醒的是線程池中的全部線程。
注意:
1:這些方法都須要定義在同步中。
2:由於這些方法必需要標示所屬的鎖。
你要知道 A鎖上的線程被wait了,那這個線程就至關於處於A鎖的線程池中,只能A鎖的notify喚醒。
3:這三個方法都定義在Object類中。爲何操做線程的方法定義在Object類中?
由於這三個方法都須要定義同步內,並標示所屬的同步鎖,既然被鎖調用,而鎖又能夠是任意對象,那麼能被任意對象調用的方法必定定義在Object類中。
wait和sleep區別: 分析這兩個方法:從執行權和鎖上來分析:
wait:能夠指定時間也能夠不指定時間。不指定時間,只能由對應的notify或者notifyAll來喚醒。
sleep:必須指定時間,時間到自動從凍結狀態轉成運行狀態(臨時阻塞狀態)。
wait:線程會釋放執行權,並且線程會釋放鎖。
Sleep:線程會釋放執行權,但不是不釋放鎖。
線程的中止:經過stop方法就能夠中止線程。可是這個方式過期了。
中止線程:原理就是:讓線程運行的代碼結束,也就是結束run方法。
怎麼結束run方法?通常run方法裏確定定義循環。因此只要結束循環便可。
第一種方式:定義循環的結束標記。
第二種方式:若是線程處於了凍結狀態,是不可能讀到標記的,這時就須要經過Thread類中的interrupt方法,將其凍結狀態強制清除。讓線程恢復具有執行資格的狀態,讓線程能夠讀到標記,並結束。
---------< java.lang.Thread> ----------
interrupt():中斷線程。
setPriority(int newPriority):更改線程的優先級。
getPriority():返回線程的優先級。
toString():返回該線程的字符串表示形式,包括線程名稱、優先級和線程組。
Thread.yield():暫停當前正在執行的線程對象,並執行其餘線程。
setDaemon(true):將該線程標記爲守護線程或用戶線程。將該線程標記爲守護線程或用戶線程。當正在運行的線程都是守護線程時,Java 虛擬機退出。該方法必須在啓動線程前調用。
join:臨時加入一個線程的時候可使用join方法。
當A線程執行到了B線程的join方式。A線程處於凍結狀態,釋放了執行權,B開始執行。A何時執行呢?只有當B線程運行結束後,A才從凍結狀態恢復運行狀態執行。
-----------------------------------------------------------
Lock接口:多線程在JDK1.5版本升級時,推出一個接口Lock接口。
解決線程安全問題使用同步的形式,(同步代碼塊,要麼同步函數)其實最終使用的都是鎖機制。
到了後期版本,直接將鎖封裝成了對象。線程進入同步就是具有了鎖,執行完,離開同步,就是釋放了鎖。
在後期對鎖的分析過程當中,發現,獲取鎖,或者釋放鎖的動做應該是鎖這個事物更清楚。因此將這些動做定義在了鎖當中,並把鎖定義成對象。
因此同步是隱示的鎖操做,而Lock對象是顯示的鎖操做,它的出現就替代了同步。
在以前的版本中使用Object類中wait、notify、notifyAll的方式來完成的。那是由於同步中的鎖是任意對象,因此操做鎖的等待喚醒的方法都定義在Object類中。
而如今鎖是指定對象Lock。因此查找等待喚醒機制方式須要經過Lock接口來完成。而Lock接口中並無直接操做等待喚醒的方法,而是將這些方式又單獨封裝到了一個對象中。這個對象就是Condition,將Object中的三個方法進行單獨的封裝。並提供了功能一致的方法 await()、signal()、signalAll()體現新版本對象的好處。
< java.util.concurrent.locks >Condition接口:await()、signal()、signalAll();
--------------------------------------------------------
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
}
finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
}
finally {
lock.unlock();
}
}
}
------------------------------------------------------------------------------------------------
API:(Application Programming Interface,應用程序編程接口)是一些預先定義的函數,目的是提供應用程序與開發人員基於某軟件或硬件的以訪問一組例程的能力,而又無需訪問源碼,或理解內部工做機制的細節。
--< java.lang> -- String字符串:★★★☆
java中用String類進行描述。對字符串進行了對象的封裝。這樣的好處是能夠對字符串這種常見數據進行方便的操做。對象封裝後,能夠定義N多屬性和行爲。
如何定義字符串對象呢?String s = "abc";只要是雙引號引發的數據都是字符串對象。
特色:字符串一旦被初始化,就不能夠被改變,存放在方法區中的常量池中。
------------------------------------------------------
String s1 = "abc"; // s1指向的內存中只有一個對象abc。
String s2 = new String("abc"); // s2指向的內容中有兩個對象abc、new 。
System.out.println(s1==s2);//false
System.out.println(s1.equals(s2));//true ,字符串中equals比較的是字符串內容是否相同。
-------------------------------------------------------
字符串的方法:
1:構造方法:將字節數組或者字符數組轉成字符串。
String s1 = new String();//建立了一個空內容的字符串。
String s2 = null;//s2沒有任何對象指向,是一個null常量值。
String s3 = "";//s3指向一個具體的字符串對象,只不過這個字符串中沒有內容。
//通常在定義字符串時,不用new。
String s4 = new String("abc");
String s5 = "abc"; 通常用此寫法
new String(char[]);//將字符數組轉成字符串。
new String(char[],offset,count);//將字符數組中的一部分轉成字符串。
2:通常方法:
按照面向對象的思想:
2.1 獲取:
2.1.1:獲取字符串的長度。length();
2.1.2:指定位置的字符。char charAt(int index);
2.1.3:獲取指定字符的位置。若是不存在返回-1,因此能夠經過返回值-1來判斷某一個字符不存在的狀況。
int indexOf(int ch);//返回第一次找到的字符角標
int indexOf(int ch,int fromIndex); //返回從指定位置開始第一次找到的角標
int indexOf(String str); //返回第一次找到的字符串角標
int indexOf(String str,int fromIndex);
int lastIndexOf(int ch);
int lastIndexOf(int ch,int fromIndex);
int lastIndexOf(String str);
int lastIndexOf(String str,int fromIndex);
2.1.4:獲取子串。
String substring(int start);//從start位開始,到length()-1爲止.
String substring(int start,int end);//從start開始到end爲止。//包含start位,不包含end位。
substring(0,str.length());//獲取整串
2.2 判斷:
2.2.1:字符串中包含指定的字符串嗎?
boolean contains(String substring);
2.2.2:字符串是否以指定字符串開頭啊?
boolean startsWith(string);
2.2.3:字符串是否以指定字符串結尾啊?
boolean endsWith(string);
2.2.4:判斷字符串是否相同
boolean equals(string);//覆蓋了Object中的方法,判斷字符串內容是否相同。
2.2.5:判斷字符串內容是否相同,忽略大小寫。
boolean equalsIgnoreCase(string) ;
2.3 轉換:
2.3.1:經過構造函數能夠將字符數組或者字節數組轉成字符串。
2.3.2:能夠經過字符串中的靜態方法,將字符數組轉成字符串。
static String copyValueOf(char[] );
static String copyValueOf(char[],int offset,int count);
static String valueOf(char[]);
static String valueOf(char[],int offset,int count);
2.3.3:將基本數據類型或者對象轉成字符串。
static String valueOf(char);
static String valueOf(boolean);
static String valueOf(double);
static String valueOf(float);
static String valueOf(int);
static String valueOf(long);
static String valueOf(Object);
2.3.4:將字符串轉成大小寫。
String toLowerCase();
String toUpperCase();
2.3.5:將字符串轉成數組。
char[] toCharArray();//轉成字符數組。
byte[] getBytes();//能夠加入編碼表。轉成字節數組。
2.3.6:將字符串轉成字符串數組。切割方法。
String[] split(分割的規則-字符串);
2.3.7:將字符串進行內容替換。注意:修改後變成新字符串,並非將原字符串直接修改。
String replace(oldChar,newChar);
String replace(oldstring,newstring);
2.3.8: String concat(string); //對字符串進行追加。
String trim();//去除字符串兩端的空格
int compareTo();//若是參數字符串等於此字符串,則返回值 0;若是此字符串按字典順序小於字符串參數,則返回一個小於 0 的值;若是此字符串按字典順序大於字符串參數,則返回一個大於 0 的值。
------------------------------------------------------------------------------------------------
--< java.lang> -- StringBuffer字符串緩衝區:★★★☆
構造一個其中不帶字符的字符串緩衝區,初始容量爲 16 個字符。
特色:
1:能夠對字符串內容進行修改。
2:是一個容器。
3:是可變長度的。
4:緩衝區中能夠存儲任意類型的數據。
5:最終須要變成字符串。
容器一般具有一些固定的方法:
1,添加。
StringBuffer append(data):在緩衝區中追加數據。追加到尾部。
StringBuffer insert(index,data):在指定位置插入數據。
2,刪除。
StringBuffer delete(start,end);刪除從start至end-1範圍的元素
StringBuffer deleteCharAt(index);刪除指定位置的元素
//sb.delete(0,sb.length());//清空緩衝區。
3,修改。
StringBuffer replace(start,end,string);將start至end-1替換成string
void setCharAt(index,char);替換指定位置的字符
void setLength(len);將原字符串置爲指定長度的字符串
4,查找。(查不到返回-1)
int indexOf(string); 返回指定子字符串在此字符串中第一次出現處的索引。
int indexOf(string,int fromIndex);從指定位置開始查找字符串
int lastIndexOf(string); 返回指定子字符串在此字符串中最右邊出現處的索引。
int lastIndexOf(string,int fromIndex); 從指定的索引開始反向搜索
5,獲取子串。
string substring(start); 返回start到結尾的子串
string substring(start,end); 返回start至end-1的子串
6,反轉。
StringBuffer reverse();字符串反轉
------------------------------------------------------------------------------------------------
--< java.lang> -- StringBuilder字符串緩衝區:★★★☆
JDK1.5出現StringBuiler;構造一個其中不帶字符的字符串生成器,初始容量爲 16 個字符。該類被設計用做 StringBuffer 的一個簡易替換,用在字符串緩衝區被單個線程使用的時候(這種狀況很廣泛)。
方法和StringBuffer同樣;
StringBuffer 和 StringBuilder 的區別:
StringBuffer線程安全。
StringBuilder線程不安全。
單線程操做,使用StringBuilder 效率高。
多線程操做,使用StringBuffer 安全。
---------------------------------------------------------
StringBuilder sb = new StringBuilder("abcdefg");
sb.append("ak"); //abcdefgak
sb.insert(1,"et");//aetbcdefg
sb.deleteCharAt(2);//abdefg
sb.delete(2,4);//abefg
sb.setLength(4);//abcd
sb.setCharAt(0,'k');//kbcdefg
sb.replace(0,2,"hhhh");//hhhhcdefg
//想要使用緩衝區,先要創建對象。
StringBuffer sb = new StringBuffer();
sb.append(12).append("haha");//方法調用鏈。
String s = "abc"+4+'q';
s = new StringBuffer().append("abc").append(4).append('q').toString();
---------------------------------------------------------
class Test{
public static void main(String[] args) {
String s1 = "java";
String s2 = "hello";
method_1(s1,s2);
System.out.println(s1+"...."+s2); //java....hello
StringBuilder s11 = new StringBuilder("java");
StringBuilder s22 = new StringBuilder("hello");
method_2(s11,s22);
System.out.println(s11+"-----"+s22); //javahello-----hello
}
public static void method_1(String s1,String s2){
s1.replace('a','k');
s1 = s2;
}
public static void method_2(StringBuilder s1,StringBuilder s2){
s1.append(s2);
s1 = s2;
}
}
---------------------------------------------------------
基本數據類型對象包裝類:是按照面向對象思想將基本數據類型封裝成了對象。
好處:
1:能夠經過對象中的屬性和行爲操做基本數據。
2:能夠實現基本數據類型和字符串之間的轉換。
關鍵字 對應的類名
byte Byte
short Short paserShort(numstring);
int Integer 靜態方法:parseInt(numstring)
long Long
float Float
double Double
char Character
Boolean Boolean
基本數據類型對象包裝類:都有 XXX parseXXX 方法
只有一個類型沒有parse方法:Character ;
--------------------------------------------------------
Integer對象: ★★★☆
數字格式的字符串轉成基本數據類型的方法:
1:將該字符串封裝成了Integer對象,並調用對象的方法intValue();
2:使用Integer.parseInt(numstring):不用創建對象,直接類名調用;
將基本類型轉成字符串:
1:Integer中的靜態方法 String toString(int);
2:int+"";
將一個十進制整數轉成其餘進制:
轉成二進制:toBinaryString
轉成八進制:toOctalString
轉成十六進制:toHexString
toString(int num,int radix);
將其餘進制轉換十進制:
parseInt(string,radix); //將給定的數轉成指定的基數進制;
在jdk1.5版本後,對基本數據類型對象包裝類進行升級。在升級中,使用基本數據類型對象包裝類能夠像使用基本數據類型同樣,進行運算。
Integer i = new Integer(4); //1.5版本以前的寫法;
Integer i = 4; //自動裝箱,1.5版本後的寫法;
i = i + 5;
//i對象是不能直接和5相加的,其實底層先將i轉成int類型,在和5相加。而轉成int類型的操做是隱式的。自動拆箱:拆箱的原理就是i.intValue();i+5運算完是一個int整數。如何賦值給引用類型i呢?其實有對結果進行裝箱。
Integer c = 127;
Integer d = 127;
System.out.println(c = = d); //true
//在裝箱時,若是數值在byte範圍以內,那麼數值相同,不會產生新的對象,也就是說多個數值相同的引用指向的是同一個對象。
------------------------------------------------------------------------------------------------
集合框架:★★★★★,用於存儲數據的容器。
特色:
1:對象封裝數據,對象多了也須要存儲。集合用於存儲對象。
2:對象的個數肯定可使用數組,可是不肯定怎麼辦?能夠用集合。由於集合是可變長度的。
集合和數組的區別:
1:數組是固定長度的;集合可變長度的。
2:數組能夠存儲基本數據類型,也能夠存儲引用數據類型;集合只能存儲引用數據類型。
3:數組存儲的元素必須是同一個數據類型;集合存儲的對象能夠是不一樣數據類型。
數據結構:就是容器中存儲數據的方式。
對於集合容器,有不少種。由於每個容器的自身特色不一樣,其實原理在於每一個容器的內部數據結構不一樣。
集合容器在不斷向上抽取過程當中。出現了集合體系。
在使用一個體系時,原則:參閱頂層內容。創建底層對象。
------------------------------------------------------------
--< java.util> -- Collection接口:
Collection:
|--List:有序(元素存入集合的順序和取出的順序一致),元素都有索引。元素能夠重複。
|--Set:無序(存入和取出順序有可能不一致),不能夠存儲重複元素。必須保證元素惟一性。
1,添加:
add(object):添加一個元素
addAll(Collection) :添加一個集合中的全部元素。
2,刪除:
clear():將集合中的元素全刪除,清空集合。
remove(obj) :刪除集合中指定的對象。注意:刪除成功,集合的長度會改變。
removeAll(collection) :刪除部分元素。部分元素和傳入Collection一致。
3,判斷:
boolean contains(obj) :集合中是否包含指定元素 。
boolean containsAll(Collection) :集合中是否包含指定的多個元素。
boolean isEmpty():集合中是否有元素。
4,獲取:
int size():集合中有幾個元素。
5,取交集:
boolean retainAll(Collection) :對當前集合中保留和指定集合中的相同的元素。若是兩個集合元素相同,返回flase;若是retainAll修改了當前集合,返回true。
6,獲取集合中全部元素:
Iterator iterator():迭代器
7,將集合變成數組:
toArray();
------------------------------------------------------------
--< java.util> -- Iterator接口:
迭代器:是一個接口。做用:用於取集合中的元素。
|
hasNext |
next |
|
|
remove |
每個集合都有本身的數據結構,都有特定的取出本身內部元素的方式。爲了便於操做全部的容器,取出元素。將容器內部的取出方式按照一個統一的規則向外提供,這個規則就是Iterator接口。
也就說,只要經過該接口就能夠取出Collection集合中的元素,至於每個具體的容器依據本身的數據結構,如何實現的具體取出細節,這個不用關心,這樣就下降了取出元素和具體集合的耦合性。
Iterator it = coll.iterator();//獲取容器中的迭代器對象,至於這個對象是是什麼不重要。這對象確定符合一個規則Iterator接口。
-----------------------------------------------------------------------------
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add("abc0");
coll.add("abc1");
coll.add("abc2");
//--------------方式1----------------------
Iterator it = coll.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//---------------方式2用此種----------------------
for(Iterator it = coll.iterator();it.hasNext(); ){
System.out.println(it.next());
}
}
-----------------------------------------------------------------------------
--< java.util> -- List接口:
List自己是Collection接口的子接口,具有了Collection的全部方法。如今學習List體系特有的共性方法,查閱方法發現List的特有方法都有索引,這是該集合最大的特色。
List:有序(元素存入集合的順序和取出的順序一致),元素都有索引。元素能夠重複。
|--ArrayList:底層的數據結構是數組,線程不一樣步,ArrayList替代了Vector,查詢元素的速度很是快。
|--LinkedList:底層的數據結構是鏈表,線程不一樣步,增刪元素的速度很是快。
|--Vector:底層的數據結構就是數組,線程同步的,Vector不管查詢和增刪都巨慢。
1,添加:
add(index,element) :在指定的索引位插入元素。
addAll(index,collection) :在指定的索引位插入一堆元素。
2,刪除:
remove(index) :刪除指定索引位的元素。 返回被刪的元素。
3,獲取:
Object get(index) :經過索引獲取指定元素。
int indexOf(obj) :獲取指定元素第一次出現的索引位,若是該元素不存在返回-1;
因此,經過-1,能夠判斷一個元素是否存在。
int lastIndexOf(Object o) :反向索引指定元素的位置。
List subList(start,end) :獲取子列表。
4,修改:
Object set(index,element) :對指定索引位進行元素的修改。
5,獲取全部元素:
ListIterator listIterator():list集合特有的迭代器。
List集合支持對元素的增、刪、改、查。
List集合由於角標有了本身的獲取元素的方式: 遍歷。
for(int x=0; x<list.size(); x++){
sop("get:"+list.get(x));
}
在進行list列表元素迭代的時候,若是想要在迭代過程當中,想要對元素進行操做的時候,好比知足條件添加新元素。會發生.ConcurrentModificationException併發修改異常。
致使的緣由是:
集合引用和迭代器引用在同時操做元素,經過集合獲取到對應的迭代器後,在迭代中,進行集合引用的元素添加,迭代器並不知道,因此會出現異常狀況。
如何解決呢?
既然是在迭代中對元素進行操做,找迭代器的方法最爲合適.但是Iterator中只有hasNext,next,remove方法.經過查閱的它的子接口,ListIterator,發現該列表迭代器接口具有了對元素的增、刪、改、查的動做。
ListIterator是List集合特有的迭代器。
ListIterator it = list.listIterator;//取代Iterator it = list.iterator;
方法摘要 |
|
|
|
|
hasNext |
|
hasPrevious |
next |
|
|
nextIndex |
previous |
|
|
previousIndex |
|
remove |
|
可變長度數組的原理:
當元素超出數組長度,會產生一個新數組,將原數組的數據複製到新數組中,再將新的元素添加到新數組中。
ArrayList:是按照原數組的50%延長。構造一個初始容量爲 10 的空列表。
Vector:是按照原數組的100%延長。
注意:對於list集合,底層判斷元素是否相同,其實用的是元素自身的equals方法完成的。因此建議元素都要複寫equals方法,創建元素對象本身的比較相同的條件依據。
LinkedList:的特有方法。
addFirst();
addLast();
在jdk1.6之後。
offerFirst();
offerLast();
getFirst():獲取鏈表中的第一個元素。若是鏈表爲空,拋出NoSuchElementException;
getLast();
在jdk1.6之後。
peekFirst();獲取鏈表中的第一個元素。若是鏈表爲空,返回null。
peekLast();
removeFirst():獲取鏈表中的第一個元素,可是會刪除鏈表中的第一個元素。若是鏈表爲空,拋出NoSuchElementException
removeLast();
在jdk1.6之後。
pollFirst();獲取鏈表中的第一個元素,可是會刪除鏈表中的第一個元素。若是鏈表爲空,返回null。
pollLast();
------------------------------------------------------------
--< java.util> -- Set接口:
Set接口中的方法和Collection中方法一致的。Set接口取出方式只有一種,迭代器。
|--HashSet:底層數據結構是哈希表,線程是不一樣步的。無序,高效;
HashSet集合保證元素惟一性:經過元素的hashCode方法,和equals方法完成的。
當元素的hashCode值相同時,才繼續判斷元素的equals是否爲true。
若是爲true,那麼視爲相同元素,不存。若是爲false,那麼存儲。
若是hashCode值不一樣,那麼不判斷equals,從而提升對象比較的速度。
|--LinkedHashSet:有序,hashset的子類。
|--TreeSet:對Set集合中的元素的進行指定順序的排序。不一樣步。TreeSet底層的數據結構就是二叉樹。
哈希表的原理:
1,對對象元素中的關鍵字(對象中的特有數據),進行哈希算法的運算,並得出一個具體的算法值,這個值 稱爲哈希值。
2,哈希值就是這個元素的位置。
3,若是哈希值出現衝突,再次判斷這個關鍵字對應的對象是否相同。若是對象相同,就不存儲,由於元素重複。若是對象不一樣,就存儲,在原來對象的哈希值基礎 +1順延。
4,存儲哈希值的結構,咱們稱爲哈希表。
5,既然哈希表是根據哈希值存儲的,爲了提升效率,最好保證對象的關鍵字是惟一的。
這樣能夠儘可能少的判斷關鍵字對應的對象是否相同,提升了哈希表的操做效率。
對於ArrayList集合,判斷元素是否存在,或者刪元素底層依據都是equals方法。
對於HashSet集合,判斷元素是否存在,或者刪除元素,底層依據的是hashCode方法和equals方法。
TreeSet:
用於對Set集合進行元素的指定順序排序,排序須要依據元素自身具有的比較性。
若是元素不具有比較性,在運行時會發生ClassCastException異常。
因此須要元素實現Comparable接口,強制讓元素具有比較性,複寫compareTo方法。
依據compareTo方法的返回值,肯定元素在TreeSet數據結構中的位置。
TreeSet方法保證元素惟一性的方式:就是參考比較方法的結果是否爲0,若是return 0,視爲兩個對象重複,不存。
注意:在進行比較時,若是判斷元素不惟一,好比,同姓名,同年齡,才視爲同一我的。
在判斷時,須要分主要條件和次要條件,當主要條件相同時,再判斷次要條件,按照次要條件排序。
TreeSet集合排序有兩種方式,Comparable和Comparator區別:
1:讓元素自身具有比較性,須要元素對象實現Comparable接口,覆蓋compareTo方法。
2:讓集合自身具有比較性,須要定義一個實現了Comparator接口的比較器,並覆蓋compare方法,並將該類對象做爲實際參數傳遞給TreeSet集合的構造函數。
第二種方式較爲靈活。
------------------------------------------------------------
Map集合:
|--Hashtable:底層是哈希表數據結構,是線程同步的。不能夠存儲null鍵,null值。
|--HashMap:底層是哈希表數據結構,是線程不一樣步的。能夠存儲null鍵,null值。替代了Hashtable.
|--TreeMap:底層是二叉樹結構,能夠對map集合中的鍵進行指定順序的排序。
Map集合存儲和Collection有着很大不一樣:
Collection一次存一個元素;Map一次存一對元素。
Collection是單列集合;Map是雙列集合。
Map中的存儲的一對元素:一個是鍵,一個是值,鍵與值之間有對應(映射)關係。
特色:要保證map集合中鍵的惟一性。
1,添加。
put(key,value):當存儲的鍵相同時,新的值會替換老的值,並將老值返回。若是鍵沒有重複,返回null。
void putAll(Map);
2,刪除。
void clear():清空
value remove(key) :刪除指定鍵。
3,判斷。
boolean isEmpty():
boolean containsKey(key):是否包含key
boolean containsValue(value) :是否包含value
4,取出。
int size():返回長度
value get(key) :經過指定鍵獲取對應的值。若是返回null,能夠判斷該鍵不存在。固然有特殊狀況,就是在hashmap集合中,是能夠存儲null鍵null值的。
Collection values():獲取map集合中的全部的值。
5,想要獲取map中的全部元素:
原理:map中是沒有迭代器的,collection具有迭代器,只要將map集合轉成Set集合,可使用迭代器了。之因此轉成set,是由於map集合具有着鍵的惟一性,其實set集合就來自於map,set集合底層其實用的就是map的方法。
★ 把map集合轉成set的方法:
Set keySet();
Set entrySet();//取的是鍵和值的映射關係。
Entry就是Map接口中的內部接口;
爲何要定義在map內部呢?entry是訪問鍵值關係的入口,是map的入口,訪問的是map中的鍵值對。
---------------------------------------------------------
取出map集合中全部元素的方式一:keySet()方法。
能夠將map集合中的鍵都取出存放到set集合中。對set集合進行迭代。迭代完成,再經過get方法對獲取到的鍵進行值的獲取。
Set keySet = map.keySet();
Iterator it = keySet.iterator();
while(it.hasNext()) {
Object key = it.next();
Object value = map.get(key);
System.out.println(key+":"+value);
}
--------------------------------------------------------
取出map集合中全部元素的方式二:entrySet()方法。
Set entrySet = map.entrySet();
Iterator it = entrySet.iterator();
while(it.hasNext()) {
Map.Entry me = (Map.Entry)it.next();
System.out.println(me.getKey()+"::::"+me.getValue());
}
--------------------------------------------------------
使用集合的技巧:
看到Array就是數組結構,有角標,查詢速度很快。
看到link就是鏈表結構:增刪速度快,並且有特有方法。addFirst; addLast; removeFirst(); removeLast(); getFirst();getLast();
看到hash就是哈希表,就要想要哈希值,就要想到惟一性,就要想到存入到該結構的中的元素必須覆蓋hashCode,equals方法。
看到tree就是二叉樹,就要想到排序,就想要用到比較。
比較的兩種方式:
一個是Comparable:覆蓋compareTo方法;
一個是Comparator:覆蓋compare方法。
LinkedHashSet,LinkedHashMap:這兩個集合能夠保證哈希表有存入順序和取出順序一致,保證哈希表有序。
集合何時用?
當存儲的是一個元素時,就用Collection。當存儲對象之間存在着映射關係時,就使用Map集合。
保證惟一,就用Set。不保證惟一,就用List。
------------------------------------------------------------------------------------------------
Collections:它的出現給集合操做提供了更多的功能。這個類不須要建立對象,內部提供的都是靜態方法。
靜態方法:
Collections.sort(list);//list集合進行元素的天然順序排序。
Collections.sort(list,new ComparatorByLen());//按指定的比較器方法排序。
class ComparatorByLen implements Comparator<String>{
public int compare(String s1,String s2){
int temp = s1.length()-s2.length();
return temp==0?s1.compareTo(s2):temp;
}
}
Collections.max(list); //返回list中字典順序最大的元素。
int index = Collections.binarySearch(list,"zz");//二分查找,返回角標。
Collections.reverseOrder();//逆向反轉排序。
Collections.shuffle(list);//隨機對list中的元素進行位置的置換。
將非同步集合轉成同步集合的方法:Collections中的 XXX synchronizedXXX(XXX);
List synchronizedList(list);
Map synchronizedMap(map);
原理:定義一個類,將集合全部的方法加同一把鎖後返回。
Collection 和 Collections的區別:
Collections是個java.util下的類,是針對集合類的一個工具類,提供一系列靜態方法,實現對集合的查找、排序、替換、線程安全化(將非同步的集合轉換成同步的)等操做。
Collection是個java.util下的接口,它是各類集合結構的父接口,繼承於它的接口主要有Set和List,提供了關於集合的一些操做,如插入、刪除、判斷一個元素是否其成員、遍歷等。
-------------------------------------------------------
Arrays:
用於操做數組對象的工具類,裏面都是靜態方法。
asList方法:將數組轉換成list集合。
String[] arr = {"abc","kk","qq"};
List<String> list = Arrays.asList(arr);//將arr數組轉成list集合。
將數組轉換成集合,有什麼好處呢?用aslist方法,將數組變成集合;
能夠經過list集合中的方法來操做數組中的元素:isEmpty()、contains、indexOf、set;
注意(侷限性):數組是固定長度,不可使用集合對象增長或者刪除等,會改變數組長度的功能方法。好比add、remove、clear。(會報不支持操做異常UnsupportedOperationException);
若是數組中存儲的引用數據類型,直接做爲集合的元素能夠直接用集合方法操做。
若是數組中存儲的是基本數據類型,asList會將數組實體做爲集合元素存在。
集合變數組:用的是Collection接口中的方法:toArray();
若是給toArray傳遞的指定類型的數據長度小於了集合的size,那麼toArray方法,會自定再建立一個該類型的數據,長度爲集合的size。
若是傳遞的指定的類型的數組的長度大於了集合的size,那麼toArray方法,就不會建立新數組,直接使用該數組便可,並將集合中的元素存儲到數組中,其餘爲存儲元素的位置默認值null。
因此,在傳遞指定類型數組時,最好的方式就是指定的長度和size相等的數組。
將集合變成數組後有什麼好處?限定了對集合中的元素進行增刪操做,只要獲取這些元素便可。
------------------------------------------------------------------------------------------------
Jdk5.0新特性:
Collection在jdk1.5之後,有了一個父接口Iterable,這個接口的出現的將iterator方法進行抽取,提升了擴展性。
--------------------------------------------------
加強for循環:foreach語句,foreach簡化了迭代器。
格式:// 加強for循環括號裏寫兩個參數,第一個是聲明一個變量,第二個就是須要迭代的容器
for( 元素類型 變量名 : Collection集合 & 數組 ) {
…
}
高級for循環和傳統for循環的區別:
高級for循環在使用時,必需要明確被遍歷的目標。這個目標,能夠是Collection集合或者數組,若是遍歷Collection集合,在遍歷過程當中還須要對元素進行操做,好比刪除,須要使用迭代器。
若是遍歷數組,還須要對數組元素進行操做,建議用傳統for循環由於能夠定義角標經過角標操做元素。若是隻爲遍歷獲取,能夠簡化成高級for循環,它的出現爲了簡化書寫。
高級for循環能夠遍歷map集合嗎?不能夠。可是能夠將map轉成set後再使用foreach語句。
1)、做用:對存儲對象的容器進行迭代: 數組 collection map
2)、加強for循環迭代數組:
String [] arr = {"a", "b", "c"};//數組的靜態定義方式,只試用於數組首次定義的時候
for(String s : arr) {
System.out.println(s);
}
3)、單列集合 Collection:
List list = new ArrayList();
list.add("aaa");
// 加強for循環, 沒有使用泛型的集合能不能使用加強for循環迭代?能
for(Object obj : list) {
String s = (String) obj;
System.out.println(s);
}
4)、雙列集合 Map:
Map map = new HashMap();
map.put("a", "aaa");
// 傳統方式:必須掌握這種方式
Set entrys = map.entrySet(); // 1.得到全部的鍵值對Entry對象
iter = entrys.iterator(); // 2.迭代出全部的entry
while(iter.hasNext()) {
Map.Entry entry = (Entry) iter.next();
String key = (String) entry.getKey(); // 分別得到key和value
String value = (String) entry.getValue();
System.out.println(key + "=" + value);
}
// 加強for循環迭代:原則上map集合是沒法使用加強for循環來迭代的,由於加強for循環只能針對實現了Iterable接口的集合進行迭代;Iterable是jdk5中新定義的接口,就一個方法iterator方法,只有實現了Iterable接口的類,才能保證必定有iterator方法,java有這樣的限定是由於加強for循環內部仍是用迭代器實現的,而實際上,咱們能夠經過某種方式來使用加強for循環。
for(Object obj : map.entrySet()) {
Map.Entry entry = (Entry) obj; // obj 依次表示Entry
System.out.println(entry.getKey() + "=" + entry.getValue());
}
5)、集合迭代注意問題:在迭代集合的過程當中,不能對集合進行增刪操做(會報併發訪問異常);能夠用迭代器的方法進行操做(子類listIterator:有增刪的方法)。
6)、加強for循環注意問題:在使用加強for循環時,不能對元素進行賦值;
int[] arr = {1,2,3};
for(int num : arr) {
num = 0; //不能改變數組的值
}
System.out.println(arr[1]); //2
--------------------------------------------------
可變參數(...):用到函數的參數上,當要操做的同一個類型元素個數不肯定的時候,但是用這個方式,這個參數能夠接受任意個數的同一類型的數據。
和之前接收數組不同的是:
之前定義數組類型,須要先建立一個數組對象,再將這個數組對象做爲參數傳遞給函數。如今,直接將數組中的元素做爲參數傳遞便可。底層實際上是將這些元素進行數組的封裝,而這個封裝動做,是在底層完成的,被隱藏了。因此簡化了用戶的書寫,少了調用者定義數組的動做。
若是在參數列表中使用了可變參數,可變參數必須定義在參數列表結尾(也就是必須是最後一個參數,不然編譯會失敗。)。
若是要獲取多個int數的和呢?可使用將多個int數封裝到數組中,直接對數組求和便可。
---------------------------------------------------
靜態導入:導入了類中的全部靜態成員,簡化靜態成員的書寫。
import static java.util.Collections.*; //導入了Collections類中的全部靜態成員
---------------------------------------------------
枚舉:關鍵字 enum
問題:對象的某個屬性的值不能是任意的,必須爲固定的一組取值其中的某一個;
解決辦法:
1)、在setGrade方法中作判斷,不符合格式要求就拋出異常;
2)、直接限定用戶的選擇,經過自定義類模擬枚舉的方式來限定用戶的輸入,寫一個Grade類,私有構造函數,對外提供5個靜態的常量表示類的實例;
3)、jdk5中新定義了枚舉類型,專門用於解決此類問題;
4)、枚舉就是一個特殊的java類,能夠定義屬性、方法、構造函數、實現接口、繼承類;
------------------------------------------------------------------------------
自動拆裝箱:java中數據類型分爲兩種 : 基本數據類型 引用數據類型(對象)
在 java程序中全部的數據都須要當作對象來處理,針對8種基本數據類型提供了包裝類,以下:
int --> Integer
byte --> Byte
short --> Short
long --> Long
char --> Character
double --> Double
float --> Float
boolean --> Boolean
jdk5之前基本數據類型和包裝類之間須要互轉:
基本---引用 Integer x = new Integer(x);
引用---基本 int num = x.intValue();
1)、Integer x = 1; x = x + 1; 經歷了什麼過程?裝箱à 拆箱 à 裝箱;
2)、爲了優化,虛擬機爲包裝類提供了緩衝池,Integer池的大小 -128~127 一個字節的大小;
3)、String池:Java爲了優化字符串操做 提供了一個緩衝池;
----------------------------------------------------------
泛型:jdk1.5版本之後出現的一個安全機制。表現格式:<>
好處:
1:將運行時期的問題ClassCastException問題轉換成了編譯失敗,體如今編譯時期,程序員就能夠解決問題。
2:避免了強制轉換的麻煩。
只要帶有<>的類或者接口,都屬於帶有類型參數的類或者接口,在使用這些類或者接口時,必須給<>中傳遞一個具體的引用數據類型。
泛型技術:其實應用在編譯時期,是給編譯器使用的技術,到了運行時期,泛型就不存在了。
爲何? 由於泛型的擦除:也就是說,編輯器檢查了泛型的類型正確後,在生成的類文件中是沒有泛型的。
在運行時,如何知道獲取的元素類型而不用強轉呢?
泛型的補償:由於存儲的時候,類型已經肯定了是同一個類型的元素,因此在運行時,只要獲取到該元素的類型,在內部進行一次轉換便可,因此使用者不用再作轉換動做了。
何時用泛型類呢?
當類中的操做的引用數據類型不肯定的時候,之前用的Object來進行擴展的,如今能夠用泛型來表示。這樣能夠避免強轉的麻煩,並且將運行問題轉移到的編譯時期。
----------------------------------------------------------
泛型在程序定義上的體現:
//泛型類:將泛型定義在類上。
class Tool<Q> {
private Q obj;
public void setObject(Q obj) {
this.obj = obj;
}
public Q getObject() {
return obj;
}
}
//當方法操做的引用數據類型不肯定的時候,能夠將泛型定義在方法上。
public <W> void method(W w) {
System.out.println("method:"+w);
}
//靜態方法上的泛型:靜態方法沒法訪問類上定義的泛型。若是靜態方法操做的引用數據類型不肯定的時候,必需要將泛型定義在方法上。
public static< Q> void function(Q t) {
System.out.println("function:"+t);
}
//泛型接口.
interface Inter<T> {
void show(T t);
}
class InterImpl<R> implements Inter<R> {
public void show(R r) {
System.out.println("show:"+r);
}
}
------------------------------------------------------------
泛型中的通配符:能夠解決當具體類型不肯定的時候,這個通配符就是 ? ;當操做類型時,不須要使用類型的具體功能時,只使用Object類中的功能。那麼能夠用 ? 通配符來表未知類型。
泛型限定:
上限:?extends E:能夠接收E類型或者E的子類型對象。
下限:?super E:能夠接收E類型或者E的父類型對象。
上限何時用:往集合中添加元素時,既能夠添加E類型對象,又能夠添加E的子類型對象。爲何?由於取的時候,E類型既能夠接收E類對象,又能夠接收E的子類型對象。
下限何時用:當從集合中獲取元素進行操做的時候,能夠用當前元素的類型接收,也能夠用當前元素的父類型接收。
泛型的細節:
1)、泛型到底表明什麼類型取決於調用者傳入的類型,若是沒傳,默認是Object類型;
2)、使用帶泛型的類建立對象時,等式兩邊指定的泛型必須一致;
緣由:編譯器檢查對象調用方法時只看變量,然而程序運行期間調用方法時就要考慮對象具體類型了;
3)、等式兩邊能夠在任意一邊使用泛型,在另外一邊不使用(考慮向後兼容);
ArrayList<String> al = new ArrayList<Object>(); //錯
//要保證左右兩邊的泛型具體類型一致就能夠了,這樣不容易出錯。
ArrayList<? extends Object> al = new ArrayList<String>();
al.add("aa"); //錯
//由於集合具體對象中既可存儲String,也能夠存儲Object的其餘子類,因此添加具體的類型對象不合適,類型檢查會出現安全問題。 ?extends Object 表明Object的子類型不肯定,怎麼能添加具體類型的對象呢?
public static void method(ArrayList<? extends Object> al) {
al.add("abc"); //錯
//只能對al集合中的元素調用Object類中的方法,具體子類型的方法都不能用,由於子類型不肯定。
}
------------------------------------------------------------------------------------------------------------------------------------------------
API--- java.lang.System: 屬性和行爲都是靜態的。
long currentTimeMillis(); // 返回當前時間毫秒值
exit(); // 退出虛擬機
Properties getProperties() ; // 獲取當前系統的屬性信息
Properties prop = System.getProperties(); //獲取系統的屬性信息,並將這些信息存儲到Properties集合中。
System.setProperty("myname","畢老師"); //給系統屬性信息集添加具體的屬性信息
//臨時設置方式:運行jvm時,能夠經過jvm的參數進行系統屬性的臨時設置,能夠在java命令的後面加入 –D<name>=<value> 用法:java –Dmyname=小明 類名。
String name = System.getProperty("os.name");//獲取指定屬性的信息
//想要知道該系統是不是該軟件所支持的系統中的一個。
Set<String> hs = new HashSet<String>();
hs.add("Windows XP");
hs.add("Windows 7");
if(hs.contains(name))
System.out.println("能夠支持");
else
System.out.println("不支持");
--------------------------------------------------------------------------------------------------------------------
API--- java.lang.Runtime: 類中沒有構造方法,不能建立對象。
可是有非靜態方法。說明該類中應該定義好了對象,並能夠經過一個static方法獲取這個對象。用這個對象來調用非靜態方法。這個方法就是 static Runtime getRuntime();
這個Runtime其實使用單例設計模式進行設計。
class RuntimeDemo {
public static void main(String[] args) throws Exception {
Runtime r = Runtime.getRuntime();
Process p = r.exec("notepad.exe SystemDemo.java"); //運行指定的程序
Thread.sleep(4000);
p.destroy(); //殺掉進程
}
}
--------------------------------------------------------------------------------------------------------------------
API--- java.util.Math: 用於數學運算的工具類,屬性和行爲都是靜態的。該類是final不容許繼承。
static double ceil(double a) ; //返回大於指定數值的最小整數
static double floor(double a) ; //返回小於指定數值的最大整數
static long round(double a) ; //四捨五入成整數
static double pow(double a, double b) ; //a的b次冪
static double random(); //返回0~1的僞隨機數
public static void main(String[] args) {
Random r = new Random();
for(int x=0; x<10; x++) {
//double d = Math.floor(Math.random()*10+1);
//int d = (int)(Math.random()*10+1);
int d = r.nextInt(10)+1;
System.out.println(d);
}
}
--------------------------------------------------------------------------------------------------------------------
API--- java.util.Date:日期類,月份從0-11;
/*
日期對象和毫秒值之間的轉換。
1,日期對象轉成毫秒值。Date類中的getTime方法。
2,如何將獲取到的毫秒值轉成具體的日期呢?
Date類中的setTime方法。也能夠經過構造函數。
*/
//日期對象轉成毫秒值
Date d = new Date();
long time1 = d.getTime();
long time2 = System.currentTimeMillis(); / /毫秒值。
//毫秒值轉成具體的日期
long time = 1322709921312l;
Date d = new Date();
d.setTime(time);
/*
將日期字符串轉換成日期對象:使用的就是DateFormat方法中的 Date parse(String source) ;
*/
public static void method() throws Exception {
String str_time = "2011/10/25";
DateFormat df = new SimpleDateFormat("yyyy/MM/dd"); //SimpleDateFormat做爲能夠指定用戶自定義的格式來完成格式化。
Date d = df.parse(str_time);
}
/*
若是不須要使用特定的格式化風格,徹底可使用DateFormat類中的靜態工廠方法獲取具體的已經封裝好風格的對象。getDateInstance();getDateTimeInstance();
*/
Date d = new Date();
DateFormat df = DateFormat.getDateInstance(DateFormat.LONG);
df = DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG);
String str_time = df.format(d);
//將日期對象轉換成字符串的方式:DateFormat類中的format方法。
//建立日期格式對象。
DateFormat df = new SimpleDateFormat(); //該對象的創建內部會封裝一個默認的日期格式。11-12-1 下午1:48
//若是想要自定義日期格式的話。可以使用SimpleDateFormat的構造函數。將具體的格式做爲參數傳入到構造函數中。如何表示日期中年的部分呢?能夠必需要參與格式對象文檔。
df = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
//調用DateFormat中的format方法。對已有的日期對象進行格式化。
String str_time = df.format(d);
--------------------------------------------------------------------------------------------------------------------
API--- java.util. Calendar:日曆類
public static void method(){
Calendar c = Calendar.getInstance();
System.out.println(c.get(Calendar.YEAR)+"年"+(c.get(Calendar.MONTH)+1)+"月"
+getNum(c.get(Calendar.DAY_OF_MONTH))+"日"
+"星期"+getWeek(c.get(Calendar.DAY_OF_WEEK)));
}
public static String getNum(int num){
return num>9 ? num+"" : "0"+num;
}
public static String getWeek(int index){
/*
查表法:創建數據的對應關係.
最好:數據個數是肯定的,並且有對應關係。若是對應關係的一方,是數字,並且能夠做爲角標,那麼能夠經過數組來做爲表。
*/
String[] weeks = {"","日","一","二","三","四","五","六"};
return weeks[index];
}
------------------------------------------------------------------------------------------------------------------------------------------------
IO流:★★★★★,用於處理設備上數據。
流:能夠理解數據的流動,就是一個數據流。IO流最終要以對象來體現,對象都存在IO包中。
流也進行分類:
1:輸入流(讀)和輸出流(寫)。
2:由於處理的數據不一樣,分爲字節流和字符流。
字節流:處理字節數據的流對象。設備上的數據不管是圖片或者dvd,文字,它們都以二進制存儲的。二進制的最終都是以一個8位爲數據單元進行體現,因此計算機中的最小數據單元就是字節。意味着,字節流能夠處理設備上的全部數據,因此字節流同樣能夠處理字符數據。
那麼爲何要有字符流呢?由於字符每一個國家都不同,因此涉及到了字符編碼問題,那麼GBK編碼的中文用unicode編碼解析是有問題的,因此須要獲取中文字節數據的同時+ 指定的編碼表才能夠解析正確數據。爲了方便於文字的解析,因此將字節流和編碼表封裝成對象,這個對象就是字符流。只要操做字符數據,優先考慮使用字符流體系。
注意:流的操做只有兩種:讀和寫。
流的體系由於功能不一樣,可是有共性內容,不斷抽取,造成繼承體系。該體系一共有四個基類,並且都是抽象類。
字節流:InputStream OutputStream
字符流:Reader Writer
在這四個系統中,它們的子類,都有一個共性特色:子類名後綴都是父類名,前綴名都是這個子類的功能名稱。
--------------------------------------------------------------------------------------------------------------------
public static void main(String[] args) throws IOException { //讀、寫都會發生IO異常
/*
1:建立一個字符輸出流對象,用於操做文件。該對象一創建,就必須明確數據存儲位置,是一個文件。
2:對象產生後,會在堆內存中有一個實體,同時也調用了系統底層資源,在指定的位置建立了一個存儲數據的文件。
3:若是指定位置,出現了同名文件,文件會被覆蓋。
*/
FileWriter fw = new FileWriter("demo.txt"); // FileNotFoundException
/*
調用Writer類中的write方法寫入字符串。字符串並未直接寫入到目的地中,而是寫入到了流中,(實際上是寫入到內存緩衝區中)。怎麼把數據弄到文件中?
*/
fw.write("abcde");
fw.flush(); // 刷新緩衝區,將緩衝區中的數據刷到目的地文件中。
fw.close(); // 關閉流,其實關閉的就是java調用的系統底層資源。在關閉前,會先刷新該流。
}
close()和flush()的區別:
flush():將緩衝區的數據刷到目的地中後,流可使用。
close():將緩衝區的數據刷到目的地中後,流就關閉了,該方法主要用於結束調用的底層資源。這個動做必定作。
--------------------------------------------------------------------------------------------------------------------
io異常的處理方式:io必定要寫finally;
FileWriter寫入數據的細節:
1:window中的換行符:\r\n兩個符號組成。 linux:\n。
2:續寫數據,只要在構造函數中傳入新的參數true。
3:目錄分割符:window \\ /
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("demo.txt",true);
fw.write("abcde");
}
catch (IOException e ){
System.out.println(e.toString()+"....");
}
finally{
if(fw!=null)
try{
fw.close();
}
catch (IOException e){
System.out.println("close:"+e.toString());
}
}
}
--------------------------------------------------------------------------------------------------------------------
FileReader:使用Reader體系,讀取一個文本文件中的數據。返回 -1 ,標誌讀到結尾。
import java.io.*;
class FileReaderDemo {
public static void main(String[] args) throws IOException {
/*
建立能夠讀取文本文件的流對象,FileReader讓建立好的流對象和指定的文件相關聯。
*/
FileReader fr = new FileReader("demo.txt");
int ch = 0;
while((ch = fr.read())!= -1) { //條件是沒有讀到結尾
System.out.println((char)ch); //調用讀取流的read方法,讀取一個字符。
}
fr.close();
}
}
--------------------------------------------------------------------------------------------------------------------
讀取數據的第二種方式:第二種方式較爲高效,自定義緩衝區。
import java.io.*;
class FileReaderDemo2 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt"); //建立讀取流對象和指定文件關聯。
//由於要使用read(char[])方法,將讀取到字符存入數組。因此要建立一個字符數組,通常數組的長度都是1024的整數倍。
char[] buf = new char[1024];
int len = 0;
while(( len=fr.read(buf)) != -1) {
System.out.println(new String(buf,0,len));
}
fr.close();
}
}
--------------------------------------------------------------------------------------------------------------------
IO中的使用到了一個設計模式:裝飾設計模式。
裝飾設計模式解決:對一組類進行功能的加強。
包裝:寫一個類(包裝類)對被包裝對象進行包裝;
* 一、包裝類和被包裝對象要實現一樣的接口;
* 二、包裝類要持有一個被包裝對象;
* 三、包裝類在實現接口時,大部分方法是靠調用被包裝對象來實現的,對於須要修改的方法咱們本身實現;
--------------------------------------------------------------------------------------------------------------------
字符流:
Reader:用於讀取字符流的抽象類。子類必須實現的方法只有 read(char[], int, int) 和 close()。
|---BufferedReader:從字符輸入流中讀取文本,緩衝各個字符,從而實現字符、數組和行的高效讀取。 能夠指定緩衝區的大小,或者可以使用默認的大小。大多數狀況下,默認值就足夠大了。
|---LineNumberReader:跟蹤行號的緩衝字符輸入流。此類定義了方法 setLineNumber(int) 和 getLineNumber(),它們可分別用於設置和獲取當前行號。
|---InputStreamReader:是字節流通向字符流的橋樑:它使用指定的 charset 讀取字節並將其解碼爲字符。它使用的字符集能夠由名稱指定或顯式給定,或者能夠接受平臺默認的字符集。
|---FileReader:用來讀取字符文件的便捷類。此類的構造方法假定默認字符編碼和默認字節緩衝區大小都是適當的。要本身指定這些值,能夠先在 FileInputStream 上構造一個 InputStreamReader。
|---CharArrayReader:
|---StringReader:
-------------------------------------------------
Writer:寫入字符流的抽象類。子類必須實現的方法僅有 write(char[], int, int)、flush() 和 close()。
|---BufferedWriter:將文本寫入字符輸出流,緩衝各個字符,從而提供單個字符、數組和字符串的高效寫入。
|---OutputStreamWriter:是字符流通向字節流的橋樑:可以使用指定的 charset 將要寫入流中的字符編碼成字節。它使用的字符集能夠由名稱指定或顯式給定,不然將接受平臺默認的字符集。
|---FileWriter:用來寫入字符文件的便捷類。此類的構造方法假定默認字符編碼和默認字節緩衝區大小都是可接受的。要本身指定這些值,能夠先在 FileOutputStream 上構造一個 OutputStreamWriter。
|---PrintWriter:
|---CharArrayWriter:
|---StringWriter:
---------------------------------
字節流:
InputStream:是表示字節輸入流的全部類的超類。
|--- FileInputStream:從文件系統中的某個文件中得到輸入字節。哪些文件可用取決於主機環境。FileInputStream 用於讀取諸如圖像數據之類的原始字節流。要讀取字符流,請考慮使用 FileReader。
|--- FilterInputStream:包含其餘一些輸入流,它將這些流用做其基本數據源,它能夠直接傳輸數據或提供一些額外的功能。
|--- BufferedInputStream:該類實現緩衝的輸入流。
|--- Stream:
|--- ObjectInputStream:
|--- PipedInputStream:
-----------------------------------------------
OutputStream:此抽象類是表示輸出字節流的全部類的超類。
|--- FileOutputStream:文件輸出流是用於將數據寫入 File
或 FileDescriptor
的輸出流。
|--- FilterOutputStream:此類是過濾輸出流的全部類的超類。
|--- BufferedOutputStream:該類實現緩衝的輸出流。
|--- PrintStream:
|--- DataOutputStream:
|--- ObjectOutputStream:
|--- PipedOutputStream:
--------------------------------
緩衝區是提升效率用的,給誰提升呢?
BufferedWriter:是給字符輸出流提升效率用的,那就意味着,緩衝區對象創建時,必需要先有流對象。明確要提升具體的流對象的效率。
FileWriter fw = new FileWriter("bufdemo.txt");
BufferedWriter bufw = new BufferedWriter(fw);//讓緩衝區和指定流相關聯。
for(int x=0; x<4; x++){
bufw.write(x+"abc");
bufw.newLine(); //寫入一個換行符,這個換行符能夠依據平臺的不一樣寫入不一樣的換行符。
bufw.flush();//對緩衝區進行刷新,可讓數據到目的地中。
}
bufw.close();//關閉緩衝區,其實就是在關閉具體的流。
-----------------------------
BufferedReader:
FileReader fr = new FileReader("bufdemo.txt");
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null){ //readLine方法返回的時候是不帶換行符的。
System.out.println(line);
}
bufr.close();
-----------------------------
//記住,只要一讀取鍵盤錄入,就用這句話。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));//輸出到控制檯
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
bufw.write(line.toUpperCase());//將輸入的字符轉成大寫字符輸出
bufw.newLine();
bufw.flush();
}
bufw.close();
bufr.close();
------------------------------
流對象:其實很簡單,就是讀取和寫入。可是由於功能的不一樣,流的體系中提供N多的對象。那麼開始時,到底該用哪一個對象更爲合適呢?這就須要明確流的操做規律。
流的操做規律:
1,明確源和目的。
數據源:就是須要讀取,可使用兩個體系:InputStream、Reader;
數據匯:就是須要寫入,可使用兩個體系:OutputStream、Writer;
2,操做的數據是不是純文本數據?
若是是:數據源:Reader
數據匯:Writer
若是不是:數據源:InputStream
數據匯:OutputStream
3,雖然肯定了一個體系,可是該體系中有太多的對象,到底用哪一個呢?
明確操做的數據設備。
數據源對應的設備:硬盤(File),內存(數組),鍵盤(System.in)
數據匯對應的設備:硬盤(File),內存(數組),控制檯(System.out)。
4,須要在基本操做上附加其餘功能嗎?好比緩衝。
若是須要就進行裝飾。
轉換流特有功能:轉換流能夠將字節轉成字符,緣由在於,將獲取到的字節經過查編碼表獲取到指定對應字符。
轉換流的最強功能就是基於 字節流 + 編碼表 。沒有轉換,沒有字符流。
發現轉換流有一個子類就是操做文件的字符流對象:
InputStreamReader
|--FileReader
OutputStreamWriter
|--FileWrier
想要操做文本文件,必需要進行編碼轉換,而編碼轉換動做轉換流都完成了。因此操做文件的流對象只要繼承自轉換流就能夠讀取一個字符了。
可是子類有一個侷限性,就是子類中使用的編碼是固定的,是本機默認的編碼表,對於簡體中文版的系統默認碼錶是GBK。
FileReader fr = new FileReader("a.txt");
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"gbk");
以上兩句代碼功能一致,
若是僅僅使用平臺默認碼錶,就使用FileReader fr = new FileReader("a.txt"); //由於簡化。
若是須要制定碼錶,必須用轉換流。
轉換流 = 字節流+編碼表。
轉換流的子類File = 字節流 + 默認編碼表。
凡是操做設備上的文本數據,涉及編碼轉換,必須使用轉換流。
-----------------------------------------------------------------------------------------------
File類:將文件系統中的文件和文件夾封裝成了對象。提供了更多的屬性和行爲能夠對這些文件和文件夾進行操做。這些是流對象辦不到的,由於流只操做數據。
File類常見方法:
1:建立。
boolean createNewFile():在指定目錄下建立文件,若是該文件已存在,則不建立。而對操做文件的輸出流而言,輸出流對象已創建,就會建立文件,若是文件已存在,會覆蓋。除非續寫。
boolean mkdir():建立此抽象路徑名指定的目錄。
boolean mkdirs():建立多級目錄。
2:刪除。
boolean delete():刪除此抽象路徑名錶示的文件或目錄。
void deleteOnExit():在虛擬機退出時刪除。
注意:在刪除文件夾時,必須保證這個文件夾中沒有任何內容,才能夠將該文件夾用delete刪除。
window的刪除動做,是從裏往外刪。注意:java刪除文件不走回收站。要慎用。
3:獲取.
long length():獲取文件大小。
String getName():返回由此抽象路徑名錶示的文件或目錄的名稱。
String getPath():將此抽象路徑名轉換爲一個路徑名字符串。
String getAbsolutePath():返回此抽象路徑名的絕對路徑名字符串。
String getParent():返回此抽象路徑名父目錄的抽象路徑名,若是此路徑名沒有指定父目錄,則返回 null
。
long lastModified():返回此抽象路徑名錶示的文件最後一次被修改的時間。
File.pathSeparator:返回當前系統默認的路徑分隔符,windows默認爲 「;」。
File.Separator:返回當前系統默認的目錄分隔符,windows默認爲 「\」。
4:判斷:
boolean exists():判斷文件或者文件夾是否存在。
boolean isDirectory():測試此抽象路徑名錶示的文件是不是一個目錄。
boolean isFile():測試此抽象路徑名錶示的文件是不是一個標準文件。
boolean isHidden():測試此抽象路徑名指定的文件是不是一個隱藏文件。
boolean isAbsolute():測試此抽象路徑名是否爲絕對路徑名。
5:重命名。
boolean renameTo(File dest):能夠實現移動的效果。剪切+重命名。
String[] list():列出指定目錄下的當前的文件和文件夾的名稱。包含隱藏文件。
若是調用list方法的File 對象中封裝的是一個文件,那麼list方法返回數組爲null。若是封裝的對象不存在也會返回null。只有封裝的對象存在而且是文件夾時,這個方法纔有效。
------------------------------------------------------------------------------------------------
遞歸:就是函數自身調用自身。
何時用遞歸呢?
當一個功能被重複使用,而每一次使用該功能時的參數不肯定,都由上次的功能元素結果來肯定。
簡單說:功能內部又用到該功能,可是傳遞的參數值不肯定。(每次功能參與運算的未知內容不肯定)。
遞歸的注意事項:
1:必定要定義遞歸的條件。
2:遞歸的次數不要過多。容易出現 StackOverflowError 棧內存溢出錯誤。
其實遞歸就是在棧內存中不斷的加載同一個函數。
------------------------------------------------------------------------------------------------
Java.util.Properties:一個能夠將鍵值進行持久化存儲的對象。Map--Hashtable的子類。
Map
|--Hashtable
|--Properties:用於屬性配置文件,鍵和值都是字符串類型。
特色:1:能夠持久化存儲數據。2:鍵值都是字符串。3:通常用於配置文件。
|-- load():將流中的數據加載進集合。
原理:其實就是將讀取流和指定文件相關聯,並讀取一行數據,由於數據是規則的key=value,因此獲取一行後,經過 = 對該行數據進行切割,左邊就是鍵,右邊就是值,將鍵、值存儲到properties集合中。
|-- store():寫入各個項後,刷新輸出流。
|-- list():將集合的鍵值數據列出到指定的目的地。
-------------------------------------------------------------------------------------------------
如下介紹IO包中擴展功能的流對象:基本都是裝飾設計模式。
Java.io.outputstream.PrintStream:打印流
1:提供了更多的功能,好比打印方法。能夠直接打印任意類型的數據。
2:它有一個自動刷新機制,建立該對象,指定參數,對於指定方法能夠自動刷新。
3:它使用的本機默認的字符編碼.
4:該流的print方法不拋出IOException。
該對象的構造函數。
PrintStream(File file) :建立具備指定文件且不帶自動行刷新的新打印流。
PrintStream(File file, String csn) :建立具備指定文件名稱和字符集且不帶自動行刷新的新打印流。
PrintStream(OutputStream out) :建立新的打印流。
PrintStream(OutputStream out, boolean autoFlush) :建立新的打印流。
PrintStream(OutputStream out, boolean autoFlush, String encoding) :建立新的打印流。
PrintStream(String fileName) :建立具備指定文件名稱且不帶自動行刷新的新打印流。
PrintStream(String fileName, String csn)
PrintStream能夠操做目的:1:File對象。2:字符串路徑。3:字節輸出流。
前兩個都JDK1.5版本纔出現。並且在操做文本文件時,可指定字符編碼了。
當目的是一個字節輸出流時,若是使用的println方法,能夠在printStream對象上加入一個true參數。這樣對於println方法能夠進行自動的刷新,而不是等待緩衝區滿了再刷新。最終print方法都將具體的數據轉成字符串,並且都對IO異常進行了內部處理。
既然操做的數據都轉成了字符串,那麼使用PrintWriter更好一些。由於PrintWrite是字符流的子類,能夠直接操做字符數據,同時也能夠指定具體的編碼。
--------------------------------------------------------
PrintWriter:具有了PrintStream的特色同時,還有自身特色:
該對象的目的地有四個:1:File對象。2:字符串路徑。3:字節輸出流。4:字符輸出流。
開發時儘可能使用PrintWriter。
方法中直接操做文件的第二參數是編碼表。
直接操做輸出流的,第二參數是自動刷新。
//讀取鍵盤錄入將數據轉成大寫顯示在控制檯.
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));//源:鍵盤輸入
//目的:把數據寫到文件中,還想自動刷新。
PrintWriter out = new PrintWriter(new FileWriter("out.txt"),true);//設置true後自動刷新
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
out.println(line.toUpperCase());//轉大寫輸出
}
//注意:System.in,System.out這兩個標準的輸入輸出流,在jvm啓動時已經存在了。隨時可使用。當jvm結束了,這兩個流就結束了。可是,當使用了顯示的close方法關閉時,這兩個流在提早結束了。
out.close();
bufr.close();
------------------------------------------------------------------------------------------------
SequenceInputStream:序列流,做用就是將多個讀取流合併成一個讀取流。實現數據合併。
表示其餘輸入流的邏輯串聯。它從輸入流的有序集合開始,並從第一個輸入流開始讀取,直到到達文件末尾,接着從第二個輸入流讀取,依次類推,直到到達包含的最後一個輸入流的文件末尾爲止。
這樣作,能夠更方便的操做多個讀取流,其實這個序列流內部會有一個有序的集合容器,用於存儲多個讀取流對象。
該對象的構造函數參數是枚舉,想要獲取枚舉,須要有Vector集合,但不高效。需用ArrayList,但ArrayList中沒有枚舉,只有本身去建立枚舉對象。
可是方法怎麼實現呢?由於枚舉操做的是具體集合中的元素,因此沒法具體實現,可是枚舉和迭代器是功能同樣的,因此,能夠用迭代替代枚舉。
合併原理:多個讀取流對應一個輸出流。
切割原理:一個讀取流對應多個輸出流。
import java.io.*;
import java.util.*;
class SplitFileDemo{
private static final String CFG = ".properties";
private static final String SP = ".part";
public static void main(String[] args) throws IOException{
File file = new File("c:\\0.bmp");
File dir = new File("c:\\partfiles");
meger(dir);
}
//數據的合併。
public static void meger(File dir)throws IOException{
if(!(dir.exists()&& dir.isDirectory()))
throw new RuntimeException("指定的目錄不存在,或者不是正確的目錄");
File[] files = dir.listFiles(new SuffixFilter(CFG));
if(files.length==0)
throw new RuntimeException("擴展名.proerpties的文件不存在");
//獲取到配置文件
File config = files[0];
//獲取配置文件的信息。
Properties prop = new Properties();
FileInputStream fis = new FileInputStream(config);
prop.load(fis);
String fileName = prop.getProperty("filename");
int partcount = Integer.parseInt(prop.getProperty("partcount"));
//--------------------------
File[] partFiles = dir.listFiles(new SuffixFilter(SP));
if(partFiles.length!=partcount)
throw new RuntimeException("缺乏碎片文件");
//---------------------
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
for(int x=0; x<partcount; x++){
al.add(new FileInputStream(new File(dir,x+SP)));
}
Enumeration<FileInputStream> en = Collections.enumeration(al);
SequenceInputStream sis = new SequenceInputStream(en);
File file = new File(dir,fileName);
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
//帶有配置信息的數據切割。
public static void splitFile(File file)throws IOException{
//用一個讀取流和文件關聯。
FileInputStream fis = new FileInputStream(file);
//建立目的地。由於有多個。因此先建立引用。
FileOutputStream fos = null;
//指定碎片的位置。
File dir = new File("c:\\partfiles");
if(!dir.exists())
dir.mkdir();
//碎片文件大小引用。
File f = null;
byte[] buf = new byte[1024*1024];
//由於切割完的文件一般都有規律的。爲了簡單標記規律使用計數器。
int count = 0;
int len = 0;
while((len=fis.read(buf))!=-1){
f = new File(dir,(count++)+".part");
fos = new FileOutputStream(f);
fos.write(buf,0,len);
fos.close();
}
//碎片文件生成後,還須要定義配置文件記錄生成的碎片文件個數。以及被切割文件的名稱。
//定義簡單的鍵值信息,但是用Properties。
String filename = file.getName();
Properties prop = new Properties();
prop.setProperty("filename",filename);
prop.setProperty("partcount",count+"");
File config = new File(dir,count+".properties");
fos = new FileOutputStream(config);
prop.store(fos,"");
fos.close();
fis.close();
}
}
class SuffixFilter implements FileFilter{
private String suffix;
SuffixFilter(String suffix){
this.suffix = suffix;
}
public boolean accept(File file){
return file.getName().endsWith(suffix);
}
}
-----------------------------------------------------------------------------------------------
RandomAccessFile:
特色:
1:該對象便可讀取,又可寫入。
2:該對象中的定義了一個大型的byte數組,經過定義指針來操做這個數組。
3:能夠經過該對象的getFilePointer()獲取指針的位置,經過seek()方法設置指針的位置。
4:該對象操做的源和目的必須是文件。
5:其實該對象內部封裝了字節讀取流和字節寫入流。
注意:實現隨機訪問,最好是數據有規律。
class RandomAccessFileDemo{
public static void main(String[] args) throws IOException{
write();
read();
randomWrite();
}
//隨機寫入數據,能夠實現已有數據的修改。
public static void randomWrite()throws IOException{
RandomAccessFile raf = new RandomAccessFile("random.txt","rw");
raf.seek(8*4);
System.out.println("pos :"+raf.getFilePointer());
raf.write("王武".getBytes());
raf.writeInt(102);
raf.close();
}
public static void read()throws IOException{
RandomAccessFile raf = new RandomAccessFile("random.txt","r");//只讀模式。
//指定指針的位置。
raf.seek(8*1);//實現隨機讀取文件中的數據。注意:數據最好有規律。
System.out.println("pos1 :"+raf.getFilePointer());
byte[] buf = new byte[4];
raf.read(buf);
String name = new String(buf);
int age = raf.readInt();
System.out.println(name+"::"+age);
System.out.println("pos2 :"+raf.getFilePointer());
raf.close();
}
public static void write()throws IOException{
//rw:當這個文件不存在,會建立該文件。當文件已存在,不會建立。因此不會像輸出流同樣覆蓋。
RandomAccessFile raf = new RandomAccessFile("random.txt","rw");//rw讀寫模式
//往文件中寫入人的基本信息,姓名,年齡。
raf.write("張三".getBytes());
raf.writeInt(97);
raf.close();
}
}
------------------------------------------------------------------------------------------------
管道流:管道讀取流和管道寫入流能夠像管道同樣對接上,管道讀取流就能夠讀取管道寫入流寫入的數據。
注意:須要加入多線程技術,由於單線程,先執行read,會發生死鎖,由於read方法是阻塞式的,沒有數據的read方法會讓線程等待。
public static void main(String[] args) throws IOException{
PipedInputStream pipin = new PipedInputStream();
PipedOutputStream pipout = new PipedOutputStream();
pipin.connect(pipout);
new Thread(new Input(pipin)).start();
new Thread(new Output(pipout)).start();
}
------------------------------------------------------------------------------------------------
對象的序列化:目的:將一個具體的對象進行持久化,寫入到硬盤上。
注意:靜態數據不能被序列化,由於靜態數據不在堆內存中,是存儲在靜態方法區中。
如何將非靜態的數據不進行序列化?用transient 關鍵字修飾此變量便可。
Serializable:用於啓動對象的序列化功能,能夠強制讓指定類具有序列化功能,該接口中沒有成員,這是一個標記接口。這個標記接口用於給序列化類提供UID。這個uid是依據類中的成員的數字簽名進行運行獲取的。若是不須要自動獲取一個uid,能夠在類中,手動指定一個名稱爲serialVersionUID id號。依據編譯器的不一樣,或者對信息的高度敏感性。最好每個序列化的類都進行手動顯示的UID的指定。
import java.io.*;
class ObjectStreamDemo {
public static void main(String[] args) throws Exception{
writeObj();
readObj();
}
public static void readObj()throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
Object obj = ois.readObject();//讀取一個對象。
System.out.println(obj.toString());
}
public static void writeObj()throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(new Person("lisi",25)); //寫入一個對象。
oos.close();
}
}
class Person implements Serializable{
private static final long serialVersionUID = 42L;
private transient String name;//用transient修飾後name將不會進行序列化
public int age;
Person(String name,int age){
this.name = name;
this.age = age;
}
public String toString(){
return name+"::"+age;
}
}
-----------------------------------------------------------------------------------------------
DataOutputStream、DataInputStream:專門用於操做基本數據類型數據的對象。
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeInt(256);
dos.close();
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
int num = dis.readInt();
System.out.println(num);
dis.close();
-----------------------------------------------------------------------------------------------
ByteArrayInputStream:源:內存
ByteArrayOutputStream:目的:內存。
這兩個流對象不涉及底層資源調用,操做的都是內存中數組,因此不須要關閉。
直接操做字節數組就能夠了,爲何還要把數組封裝到流對象中呢?由於數組自己沒有方法,只有一個length屬性。爲了便於數組的操做,將數組進行封裝,對外提供方法操做數組中的元素。
對於數組元素操做無非兩種操做:設置(寫)和獲取(讀),而這兩操做正好對應流的讀寫操做。這兩個對象就是使用了流的讀寫思想來操做數組。
//建立源:
ByteArrayInputStream bis = new ByteArrayInputStream("abcdef".getBytes());
//建立目的:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int ch = 0;
while((ch=bis.read())!=-1){
bos.write(ch);
}
System.out.println(bos.toString());
-----------------------------------------------------------------------------------------------
網絡編程:
端口:
物理端口:
邏輯端口:用於標識進程的邏輯地址,不一樣進程的標識;有效端口:0~65535,其中0~1024系統使用或保留端口。
java 中ip對象:InetAddress.
import java.net.*;
class IPDemo{
public static void main(String[] args) throws UnknownHostException{
//經過名稱(ip字符串or主機名)來獲取一個ip對象。
InetAddress ip = InetAddress.getByName("www.baidu.com");//java.net.UnknownHostException
System.out.println("addr:"+ip.getHostAddress());
System.out.println("name:"+ip.getHostName());
}
}
Socket:★★★★,套接字,通訊的端點。
就是爲網絡服務提供的一種機制,通訊的兩端都有Socket,網絡通訊其實就是Socket間的通訊,數據在兩個Socket間經過IO傳輸。
UDP傳輸:
1,只要是網絡傳輸,必須有socket 。
2,數據必定要封裝到數據包中,數據包中包括目的地址、端口、數據等信息。
直接操做udp不可能,對於java語言應該將udp封裝成對象,易於咱們的使用,這個對象就是DatagramSocket. 封裝了udp傳輸協議的socket對象。
由於數據包中包含的信息較多,爲了操做這些信息方便,也同樣會將其封裝成對象。這個數據包對象就是:DatagramPacket.經過這個對象中的方法,就能夠獲取到數據包中的各類信息。
DatagramSocket具有發送和接受功能,在進行udp傳輸時,須要明確一個是發送端,一個是接收端。
udp的發送端:
1,創建udp的socket服務,建立對象時若是沒有明確端口,系統會自動分配一個未被使用的端口。
2,明確要發送的具體數據。
3,將數據封裝成了數據包。
4,用socket服務的send方法將數據包發送出去。
5,關閉資源。
--------------------------------------------------------------
import java.net.*;
class UdpSend{
public static void main(String[] args)throws Exception {
// 1,創建udp的socket服務。
DatagramSocket ds = new DatagramSocket(8888);//指定發送端口,不指定系統會隨機分配。
// 2,明確要發送的具體數據。
String text = "udp傳輸演示 哥們來了";
byte[] buf = text.getBytes();
// 3,將數據封裝成了數據包。
DatagramPacket dp = new DatagramPacket(buf,
buf.length,InetAddress.getByName("10.1.31.127"),10000);
// 4,用socket服務的send方法將數據包發送出去。
ds.send(dp);
// 5,關閉資源。
ds.close();
}
}
-------------------------------------------------------------
udp的接收端:
1,建立udp的socket服務,必需要明確一個端口,做用在於,只有發送到這個端口的數據纔是這個接收端能夠處理的數據。
2,定義數據包,用於存儲接收到數據。
3,經過socket服務的接收方法將收到的數據存儲到數據包中。
4,經過數據包的方法獲取數據包中的具體數據內容,好比ip、端口、數據等等。
5,關閉資源。
-------------------------------------------------------------
class UdpRece {
public static void main(String[] args) throws Exception{
// 1,建立udp的socket服務。
DatagramSocket ds = new DatagramSocket(10000);
// 2,定義數據包,用於存儲接收到數據。先定義字節數組,數據包會把數據存儲到字節數組中。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
// 3,經過socket服務的接收方法將收到的數據存儲到數據包中。
ds.receive(dp);//該方法是阻塞式方法。
// 4,經過數據包的方法獲取數據包中的具體數據內容,好比ip,端口,數據等等。
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String text = new String(dp.getData(),0,dp.getLength());//將字節數組中的有效部分轉成字符串。
System.out.println(ip+":"+port+"--"+text);
// 5,關閉資源。
ds.close();
}
}
-------------------------------------------------------------
TCP傳輸:兩個端點的創建鏈接後會有一個傳輸數據的通道,這通道稱爲流,並且是創建在網絡基礎上的流,稱之爲socket流。該流中既有讀取,也有寫入。
tcp的兩個端點:一個是客戶端,一個是服務端。
客戶端:對應的對象,Socket
服務端:對應的對象,ServerSocket
TCP客戶端:
1,創建tcp的socket服務,最好明確具體的地址和端口。這個對象在建立時,就已經能夠對指定ip和端口進行鏈接(三次握手)。
2,若是鏈接成功,就意味着通道創建了,socket流就已經產生了。只要獲取到socket流中的讀取流和寫入流便可,只要經過getInputStream和getOutputStream就能夠獲取兩個流對象。
3,關閉資源。
--------------------------------------------------------------
import java.net.*;
import java.io.*;
//需求:客戶端給服務器端發送一個數據。
class TcpClient{
public static void main(String[] args) throws Exception{
Socket s = new Socket("10.1.31.69",10002);
OutputStream out = s.getOutputStream();//獲取了socket流中的輸出流對象。
out.write("tcp演示,哥們又來了!".getBytes());
s.close();
}
}
--------------------------------------------------------------
TCP服務端:
1,建立服務端socket服務,並監聽一個端口。
2,服務端爲了給客戶端提供服務,獲取客戶端的內容,能夠經過accept方法獲取鏈接過來的客戶端對象。
3,能夠經過獲取到的socket對象中的socket流和具體的客戶端進行通信。
4,若是通信結束,關閉資源。注意:要先關客戶端,再關服務端。
--------------------------------------------------------------
class TcpServer{
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(10002);//創建服務端的socket服務
Socket s = ss.accept();//獲取客戶端對象
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+".....connected");
// 能夠經過獲取到的socket對象中的socket流和具體的客戶端進行通信。
InputStream in = s.getInputStream();//讀取客戶端的數據,使用客戶端對象的socket讀取流
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(text);
// 若是通信結束,關閉資源。注意:要先關客戶端,在關服務端。
s.close();
ss.close();
}
}
-----------------------------------------------------------------------------------------------
反射技術:其實就是動態加載一個指定的類,並獲取該類中的全部的內容。並且將字節碼文件封裝成對象,並將字節碼文件中的內容都封裝成對象,這樣便於操做這些成員。簡單說:反射技術能夠對一個類進行解剖。
反射的好處:大大的加強了程序的擴展性。
反射的基本步驟:
1、得到Class對象,就是獲取到指定的名稱的字節碼文件對象。
2、實例化對象,得到類的屬性、方法或構造函數。
3、訪問屬性、調用方法、調用構造函數建立對象。
獲取這個Class對象,有三種方式:
1:經過每一個對象都具有的方法getClass來獲取。弊端:必需要建立該類對象,才能夠調用getClass方法。
2:每個數據類型(基本數據類型和引用數據類型)都有一個靜態的屬性class。弊端:必需要先明確該類。
前兩種方式不利於程序的擴展,由於都須要在程序使用具體的類來完成。
3:使用的Class類中的方法,靜態的forName方法。
指定什麼類名,就獲取什麼類字節碼文件對象,這種方式的擴展性最強,只要將類名的字符串傳入便可。
// 1. 根據給定的類名來得到 用於類加載
String classname = "cn.itcast.reflect.Person";// 來自配置文件
Class clazz = Class.forName(classname);// 此對象表明Person.class
// 2. 若是拿到了對象,不知道是什麼類型 用於得到對象的類型
Object obj = new Person();
Class clazz1 = obj.getClass();// 得到對象具體的類型
// 3. 若是是明確地得到某個類的Class對象 主要用於傳參
Class clazz2 = Person.class;
反射的用法:
1)、須要得到java類的各個組成部分,首先須要得到類的Class對象,得到Class對象的三種方式:
Class.forName(classname) 用於作類加載
obj.getClass() 用於得到對象的類型
類名.class 用於得到指定的類型,傳參用
2)、反射類的成員方法:
Class clazz = Person.class;
Method method = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});
method.invoke();
3)、反射類的構造函數:
Constructor con = clazz.getConstructor(new Class[]{paramClazz1, paramClazz2,...})
con.newInstance(params...)
4)、反射類的屬性:
Field field = clazz.getField(fieldName);
field.setAccessible(true);
field.setObject(value);
獲取了字節碼文件對象後,最終都須要建立指定類的對象:
建立對象的兩種方式(其實就是對象在進行實例化時的初始化方式):
1,調用空參數的構造函數:使用了Class類中的newInstance()方法。
2,調用帶參數的構造函數:先要獲取指定參數列表的構造函數對象,而後經過該構造函數的對象的newInstance(實際參數) 進行對象的初始化。
綜上所述,第二種方式,必需要先明確具體的構造函數的參數類型,不便於擴展。因此通常狀況下,被反射的類,內部一般都會提供一個公有的空參數的構造函數。
------------------------------------------------------
// 如何生成獲取到字節碼文件對象的實例對象。
Class clazz = Class.forName("cn.itcast.bean.Person");//類加載
// 直接得到指定的類型
clazz = Person.class;
// 根據對象得到類型
Object obj = new Person("zhangsan", 19);
clazz = obj.getClass();
Object obj = clazz.newInstance();//該實例化對象的方法調用就是指定類中的空參數構造函數,給建立對象進行初始化。當指定類中沒有空參數構造函數時,該如何建立該類對象呢?請看method_2();
public static void method_2() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
//既然類中沒有空參數的構造函數,那麼只有獲取指定參數的構造函數,用該函數來進行實例化。
//獲取一個帶參數的構造器。
Constructor constructor = clazz.getConstructor(String.class,int.class);
//想要對對象進行初始化,使用構造器的方法newInstance();
Object obj = constructor.newInstance("zhagnsan",30);
//獲取全部構造器。
Constructor[] constructors = clazz.getConstructors();//只包含公共的
constructors = clazz.getDeclaredConstructors();//包含私有的
for(Constructor con : constructors) {
System.out.println(con);
}
}
------------------------------------------------------
反射指定類中的方法:
//獲取類中全部的方法。
public static void method_1() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
Method[] methods = clazz.getMethods();//獲取的是該類中的公有方法和父類中的公有方法。
methods = clazz.getDeclaredMethods();//獲取本類中的方法,包含私有方法。
for(Method method : methods) {
System.out.println(method);
}
}
//獲取指定方法;
public static void method_2() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
//獲取指定名稱的方法。
Method method = clazz.getMethod("show", int.class,String.class);
//想要運行指定方法,固然是方法對象最清楚,爲了讓方法運行,調用方法對象的invoke方法便可,可是方法運行必需要明確所屬的對象和具體的實際參數。
Object obj = clazz.newInstance();
method.invoke(obj, 39,"hehehe");//執行一個方法
}
//想要運行私有方法。
public static void method_3() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
//想要獲取私有方法。必須用getDeclearMethod();
Method method = clazz.getDeclaredMethod("method", null);
// 私有方法不能直接訪問,由於權限不夠。非要訪問,能夠經過暴力的方式。
method.setAccessible(true);//通常不多用,由於私有就是隱藏起來,因此儘可能不要訪問。
}
//反射靜態方法。
public static void method_4() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
Method method = clazz.getMethod("function",null);
method.invoke(null,null);
}
------------------------------------------------------------------------------------------------
正則表達式:★★★☆,實際上是用來操做字符串的一些規則。
好處:正則的出現,對字符串的複雜操做變得更爲簡單。
特色:將對字符串操做的代碼用一些符號來表示。只要使用了指定符號,就能夠調用底層的代碼對字符串進行操做。符號的出現,簡化了代碼的書寫。
弊端:符號的出現雖然簡化了書寫,可是卻下降了閱讀性。
其實更可能是用正則解決字符串操做的問題。
組:用小括號標示,每定義一個小括號,就是一個組,並且有自動編號,從1開始。
只要使用組,對應的數字就是使用該組的內容。別忘了,數組要加\\。
(aaa(wwww(ccc))(eee))技巧,從左括號開始數便可。有幾個左括號就是幾組。
常見操做:
1,匹配:其實用的就是String類中的matches方法。
String reg = "[1-9][0-9]{4,14}";
boolean b = qq.matches(reg);//將正則和字符串關聯對字符串進行匹配。
2,切割:其實用的就是String類中的split方法。
3,替換:其實用的就是String類中的replaceAll();
4,獲取:
1),先要將正則表達式編譯成正則對象。使用的是Pattern中靜態方法 compile(regex);
2),經過Pattern對象獲取Matcher對象。
Pattern用於描述正則表達式,能夠對正則表達式進行解析。
而將規則操做字符串,須要重新封裝到匹配器對象Matcher中。
而後使用Matcher對象的方法來操做字符串。
如何獲取匹配器對象呢?
經過Pattern對象中的matcher方法。該方法能夠正則規則和字符串想關聯。並返回匹配器對象。
3),使用Matcher對象中的方法便可對字符串進行各類正則操做。
-----------------------------------------------------------------------------------------------