Java后端高頻知識點學習筆記1---Java基礎


Java后端高頻知識點學習筆記1---Java基礎

參考地址:牛_客_網
https://www.nowcoder.com/discuss/819297

1、重載(Overload)和重寫(Override)的區別

重載同一類中多個同名方法根據不同的傳參來執行不同的處理邏輯;方法名必須相同,參數不同(類型、個數、順序),返回值類型可以相同也可以不同(因為返回值類型不是方法簽名的一部分)

重寫子類對父類的方法的實現過程進行重新編寫。方法名,參數列表和返回值類型都不能改變。拋出的異常范圍小於等於父類,訪問修飾符范圍大於等於父類。

什么是方法簽名?
答:方法簽名是區分不同方法的標識符方法是由方法名、形參列表、返回值以及方法體構成方法簽名是由方法名與形參列表構成,也就是說,方法名和形參列表可以唯一地確定一個方法,與方法的返回值沒有關系

構造器是否可以被重寫,是否可以被重載?
答:構造器可以被重載(Overload),不能被重寫(Override)

靜態方法能否被重寫,能否被重載?
答:靜態方法不能被重寫,可以被重載
靜態方法可以被繼承;靜態方法是類在加載時就被加載到內存中的方法,在整個運行過程中保持不變,因而不能重寫;在Java中,如果父類中含有一個靜態方法,且在子類中也含有一個返回類型、方法名、參數列表均與之相同的靜態方法,那么該子類實際上只是將父類中的該同名方法進行了隱藏,而非重寫,可以通過類名.方法名調用被隱藏的方法;換句話說,父類和子類中含有的其實是兩個沒有關系的方法,它們的行為也並不具有多態性

2、Java面向對象的三大特性

封裝:把一個對象的屬性私有化,不允許外部對象直接訪問這些私有屬性,同時向外界提供一些訪問私有屬性的方法
繼承:子類繼承父類的非私有屬性和方法;子類可以對父類的方法進行重寫;也可以進行擴展,擁有自己的屬性和方法;一個子類只能擁有一個父類,但是可以通過實現多個接口來達到多重繼承的目的
多態:同一個操作作用在不同對象時,可以產生不同的執行結果;在Java語言中,多態主要有兩種表現形式;
___編譯時多態-->重載
___運行時多態-->重寫

  • 重載:同一個類中有多個同名方法,根據不同的傳參可以執行不同的處理邏輯;在編譯時就可以確定到底調用哪個方法,它是一種編譯時多態
  • 重寫:子類對父類的方法的實現過程進行重新編寫,方法名,參數列表和返回值類型都不能改變,因此同樣的方法在父類與子類中有着不同的表現形式;
    Java語言中,父類的引用變量不僅可以指向父類的實例對象,也可以指向子類的實例對象。而程序調用的方法在運行時才動態綁定,就是引用變量所指向的具體實例對象的方法,而不是引用變量的類型中的定義的方法;這就會出現相同類型的變量調用同一個方法時呈現出多種不同的行為特征,這就是多態;在運行時才能確定調用哪個方法,被稱為運行時多態

使用多態的好處?
1、應用程序不必為每一個子類編寫功能調用,只需要對抽象父類進行處理即可,提高程序的可復用性
2、子類的功能可以被父類的引用變量所調用,提高可擴充性和可維護性

3、Java面向對象的5大設計原則

原則 描述
單一職責 一個類只負責一個功能的實現
里氏替換 只要父類出現的地方,都可以用子類替換
依賴倒置 高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;就是面向接口編程
接口隔離 接口的功能盡可能單一;接口更可能細化,不要建立臃腫龐大的接口
開閉 盡量通過擴展來面對需求的更改或者系統的變化,盡量不要對原有內容修改

4、String、StringBuilder和StringBuffer的區別是什么?

