天天看點

SD 01:Java基礎

⾯向對象是⼀種基于⾯向過程的程式設計思想,是向現實世界模型的⾃然延伸,這是⼀種“萬物皆對象”的程式設計思想。由 執⾏者變為指ഀ者,在現實⽣活中的任何物體都可以歸為⼀類事物,⽽每⼀個個體都是⼀類事物的執行個體。⾯向對象 的程式設計是以對象為中⼼,以消息為驅動。

差別:

(1)程式設計思路不同:⾯向過程以實作功能的函數開發為主,⽽⾯向對象要⾸先抽象出類、屬性及其⽅法,然後通 過執行個體化類、執⾏⽅法來完成功能。

(2)封裝性:都具有封裝性,但是⾯向過程是封裝的是功能,⽽⾯向對象封裝的是資料和功能。

(3)⾯向對象具有繼承性和多态性,⽽⾯向過程沒有繼承性和多态性,是以⾯向對象優勢很明顯

(1)封裝:通常認為封裝是把資料和操作資料的⽅法封裝起來,對資料的通路隻能通過已定義的接⼝。

(2)繼 承:繼承是從已有類得到繼承資訊建立新類的過程。提供繼承資訊的類被稱為⽗類(超類/基類),得到繼承資訊 的被稱為⼦類(派⽣類)。

(3)多态:分為編譯時多态(⽅法᯿載)和運⾏時多态(⽅法᯿寫)。要實作多态需 要做兩件事:⼀是⼦類繼承⽗類并᯿寫⽗類中的⽅法,⼆是⽤⽗類型引⽤⼦類型對象,這樣同樣的引⽤調⽤同樣的 ⽅法就會根據⼦類對象的不同⽽表現出不同的⾏為。

⼏點補充

(1)⼦類擁有⽗類對象所有的屬性和⽅法(包括私有屬性和私有⽅法),但是⽗類中的私有屬性和⽅法⼦類是⽆法 通路,隻是擁有。因為在⼀個⼦類被建立的時候,⾸先會在記憶體中建立⼀個⽗類對象,然後在⽗類對象外部放上⼦ 類獨有的屬性,兩者合起來形成⼀個⼦類的對象;

(2)⼦類可以擁有⾃⼰屬性和⽅法;

(3)⼦類可以⽤⾃⼰的 ⽅式實作⽗類的⽅法。(重寫)

jdk(java development kit):是 java 開發⼯具包,是整個 java 的核⼼,包括了 java 運⾏環境 jre、java ⼯具 和 java 基礎類庫。

jre( java runtime environment):是 java 的運⾏環境,包含 jvm 标準實作及 java 核⼼類庫。

jvm(java virtual machine):是 java 虛拟機,是整個 java 實作跨平台的最核⼼的部分,能夠運⾏以 java 語⾔ 寫作的軟體程式。所有的 java 程式會⾸先被編譯為 .class 的類⽂件,這種類⽂件可以在虛拟機上執⾏。

(1)重載:編譯時多态、同⼀個類中同名的⽅法具有不同的參數清單、不能根據傳回類型進⾏區分【因為:函數 調⽤時不能指定類型資訊,編譯器不知道你要調哪個函數】;

(2)重寫(⼜名覆寫):運⾏時多态、⼦類與⽗類之間、⼦類᯿寫⽗類的⽅法具有相同的傳回類型、更好的通路 權限。

java 中 static ⽅法不能被覆寫,因為⽅法覆寫是基于運⾏時動态綁定的,⽽ static ⽅法是編譯時靜态綁定的。 static ⽅法跟類的任何執行個體都不相關,是以概念上不适⽤。

java 中也不可以覆寫 private 的⽅法,因為 private 修飾的變量和⽅法隻能在目前類中使⽤, 如果是其他的類繼承 目前類是不能通路到 private 變量或⽅法的,當然也不能覆寫。

靜态⽅法補充:靜态的⽅法可以被繼承,但是不能重寫。如果⽗類和⼦類中存在同樣名稱和參數的靜态⽅ 法,那麼該⼦類的⽅法會把原來繼承過來的⽗類的⽅法隐藏,⽽不是重寫。通俗的講就是⽗類的⽅法和⼦類的⽅法是兩個沒有關系的⽅法,具體調⽤哪⼀個⽅法是看是哪個對象的引⽤;這種⽗⼦類⽅法也不在存在多态的性質。

在講繼承的時候我們就知道⽗類的私有屬性和構造⽅法并不能被繼承,是以 constructor 也就不能被 override(重寫),但是可以 overload(重載),是以你可以看到⼀個類中有多個構造函數的情況。

(1)名字與類名相同;

