練習:對兩個變量的數據進行互換。不需要第三方變量。
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,靜態方法只能訪問靜態成員,不可以訪問非靜態成員。
(這句話是針對同一個類環境下的,比如說,一個類有多個成員(屬性,方法,字段),
靜態方法A,那麼可以訪問同類名下其他
靜態成員,你如果訪問非
靜態成員就不行
)
因爲靜態方法加載時,優先於對象存在,所以沒有辦法訪問對象中的成員。
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 abstractvoid 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(Object) 方法,兩個對象是相等的,那麼對這兩個對象中的每個對象調用 hashCode 方法都必須生成相同的整數結果;
而根據對象的屬性不同,判斷對象是否相同的具體內容也不一樣。所以在定義類時,一般都會複寫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,在應用中都會被複寫,建立具體對象的特有的內容。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
創建線程的第二種方式:實現一個接口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(對象) { // 任意對象都可以。這個對象就是鎖。
需要被同步的代碼;
}
---------------------------------------------------------------
同步:★★★★★ //就是在操作共享數據代碼時,訪問時只能讓一個線程進去訪問,此線程執行完退出後,別的線程才能再對此共享數據代碼進行訪問。
好處:解決了線程安全問題。Synchronized
弊端:相對降低性能,因爲判斷鎖需要消耗資源,產生了死鎖。
定義同步是有前提的:
1,必須要有兩個或者兩個以上的線程,才需要同步。
2,多個線程必須保證使用的是同一個鎖。
同步的第二種表現形式: //對共享資源的方法定義同步
同步方法:其實就是將同步關鍵字定義在方法上,讓方法具備了同步性。
同步方法是用的哪個鎖呢? //synchronized(this)用以定義需要進行同步的某一部分代碼塊
通過驗證,方法都有自己所屬的對象this,所以同步方法所使用的鎖就是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的出現替代了同步:lock.lock();………lock.unlock();
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接口:
迭代器:是一個接口。作用:用於取集合中的元素。
boolean
|
|
|
|
void
|
remove() 從迭代器指向的 collection 中移除迭代器返回的最後一個元素(可選操作)。
|
每一個集合都有自己的數據結構(就是容器中存儲數據的方式),都有特定的取出自己內部元素的方式。爲了便於操作所有的容器,取出元素。將容器內部的取出方式按照一個統一的規則向外提供,這個規則就是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;
方法摘要
|
void
|
add( E e) 將指定的元素插入列表(可選操作)。
|
boolean
|
hasNext() 以正向遍歷列表時,如果列表迭代器有多個元素,則返回 true(換句話說,如果 next 返回一個元素而不是拋出異常,則返回 true)。
|
boolean
|
|
|
|
int
|
|
|
|
int
|
|
void
|
remove() 從列表中移除由 next 或 previous 返回的最後一個元素(可選操作)。
|
void
|
set( E e) 用指定元素替換 next 或 previous 返回的最後一個元素(可選操作)。
|
可變長度數組的原理:
當元素超出數組長度,會產生一個新數組,將原數組的數據複製到新數組中,再將新的元素添加到新數組中。
ArrayList:是按照原數組的50%延長。構造一個初始容量爲 10 的空列表。
Vector:是按照原數組的100%延長。
注意:對於list集合,底層判斷元素是否相同,其實用的是元素自身的equals方法完成的。所以建議元素都要複寫equals方法,建立元素對象自己的比較相同的條件依據。
LinkedList:的特有方法。
addFirst();
addLast();
在jdk1.6以後。
offerFirst();
offerLast();
getFirst():獲取鏈表中的第一個元素。如果鏈表爲空,拋出NoSuchElementException;
getLast();獲取鏈表中的最後一個元素。如果鏈表爲空,拋出NoSuchElementException;
在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的方法。
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類型的對象
//因爲集合具體對象中既可存儲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方法,讀取一個字符。
read()方法一次讀一個字節的二進制數據—是int型的!
}
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];//讀取的字符數組長度是1024
int len = 0;
while(( len=fr.read(buf)) != -1) {
System.out.println(new String(buf,0,len));//將char類型的數據從0到len轉換成String
}
fr.close();
}
}
--------------------------------------------------------------------------------------------------------------------
IO中的使用到了一個設計模式:裝飾設計模式。
裝飾設計模式解決:對一組類進行功能的增強。
包裝:寫一個類(包裝類)對被包裝對象進行包裝;
* 1、包裝類和被包裝對象要實現同樣的接口;
* 2、包裝類要持有一個被包裝對象;
* 3、包裝類在實現接口時,大部分方法是靠調用被包裝對象來實現的,對於需要修改的方法我們自己實現;
--------------------------------------------------------------------------------------------------------------------
字符流:
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.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。只有封裝的對象存在並且是文件夾時,這個list()方法纔有效。
------------------------------------------------------------------------------------------------
遞歸:就是方法自身調用自身。
什麼時候用遞歸呢?
當一個功能被重複使用,而每一次使用該功能時的參數不確定,都由上次的功能元素結果來確定。
簡單說:功能內部又用到該功能,但是傳遞的參數值不確定。(每次功能參與運算的未知內容不確定)。
遞歸的注意事項:
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.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對象中的方法即可對字符串進行各種正則操作。
-------------------------------------------------------------------------------------