(1)String是不可變的,StringBuilder和StringBuffer是可變的

  • String不可變,String類中 使用final關鍵字修飾的字符數組 來保存字符串;private final char value[];從Java9開始,String類改用byte字節數組存儲字符串; private final byte[] value; StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在AbstractStringBuilder中也是使用字符數組保存字符串;char[] value;但是沒有用final關鍵字修飾,所以這兩種對象都是可變的

(2)String和StringBuffer是線程安全的,StringBuilder不是線程安全的

  • String中的對象的不可變,StringBuffer對方法加了synchronized同步鎖,所以是線程安全的
    StringBuilder沒有對方法加同步鎖,所以不是線程安全的

(3)執行效率:StringBuilder最高,StringBuffer次之,String最低

  • 每次對String類型進行改變的時候,都會生成一個新的String對象,然后將指針指向新的String對象;StringBuffer每次都會對StringBuffer對象本身進行操作,而不是生成新的對象並改變對象引用,StringBuilder同理;相同情況下使用StringBuilder相比使用StringBuffer僅能獲得10%~15%左右的性能提升,但要冒多線程不安全的風險

總結:
操作少量數據時,優先使用 String

  • (線程安全,效率最低)

當在單線程下操作大量數據,優先使用 StringBuilder

  • (線程不安全,效率最高)

當在多線程下操作大量數據,優先使用 StringBuffer

  • (線程安全,效率中等)

5、String為什么要設置成不可變的?

(1)實現字符串常量池

  • 字符串常量池(String pool)是Java堆內存中一個特殊的存儲區域,當創建一個String對象時,假如此字符串值已經存在於常量池中,則不會創建一個新的對象,而是引用已經存在的對象;假若字符串對象允許改變,那么將會導致各種邏輯錯誤,比如改變一個對象會影響到另一個獨立對象,嚴格來說,這種常量池的思想,是一種優化手段

(2)允許String對象緩存HashCode

  • Java中String對象的哈希碼被頻繁地使用,比如在HashMap等容器中;字符串的不變性保證了hash碼的唯一性;因此可以放心地進行緩存,這也是一種性能優化手段,意味着不必每次都去計算新的哈希碼

(3)安全性

  • String被許多的Java類(庫)用來當做參數,例如:網絡連接地址URL,文件路徑path,還有反射機制所需要的String參數等,假若String不是固定不變的,將會引起各種安全隱患;
    數據庫的用戶名、密碼都是以字符串的形式傳入來獲得數據庫的連接,或者在socket編程中,主機名和端口都是以字符串的形式傳入;因為字符串是不可變的,所以它的值是不可改變的,否則黑客們可以鑽到空子,改變字符串指向的對象的值,造成安全漏洞。
    在並發場景下,多個線程同時讀寫資源時,由於 String 是不可變的,不會引發線程的問題而保證了線程安全

總體來說,String不可變的原因包括設計、效率優化 與 安全性這三大方面

6、自動裝箱與拆箱

裝箱:將基本類型用它們對應的引用類型包裝起來
拆箱:將包裝類型轉化為基本數據類型
如下代碼:

Integer i = 100; // 實際上是Integer.valueOf(100)
int n = i; // 實際上是 i.intValue()

如上代碼所示,在裝箱的時候自動調用的是Integer的valueOf(int)方法;而在拆箱的時候自動調用的是Integer(對象)的intValue()方法

Integer緩存 :-128~127

詳解:在調用Integer.valueOf()方法中,如果數值在[-128,127]之間,將直接從IntegerCache中獲取
因為IntegerCache中緩存了[-128,127]之間的值,通過Integer.valueOf()方法創建數值在[-128,127]之間的Integer對象時,便返回指向IntegerCache中已經存在的對象的引用;否則創建一個新的Integer對象

7、抽象類和接口的異同?

  • 不同點