(2)沒有傳回值,但不能⽤ void 聲明構造函數;

(3)成類的對象時⾃動執⾏,⽆需調⽤。

java 程式在執⾏⼦類的構造⽅法之前,如果沒有⽤ super() 來調⽤⽗類特定的構造⽅法,則會調⽤⽗類中“沒有參 數的構造⽅法”。

是以,如果⽗類中隻定義了有參數的構造⽅法,⽽在⼦類的構造⽅法中⼜沒有⽤ super() 來調⽤⽗類中特定的構造 ⽅法,則編譯時将發⽣錯誤,因為 java 程式在⽗類中找不到沒有參數的構造⽅法可供執⾏。解決辦法是:在⽗類 ⾥加上⼀個不做事且沒有參數的構造⽅法。

1、使⽤ new 關鍵字;

2、使⽤ class 類的 newinstance ⽅法,該⽅法調⽤⽆參的構造器建立對象(反射): class.forname.newinstance();

3、使⽤ clone() ⽅法;

4、反序列化,⽐如調⽤ objectinputstream 類的 readobject() ⽅法。

(1)抽象類中可以定義構造函數,接⼝不能定義構造函數

(2)抽象類中可以有抽象⽅法和具體⽅法,⽽接⼝中隻能有抽象⽅法(public abstract)

(3)抽象類中的成員權限可以是 public、預設、protected(抽象類中抽象⽅法就是為了᯿寫,是以不能被 private 修飾),⽽接⼝中的成員隻可以是 public(⽅法預設:public abstrat、成員變量預設:public static final);

(4)抽象類中可以包含靜态⽅法,⽽接⼝中不可以包含靜态⽅法;

1、在 jdk1.8中,允許在接⼝中包含帶有具體實作的⽅法,使⽤ default 修飾,這類⽅法就是預設⽅法。

2、抽象類中可以包含靜态⽅法,在 jdk1.8 之前接⼝中不能包含靜态⽅法,jdk1.8 以後可以包含。之前不能包含 是因為,接⼝不可以實作⽅法,隻可以定義⽅法,是以不能使⽤靜态⽅法(因為靜态⽅法必須實作)。現在可以包 含了,隻能直接⽤接⼝調⽤靜态⽅法。jdk1.8 仍然不可以包含靜态代碼塊。

靜态變量:是被 static 修飾的變量,也稱為類變量,它屬于類,是以不管建立多少個對象,靜态變量在記憶體中有且 僅有⼀個拷⻉;靜态變量可以實作讓多個對象共享記憶體。

執行個體變量:屬于某⼀執行個體,需要先建立對象,然後通過對象才能通路到它。

對于 short s1 = 1; s1 = s1 + 1; 來說,在 s1 + 1 運算時會⾃動提升表達式的類型為 int ,那麼将 int 型值指派給 short 型變量,s1 會出現類型轉換錯誤。

對于 short s1 = 1; s1 += 1; 來說,+= 是 java 語⾔規定的運算符,java 編譯器會對它進⾏特殊處理,是以可以正确 編譯。

(1)int 是 java 的⼋種基本資料類型之⼀,⽽ integer 是 java 為 int 類型提供的封裝類;

(2)int 型變量的預設值是 0,integer 變量的預設值是 null,這⼀點說明 integer 可以區分出未指派和值為 0 的 區分;

(3)integer 變量必須執行個體化後才可以使⽤,⽽ int 不需要。

1、由于 integer 變量實際上是對⼀個 integer 對象的引⽤,是以兩個通過 new ⽣成的 integer 變量永遠是不相等 的,因為其記憶體位址是不同的;

2、integer 變量和 int 變量⽐較時,隻要兩個變量的值是相等的,則結果為 true。因為包裝類 integer 和基本資料 類型 int 類型進⾏⽐較時,java 會⾃動拆包裝類為 int,然後進⾏⽐較,實際上就是兩個 int 型變量在進⾏⽐較;

3、⾮ new ⽣成的 integer 變量和 new integer() ⽣成的變量進⾏⽐較時,結果為 false。因為⾮ new ⽣成的 integer 變量指向的是 java 常量池中的對象,⽽ new integer() ⽣成的變量指向堆中建立的對象,兩者在記憶體中的 位址不同;

4、對于兩個⾮ new ⽣成的 integer 對象進⾏⽐較時,如果兩個變量的值在區間 [-128, 127] 之間,則⽐較結果為 true,否則為 false。java 在編譯 integer i = 100 時,會編譯成 integer i = integer.valueof(100),⽽ integer 類型 的 valueof 的源碼如下所示:

