java文法基礎:
1,關鍵字:其實就是某種語言賦予了特殊含義的單詞。
保留字:其實就是還沒有賦予特殊含義,但是準備日後要使用過的單詞。
2,标示符:其實就是在程式中自定義的名詞。比如類名,變量名,函數名。包含 0-9、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 inthalfSeach_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;
}
elseif(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
---------------------------------------------------------------------------------------------
設計模式:解決問題最行之有效的思想。是一套被反複使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人了解、保證代碼可靠性。
java中有23種設計模式:
單例設計模式:★★★★★
解決的問題:保證一個類在記憶體中的對象唯一性。
比如:多程式讀取一個配置檔案時,建議配置檔案封裝成對象。會友善操作其中資料,又要保證多個程式讀到的是同一個配置檔案對象,就需要該配置檔案對象在記憶體中是唯一的。
Runtime()方法就是單例設計模式進行設計的。
如何保證對象唯一性呢?
思想:
1,不讓其他程式建立該類對象。
2,在本類中建立一個本類對象。
3,對外提供方法,讓其他程式擷取這個對象。
步驟:
1,因為建立對象都需要構造函數初始化,隻要将本類中的構造函數私有化,其他程式就無法再建立該類對象;
2,就在類中建立一個本類的對象;
3,定義一個方法,傳回該對象,讓其他程式可以通過方法就得到本類對象。(作用:可控)
代碼展現:
1,私有化構造函數;
2,建立私有并靜态的本類對象;
3,定義公有并靜态的方法,傳回該對象。
---------------------------------------------
//餓漢式
class Single{
private Single(){} //私有化構造函數。
private staticSingle s = new Single(); //建立私有并靜态的本類對象。
public static SinglegetInstance(){ //定義公有并靜态的方法,傳回該對象。
return s;
}
}
---------------------------------------------
//懶漢式:延遲加載方式。
class Single2{
private Single2(){}
private staticSingle2 s = null;
public static Single2getInstance(){
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 finalvoid 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{
publicstatic final int x = 3;
publicabstract 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 = newCat();
多态的好處:提高了程式的擴充性。
多态的弊端:當父類引用指向子類對象時,雖然提高了擴充性,但是隻能通路父類中具備的方法,不可以通路子類中特有的方法。(前期不能使用後期産生的功能,即通路的局限性)
多态的前提:
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 voidmain(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("innershow run "+num);
}
}
public void method(){
Inner in = newInner();//建立内部類的對象。
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("showrun");
}
}.show();
//2
Object obj = newObject(){
void show(){
System.out.println("showrun");
}
};
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 voidmain(String[] args) {//static不允許this
This.new Inner().show();//錯誤,Inner類需要定義成static
}
}
------------------------------------------------------
interface Inter{
void show();
}
class Outer{//通過匿名内部類補足Outer類中的代碼。
publicstatic Inter method(){
returnnew Inter(){
publicvoid show(){}
};
}
}
class InnerClassDemo7 {
public static voidmain(String[] args) {
Outer.method().show();
function (new Inter(){
publicvoid show(){}
}); //匿名内部類作為方法的參數進行傳遞。
}
publicstatic void function(Inter in){
in.show();
}
}
------------------------------------------------------------------------------------------------
異 常:★★★★
異常:就是不正常。程式在運作時出現的不正常情況。其實就是程式中出現的問題。這個問題按照面向對象思想進行描述,并封裝成了對象。因為問題的産生有産生的原因、有問題的名稱、有問題的描述等多個屬性資訊存在。當出現多屬性資訊最友善的方式就是将這些資訊進行封裝。異常就是java按照面向對象的思想将問題進行對象封裝。這樣就友善于操作問題以及處理問題。
出現的問題有很多種,比如角标越界,空指針等都是。就對這些問題進行分類。而且這些問題都有共性内容比如:每一個問題都有名稱,同時還有問題描述的資訊,問題出現的位置,是以可以不斷的向上抽取。形成了異常體系。
--------java.lang.Throwable:
Throwable:可抛出的。
|--Error:錯誤,一般情況下,不編寫針對性的代碼進行處理,通常是jvm發生的,需要對程式進行修正。
|--Exception:異常,可以有針對性的處理方式
無論是錯誤還是異常,它們都有具體的子類展現每一個問題,它們的子類都有一個共性,就是都以父類名才作為子類的字尾名。
這個體系中的所有類和對象都具備一個獨有的特點;就是可抛性。
可抛性的展現:就是這個體系中的類和對象都可以被throws和throw兩個關鍵字所操作。
------------------------------------------------------
class ExceptionDemo{
public static voidmain(String[] args) {
// byte[] buf = newbyte[1024*1024*700];//java.lang.OutOfMemoryError記憶體溢出錯誤
}
}
------------------------------------------------------
在開發時,如果定義功能時,發現該功能會出現一些問題,應該将問題在定義功能時标示出來,這樣調用者就可以在使用這個功能的時候,預先給出處理方式。
如何标示呢?通過throws關鍵字完成,格式:throws 異常類名,異常類名...
這樣标示後,調用者,在使用該功能時,就必須要處理,否則編譯失敗。
處理方式有兩種:1、捕捉;2、抛出。
對于捕捉: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聲明的。
常見異常:
1、腳标越界異常(IndexOutOfBoundsException)包括數組、字元串;
空指針異常(NullPointerException)
2、類型轉換異常:ClassCastException
3、沒有這個元素異常:NullPointerException
4、不支援操作異常;
異常要盡量避免,如果避免不了,需要預先給出處理方式。比如家庭備藥,比如滅火器。
-----------------------------------------------------------------------------------------------
包:定義包用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目前目錄下的所有的類。不包含子包。
importpacka.abc.*;//導入了packa包中的子包abc下的目前的所有類。
如果導入的兩個包中存在着相同名稱的類。這時如果用到該類,必須在代碼中指定包名。
常見的軟體包:
java.lang :language java的核心包,ObjectSystem 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();
Thread t1 = new Thread(t); //建立線程。
t1.start();
為什麼要有Runnable接口的出現?
1:通過繼承Thread類的方式,可以完成多線程的建立。但是這種方式有一個局限性,如果一個類已經有了自己的父類,就不可以繼承Thread類,因為java單繼承的局限性。
可是該類中的還有部分代碼需要被多個線程同時執行。這時怎麼辦呢?
隻有對該類進行額外的功能擴充,java就提供了一個接口Runnable。這個接口中定義了run方法,其實run方法的定義就是為了存儲多線程要運作的代碼。
是以,通常建立線程都用第二種方式。
因為實作Runnable接口可以避免單繼承的局限性。
2:其實是将不同類中需要被多線程執行的代碼進行抽取。将多線程要運作的代碼的位置單獨定義到接口中。為其他類進行功能擴充提供了前提。
是以Thread類在描述線程時,内部定義的run方法,也來自于Runnable接口。
實作Runnable接口可以避免單繼承的局限性。而且,繼承Thread,是可以對Thread類中的方法,進行子類複寫的。但是不需要做這個複寫動作的話,隻為定義線程代碼存放位置,實作Runnable接口更友善一些。是以Runnable接口将線程要執行的任務封裝成了對象。
-------------------------------------------------------
//面試
new Thread(newRunnable(){ //匿名
public voidrun(){
System.out.println("runnablerun");
}
})
{
public voidrun(){
System.out.println("subthreadrun");
}
}.start(); //結果:subthreadrun
---------------------------------------------------------
Try {
Thread.sleep(10);
}catch(InterruptedExceptione){}// 當刻意讓線程稍微停一下,模拟cpu 切換情況。
多線程安全問題的原因:
通過圖解:發現一個線程在執行多條語句時,并運算同一個資料時,在執行過程中,其他線程參與進來,并操作了這個資料。導緻到了錯誤資料的産生。
涉及到兩個因素:
1,多個線程在操作共享資料。
2,有多條語句對共享資料進行運算。
原因:這多條語句,在某一個時刻被一個線程執行時,還沒有執行完,就被其他線程執行了。
解決安全問題的原理:
隻要将操作共享資料的語句在某一時段讓一個線程執行完,在執行過程中,其他線程不能進來執行就可以解決這個問題。
如何進行多句操作共享資料代碼的封裝呢?
java中提供了一個解決方式:就是同步代碼塊。
格式:
synchronized(對象) { //任意對象都可以。這個對象就是鎖。
需要被同步的代碼;
}
---------------------------------------------------------------
同步:★★★★★
好處:解決了線程安全問題。
弊端:相對降低性能,因為判斷鎖需要消耗資源,産生了死鎖。
定義同步是有前提的:
1,必須要有兩個或者兩個以上的線程,才需要同步。
2,多個線程必須保證使用的是同一個鎖。
同步的第二種表現形式:
同步函數:其實就是将同步關鍵字定義在函數上,讓函數具備了同步性。
同步函數是用的哪個鎖呢?
通過驗證,函數都有自己所屬的對象this,是以同步函數所使用的鎖就是this鎖。
當同步函數被static修飾時,這時的同步用的是哪個鎖呢?
靜态函數在加載時所屬于類,這時有可能還沒有該類産生的對象,但是該類的位元組碼檔案加載進記憶體就已經被封裝成了對象,這個對象就是該類的位元組碼檔案對象。
是以靜态加載時,隻有一個對象存在,那麼靜态同步函數就使用的這個對象。
這個對象就是 類名.class
同步代碼塊和同步函數的差別?
同步代碼塊使用的鎖可以是任意對象。
同步函數使用的鎖是this,靜态同步函數的鎖是該類的位元組碼檔案對象。
在一個類中隻有一個同步,可以使用同步函數。如果有多同步,必須使用同步代碼塊,來确定不同的鎖。是以同步代碼塊相對靈活一些。
-------------------------------------------------------
★考點問題:請寫一個延遲加載的單例模式?寫懶漢式;當出現多線程通路時怎麼解決?加同步,解決安全問題;效率高嗎?不高;怎樣解決?通過雙重判斷的形式解決。
//懶漢式:延遲加載方式。
當多線程通路懶漢式時,因為懶漢式的方法内對共性資料進行多條語句的操作。是以容易出現線程安全問題。為了解決,加入同步機制,解決安全問題。但是卻帶來了效率降低。
為了效率問題,通過雙重判斷的形式解決。
class Single{
private static Singles = null;
private Single(){}
public static SinglegetInstance(){ //鎖是誰?位元組碼檔案對象;
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 Locklock = new ReentrantLock();
finalCondition notFull = lock.newCondition();
finalCondition notEmpty = lock.newCondition();
finalObject[] items = new Object[100];
int putptr,takeptr, count;
public voidput(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 Objecttake() 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 = newString();//建立了一個空内容的字元串。
String s2 =null;//s2沒有任何對象指向,是一個null常量值。
String s3 ="";//s3指向一個具體的字元串對象,隻不過這個字元串中沒有内容。
//一般在定義字元串時,不用new。
String s4 = newString("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(intch);//傳回第一次找到的字元角标
intindexOf(int ch,int fromIndex); //傳回從指定位置開始第一次找到的角标
intindexOf(String str); //傳回第一次找到的字元串角标
intindexOf(String str,int fromIndex);
int lastIndexOf(int ch);
intlastIndexOf(int ch,int fromIndex);
intlastIndexOf(String str);
intlastIndexOf(String str,int fromIndex);
2.1.4:擷取子串。
String substring(int start);//從start位開始,到length()-1為止.
String substring(intstart,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 StringcopyValueOf(char[],int offset,int count);
static String valueOf(char[]);
static StringvalueOf(char[],int offset,int count);
2.3.3:将基本資料類型或者對象轉成字元串。
static StringvalueOf(char);
static StringvalueOf(boolean);
static StringvalueOf(double);
static StringvalueOf(float);
static StringvalueOf(int);
static StringvalueOf(long);
static StringvalueOf(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);
Stringreplace(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範圍的元素
StringBufferdeleteCharAt(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,intfromIndex);從指定位置開始查找字元串
int lastIndexOf(string); 傳回指定子字元串在此字元串中最右邊出現處的索引。
intlastIndexOf(string,int fromIndex); 從指定的索引開始反向搜尋
5,擷取子串。
string substring(start); 傳回start到結尾的子串
stringsubstring(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 = newStringBuffer().append("abc").append(4).append('q').toString();
---------------------------------------------------------
class Test{
public static voidmain(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 voidmethod_1(String s1,String s2){
s1.replace('a','k');
s1 = s2;
}
public static voidmethod_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 = newInteger(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) :集合中是否包含指定元素 。
booleancontainsAll(Collection) :集合中是否包含指定的多個元素。
boolean isEmpty():集合中是否有元素。
4,擷取:
int size():集合中有幾個元素。
5,取交集:
boolean retainAll(Collection) :對目前集合中保留和指定集合中的相同的元素。如果兩個集合元素相同,傳回flase;如果retainAll修改了目前集合,傳回true。
6,擷取集合中所有元素:
Iterator iterator():疊代器
7,将集合變成數組:
toArray();
------------------------------------------------------------
--< java.util>-- Iterator接口:
疊代器:是一個接口。作用:用于取集合中的元素。
| 如果仍有元素可以疊代,則傳回 true。 |
| 傳回疊代的下一個元素。 |
| 從疊代器指向的 collection 中移除疊代器傳回的最後一個元素(可選操作)。 |
每一個集合都有自己的資料結構,都有特定的取出自己内部元素的方式。為了便于操作所有的容器,取出元素。将容器内部的取出方式按照一個統一的規則向外提供,這個規則就是Iterator接口。
也就說,隻要通過該接口就可以取出Collection集合中的元素,至于每一個具體的容器依據自己的資料結構,如何實作的具體取出細節,這個不用關心,這樣就降低了取出元素和具體集合的耦合性。
Iterator it = coll.iterator();//擷取容器中的疊代器對象,至于這個對象是是什麼不重要。這對象肯定符合一個規則Iterator接口。
-----------------------------------------------------------------------------
public static voidmain(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(Objecto) :反向索引指定元素的位置。
List subList(start,end) :擷取子清單。
4,修改:
Objectset(index,element) :對指定索引位進行元素的修改。
5,擷取所有元素:
ListIterator listIterator():list集合特有的疊代器。
List集合支援對元素的增、删、改、查。
List集合因為角标有了自己的擷取元素的方式: 周遊。
for(intx=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;
方法摘要 | |
| 将指定的元素插入清單(可選操作)。 |
| 以正向周遊清單時,如果清單疊代器有多個元素,則傳回 true(換句話說,如果 next 傳回一個元素而不是抛出異常,則傳回 true)。 |
| 如果以逆向周遊清單,清單疊代器有多個元素,則傳回 true。 |
| 傳回清單中的下一個元素。 |
| 傳回對 next 的後續調用所傳回元素的索引。 |
| 傳回清單中的前一個元素。 |
| 傳回對 previous 的後續調用所傳回元素的索引。 |
| 從清單中移除由 next 或 previous 傳回的最後一個元素(可選操作)。 |
| 用指定元素替換 next 或 previous 傳回的最後一個元素(可選操作)。 |
可變長度數組的原理:
當元素超出數組長度,會産生一個新數組,将原數組的資料複制到新數組中,再将新的元素添加到新數組中。
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方法對擷取到的鍵進行值的擷取。
SetkeySet = map.keySet();
Iteratorit = keySet.iterator();
while(it.hasNext()){
Objectkey = it.next();
Objectvalue = map.get(key);
System.out.println(key+":"+value);
}
--------------------------------------------------------
取出map集合中所有元素的方式二:entrySet()方法。
SetentrySet = map.entrySet();
Iteratorit = 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,newComparatorByLen());//按指定的比較器方法排序。
classComparatorByLen 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中的 XXXsynchronizedXXX(XXX);
ListsynchronizedList(list);
MapsynchronizedMap(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(Strings : arr) {
System.out.println(s);
}
3)、單列集合 Collection:
Listlist = 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");
// 傳統方式:必須掌握這種方式
Setentrys = 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.Entryentry = (Entry) obj; // obj 依次表示Entry
System.out.println(entry.getKey()+ "=" + entry.getValue());
}
5)、集合疊代注意問題:在疊代集合的過程中,不能對集合進行增删操作(會報并發通路異常);可以用疊代器的方法進行操作(子類listIterator:有增删的方法)。
6)、增強for循環注意問題:在使用增強for循環時,不能對元素進行指派;
int[]arr = {1,2,3};
for(intnum : 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> voidmethod(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的其他子類,是以添加具體的類型對象不合适,類型檢查會出現安全問題。 ?extendsObject 代表Object的子類型不确定,怎麼能添加具體類型的對象呢?
public static voidmethod(ArrayList<? extends Object> al) {
al.add("abc"); //錯
//隻能對al集合中的元素調用Object類中的方法,具體子類型的方法都不能用,因為子類型不确定。
}
------------------------------------------------------------------------------------------------------------------------------------------------
API--- java.lang.System: 屬性和行為都是靜态的。
longcurrentTimeMillis(); // 傳回目前時間毫秒值
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 {
publicstatic void main(String[] args) throws Exception {
Runtimer = Runtime.getRuntime();
Processp = 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的僞随機數
publicstatic void main(String[] args) {
Randomr = new Random();
for(intx=0; x<10; x++) {
//doubled = Math.floor(Math.random()*10+1);
//intd = (int)(Math.random()*10+1);
intd = r.nextInt(10)+1;
System.out.println(d);
}
}
--------------------------------------------------------------------------------------------------------------------
API--- java.util.Date:日期類,月份從0-11;
//日期對象轉成毫秒值
Dated = new Date();
longtime1 = d.getTime();
longtime2 = System.currentTimeMillis(); / /毫秒值。
//毫秒值轉成具體的日期
longtime = 1322709921312l;
Dated = new Date();
d.setTime(time);
publicstatic void method() throws Exception {
Stringstr_time = "2011/10/25";
DateFormat df = new SimpleDateFormat("yyyy/MM/dd");//SimpleDateFormat作為可以指定使用者自定義的格式來完成格式化。
Date d = df.parse(str_time);
}
Dated = new Date();
DateFormatdf = DateFormat.getDateInstance(DateFormat.LONG);
df= DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG);
Stringstr_time = df.format(d);
//将日期對象轉換成字元串的方式:DateFormat類中的format方法。
//建立日期格式對象。
DateFormat df = new SimpleDateFormat(); //該對象的建立内部會封裝一個預設的日期格式。11-12-1 下午1:48
//如果想要自定義日期格式的話。可使用SimpleDateFormat的構造函數。将具體的格式作為參數傳入到構造函數中。如何表示日期中年的部分呢?可以必須要參與格式對象文檔。
df = newSimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
//調用DateFormat中的format方法。對已有的日期對象進行格式化。
String str_time = df.format(d);
--------------------------------------------------------------------------------------------------------------------
API--- java.util. Calendar:月曆類
publicstatic void method(){
Calendarc = 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)));
}
publicstatic String getNum(int num){
returnnum>9 ? num+"" : "0"+num;
}
publicstatic String getWeek(int index){
String[]weeks = {"","日","一","二","三","四","五","六"};
returnweeks[index];
}
------------------------------------------------------------------------------------------------------------------------------------------------
IO流:★★★★★,用于處理裝置上資料。
流:可以了解資料的流動,就是一個資料流。IO流最終要以對象來展現,對象都存在IO包中。
流也進行分類:
1:輸入流(讀)和輸出流(寫)。
2:因為處理的資料不同,分為位元組流和字元流。
位元組流:處理位元組資料的流對象。裝置上的資料無論是圖檔或者dvd,文字,它們都以二進制存儲的。二進制的最終都是以一個8位為資料單元進行展現,是以計算機中的最小資料單元就是位元組。意味着,位元組流可以處理裝置上的所有資料,是以位元組流一樣可以處理字元資料。
那麼為什麼要有字元流呢?因為字元每個國家都不一樣,是以涉及到了字元編碼問題,那麼GBK編碼的中文用unicode編碼解析是有問題的,是以需要擷取中文位元組資料的同時+ 指定的編碼表才可以解析正确資料。為了友善于文字的解析,是以将位元組流和編碼表封裝成對象,這個對象就是字元流。隻要操作字元資料,優先考慮使用字元流體系。
注意:流的操作隻有兩種:讀和寫。
流的體系因為功能不同,但是有共性内容,不斷抽取,形成繼承體系。該體系一共有四個基類,而且都是抽象類。
位元組流:InputStream OutputStream
字元流:Reader Writer
在這四個系統中,它們的子類,都有一個共性特點:子類名字尾都是父類名,字首名都是這個子類的功能名稱。
--------------------------------------------------------------------------------------------------------------------
public static void main(String[] args)throws IOException { //讀、寫都會發生IO異常
FileWriterfw = new FileWriter("demo.txt"); // FileNotFoundException
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) {
FileWriterfw = 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 {
publicstatic void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt");
intch = 0;
while((ch =fr.read())!= -1) { //條件是沒有讀到結尾
System.out.println((char)ch); //調用讀取流的read方法,讀取一個字元。
}
fr.close();
}
}
--------------------------------------------------------------------------------------------------------------------
讀取資料的第二種方式:第二種方式較為高效,自定義緩沖區。
import java.io.*;
class FileReaderDemo2 {
publicstatic void main(String[] args) throws IOException {
FileReaderfr = new FileReader("demo.txt"); //建立讀取流對象和指定檔案關聯。
//因為要使用read(char[])方法,将讀取到字元存入數組。是以要建立一個字元數組,一般數組的長度都是1024的整數倍。
char[]buf = new char[1024];
intlen = 0;
while((len=fr.read(buf)) != -1) {
System.out.println(newString(buf,0,len));
}
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 = newFileWriter("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 = newFileReader("bufdemo.txt");
BufferedReaderbufr = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null){ //readLine方法傳回的時候是不帶換行符的。
System.out.println(line);
}
bufr.close();
-----------------------------
//記住,隻要一讀取鍵盤錄入,就用這句話。
BufferedReader bufr = new BufferedReader(newInputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(newOutputStreamWriter(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 = newFileReader("a.txt");
InputStreamReader isr = newInputStreamReader(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(Filedest):可以實作移動的效果。剪切+重命名。
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, Stringencoding) :建立新的列印流。
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(newInputStreamReader(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 = newFile("c:\\0.bmp");
File dir = newFile("c:\\partfiles");
meger(dir);
}
//資料的合并。
public static void meger(File dir)throwsIOException{
if(!(dir.exists() &&dir.isDirectory()))
throw new RuntimeException("指定的目錄不存在,或者不是正确的目錄");
File[] files = dir.listFiles(newSuffixFilter(CFG));
if(files.length==0)
throw new RuntimeException("擴充名.proerpties的檔案不存在");
//擷取到配置檔案
File config = files[0];
//擷取配置檔案的資訊。
Properties prop = new Properties();
FileInputStream fis = newFileInputStream(config);
prop.load(fis);
String fileName =prop.getProperty("filename");
int partcount =Integer.parseInt(prop.getProperty("partcount"));
//--------------------------
File[] partFiles = dir.listFiles(newSuffixFilter(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(newFile(dir,x+SP)));
}
Enumeration<FileInputStream> en =Collections.enumeration(al);
SequenceInputStream sis = newSequenceInputStream(en);
File file = new File(dir,fileName);
FileOutputStream fos = newFileOutputStream(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(Filefile)throws IOException{
//用一個讀取流和檔案關聯。
FileInputStream fis = newFileInputStream(file);
//建立目的地。因為有多個。是以先建立引用。
FileOutputStream fos = null;
//指定碎片的位置。
File dir = newFile("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 = newFile(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 = newFile(dir,count+".properties");
fos = new FileOutputStream(config);
prop.store(fos,"");
fos.close();
fis.close();
}
}
class SuffixFilterimplements 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 voidmain(String[] args) throws IOException{
write();
read();
randomWrite();
}
//随機寫入資料,可以實作已有資料的修改。
public static voidrandomWrite()throws IOException{
RandomAccessFileraf = 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 voidread()throws IOException{
RandomAccessFileraf = new RandomAccessFile("random.txt","r");//隻讀模式。
//指定指針的位置。
raf.seek(8*1);//實作随機讀取檔案中的資料。注意:資料最好有規律。
System.out.println("pos1:"+raf.getFilePointer());
byte[] buf = newbyte[4];
raf.read(buf);
String name = newString(buf);
int age =raf.readInt();
System.out.println(name+"::"+age);
System.out.println("pos2:"+raf.getFilePointer());
raf.close();
}
public static voidwrite()throws IOException{
//rw:當這個檔案不存在,會建立該檔案。當檔案已存在,不會建立。是以不會像輸出流一樣覆寫。
RandomAccessFileraf = 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();
PipedOutputStreampipout = new PipedOutputStream();
pipin.connect(pipout);
new Thread(newInput(pipin)).start();
new Thread(newOutput(pipout)).start();
}
------------------------------------------------------------------------------------------------
對象的序列化:目的:将一個具體的對象進行持久化,寫入到硬碟上。
注意:靜态資料不能被序列化,因為靜态資料不在堆記憶體中,是存儲在靜态方法區中。
如何将非靜态的資料不進行序列化?用transient 關鍵字修飾此變量即可。
Serializable:用于啟動對象的序列化功能,可以強制讓指定類具備序列化功能,該接口中沒有成員,這是一個标記接口。這個标記接口用于給序列化類提供UID。這個uid是依據類中的成員的數字簽名進行運作擷取的。如果不需要自動擷取一個uid,可以在類中,手動指定一個名稱為serialVersionUID id号。依據編譯器的不同,或者對資訊的高度敏感性。最好每一個序列化的類都進行手動顯示的UID的指定。
import java.io.*;
class ObjectStreamDemo {
public static voidmain(String[] args) throws Exception{
writeObj();
readObj();
}
public static voidreadObj()throws Exception{
ObjectInputStream ois = newObjectInputStream(new FileInputStream("obj.txt"));
Object obj =ois.readObject();//讀取一個對象。
System.out.println(obj.toString());
}
public static voidwriteObj()throws IOException{
ObjectOutputStreamoos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(newPerson("lisi",25)); //寫入一個對象。
oos.close();
}
}
class Person implements Serializable{
private static finallong serialVersionUID = 42L;
private transient Stringname;//用transient修飾後name将不會進行序列化
public int age;
Person(String name,intage){
this.name = name;
this.age = age;
}
public StringtoString(){
returnname+"::"+age;
}
}
-----------------------------------------------------------------------------------------------
DataOutputStream、DataInputStream:專門用于操作基本資料類型資料的對象。
DataOutputStream dos = new DataOutputStream(newFileOutputStream("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屬性。為了便于數組的操作,将數組進行封裝,對外提供方法操作數組中的元素。
對于數組元素操作無非兩種操作:設定(寫)和擷取(讀),而這兩操作正好對應流的讀寫操作。這兩個對象就是使用了流的讀寫思想來操作數組。
//建立源:
ByteArrayInputStreambis = new ByteArrayInputStream("abcdef".getBytes());
//建立目的:
ByteArrayOutputStreambos = 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 voidmain(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 voidmain(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 voidmain(String[] args) throws Exception{
// 1,建立udp的socket服務。
DatagramSocket ds= new DatagramSocket(10000);
// 2,定義資料包,用于存儲接收到資料。先定義位元組數組,資料包會把資料存儲到位元組數組中。
byte[] buf = newbyte[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 = newString(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 voidmain(String[] args) throws Exception{
Socket s = newSocket("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 voidmain(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 = newbyte[1024];
int len =in.read(buf);
String text = newString(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)、反射類的成員方法:
Classclazz = Person.class;
Methodmethod = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});
method.invoke();
3)、反射類的構造函數:
Constructorcon = clazz.getConstructor(new Class[]{paramClazz1, paramClazz2,...})
con.newInstance(params...)
4)、反射類的屬性:
Fieldfield = 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 voidmethod_2() throws Exception {
Class clazz =Class.forName("cn.itcast.bean.Person");
//既然類中沒有空參數的構造函數,那麼隻有擷取指定參數的構造函數,用該函數來進行執行個體化。
//擷取一個帶參數的構造器。
Constructorconstructor = clazz.getConstructor(String.class,int.class);
//想要對對象進行初始化,使用構造器的方法newInstance();
Object obj =constructor.newInstance("zhagnsan",30);
//擷取所有構造器。
Constructor[]constructors = clazz.getConstructors();//隻包含公共的
constructors= clazz.getDeclaredConstructors();//包含私有的
for(Constructorcon : constructors) {
System.out.println(con);
}
}
------------------------------------------------------
反射指定類中的方法:
//擷取類中所有的方法。
public static voidmethod_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 voidmethod_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 voidmethod_3() throws Exception {
Class clazz =Class.forName("cn.itcast.bean.Person");
//想要擷取私有方法。必須用getDeclearMethod();
Method method =clazz.getDeclaredMethod("method", null);
// 私有方法不能直接通路,因為權限不夠。非要通路,可以通過暴力的方式。
method.setAccessible(true);//一般很少用,因為私有就是隐藏起來,是以盡量不要通路。
}
//反射靜态方法。
public static voidmethod_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對象中的方法即可對字元串進行各種正則操作。