從大學(xué)課程設(shè)計(jì)、畢業(yè)設(shè)計(jì)到工作中的一些小功能調(diào)整,自己的Java水平一直是會(huì)點(diǎn)語(yǔ)法+面向搜索引擎編程級(jí)別。想要更加深入地學(xué)習(xí)Java的東西,但是Java圣經(jīng)又太厚,自己也不是特別擅長(zhǎng)看這些技術(shù)書籍的人,在別人的建議下選了《Head First Java》來(lái)看。
縱觀全書,這本書的重點(diǎn)在Java的基礎(chǔ)語(yǔ)法、對(duì)象、多態(tài)與繼承等概念上的講解(講得挺好的),對(duì)于一些異常處理、線程、IO、網(wǎng)絡(luò)方面更多是簡(jiǎn)單的介紹,并沒有深入講解。
原本想通過(guò)這本書學(xué)習(xí)線程方面的概念,為閱讀其他更加深入講解線程方面的文章打下基礎(chǔ),但是似乎無(wú)法達(dá)到目的。
盡管如此,我還是通過(guò)閱讀這本書學(xué)習(xí)的到了一些新的東西。這篇筆記,就是為了整理記錄這些新學(xué)到的知識(shí),以鞏固我對(duì)這些知識(shí)的掌握。
對(duì)象的聲明、創(chuàng)建與賦值
當(dāng)我們聲明一個(gè)非基本數(shù)據(jù)類型的變量時(shí),我們通常這樣寫:
Dog myDog = new Dog();
在這簡(jiǎn)單的一行代碼中,其實(shí)包含了三個(gè)步驟:
// 1. 聲明一個(gè)Dog類型的引用變量Dog myDog// 2. 創(chuàng)建Dog對(duì)象 new Dog// 3. 將創(chuàng)建的Dog對(duì)象,賦值給myDog這個(gè)引用變量Dog myDog = new Dog();
所以,當(dāng)我們聲明對(duì)象數(shù)據(jù)的時(shí)候,實(shí)際上是聲明了該對(duì)象的引用變量數(shù)據(jù)。
對(duì)象的生存空間
在Java虛擬機(jī)驅(qū)動(dòng)的時(shí)候,會(huì)從底層操作系統(tǒng)獲得一塊內(nèi)存來(lái)執(zhí)行Java程序。在內(nèi)存中,要關(guān)注這兩塊區(qū)域:對(duì)象的生存空間堆和方法調(diào)用及變量生存的空間棧。
堆又被稱為可垃圾回收的堆,一旦對(duì)象失去了引用,就有可能被回收。
實(shí)例變量是被聲明在類而不是方法中,所以實(shí)例變量存在于所屬的對(duì)象中(堆中)。
而局部變量則是被生命在方法中,所以局部變量存在于棧中。
賦值與引用的例子
Book a = new Book();
Book b = new Book(); //這里有兩個(gè)引用變量,兩個(gè)對(duì)象Book c = a; //此時(shí)有三個(gè)引用變量,兩個(gè)引用變量,c與a指向同一個(gè)對(duì)象b = a; // 此時(shí)b也與a指向同一個(gè)對(duì)象,b原本指向的對(duì)象失去了引用,處于可回收狀態(tài)
繼承與多態(tài)
Animal[] animals = new Animal[5];
animals[0] = new Dog();
animals[1] = new Cat();
Class Vet{ public void giveshot(Animal a){
a.makeNoise();
}
Vet vet = new Vet();
v.giveshot(new Dog());
}
父類的方法可以在子類中被覆蓋,但是標(biāo)記了final的方法無(wú)法被覆蓋。
一個(gè)類只能繼承一個(gè)父類。但是繼承可以是多層繼承。B繼承A,C繼承B,則C也是A的子類。
所有對(duì)象都是Object的子類,可以用Object來(lái)實(shí)現(xiàn)多態(tài),但是一般不這樣做。
Object o = new Dog();int i = o.hashCode(); // 可行,因?yàn)镺bject本身有hashCode()方法o.makeNoise(); // 不可執(zhí)行,因?yàn)榇藭r(shí)o的引用類型是Object,Object沒有makeNoise()方法,無(wú)法執(zhí)行。Animal a = (Dog) o;
a.makeNoise(); // 可行,當(dāng)o賦值給a時(shí)進(jìn)行了類型轉(zhuǎn)換,Dog是Animal的子類,所以可以賦值成功。Animal有makeNoise()方法,所以可以執(zhí)行
public Dog(String name){//調(diào)用父類的構(gòu)造函數(shù)super();
}
Class Dog{
String name; public Dog(){ // 調(diào)用了有一個(gè)String參數(shù)的構(gòu)造函數(shù)
this("myDog");
} public Dog(String name){ //調(diào)用父類的構(gòu)造函數(shù)
super();
}
}
接口與抽象類
抽象類沒有實(shí)體,抽象類中的方法也沒有方法體。
接口類是抽象類,抽象類不一定是接口類。
implements接口后,要實(shí)現(xiàn)該接口類的所有方法。
extends只能extend一個(gè)父類,implements可以implement好多個(gè)接口。
靜態(tài)方法
// 非靜態(tài)方法Dog d = new Dog();
d.makeNoise();// 靜態(tài)方法Math.min(3,7);
靜態(tài)方法中沒有實(shí)例變量,也不允許調(diào)用非靜態(tài)的變量。
靜態(tài)方法不允許調(diào)用費(fèi)靜態(tài)的方法。
靜態(tài)變量是同一個(gè)類所有實(shí)例共享的。每一個(gè)實(shí)例都有一個(gè)屬于自己的實(shí)例變量。但靜態(tài)變量是每個(gè)類一個(gè)。這里可能會(huì)有多個(gè)實(shí)例對(duì)同一個(gè)靜態(tài)變量同時(shí)進(jìn)行修改的問題。
靜態(tài)變量會(huì)在該類有任何靜態(tài)方法執(zhí)行之前就初始化。
final的變量代表值無(wú)法被改變,final的方法代表無(wú)法被覆蓋,final的類代表無(wú)法被繼承。以下例子來(lái)自java提高篇(十五)-----關(guān)鍵字final
public class Person { private String name;
Person(String name){ this.name = name;
}
public String getName() { return name;
} public void setName(String name) { this.name = name;
}
}public class FinalTest { private final String final_01 = "chenssy"; //編譯期常量,必須要進(jìn)行初始化,且不可更改
private final String final_02; //構(gòu)造器常量,在實(shí)例化一個(gè)對(duì)象時(shí)被初始化
private static Random random = new Random(); private final int final_03 = random.nextInt(50); //使用隨機(jī)數(shù)來(lái)進(jìn)行初始化
//引用
public final Person final_04 = new Person("chen_ssy"); //final指向引用數(shù)據(jù)類型
FinalTest(String final_02){ this.final_02 = final_02;
}
public String toString(){ return "final_01 = " + final_01 +" final_02 = " + final_02 + " final_03 = " + final_03 + " final_04 = " + final_04.getName();
}
public static void main(String[] args) {
System.out.println("------------第一次創(chuàng)建對(duì)象------------");
FinalTest final1 = new FinalTest("cm");
System.out.println(final1);
System.out.println("------------第二次創(chuàng)建對(duì)象------------");
FinalTest final2 = new FinalTest("zj");
System.out.println(final2);
System.out.println("------------修改引用對(duì)象--------------");
final2.final_04.setName("chenssy");
System.out.println(final2);
}
}
------------------
Output:
------------第一次創(chuàng)建對(duì)象------------
final_01 = chenssy final_02 = cm final_03 = 34 final_04 = chen_ssy
------------第二次創(chuàng)建對(duì)象------------
final_01 = chenssy final_02 = zj final_03 = 46 final_04 = chen_ssy
------------修改引用對(duì)象--------------
final_01 = chenssy final_02 = zj final_03 = 46 final_04 = chenssy
異常處理
finally塊中的代碼無(wú)論有無(wú)異常都會(huì)執(zhí)行。
異常也是多態(tài)的,Exception是所有異常的父類。
異??梢詔hrows 也可以 try/catch。
可以為每個(gè)不同的異常編寫不同的catch塊,但是要注意子類要放在父類之前catch
try{ // do somethings} catch (DogException de){ //deal DogException} catch (AnimalException ae){ //deal AnimalException}// 如果AnimalException的catch塊在DogException之前,那么DogException也會(huì)被AnimalException的catch塊捕獲,就不會(huì)落到后面的DogException的catch塊了。
內(nèi)部類
內(nèi)部類可以使用外部類的所有方法和變量,包括標(biāo)記為private的。
內(nèi)部類的實(shí)例一定會(huì)綁定在外部類的實(shí)例上。
內(nèi)部類的其中一個(gè)適用場(chǎng)景:一個(gè)界面,需要監(jiān)聽多個(gè)按鈕點(diǎn)擊事件并且不同按鈕的點(diǎn)擊事件有不同的響應(yīng)。
IO
IO這一章簡(jiǎn)單地講了一下文件操作,著重講了序列化的內(nèi)容。
其實(shí)這章序列化說(shuō)得不好,建議看這里Java 序列化的高級(jí)認(rèn)識(shí)
序列化程序會(huì)對(duì)象相關(guān)的所有東西都存儲(chǔ)起來(lái),被對(duì)象的實(shí)例變量所引用的所有對(duì)象都會(huì)被實(shí)例化。
如果要讓類能夠被序列化,必須實(shí)現(xiàn)Serializable
如果某實(shí)例變量不能/不應(yīng)該被序列化,就把他標(biāo)記為瞬時(shí)的(transient),這樣序列化時(shí)候,程序會(huì)將它跳過(guò)。到了解序列化的時(shí)候這個(gè)引用變量會(huì)被置為null。
解序列化的時(shí)候,所有的類都必須讓JVM找到。
解序列化時(shí),新的對(duì)象會(huì)被分配到堆上,但構(gòu)造函數(shù)不會(huì)執(zhí)行。
如果對(duì)象在繼承樹上的有不可序列化的祖先類,則該不可序列化類以及在它之上的類的構(gòu)造函數(shù)(就算是可序列化的類)就會(huì)執(zhí)行。從第一個(gè)不可序列化的父類開始,之上的類都會(huì)回到初始狀態(tài)。
靜態(tài)文件不會(huì)被序列化。
序列化標(biāo)志:如果在解序列化之前,類已經(jīng)發(fā)生了修改,可能會(huì)導(dǎo)致解序列化失敗。
會(huì)損害序列化的修改:
較為安全的修改:
網(wǎng)絡(luò)通信與多線程
服務(wù)器與客戶端通過(guò)socket連接來(lái)溝通。
當(dāng)ServerSocket接收到請(qǐng)求時(shí),會(huì)在另外的一個(gè)端口做一個(gè)socket連接來(lái)處理客戶端的請(qǐng)求。
線程代表獨(dú)立的執(zhí)行空間。
多線程同時(shí)執(zhí)行時(shí),實(shí)際上是多個(gè)線程隨機(jī)輪流執(zhí)行的。
線程進(jìn)入可執(zhí)行狀態(tài)時(shí),會(huì)在執(zhí)行中和可執(zhí)行這兩種狀態(tài)中切換。但是也可能進(jìn)入堵塞狀態(tài)。堵塞狀態(tài)可能是閑置、等待其他縣城完成、等待串流數(shù)據(jù)、等待被占用的對(duì)象等原因引起的。
構(gòu)造線程時(shí)需要傳入一個(gè)任務(wù)對(duì)象,這個(gè)任務(wù)對(duì)象需要實(shí)現(xiàn)Runnable接口。
并發(fā):不同線程對(duì)同一個(gè)對(duì)象同時(shí)進(jìn)行處理,可能引起問題。
鎖:要讓對(duì)象在線程上有足夠的安全性,就要對(duì)不可分割執(zhí)行的指令上鎖(同步化)。
如果線程嘗試進(jìn)入同步化的方法,必須取得對(duì)象的鑰匙如果鑰匙被別的線程拿走了,線程只能等待。
如果兩個(gè)線程互相持有對(duì)方正在等待執(zhí)行的方法的鑰匙,就會(huì)發(fā)生死鎖。
集合與排序
TreeSet:有序且防止重復(fù)的集合。
HashMap:Key-Value集合,Key不可重復(fù)
HashSet:防止重復(fù)的集合,可快速找到相符元素
LinkedList:針對(duì)經(jīng)常插入或者刪除中間元素所涉及的高效率集合(不如ArrayList實(shí)用)
LinkedHashMap:可記住元素插入順序,可設(shè)定依照原宿上次存儲(chǔ)先后來(lái)排序的HashMap。
// 實(shí)現(xiàn)Comparable接口class Song inplements Comparable<Song>{
public int compareTo(Song s){ return title. compareTo(s.getTitle());
}
}// 實(shí)現(xiàn)Comparatorclass Song{class ArtistCompare implements Comparator<Song>{
public int Compare (Song one, Song two){ return one.getArtist().compareTo(two.getArtist());
}
ArtistCompare artistCompare = new ArtistCompare();
Collections.sort(songList, artistCompare);
}
}
如果equal()被覆蓋過(guò),hashCode()方法也應(yīng)該相應(yīng)覆蓋。
equal()默認(rèn)行為是執(zhí)行==的比較,即判斷兩個(gè)引用變量是否引用堆中的同一個(gè)對(duì)象。如果equals()沒有被覆蓋過(guò),那么兩個(gè)對(duì)象永遠(yuǎn)不會(huì)被視為相等的。
a.equals(b)必須與a.hashCode()==b.hashCode()等值,但a.hashCode()==b.hashCode() 不一定與a.equals(b)等值。
泛型
//這里的list僅接受ArrayList<Animal>public void takeThing(ArrayList<Animal> list)
//這里的list對(duì)象可以接受ArrayList<Dog>、ArrayList<Cat>等繼承Animal的對(duì)象的ArrayList// 泛型的extends等價(jià)于實(shí)體類的extends或者implementspublic <T extends Animal> void takeThing(ArrayList<T> list)//萬(wàn)用字符也可以讓ArrayList接受Animal的子類// 使用萬(wàn)用字符,能夠調(diào)用list中任何元素的方法,但是不能增加元素。public void takeAnimals(ArrayList<? extends Animal> animals){ for(Animal a:animals){
a.eat(); //合法
}
animals.add(new Dog()); //不合法的操作}// 第二、第三種寫法執(zhí)行起來(lái)是一樣的,但是在一般用第二種,因?yàn)樾枰獋魅攵鄠€(gè)對(duì)象時(shí),第二種方法不需要多次聲明public <T extends Animal> void takeThing(ArrayList<T> one, ArrayList<T> two)
遠(yuǎn)端過(guò)程調(diào)用
遠(yuǎn)端過(guò)程調(diào)用的過(guò)程:
啟動(dòng)RMI registry
遠(yuǎn)程服務(wù)被初始化(生成stub和skeleton)
遠(yuǎn)程服務(wù)向RMI registry注冊(cè)
客戶端查詢RMI registry
客戶端從RMI registry獲取stub
客戶端調(diào)用stub上的方法
stub將方法的調(diào)用送到服務(wù)器上
碎片知識(shí)
integer --- 0
floating point --- 0.0
boolean --- false
reference --- null
int a = 3;byte b =3;if(a == b){
// true}
Foo c = new Foo();
Foo d = new Foo();
Foo e = c;if(c == d){// false}if(c == e){// true}if(c.equals(d)){// true}
在&&表達(dá)式中,左右兩邊都為true這個(gè)表達(dá)式返回true,當(dāng)左邊返回false時(shí),JVM不會(huì)執(zhí)行右邊的計(jì)算就直接返回false;
在||表達(dá)式中,左右兩邊都為false這個(gè)表達(dá)式返回false,當(dāng)左邊返回true時(shí),JVM不會(huì)執(zhí)行右邊的計(jì)算就直接返回true;
& 和 | 在boolean表達(dá)式會(huì)強(qiáng)制JVM執(zhí)行兩邊的運(yùn)算,但一般長(zhǎng)表達(dá)是用在位運(yùn)算中。
需要拓展的知識(shí)點(diǎn)
// 位非 ~int x = 10; //00001010x = ~x; //11110101// 位與 & 、位或 |、位異或 ^int x = 10; //00001010int x = 6; //00000110// 位與 &: 兩位都是1返回1,否則返回0int a =x&y; //00000010// 位或 |: 有一位為1就返回1,否則返回0int a =x&y; //00001110// 位異或 ^: 位相同返回1,否則返回0int a =x&y; //11110010// 移位運(yùn)算 左移<<,右移>>,無(wú)符號(hào)右移>>>,需要結(jié)合數(shù)據(jù)類型來(lái)看。int x = -11; //11111011// 左移 1位,等于值*2,向左邊移動(dòng),并且在低位補(bǔ)0.int a= x<<1; //11110110// 右移1位,等于值/2,帶符號(hào)右移,若左操作數(shù)是正數(shù),則高位補(bǔ)“0”,若左操作數(shù)是負(fù)數(shù),則高位補(bǔ)“1”.int a= x>>1; //1111101// 無(wú)符號(hào)右移,無(wú)論左操作數(shù)是正數(shù)還是負(fù)數(shù),在高位都補(bǔ)“0”int a= x>>>1; //0111101
不變性:String類型和包裝類都是不可變的。創(chuàng)建后就值就不可以改變。JVM中有一個(gè)String Pool,不受GC的影響。如果新建的String引用變量的值在String Pool中有相同值的對(duì)象,會(huì)直接引用這個(gè)對(duì)象,而不是新建一個(gè)String對(duì)象。
斷言:執(zhí)行時(shí)沒有特殊設(shè)置JVM會(huì)自動(dòng)忽略斷言,運(yùn)行時(shí)打開JVM的斷言設(shè)置,則可以在不影響任意代碼的前提下進(jìn)行除錯(cuò)。
靜態(tài)嵌套類:可以在沒有外層實(shí)例的情況下使用的類。
public class Outer{
static class Inner{
void do(){ // do somethings
}
} class Test{
public static void main (String args[]){
Outer.Inner a = new Outer.Inner();
a.do();
}
}
}
button.addActionListen(new ActionListen{ public void actionPerformed(ActionEvent e){ // do somethings
}
})
default與protected:default-同一個(gè)包內(nèi)可存取,protected-允許不同包的子類繼承它的成員。
多維數(shù)組:int[][] a2d = new int[4][2]>>這里由5個(gè)數(shù)組組成。
枚舉ENUM:Java 枚舉(enum) 詳解7種常見的用法