序號 不同點
1、方法的定義與實現 在JDK1.8之前接口只有方法的定義,不能有方法的實現;JDK1.8中接口可以有默認方法(default修飾)和靜態方法(static修飾)的實現;JDK1.9開始接口中可以定義和實現私有方法(普通私有方法和靜態私有方法);而抽象類可以有方法的定義與實現
2、靜態常量與普通成員變量 接口里只能定義靜態常量(static final),不能定義普通成員變量;抽象類中既可以定義靜態常量,也能定義普通成員變量
3、靜態代碼塊 接口中不能包含靜態代碼塊,抽象類中可以包含靜態代碼塊
4、繼承 一個類只能繼承一個抽象類,但是一個類可以實現多個接口
5、*** 接口強調的是特定功能的實現,抽象類強調的是所屬關系
6、main()方法 接口不能有 main 方法;抽象類可以有 main 方法,並且可以運行它
7、默認修飾符 接口中定義的成員變量,只能是靜態常量,默認修飾符 public static final,而且必須給其賦初值;接口中定義的成員方法,抽象方法,默認修飾符為public abstract;抽象類中成員變量默認default(默認,什么也不寫,同一包中可見),可在子類中被重新定義,也可被重新賦值;抽象類中抽象方法被abstract修飾,不能被private、static、synchronzed和native等修飾,必須以分號結尾,不帶花括號
8、構造器 接口中不包含構造器;抽象類里可以包含構造器,抽象類中的構造器並不是用於創建對象,而是讓其子類調用這些構造器來完成屬於抽象類的初始化操作
9、使用場景 接口被用於常用的功能,便於日后維護和添加刪除抽象類更傾向於充當公共類的角色,不適用於日后重新對內部的代碼修改;功能需要累積時用抽象類,不需要累積時用接口
  • 相同點
序號 相同點
1 接口和抽象類都不能被實例化;接口的實現類或抽象類的子類都只有實現了接口或抽象類中的方法后才能被實例化
2 接口和抽象類都可以包含抽象方法

8、Java中" == "與equals()方法的區別?

" == ":對於八大基本數據類型來說,直接比較值;如果是引用數據類型,則是比較內存地址是否相同;(因為Java只有值傳遞,所以對於" == "來說,不管是比較基本數據類型,還是引用數據類型的變量,本質都是比較值,只是引用類型變量存的值是對象的地址)

equals():equals()是Object類提供的方法之一;每一個Java類都繼承自Object類,所以每一個對象都具有equals方法;Object中的equals方法是直接使用" == "運算符比較兩個對象,所以在沒有重寫equals方法的情況下,equals與" == "運算符一樣,比較的是地址;可以通過重寫equals方法來比較兩個對象的內容是否相等

9、為什么重寫equals()方法時必須重寫hashCode()方法?

equals()和hashCode()方法要遵循如下的原則
1、如果兩個對象equals()方法相等,它們的hashCode返回值一定要相同
2、如果兩個對象的hashCode()返回值相同,但它們的equals()方法不一定相等
3、如果兩個對象的hashcode()返回值不相等,則兩個對象的equals()一定不相等

如果只重寫equals()方法而不重寫hashCode()方法,將會造成equals()方法判斷出的結果是true,但hashCode()返回值不同的情況,也可能會出現hashCode()方法返回值不相等,但equals()方法相同的情況,因此equals()方法與hashCode()方法都要進行修改,使兩者遵循上述原則;即:equals方法被覆蓋,則hashCode方法也必須被覆蓋,始終遵循上述原則

問題:為什么需要hashCode()方法?
答:使用hashCode()方法提前校驗,避免每一次比較都調用equals方法,提高效率

面試官:你有沒有重寫過equals()和hashcode()?
答:在使用HashMap的“key”的部分存放自定義的對象時,重寫過hashCode和equals方法,從而保證key是唯一的

10、Java中的八大基本數據類型

Java有8中基本數據類型,其中包括
6種數字類型:byte(1)、short(2)、int(4)、long(8)、float(4)、double(8)
1種字符類型:char(2)
1種布爾型:boolean
對於boolean型,官方文檔未明確定義,它依賴於JVM廠商的具體實現

11、final關鍵字

final用來修飾類、方法和變量

