【剑指 Java】第 1 弹:靠这份 Java 基础知识总结,我拿到了满意的 Offer
前言
因為博主是 2021 屆畢業(yè)生,當(dāng)時為了準(zhǔn)備秋招,特意總結(jié)的 Java 基礎(chǔ)知識面試高頻題,最后也算找到了挺滿意的工作。因此回饋給大家,希望能對大家起到一定的幫助。
0. 入門常識
0.1 Java 特點
0.2 Java 和 C++
- 相同點:兩者均為 OOP 語言,均支持 OOP 的三大特性(封裝、繼承、多態(tài));
- 不同點:
- Java 不存在指針的概念,所以內(nèi)存更加安全;
- Java 類是單繼承(但是接口可以多繼承),C++ 的類是多繼承;
- Java 中有自動內(nèi)存管理機(jī)制,但是 C++ 中需要開發(fā)者手動釋放內(nèi)存;
- C/C++ 中,字符串和字符數(shù)組最后均有一個額外的 \0 標(biāo)志來表示結(jié)束,但 Java 中不存在這一概念;
0.3 JRE 和 JDK
- JRE(Java Runtime Environment),即 Java 運行時環(huán)境,是用來運行已經(jīng)編譯過的 Java 程序所需內(nèi)容的集合(JVM、Java 類庫、Java 命令等),不能用來開發(fā)新程序;
- JDK(Java Development Kit),即 Java 開發(fā)工具包,是功能齊全的 Java SDK,包含 JRE 擁有的一切,還有編譯器和其他工具,如果我們想要創(chuàng)建和編譯新程序,就必須使用到它;
0.4 Java 程序編譯過程
我們編譯的源代碼(xxx.java)經(jīng) JDK 中的 javac 命令編譯后,成為 JVM 能夠理解的 Java 字節(jié)碼(xxx.class),然后經(jīng)由 JVM 加載,通過解釋器 逐行解釋執(zhí)行,這就是為什么能經(jīng)常聽見說 Java 是一門編譯和解釋共存的語言。
其中 JVM 是解釋 Java 字節(jié)碼(xxx.class) 的虛擬機(jī),針對不同系統(tǒng)均有特定實現(xiàn),方便一次編譯,多次運行,即 Java 語言的平臺獨立性;
1. 數(shù)據(jù)類型
1.1 基本數(shù)據(jù)類型
| byte | 8 | 1 | Byte | ?27-2^7?27 ~ 27?12^7-127?1 | 0 |
| short | 16 | 2 | Short | ?215-2^{15}?215 ~ 215?12^{15}-1215?1 | 0 |
| char | 16 | 2 | Character | \u0000 ~ \uffff(000 ~ 655356553565535) | u0000 |
| int | 32 | 4 | Integer | ?231-2^{31}?231 ~ 231?12^{31}-1231?1 | 0 |
| long | 64 | 8 | Long | ?263-2^{63}?263 ~ 263?12^{63}-1263?1 | 0L |
| float | 32 | 4 | Float | 3.4e?453.4e^{-45}3.4e?45 ~ 1.4e381.4e^{38}1.4e38 | 0.0f |
| double | 64 | 8 | Double | 4.9e?3244.9e^{-324}4.9e?324 ~ 1.8e3081.8e^{308}1.8e308 | 0.0D |
| boolean | 不確定 | 不確定 | Boolean | true 或 false | false |
注意:
1.2 引用類型
| 數(shù)組 | null |
| 類 | null |
| 接口 | null |
1.3 封裝類
基本數(shù)據(jù)類型都有其對應(yīng)的封裝類,兩者之間的賦值通過 自動裝箱 和 自動拆箱 來完成;
- 自動裝箱:將基本數(shù)據(jù)類型裝箱為封裝類;
- 自動拆箱:將封裝類拆箱為基本數(shù)據(jù)類型;
- 基本類型與對應(yīng)封裝類的不同
- 基本類型只能按值傳遞,封裝類按引用傳遞;
- 基本類型 會在 棧 中創(chuàng)建,效率較高,但可能存在內(nèi)存泄露問題;封裝類對象會在堆中創(chuàng)建,其 引用在棧中創(chuàng)建;
1.4 緩存池
以 new Integer(123) 和 Integer.valueOf(123) 為例:
- 通過 new 的方式每次都會創(chuàng)建一個新的對象;
- 通過 valueOf() 的方式則會優(yōu)先判斷該值是否位于緩存池,如果在的話就直接返回緩存池中的內(nèi)容,多次調(diào)用指向同一個對象的引用;
| Byte | ?27-2^7?27 ~ 27?12^7-127?1 |
| Character | \u0000 ~ \u007F |
| Short | ?27-2^7?27 ~ 27?12^7-127?1 |
| Integer | ?27-2^7?27 ~ 27?12^7-127?1 |
| Boolean | true & false |
2. 字符串 String
2.1 定義
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {/** The value is used for character storage. */private final char value[]; }上述代碼為 Java 8 中 String 的定義,其底層實際上使用的是字符(char)數(shù)組,而且由于被聲明為 final,代表著它 不能被繼承。而且一旦初始化之后就不能再去引用其他數(shù)組,這樣就保證了 String 的不可變性,也因此 String 是線程安全的。
2.2 不可變性的優(yōu)點
由于 String 的 hash 值被頻繁使用,它的不可變性使得 hash 值也不可變,此時只需要進(jìn)行一次計算;
如果一個 String 對象已經(jīng)被創(chuàng)建過,那么就會優(yōu)先從字符串常量池中獲取其引用,其不可變性確保了不同引用指向同一 String 對象;
我們經(jīng)常用 String 作為我們方法的參數(shù),其不變性能夠保證參數(shù)不可變;
String 的不可變性讓它天生 具備線程安全,能夠在多個線程中方便使用而不用考慮線程安全問題。
2.3 String vs StringBuffer vs StringBuffer
主要從三個方面對三者進(jìn)行對比:
| String | 不可變 | 安全 | 操作少量的數(shù)據(jù) |
| StringBuffer | 可變 | 安全,內(nèi)部使用 synchronized 進(jìn)行同步 | 多線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù) |
| StringBuilder | 可變 | 不安全 | 單線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù),性能高于 StringBuffer |
2.4 字符串常量池(String Pool)
String Pool 位于 方法區(qū),通常保存著所有 字符串字面量(literal strings),在編譯期間就被確定。此外,還可以用 String 中的 intern() 方法在運行過程中添加到 String Pool 中。當(dāng)一個字符串調(diào)用 intern() 時,如果 String Pool 中已經(jīng)存在字面量相同的字符串,則會返回 String Pool 中的引用;如果不存在,則向 String Pool 中添加一個新的字符串,同時返回新字符串的引用;
String s1 = new String("aaa"); String s2 = new String("aaa"); // false 兩個字符串指向不同對象 System.out.println(s1 == s2); String s3 = s1.intern(); String s4 = s1.intern(); // true,常量池中存在字面量相同的字符串,直接取出 System.out.println(s3 == s4);在下面的代碼中,內(nèi)存分析如下圖:
String str1 = "村雨遙"; String str2 = "村雨遙"; String str3 = new String("村雨遙"); String str4 = new String("村雨遙");// true,兩個引用指向常量池中的同一對象 System.out.println(str1 == str2); // false,兩個引用指向堆中不同對象 System.out.println(str3 == str4);2.5 new String(“xxx”)
使用 new 的方式創(chuàng)建字符串對象,會有兩種不同的情況:
此時會創(chuàng)建兩個字符串對象,“xxx” 屬于字符串字面量,因此在編譯期會在 String Pool 中創(chuàng)建一個字符串對象,用于指向該字符串的字面量 “xxx”;然后 new 會在堆中創(chuàng)建一個字符串對象;
此時只需要創(chuàng)建一個字符串對象,由于 String Pool 中已經(jīng)存在指向 “xxx” 的對象,所以直接在堆中創(chuàng)建一個字符串對象;
3. 基礎(chǔ)語法
3.1 注釋
- 單行注釋
- 多行注釋
- 文檔注釋
3.2 常見關(guān)鍵字
3.3 標(biāo)識符和關(guān)鍵字
- 標(biāo)識符:用于給程序、類、對象、變量、方法、接口、自定義數(shù)據(jù)類型等命名;
- 關(guān)鍵字:特殊的標(biāo)識符,被 Java 賦予了特殊含義,只能有特定用途;
- 標(biāo)識符命名規(guī)則(可以參考《阿里巴巴開發(fā)手冊》,關(guān)注公眾號【村雨遙】回復(fù)【資源下載】下載 PDF)
- 標(biāo)識符由英文字符大小寫(a - z, A - Z)、數(shù)字(0 - 9)、下劃線(_)和美元符號($)組成;
- 不能以數(shù)字開頭,不能是關(guān)鍵字;
- 嚴(yán)格區(qū)分大小寫;
- 包名:多個單詞組成是所有單詞均小寫;
- 類名和接口:大寫駝峰命名法;
- 變量名和函數(shù)名:多個單詞組成時,第一個單詞全小寫,其他單詞采用大寫駝峰命名法;
- 常量名:字母全部大寫,單詞之間用下劃線(_)分割;
3.4 訪問控制符
| public | 😀 | 😀 | 😀 | 😀 |
| protected | 😀 | 😀 | 😀 | 😡 |
| default | 😀 | 😀 | 😡 | 😡 |
| private | 😀 | 😡 | 😡 | 😡 |
3.5 static、final、this、super
static 主要有如下 4 中使用場景:
- 修飾成員變量和成員方法:被 static 修飾的成員屬于類,屬于靜態(tài)成員變量,存儲在 Java 內(nèi)存中的 方法區(qū),不屬于單個對象,被所有對象共享,而且最好通過 類名.靜態(tài)成員名/靜態(tài)方法名() 調(diào)用;
- 靜態(tài)代碼塊:定義在類中方法外,先于非靜態(tài)代碼塊之前執(zhí)行(靜態(tài)代碼塊 -> 非靜態(tài)代碼塊 -> 構(gòu)造方法) ,而且不管執(zhí)行多少次創(chuàng)建新對象的操作,靜態(tài)代碼只執(zhí)行一次;
- 靜態(tài)內(nèi)部類:static 要修飾類時,只有修飾內(nèi)部類這一種用法。 非靜態(tài)內(nèi)部類在編譯后會隱含保存一個引用,用于指向創(chuàng)建它的外部類,但是靜態(tài)內(nèi)部類不存在。即 內(nèi)部類的創(chuàng)建不用依賴外圍類的創(chuàng)建,同時內(nèi)部類也只能使用任意外部類的 static 成員變量和方法;
- 靜態(tài)導(dǎo)包:用于導(dǎo)入靜態(tài)資源,import static 用于指定導(dǎo)入某一類中的靜態(tài)資源,然后我們就可以直接使用類中的靜態(tài)成員變量和方法;
- 注意:
- abstract 方法不能同時是 static 的,因為 abstract 方法需要被重寫,但 static 方法不可以;
- 不能從 static 方法內(nèi)部發(fā)出對非靜態(tài)方法的調(diào)用,因為靜態(tài)方法只能訪問靜態(tài)成員,而非靜態(tài)方法的調(diào)用需要先創(chuàng)建對象;
- static 不能用于修飾局部變量;
- 內(nèi)部類與靜態(tài)內(nèi)部類的區(qū)別:靜態(tài)內(nèi)部類相對外部類是獨立存在的,在靜態(tài)內(nèi)部類中無法直接訪問外部類中變量和方法。如果要進(jìn)行訪問,則必須 new 一個外部類對象,使用該對象來進(jìn)行訪問,但對于靜態(tài)變量和靜態(tài)方法,能夠直接調(diào)用。而普通的內(nèi)部類作為外部類的一個成員而存在,能夠直接訪問外部類屬性,調(diào)用外部類方法。
- 修飾類時,被修飾的類不能被繼承,而且類中所有成員方法均被隱式指定為 final 方法;
- 修飾方法時,表明該方法無法被重寫;
- 修飾變量時,說明該 變量是一個常量。若變量為基本數(shù)據(jù)類型,則一旦初始化后不能再改變;若變量是引用類型,則初始化后不能指向其他對象;
用于引用類的當(dāng)前實例,比如我們最常用的構(gòu)造方法中,注意不能用在 static 方法中;
public class User{int age;public User(int age){this.age = age;} }其中 this.age 說明訪問的是 User 類中的成員變量,而后面的 age 則代表傳入的形參;
用于從子類訪問父類中的變量和方法,注意不能用在 static 方法中;
public class Father{String name;public Father(String name){this.name = name;}public Father(){} } public class Son extends Father{public Son(String name){super();this.name = name + ".jr";} }3.6 continue、break 和 return
| continue | 用于循環(huán)結(jié)構(gòu),指跳出當(dāng)前循環(huán),進(jìn)入下一次循環(huán) |
| break | 用于循環(huán)結(jié)構(gòu),指跳出整個循環(huán)體,繼續(xù)執(zhí)行循環(huán)下面的語句 |
| return | 1. return ; :直接用 return 結(jié)束方法執(zhí)行,用于沒有返回值函數(shù)的方法; 2. return value; :return 一個特定值,用于有返回值函數(shù)的方法 |
3.7 while 循環(huán)與 do 循環(huán)
while 循環(huán)結(jié)構(gòu)在循環(huán)開始前會判斷下一個迭代是否應(yīng)該繼續(xù),可能一次循環(huán)體都不執(zhí)行;
do……while 會在循環(huán)的結(jié)果來判斷是否繼續(xù)下一輪迭代,至少會執(zhí)行一次循環(huán)體;
3.8 final、finally、finalize
final 既是一個修飾符,也是一個關(guān)鍵字,修飾不同對象時,表示的意義也不一樣;
- 修飾類: 表示該類無法被繼承;
- 修飾變量:若變量是基本數(shù)據(jù)類型,則其數(shù)值一旦初始化后就不能再改變,若變量是引用類型,則在其初始化之后便不能再讓其指向另一個對象,但其指向的對象的內(nèi)容是可變的;
- 修飾方法:表示方法無法被重寫,但是允許重載,private 方法會隱式指定為 final 方法;
- finally 是一個關(guān)鍵字,在異常處理時提供 finally 塊來執(zhí)行任何清除操作,無論是否有異常被拋出或捕獲,finally 塊均會被執(zhí)行,通常用于釋放資源;
- finally 正常情況下一定會被執(zhí)行,但是在如下兩種情況下不會執(zhí)行:
- 對應(yīng)的 try 未執(zhí)行,則該 try 塊的 finally 塊并不會被執(zhí)行;
- 若 try 塊中 JVM 關(guān)機(jī),則 finally 塊也不會執(zhí)行;
- finally 中如果有 return 語句,則會覆蓋 try 或 catch 中的 return 語句,導(dǎo)致兩者無法 return,所以建議 finally 中不要存在 return 關(guān)鍵字;
finallize() 是 Object 類的 protected 方法,子類能夠覆蓋該方法以實現(xiàn)資源清理工作;
GC 在回收前均會調(diào)用該方法,但是 finalize() 方法存在如下問題:
- Java 語言規(guī)范不保證 finalize() 方法會被及時執(zhí)行,也不保證他們一定被執(zhí)行;
- finalize() 方法會帶來性能問題,因為 JVM 通常在單獨的低優(yōu)先線程中完成 finalize 的執(zhí)行;
- finalize() 方法中,可將待回收對象賦值給 GC Roots 可達(dá)的對象引用,從而達(dá)到對象再生的目的;
- finalize() 方法最多由 GC 執(zhí)行一次(但是可以手動調(diào)用對象的 finalize 方法);
4. 運算符
4.1 算術(shù)運算
| + | 加法 - 相加運算符兩側(cè)的值 | A + B 等于 30 |
| - | 減法 - 左操作數(shù)減去右操作數(shù) | A – B 等于 -10 |
| * | 乘法 - 相乘操作符兩側(cè)的值 | A * B等于200 |
| / | 除法 - 左操作數(shù)除以右操作數(shù) | B / A等于2 |
| % | 取余 - 左操作數(shù)除以右操作數(shù)的余數(shù) | B%A等于0 |
| ++ | 自增: 操作數(shù)的值增加1 | B++ 或 ++B 等于 21 |
| -- | 自減: 操作數(shù)的值減少1 | B-- 或 --B 等于 19 |
注意:++ 和 -- 可以放在操作數(shù)之前,也可以放在操作數(shù)之后;位于操作數(shù)之前時,先自增/減,再賦值;位于操作數(shù)之后,先賦值,再自增/減;總結(jié)起來就是 符號在前就先加/減,符號在后就后加/減。
4.2 關(guān)系運算符
| == | 檢查如果兩個操作數(shù)的值是否相等,如果相等則條件為真。 | (A == B)為假。 |
| != | 檢查如果兩個操作數(shù)的值是否相等,如果值不相等則條件為真。 | (A != B) 為真。 |
| > | 檢查左操作數(shù)的值是否大于右操作數(shù)的值,如果是那么條件為真。 | (A> B)為假。 |
| < | 檢查左操作數(shù)的值是否小于右操作數(shù)的值,如果是那么條件為真。 | (A <B)為真。 |
| >= | 檢查左操作數(shù)的值是否大于或等于右操作數(shù)的值,如果是那么條件為真。 | (A> = B)為假。 |
| <= | 檢查左操作數(shù)的值是否小于或等于右操作數(shù)的值,如果是那么條件為真。 | (A <= B)為真。 |
4.3 位運算符
| & | 如果相對應(yīng)位都是1,則結(jié)果為1,否則為0 | (A&B),得到12,即0000 1100 |
| | | 如果相對應(yīng)位都是 0,則結(jié)果為 0,否則為 1 | 如果相對應(yīng)位都是 0,則結(jié)果為 0,否則為 1 |
| ^ | 如果相對應(yīng)位值相同,則結(jié)果為0,否則為1 | (A ^ B)得到49,即 0011 0001 |
| ? | 按位取反運算符翻轉(zhuǎn)操作數(shù)的每一位,即0變成1,1變成0。 | (?A)得到-61,即1100 0011 |
| << | 按位左移運算符。左操作數(shù)按位左移右操作數(shù)指定的位數(shù)。 | A << 2得到240,即 1111 0000 |
| >> | 按位右移運算符。左操作數(shù)按位右移右操作數(shù)指定的位數(shù)。 | A >> 2得到15即 1111 |
| >>> | 按位右移補(bǔ)零操作符。左操作數(shù)的值按右操作數(shù)指定的位數(shù)右移,移動得到的空位以零填充。 | A>>>2得到15即0000 1111 |
4.4 邏輯運算符
| && | 稱為邏輯與運算符。當(dāng)且僅當(dāng)兩個操作數(shù)都為真,條件才為真。 | (A && B)為假。 |
| || | 稱為邏輯或操作符。如果任何兩個操作數(shù)任何一個為真,條件為真。 | (A || B)為真。 |
| ! | 稱為邏輯非運算符。用來反轉(zhuǎn)操作數(shù)的邏輯狀態(tài)。如果條件為true,則邏輯非運算符將得到false。 | !(A && B)為真。 |
4.5 賦值運算符
| = | 簡單的賦值運算符,將右操作數(shù)的值賦給左側(cè)操作數(shù) | C = A + B將把A + B得到的值賦給C |
| += | 加和賦值操作符,它把左操作數(shù)和右操作數(shù)相加賦值給左操作數(shù) | C + = A等價于C = C + A |
| -= | 減和賦值操作符,它把左操作數(shù)和右操作數(shù)相減賦值給左操作數(shù) | C - = A等價于C = C - A |
| *= | 乘和賦值操作符,它把左操作數(shù)和右操作數(shù)相乘賦值給左操作數(shù) | C * = A等價于C = C * A |
| /= | 除和賦值操作符,它把左操作數(shù)和右操作數(shù)相除賦值給左操作數(shù) | C / = A,C 與 A 同類型時等價于 C = C / A |
| %= | 取模和賦值操作符,它把左操作數(shù)和右操作數(shù)取模后賦值給左操作數(shù) | C%= A等價于C = C%A |
| << = | 左移位賦值運算符 | C << = 2等價于C = C << 2 |
| >>= | 右移位賦值運算符 | C >> = 2等價于C = C >> 2 |
| &= | 按位與賦值運算符 | C&= 2等價于C = C&2 |
| ^= | 按位異或賦值操作符 | C ^ = 2等價于C = C ^ 2 |
| |= | 按位或賦值操作符 | C | = 2等價于C = C | 2 |
4.6 條件運算符(? :)
也叫作三元運算符,共有 3 個操作數(shù),且需要判斷布爾表達(dá)式的值;
variable x = (expression) ? value if true : value if false4.7 instanceof
用于操作對象實例,檢查該對象是否是一個特定類型(類類型或接口類型);
( Object reference variable ) instanceof (class/interface type)4.8 equals() 和 ==
- ==
基本數(shù)據(jù)類型用 == 比較的是值,用于引用數(shù)據(jù)類型時判斷兩個對象的內(nèi)存地址是否相等,即兩對象是否是同一個對象;
本質(zhì)來講,由于 Java 中只有值傳遞,所以不管是基本數(shù)據(jù)類型還是引用數(shù)據(jù)類型,比較的其實都是值,只不過引用類型變量存的值是對象的地址;
- equals()
作用也是判斷兩個對象是否相等,但是 不能用于基本數(shù)據(jù)類型變量的比較。存在于 Object() 類中,所以所有類都具有 equals() 方法存在兩種使用情況:
5. 方法
5.1 方法的類型
5.2 重載和重寫
重載就是同樣方法能夠根據(jù)輸入的不同,做出不同的處理。重載發(fā)生在 編譯期,而且在同一個類中,方法名必須相同,參數(shù)類型、參數(shù)個數(shù)、參數(shù)順序不同,返回值和訪問修飾符可以不同。 總的而言:重載就是同一類中多個同名方法根據(jù)不同傳參來執(zhí)行不同的邏輯處理。
重寫是當(dāng)子類繼承自父類的相同方法,輸入數(shù)據(jù)一樣,但最終響應(yīng)不同于父類。重寫發(fā)生在 運行期,是子類對父類允許訪問的方法的實現(xiàn)邏輯進(jìn)行改寫。重寫方法的方法名、參數(shù)列表以及返回值必須相同,拋出的異常范圍不超出父類,訪問修飾符的范圍也不能小于父類。此外,若父類方法別 private/final/static 修飾,則子類無法重寫父類方法,但 static 修飾的方法能被再次聲明。構(gòu)造方法是個特例,不能被重寫。總結(jié)起來就是:重寫即子類對父類方法的改造,外部樣子不能改變,但能夠改變內(nèi)部邏輯。
| 參數(shù)列表 | 必須不同 | 必須相同 |
| 返回類型 | 可不同 | 必須相同 |
| 訪問修飾符 | 可不同 | 不能比父類更嚴(yán)格 |
| 發(fā)生范圍 | 同一類中 | 父子類 |
| 異常范圍 | 可修改 | 可以減少或刪除,不能拋新異常或范圍更廣的異常 |
| 發(fā)生階段 | 編譯期 | 運行期 |
5.3 深/淺拷貝
淺拷貝是 按位拷貝對象,會創(chuàng)建一個新對象,該對象具有原始對象屬性值的精確拷貝。 若屬性是基本類型,則拷貝的是基本類型的值;若屬性是引用類型(內(nèi)存地址),則拷貝的是內(nèi)存地址。因此,一旦其中任一對象改變了該引用類型屬性,均會影響到對方;
深拷貝會 拷貝所有屬性,同時拷貝屬性指向的動態(tài)分配的內(nèi)存。當(dāng)對象和它引用的對象一起拷貝是即發(fā)生深拷貝,相比于淺拷貝,深拷貝速度較慢同時花銷更大。
淺拷貝后,改變其中任一份值都會引起另一份值的改變;而深拷貝后,改變其中任何一份值,均不會對另一份值造成影響;
5.4 值傳遞
推薦閱讀:https://juejin.im/post/5bce68226fb9a05ce46a0476
5.4.1 形參和實參
5.4.2 值傳遞和引用傳遞
方法被調(diào)用時,實參通過形參將其內(nèi)容副本傳入方法內(nèi)部,此時形參接收的內(nèi)容實際上是實參的一個拷貝,因此在方法內(nèi)對形參的任何操作均只針對于實參的拷貝,不會影響到實參原始值的內(nèi)容。即 值傳遞的是實參的一個副本,對副本的操作不會影響實參原始值,也即無論形參如何變化,都不會影響到實參的內(nèi)容。
public static void valueCrossTest(int age,float weight){System.out.println("傳入的age:"+age);System.out.println("傳入的weight:"+weight);age=33;weight=89.5f;System.out.println("方法內(nèi)重新賦值后的age:"+age);System.out.println("方法內(nèi)重新賦值后的weight:"+weight); }public static void main(String[] args) {int a=25;float w=77.5f;valueCrossTest(a,w);// a = 25,原始值不收影響System.out.println("方法執(zhí)行后的age:"+a);// w = 77.5,原始值不收影響System.out.println("方法執(zhí)行后的weight:"+w) }引用即指向真實內(nèi)容的地址值,在方法調(diào)用時,實參的地址被傳遞給相應(yīng)形參,在方法體內(nèi),形參和實參指向同一個地址內(nèi)存,因此此時操作形參也會影響到實參的真實內(nèi)容。
但 Java 中并 不存在引用傳遞,因為 無論是基本類型還是引用類型,在實參傳入形參時,均為值傳遞,即傳遞的都是一個副本,而非實參內(nèi)容本身。
如果是對基本數(shù)據(jù)類型的數(shù)據(jù)進(jìn)行操作,由于實參原始內(nèi)容和副本都是存儲實際值,并且處于不同棧區(qū),因此對形參的操作,實參原始內(nèi)容不受影響。
如果是對引用類型的數(shù)據(jù)進(jìn)行操作,分兩種情況,
- 一種是形參和實參保持指向同一個對象地址,則形參的操作,會影響實參指向的對象的內(nèi)容。
- 另一種是形參被改動指向新的對象地址(如重新賦值引用),則形參的操作,不會影響實參指向的對象的內(nèi)容。
6. 面向?qū)ο?/h2>
6.1 面向?qū)ο?vs 面向過程
推薦閱讀:https://www.zhihu.com/question/27468564/answer/757537214
- 面向?qū)ο?#xff08;Object Oriented)
面向過程是一種 對現(xiàn)實世界理解和抽象的方法,更容易維護(hù)、復(fù)用、擴(kuò)展。最主要的特點就是 繼承、封裝、多態(tài),所以 設(shè)計出的系統(tǒng)耦合性較低,但比起面向過程性能要低。
- 面向過程(Procedure Oriented)
面向過程是一種 以過程為中心 的編程思想,以正在發(fā)生為主要目標(biāo)進(jìn)行編程,不同于面向的的是誰受影響。最主要的不同就在于 封裝、繼承、多態(tài),其性能比面向?qū)ο蟾摺?/p>
- 總結(jié)
面向?qū)ο蟮姆绞绞沟妹總€類都各司其職,最后整合到一起來共同完成一個項目,而面向過程則是讓一個類中的功能越來越多,就像一個全棧工程師能夠一個人搞定所有事。
6.2 封裝、繼承、多態(tài)
將客觀事物封裝為抽象的類,同時類能把自己的數(shù)據(jù)和方法只讓可信的類或?qū)ο筮M(jìn)行操作,對不可信的類進(jìn)行信息隱藏。即把屬于同一類事物的共性(屬性與方法)歸到一個類,從而方便使用。
通過 封裝,實現(xiàn)了 專業(yè)分工,將能實現(xiàn)特定功能的代碼封裝為獨立實體,供我們在需要時調(diào)用。此外,封裝還 隱藏了信息以及實現(xiàn)細(xì)節(jié),使得我們通過訪問權(quán)限權(quán)限符就能將想要隱藏的信息隱藏起來。
可以使用現(xiàn)有類的所有功能,且無需重寫現(xiàn)有類來進(jìn)行功能擴(kuò)展,即個性對共性的屬性與方法的接受,并加入特性所特有的屬性與方法。通過繼承的新類叫做 子類/派生類,被繼承的類叫做 父類/基類/超類,具有如下特點:
- 子類擁有父類對象所有屬性和方法,但父類中的私有屬性和方法,子類是無法訪問的;
- 子類可以對父類進(jìn)行擴(kuò)展;
- 子類可以用自己的方式來實現(xiàn)父類的方法;
多態(tài)是允許 將父對象設(shè)置為和一個或多個其子對象相等的技術(shù),賦值后,父對象能夠根據(jù)指向的子類對象的特性以不同方式運作,即 父類引用指向子類對象實例,有 重載和重寫 兩種實現(xiàn)方式。具有如下特點:
- 對象類型不可變,但引用類型可變;
- 對象類型和引用類型之間有繼承(類)/實現(xiàn)(接口)的關(guān)系;
- 方法具有多態(tài)性,但屬性不具有;
- 若子類重寫了父類方法,則真正執(zhí)行的是子類覆蓋的方法,若子類未覆蓋父類方法,則調(diào)用父類的方法。
6.3 成員變量 vs 局部變量 vs 靜態(tài)變量
| 成員變量 | 1、 屬于類 2、能被訪問控制符、static、final 等修飾 | 堆 | 與對象共存亡 | 有,基本數(shù)據(jù)類型為對應(yīng)默認(rèn)值,而對象統(tǒng)一為 null | 對象調(diào)用 | 實例變量 |
| 局部變量 | 1、屬于方法(方法中的變量或參數(shù)) 2、不能被訪問控制符及 static 修飾,但可以被 final 修飾 | 棧 | 與方法共存亡 | 無,必須定義賦值后使用 | ||
| 靜態(tài)變量 | 1、屬于類 2、被 static 修飾,被所有類對象共用 | 方法區(qū) | 與類共存亡 | 同成員變量初始化值 | 類名調(diào)用(推薦)、對象調(diào)用 | 類變量 |
6.4 構(gòu)造方法的特點
6.5 抽象類 & 接口
-
接口中所有方法默認(rèn)是 public,而且不能有實現(xiàn)(Java 8 之前,Java 8 開始可以有默認(rèn)實現(xiàn));
-
接口中所有變量均為 static、final,不能有其他變量;
-
一個類可以實現(xiàn)多個接口(通過 implements 關(guān)鍵字),而且接口自身可以通過 extends 來擴(kuò)展多個接口;
-
接口是對行為的抽象,屬于行為規(guī)范;
- 抽象類中既可以有抽象方法,也可以有非抽象的方法;
- 一個類只能實現(xiàn)一個抽象類;
- 抽象方法可以被 public、protected、default 修飾,但不能用 private,否則不能被重寫;
- 抽象是對類的抽象,是一種模板設(shè)計;
6.6 Object 類中常見方法
| public final native Class<?> getClass() | 用于返回當(dāng)前運行時對象的 Class 對象,使用了final 關(guān)鍵字修飾,故不允許子類重寫 |
| public native int hashCode() | 用于返回對象的哈希碼,主要使用在哈希表中,比如 JDK 中的 HashMap |
| public boolean equals(Object obj) | 用于比較 2 個對象的內(nèi)存地址是否相等,String 類對該方法進(jìn)行了重寫用戶比較字符串的值是否相等 |
| protected native Object clone() throws CloneNotSupportedException | 用于創(chuàng)建并返回當(dāng)前對象的一份淺拷貝。一般情況下,對于任何對象 x,表達(dá)式 x.clone() != x 為true,x.clone().getClass() == x.getClass() 為 true。Object 本身沒有實現(xiàn) Cloneable 接口,所以不重寫clone方法并且進(jìn)行調(diào)用的話會發(fā)生CloneNotSupportedException 異常 |
| public String toString() | 返回類的名字@實例的哈希碼的16進(jìn)制的字符串。建議Object所有的子類都重寫這個方法 |
| public final native void notify() | 不能重寫。喚醒一個在此對象監(jiān)視器上等待的線程(監(jiān)視器相當(dāng)于就是鎖的概念)。如果有多個線程在等待只會任意喚醒一個 |
| public final native void notifyAll() | 不能重寫。跟notify一樣,唯一的區(qū)別就是會喚醒在此對象監(jiān)視器上等待的所有線程,而不是一個線程 |
| public final native void wait(long timeout) throws InterruptedException | 不能重寫。暫停線程的執(zhí)行注意:sleep方法沒有釋放鎖,而wait方法釋放了鎖 。timeout是等待時間,調(diào)用該方法后當(dāng)前線程進(jìn)入睡眠狀態(tài),知道如下時間發(fā)生: 1. 其他線程調(diào)用該對象的 notify()/notifyAll() 方法; 2. 時間間隔到了; 3. 其他線程調(diào)用了 interrupt() 中斷該線程; |
| public final void wait(long timeout, int nanos) throws InterruptedException | 多了nanos參數(shù),這個參數(shù)表示額外時間(以毫微秒為單位,范圍是 0-999999)。 所以超時的時間還需要加上 nanos 毫秒 |
| public final void wait() throws InterruptedException | 跟之前的 2 個 wait 方法一樣,只不過該方法一直等待,沒有超時時間這個概念 |
| protected void finalize() throws Throwable { } | 實例被垃圾回收器回收的時候觸發(fā)的操作 |
6.7 hashCode & equals
推薦閱讀:https://juejin.im/post/5a4379d4f265da432003874c
6.7.1 equals
| 自反性 | 對任意非空引用值 x,x.equals(x) 應(yīng)該返回 true |
| 對稱性 | 對于任何非空引用值 x和 y,當(dāng) y.equals(x) 返回 true時,x.equals(y) 也應(yīng)返回 true |
| 傳遞性 | 對于任何非空引用值x、y 和 z,如果 x.equals(y) 返回 true, 并且 y.equals(z) 返回 true,那么 x.equals(z) 也應(yīng)返回 true |
| 一致性 | 對于任何非空引用值 x 和 y,多次調(diào)用 x.equals(y) 始終返回 true 或始終返回 false, 前提是對象上 equals比較中所用的信息沒有被修改 |
| 非空性 | 對于任何非空引用值 x,x.equals(null) 都應(yīng)返回 false |
6.7.2 hashCode
hashCode 用于返回對象 hash 值,主要是為了加快查找的快捷性,因為 hashCode() 是 Object 類中的方法,所以所有 Java 類均有 hashCode(),在 HashTable 和 HashMap 這類的散列結(jié)構(gòu)中,均是通過 hashCode() 來查找在散列表中位置,通過 hashCode 能夠較快的茶道小內(nèi)存塊。
6.7.3 為什么重寫 equals() 必須重寫 hashCode()
6.8 序列化與反序列化
6.8.1 定義
- 序列化:指將對象轉(zhuǎn)換為字節(jié)序列的過程;
- 反序列化:指將字節(jié)序列轉(zhuǎn)換為目標(biāo)對象的過程;
6.8.2 需要序列化的場景
當(dāng) Java 對象需要在網(wǎng)絡(luò)上傳輸或者持久化存儲到文件中時,我們就需要對象進(jìn)行序列化;
6.8.3 如何實現(xiàn)序列化
要實現(xiàn)序列化,只需要讓類實現(xiàn) Serializable 接口即可,此時就標(biāo)注該類對象能夠被序列化;
針對類中某些數(shù)據(jù)不想序列化時,可以使用 transient 關(guān)鍵字來實現(xiàn),例如:
// 通過關(guān)鍵字 transient 修飾,表明不參與序列化 transient private String telephone;總結(jié)
以上是生活随笔為你收集整理的【剑指 Java】第 1 弹:靠这份 Java 基础知识总结,我拿到了满意的 Offer的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 详细解读 PolarDB HTAP 的功
- 下一篇: SpringBoot SSO整合