從上⾯的代碼中可以看出:java 對于 [-128, 127] 之間的數會進⾏緩存,⽐如:integer i = 127,會将 127 進⾏緩 存,下次再寫 integer j = 127 的時候,就會直接從緩存中取出,⽽對于這個區間之外的數就需要 new 了。

包裝類的緩存:

boolean:全部緩存

byte:全部緩存

character:<= 127 緩存

short:-128 — 127 緩存

long:-128 — 127 緩存

integer:-128 — 127 緩存

float:沒有緩存

doulbe:沒有緩存

⾃動裝箱是 java 編譯器在基本資料類型和對應得包裝類之間做的⼀個轉化。⽐如:把 int 轉化成 integer,double 轉化成 double 等等。反之就是⾃動拆箱。

原始類型:boolean、char、byte、short、int、long、float、double

封裝類型:boolean、character、byte、short、integer、long、float、double

在 switch(expr 1) 中,expr1 隻能是⼀個整數表達式或者枚舉常量。⽽整數表達式可以是 int 基本資料類型或者 integer 包裝類型。由于,byte、short、char 都可以隐式轉換為 int,是以,這些類型以及這些類型的包裝類型也 都是可以的。⽽ long 和 string 類型都不符合 switch 的文法規定,并且不能被隐式的轉換為 int 類型,是以,它們 不能作⽤于 switch 語句中。不過,需要注意的是在 jdk1.7 版本之後 switch 就可以作⽤在 string 上了。

clone ⽅法:⽤于建立并傳回目前對象的⼀份拷⻉;

getclass ⽅法:⽤于傳回目前運⾏時對象的 class;

tostring ⽅法:傳回對象的字元串表示形式;

finalize ⽅法:執行個體被垃圾回收器回收時觸發的⽅法;

equals ⽅法:⽤于⽐較兩個對象的記憶體位址是否相等,⼀般需要重寫;

hashcode ⽅法:⽤于傳回對象的哈希值;

notify ⽅法:喚醒⼀個在此對象螢幕上等待的線程。如果有多個線程在等待隻會喚醒⼀個

notifyall ⽅法:作⽤跟 notify() ⼀樣,隻不過會喚醒在此對象螢幕上等待的所有線程,⽽不是⼀個線程

wait ⽅法:讓目前對象等待

final:⽤于聲明屬性、⽅法和類,分别表示屬性不可變、⽅法不可覆寫、被其修飾的類不可繼承;

finally:異常處理語句結構的⼀部分,表示總是執⾏;

finallize:object類的⼀個⽅法,在垃圾回收時會調⽤被回收對象的finalize

==:如果⽐較的對象是基本資料類型,則⽐較的是數值是否相等;如果⽐較的是引⽤資料類型,則⽐較的是對象的 位址值是否相等。

equals ⽅法:⽤來⽐較兩個對象的内容是否相等。注意:equals ⽅法不能⽤于⽐較基本資料類型的變量。如果沒 有對 equals ⽅法進⾏重寫,則⽐較的是引⽤類型的變量所指向的對象的位址(很多類重寫了 equals ⽅法,⽐如 string、integer 等把它變成了值⽐較,是以⼀般情況下 equals ⽐較的是值是否相等)。

兩個對象的 hashcode() 相同,equals() 不⼀定為 true。因為在散清單中,hashcode() 相等即兩個鍵值對的哈希 值相等,然⽽哈希值相等,并不⼀定能得出鍵值對相等【散列沖突】。

這個問題應該是有個前提,就是你需要⽤到 hashmap、hashset 等 java 集合,⽤不到哈希表的話,其實僅僅重寫 equals() ⽅法也可以。⽽⼯作中的場景是常常⽤到 java 集合,是以 java 官⽅建議重寫 equals() 就⼀定要重寫 hashcode() ⽅法。

對于對象集合的判重如果⼀個集合含有 10000 個對象執行個體,僅僅使⽤ equals() ⽅法的話,那麼對于⼀個對象判 ᯿就需要⽐較 10000 次,随着集合規模的增⼤,時間開銷是很⼤的。但是同時使⽤哈希表的話,就能快速定位到 對象的⼤概存儲位置,并且在定位到⼤概存儲位置後,後續⽐較過程中,如果兩個對象的 hashcode 不相同,也不 再需要調⽤ equals() ⽅法,從⽽⼤⼤減少了 equals() ⽐較次數。

是以從程式實作原理上來講的話,既需要 equals() ⽅法,也需要 hashcode() ⽅法。那麼既然重寫了 equals(),那 麼也要重寫 hashcode() ⽅法,以保證兩者之間的配合關系。

1、如果兩個對象相等,則 hashcode ⼀定也是相同的;

2、兩個對象相等,對兩個對象分别調⽤ equals ⽅法都傳回 true;