1、final修飾的類不能被繼承,final類中的所有成員方法都會被隱式的指定為final方法(但是final修飾的類中成員變量是可變的,如果想要final類的成員變量不可變,必須給成員變量增加final修飾)

2、final修飾的方法不能被重寫

3、final修飾的變量是常量;如果是基本數據類型的變量,則其數值一旦在初始化之后便不能更改;如果是引用類型的變量,則在對其初始化之后便不能讓其指向另一個對象,即引用類型變量始終指向同一地址

12、static關鍵字

static關鍵字主要有以下4種用法:

1、修飾類成員(成員變量 和 成員方法)
static修飾的成員變量屬於這個類,被類的所有對象共享,可通過 類名.成員變量對象.成員變量 的方式調用

  • static變量也稱作靜態變量,靜態變量和非靜態變量的區別是:靜態變量被所有的對象所共享,在內存中只有一個副本,它當且僅當在類初次加載時會被初始化;而非靜態變量是實例對象所獨有的,在創建對象的時候被初始化,存在多個副本,各個對象擁有的副本互不影響;static成員變量的初始化順序按照定義的順序進行初始化

  • static方法也稱作靜態方法,靜態方法不依賴於任何對象就可以進行訪問,靜態方法只能訪問靜態成員,不能訪問類的非靜態成員,因為非靜態成員必須依賴具體的對象才能夠被調用(成員:成員方法與成員變量)

2、靜態代碼塊

靜態代碼塊定義在類中方法外,類中可以有多個static塊
靜態代碼塊在非靜態代碼塊之前;按照static塊的順序來執行 (靜態代碼塊-->非靜態代碼塊-->構造方法)
一個類不管創建多少對象,靜態代碼塊只執行一次

靜態代碼塊的加載次序:
父類靜態代碼塊-->子類靜態代碼塊-->父類非靜態代碼塊-->父類構造函數-->子類非靜態代碼塊-->子類構造函數

3、靜態內部類

static修飾類的話只能修飾內部類;靜態內部類與非靜態內部類之間存在一個最大的區別: 非靜態內部類在編譯完成之后會隱含地保存着一個引用,該引用是指向創建它的外圍類,但是靜態內部類卻沒有。沒有這個引用就意味着:(1)它的創建是不需要依賴外圍類的創建;(2)它不能使用任何外圍類的非靜態成員(成員:成員方法與成員變量)

4、靜態導包

用來導入類中的靜態資源,1.5之后的新特性;可以指定導入某個類中的指定靜態資源,並且不需要通過類名調用類中的靜態成員,可以直接使用類中靜態成員變量和成員方法;
優點:靜態導入可減少代碼輸入量
注意:當兩個不同的類中有命名相同的方法都使用靜態導入時,仍然需要通過 類名.方法名 的方式進行調用,否則會報錯!

  • 導入語法:
    import static 包名.類名.靜態成員變量;
    import static 包名.類名.靜態成員方法;
    import static 包名.類名.*;

13、深拷貝和淺拷貝

淺拷貝:對基本數據類型進行值傳遞;對引用數據類型只是進行引用的傳遞,並沒有在內存中的創建一個新的對象
深拷貝:對基本數據類型進行值傳遞;對引用數據類型,先創建一個新的對象,再復制其內容,最后傳遞新對象的引用

14、Java異常體系

Java異常體系

Java中,所有的異常都有⼀個共同的祖先java.lang包中的Throwable類:Throwable類有兩個重要的⼦類:Exception(異常) 和 Error(錯誤),⼆者都是Java異常體系的重要子類,各自都包含大量子類

Error(錯誤):程序⽆法處理的錯誤,表示運⾏應⽤程序中較嚴重問題;⼤多數錯誤與代碼編寫者執⾏的操作⽆關,⽽表示代碼運⾏時JVM出現的問題;例如,Java 虛擬機運⾏錯誤(Virtual MachineError),當 JVM 不再有繼續執⾏操作所需的內存資源時,將出現內存溢出(OutOfMemoryError);這些異常發⽣時,JVM⼀般會選擇終⽌線程

Exception(異常):程序本身可以處理的異常;Exception 類有⼀個重要的⼦類RuntimeException;RuntimeException異常由JVM拋出;還有NullPointerException(要訪問的變量沒有引⽤任何對象時,拋出該異常);ArithmeticException(算術運算異常,⼀個整數除以0時,拋出該異常)以及 ArrayIndexOutOfBoundsException(數組下標越界異常)

Exception(異常)又分為兩類:運行時異常、編譯時異常

  • 1、運行時異常(不受檢異常):RuntimeException類及其子類表示JVM在運行期間可能出現的錯誤;比如說試圖使用空值對象的引用(NulIPointerException)、數組下標越界(ArraylndexOutBoundException);此類異常屬於不可查異常,一般是由程序邏輯錯誤引起的,在程序中可以選擇捕獲處理,也可以不處理
    2、編譯時異常(受檢異常):如果程序中出現此類異常,比如說IOException,必須對該異常進行處理,否則編譯不通過;在程序中,通常不會自定義該類異常,而是直接使用系統提供的異常類

15、反射(待補充完善)

1、什么是Java的反射
反射 是 在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱為 Java 語言的反射機制

2、反射的作用
運行時,判斷對象所屬的類;構造一個對象所屬的類;判斷一個對象的方法和屬性;調用對象的方法和屬性

16、Java泛型

Java的泛型即"參數化類型"允許程序在創建集合時指定該集合元素的類型,表示該集合只能保存該類型的對象

  • 為什么要使用泛型?
    如果不使用泛型,當把一個對象存入集合后,集合就會忘記這個對象的數據類型,再次取出該對象時,該對象的編譯類型就變成了Object類型,還需要進行強制轉換;
    當使用泛型后,集合中只能存入指定類型的對象,否則將報錯,並且將對象從集合取出后無需對元素進行強制轉換,就是原本指定的類型
  • **作用:
    • 1、防止了 "在集合中存儲對象並在使用前進行類型轉換" 情況的發生,提供了編譯時的類型安全**
    • 2、確保只能把正確類型的對象放入集合中,避免了在運行時出現ClassCastException(類型轉換異常)

擴展知識:
父子對象之間的轉換分為了向上轉型 和 向下轉型;它們解釋如下:

向上轉型通俗地講,就是將子類對象向上轉為父類對象,或者說是父類引用指向子類對象,此處父類對象可以是接口
格式:父類 變量名 = new 子類();

向上轉型的好處?
向上轉型后,父類引用可以調用子類重寫(Override)過的父類方法,當需要新添功能時,可以新增一個(子)類即可,避免修改原來的代碼
向上轉型后的對象不是新創建的父類對象,而是子類對象的"簡化"狀態,它不關心子類新增的功能,只關心子類繼承和重寫的功能;當一個類有很多子類時,並且這些子類都重寫了父類中的某個方法,使用上轉型對象調用這個方法時就可能具有多種形態,因為不同的子類在重寫父類的方法時可能產生不同的行為;也就是說,不同對象的上轉型對象調用同一方法可能產生不同的行為

向下轉型:與向上轉型相反,即是把(已向上轉型的)父類對象** 轉為 子類對象;一個已經向上轉型的子類對象,將父類引用轉為子類引用,可以使用強制轉換的格式**
格式:子類 變量名 = (子類類型)父類變量;

  • 為什么要向下轉型?
    向下轉型一般是為了重新獲得因為向上轉型而丟失的子類特性;因此,通常在向下轉型前已經有向上轉型,而向下轉型通常也會結合instanceof一起使用;借由向下轉型,可以在靈活應用多態的基礎上,同時兼顧子類的獨有特征(instanceof:用來測試一個對象是否為一個類的實例)
    用法是:
boolean result = obj instanceof Class;  //測試 obj 是否為 Class 的實例

17、什么是泛型擦除