3、兩個對象有相同的 hashcode 值,它們也不⼀定是相等的;

4、是以,equals ⽅法被覆寫過,則 hashcode ⽅法也必須被覆寫;

5、hashcode() 的預設⾏為是對堆上的對象産⽣獨特值。如果沒有重寫 hashcode(),則該 class 的兩個對象⽆論 如何都不會相等(即使這兩個對象指向相同的資料)。

java 中 && 和 & 都是表示與的邏輯運算符,都表示邏輯運輸符 and,當兩邊的表達式都為 true 的時候,整個運算 結果才為 true,否則為 false。

&&:有短路功能,當第⼀個表達式的值為 false 的時候,則不再計算第⼆個表達式;

&:不管第⼀個表達式結果是否為 true,第⼆個都會執⾏。除此之外,& 還可以⽤作位運算符:當 & 兩邊的表達式 不是 boolean 類型的時候,& 表示按位操作。

java 的參數是以值傳遞的形式傳⼊⽅法中,⽽不是引⽤傳遞。

當傳遞⽅法參數類型為基本資料類型(數字以及布爾值)時,⼀個⽅法是不可能修改⼀個基本資料類型的參數。

當傳遞⽅法參數類型為引⽤資料類型時,⼀個⽅法将修改⼀個引⽤資料類型的參數所指向對象的值。即使 java 函 數在傳遞引⽤資料類型時,也隻是拷⻉了引⽤的值罷了,之是以能修改引⽤資料是因為它們同時指向了⼀個對象, 但這仍然是按值調⽤⽽不是引⽤調⽤。

等于 -1,因為在數軸上取值時,中間值(0.5)向右取整,是以正 0.5 是往上取整,負 0.5 是直接舍棄。

兩個⼆進制數異或結果是這兩個⼆進制數差的絕對值。表達式如下:a^b = |a-b|。

兩個⼆進制 a 與 b 異或,即 a 和 b 兩個數按位進⾏運算。如果對應的位相同,則為 0(相當于對應的算術相減), 如果不同即為 1(相當于對應的算術相加)。由于⼆進制每個位隻有兩種狀态,要麼是 0,要麼是 1,則按位異或 操作可表達為按位相減取值相對值,再按位累加。

(1)實作 cloneable 接⼝并᯿寫 object 類中的 clone() ⽅法;

(2)實作 serializable 接⼝,通過對象的序列化和反序列化實作克隆,可以實作真正的深克隆。

(1)淺克隆:拷⻉對象和原始對象的引⽤類型引⽤同⼀個對象。淺克隆隻是複制了對象的引⽤位址,兩個對象指 向同⼀個記憶體位址,是以修改其中任意的值,另⼀個值都會随之變化,這就是淺克隆。

(2)深克隆:拷⻉對象和原始對象的引⽤類型引⽤不同對象。深拷⻉是将對象及值複制過來,兩個對象修改其中 任意的值另⼀個值不會改變,這就是深拷⻉(例:json.parse() 和 json.stringify(),但是此⽅法⽆法複制函數類 型)。

補充:

深克隆的實作就是在引⽤類型所在的類實作 cloneable 接⼝,并使⽤ public 通路修飾符重寫 clone ⽅法。

java 中定義的 clone 沒有深淺之分,都是統⼀的調⽤ object 的 clone ⽅法。為什麼會有深克隆的概念?是由于我 們在實作的過程中刻意的嵌套了 clone ⽅法的調⽤。也就是說深克隆就是在需要克隆的對象類型的類中᯿新實作克 隆⽅法 clone()。

對象序列化是⼀個⽤于将對象狀态轉換為位元組流的過程,可以将其儲存到磁盤⽂件中或通過⽹絡發送到任何其他程 序。從位元組流建立對象的相反的過程稱為反序列化。⽽建立的位元組流是與平台⽆關的,在⼀個平台上序列化的對象 可以在不同的平台上反序列化。序列化是為了解決在對象流進⾏讀寫操作時所引發的問題。

序列化的實作:将需要被序列化的類實作 serializable 接⼝,該接⼝沒有需要實作的⽅法,隻是⽤于标注該對象是 可被序列化的,然後使⽤⼀個輸出流(如:fileoutputstream)來構造⼀個 objectoutputstream 對象,接着使 ⽤ objectoutputstream 對象的 writeobject(object obj) ⽅法可以将參數為 obj 的對象寫出,要恢複的話則使⽤ 輸⼊流。

(1)當你想把的記憶體中的對象狀态儲存到⼀個⽂件中或者資料庫中時候;

(2)當你想⽤套接字在⽹絡上傳送對象的時候;

(3)當你想通過 rmi 傳輸對象的時候