在代碼中定義List<Object>List<String>等類型,在編譯后都會變成List,JVM看到的只是List,而由泛型附加的類型信息對JVM是看不到的

在如下例子中,定義了兩個ArrayList數組;list1只能存儲字符串,list2只能存儲整數;通過list1對象和list2對象的getClass()方法獲取他們的類信息,最后發現結果為true
說明泛型類型 String 和 Integer 都被擦除掉了,只剩下原始類型

//Java代碼
import java.util.*;
public class Test {
    public static void main(String[] args) {
        List<String> l1 = new ArrayList<String>();
        List<Integer> l2 = new ArrayList<Integer>();
        System.out.println(l1.getClass() == l2.getClass());  
        System.out.println("l1.getClass():"+l1.getClass().getName());
        System.out.println("l1.getClass():"+l2.getClass().getName());
    }
}

運行結果:

true
l1.getClass():java.util.ArrayList
l1.getClass():java.util.ArrayList

18、Java Object類中的方法

方法 含義
getClass() 返回一個對象的運行時類
int hashCode() 返回該對象的哈希碼值
boolean equals(Object obj) 判斷其他對象是否與此對象 “相等”
String toString() 返回該對象的 “字符串表示”
void notify() 喚醒在此對象監視器上等待的單個線程
void notifyAll() 喚醒在此對象監視器上等待的所有線程
void wait() 導致當前線程等待並放棄鎖的持有,直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法;當前線程必須是此對象的監視器所有者,否則還是會發生 IllegalMonitorStateException 異常
protected Object clone() 創建並返回此對象的一個副本(淺拷貝,拷貝引用地址,不在內存之創建新對象並指向它)
protected void finalize() 當垃圾回收器確定不存在對該對象的更多引用時,由對象的垃圾回收器調用此方法

19、Java String類中的方法

Java String類中的方法

20、Java創建對象的5種方式?

① 使用new關鍵字
② 使用Class類的newInstance方法
③ 使用Constructor類的newInstance方法
④ 使用clone方法
⑤ 使用反序列化

21、Java訪問修飾符的范圍

Java訪問修飾符的范圍

22、Hash沖突的解決方式?

1、開放定址法
當發生沖突時,使用某種探查技術在散列表中形成一個探查序列,沿此序列逐個單元地查找,直到找到一個開放的地址(地址單元為空)或探查序列查找完為止;若探查到開放的地址,則可將待插入的新結點存人該地址單元;若探查序列查找完都沒有找到開放的地址,則失敗

按照形成探查序列的方法不同,可將開放定址法區分為線性探查法、二次探查法、隨機探查法
① 線性探查法
② 二次探查法
③ 隨機探查法

2、拉鏈法

將所有哈希地址為i的元素構成一個稱為同義詞鏈的單鏈表,並將單鏈表的頭指針存到哈希表的第i個單元中,因而查找、插入和刪除主要在同義詞鏈中進行
拉鏈法(鏈地址法)適用於經常進行插入和刪除的情況

3、再Hash法

當計算出的hash值產生沖突時,再計算另一個Hash函數的哈希值,直到沖突不再發生為止

4、建立公共溢出區

將哈希表分為基本表和溢出表兩部分,凡是和基本表發生沖突的元素,一律填入溢出表

23、Java中的流分為幾種?

按照流的流向划分,分為 輸⼊流 和 輸出流
按照操作單元划分,分為 字節流 和 字符流
按照流的角色划分,分為 節點流 和 處理流

Java IO流共涉及40多個類,這些類看上去很雜亂,但實際上很有規則,⽽且彼此之間存在⾮常緊密的聯系;Java IO流的 40 多個類都是從如下4個抽象類基類中派⽣出來的;

InputStream、InputReader: 所有的輸⼊流的基類,前者是字節輸⼊流,后者是字符輸⼊流
OutputStream、OutputWriter: 所有輸出流的基類,前者是字節輸出流,后者是字符輸出流


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM