Java面試之Java基礎問題答案口述整理

Chiakiiii發表於2020-09-18

Java面試之基礎問題答案口述整理

物件導向的理解

物件導向思想就是在計算機程式設計過程中,把具體事物的屬性特性和行為特徵抽象出來,描述成計算機事件的設計思想。它區別於程式導向的思想,強調的是通過呼叫物件的行為來實現功能,而不是自己一步步去操作實現。舉個洗衣服的例子,採用程式導向的思想去完成洗衣服這個需求,需要一步步實現,首先把衣服脫下來,再找個盆,加入洗衣粉,加水浸泡,開始洗衣服,然後清洗,再擰乾最後晾起來,它強調的是步驟;而採用物件導向的思想去完成這個需求時,我們只需要找到一個物件,然後呼叫物件的行為來實現需求,而這個物件就是全自動洗衣機,使用這個物件的洗衣功能就能完成需求,它不關注中間步驟,強調的是物件

物件和類的關係

類是對一類事物的描述,是抽象的;

物件是一類事物的例項,是具體的;

類是物件的模板,物件是類的實體。

Java語言是一種物件導向的語言,包含了三大基本特徵,即封裝繼承多型

封裝就是把一個物件的屬性私有化,對於需要訪問的屬性提供一些可以被外界訪問的公共方法。採用private關鍵字來完成封裝操作,被private關鍵字修飾的的成員變數和成員方法,只能在本類中訪問。適當的封裝可以讓程式碼更容易理解和維護,也加強了程式碼的安全性。

繼承就是子類繼承父類的屬性和行為,使得子類物件具有與父類相同的屬性和行為。子類可以直接訪問父類中的非私有的屬性和行為(共性抽取)。同時子類可以擁有自己的屬性和方法實現對父類的擴充套件,同時子類也可以根據需要重寫父類方法實現對父類的增強Java只支援單繼承

多型指的是同一行為具有多個不同的表現形式。比如跑這一行為,貓、狗、馬等跑起來是不用一樣的。像這樣同一行為,通過不同事物可以體現不同形態,這就是多型。在Java中有兩種形式可以實現多型:繼承(多個子類對同一方法的重寫)和實現(實現介面時重寫介面中的同一方法)。在Java中多型體現在:父類引用指向子類物件、方法重寫。多型的優點在於使程式編寫更簡單,同時具有良好的擴充套件性。

抽象類和介面

說到抽象類,先說抽象方法,抽象方法就是沒有方法體的方法,我們把包含抽象方法的類叫做抽象類。採用格式修飾符 + abstract class + 類名來定義一個抽象類。抽象類方法可以是publicprotecteddefault修飾。

  • 抽象類不能建立物件;
  • 抽象類中,不一定包含抽象方法,但是有抽象方法的類必定是抽象類;
  • 抽象類的子類必須重寫父類中所有的抽象方法,除非該子類也是抽象類;

介面是Java中的一種引用型別,是方法的集合,介面內部封裝了方法,包含抽象方法(JDK7及以前),預設方法和靜態方法(JDK8),私有方法(JDK9)。採用修飾符 + interface + 介面名來定義一個介面。介面不能建立物件,可以被實現(implements關鍵字)。介面方法預設是public修飾。實現介面的類必須實現介面中所有抽象方法,否則必須是一個抽象類。通過對介面中抽象方法進行重寫可以進行介面多實現,這是多型的體現。介面的好處在於:1)制定標準。制定標準的目的就是為了讓定義和實現分離,而介面作為完全的抽象,是標準制定的不二之選。2)提供抽象。介面的抽象特性得以讓介面的呼叫者和實現者可以完全的解耦。

類中各部分的初始化順序

一個類中的初始化順序

類內容(靜態變數、靜態程式碼塊)==> 例項內容(成員變數、初始化塊、構造器)

具有繼承關係的兩個類的初始化順序

父類靜態變數、靜態程式碼塊 ==> 子類靜態變數、靜態程式碼塊 ==> 父類成員變數、初始化塊、構造器 ==> 子類成員變數、初始化塊、構造器

Java建立類的例項的幾種方法

  • new關鍵字

    User user = new User();
    
  • 反射

    // 方法1: Class.forName("全類名")
    User u1 = (User) Class.forName("com.chiaki.domain.User").newInstance();
    // 方法2: 已有例項物件.getclass()
    User u = new User();
    User u2 = u.getClass().newInstance();
    // 方法3: 類名.class
    User u3 = User.class.newInstance();
    
  • 呼叫物件的clone()方法,只限於實現了java.lang.Cloneable介面的類。

    User user = new User();
    User userCopy = (User) user.clone();
    
  • 運用反序列化手段

    序列化指將物件狀態轉化為可保持或傳輸的格式的過程,被序列化的物件必須實現java.io.Serializable介面(被statetransient關鍵字修飾的成員變數不能被序列化)。因此通過反序列化手段可以將流轉化成物件,從而完成物件的建立。

JVM、JDK、JRE的關係

JVM全稱Java Virtual Machine,即Java虛擬機器,是執行Java位元組碼(.class檔案)的虛擬機器。JVM面對不同的OS會有特定的實現,目的在於使用相同的位元組碼檔案,在不同OS都會給出相同結果(一次編譯,處處執行)。

JDK全程Java Development Kit,即Java開發工具包。JDK = Java開發工具(編譯器javac、jar、javadoc等) + JRE。

JRE是Java執行時環境,用於執行已經編譯的Java程式。JRE = JVM + Java核心類庫(java.lang包)。

總結:JDK = Java開發工具 + JRE = Java開發工具 + JVM + Java核心類庫

Java的資料型別

基本型別:byte[1]、short[2]、int[4]、long[8]、float[4]、double[8]、char[2]、boolean[1]

引用型別:介面、陣列、類

為什麼有了double後還需要long?

  • long與double在java中本身都是用64位儲存的,但是他們的儲存方式不同,導致double可儲存的範圍比long大很多;
  • long可以準確儲存19位數字,而double只能準備儲存16位數字(實際測試,是17位)。double由於有exp位,可以存16位以上的數字,但是需要以低位的不精確作為代價。如果一個大於17位的long型數字存到double上,就會丟失數字末尾的精度;
  • 如果需要高於19位數字的精確儲存,則必須用BigInteger來儲存,當然會犧牲一些效能。

重寫和過載

重寫(Override)的範圍是在繼承關中的子類中,發生在執行期,指的是方法名相同,引數列表相同,返回型別相同,異常範圍小於等於父類,訪問修飾符的範圍大於等於父類;

過載(Overload)的範圍是在同一個類中,發生在編譯期,指的是方法名相同,引數列表不同(順序、個數),返回型別、異常以及訪問修飾符都可以修改。

構造方法可以過載,但不能重寫。

String、StringBuffer 和 StringBuilder

可變性:String類中使用final關鍵字修飾字元陣列,所以String物件不可變;StringBuffer和StringBuilde繼承自AbstractStringBuilder類,沒有使用final修飾,是可變的。

執行緒安全性:String物件不可變,執行緒安全;StringBuffer類中的方法使用synchronized關鍵字修飾,是執行緒安全的;StringBuilder類中的方法沒有進行同步處理,執行緒不安全。

效能:對String物件進行改變時都會生成新的String物件,然後將引用指向新的String物件。StringBuffer和StringBuilder每次都是對自身物件進行操作,但由於StringBuffer需要進行同步處理,其效能比StringBuilder低。

== 與 equals()

== : 判斷兩個物件的地址是否相等,即判斷兩個物件是不是同一個物件。當物件為基本資料型別時,比較的是值;當物件為引用資料型別時,比較的是記憶體地址。

equals() : 判斷兩個物件是否相等。分兩種情況:

  • 未重寫:與 == 等價;
  • 已重寫:比較兩個物件的內容是否相等。

hashCode() 與 equals()

hashCode()用於獲取物件的雜湊碼,返回一個int型別整數,雜湊碼可以用於確定物件在雜湊表中的索引位置,因此hashCode()在雜湊表中才有用。

equals() : 判斷兩個物件是否相等。分兩種情況:

  • 未重寫:與 == 等價;
  • 已重寫:比較兩個物件的內容是否相等。

hashCode()和equals()的相關規定:

  • 若兩個物件A和B相等,那麼A.equals(B)和B.equals(A)均返回true;
  • 若兩個物件A和B相等,那麼A和B的hashCode值也一定相等;
  • hashCode值相同的兩個物件,其不一定相等(比如String物件“通話”和“重地”);
  • 基於以上3點,在重寫equals()方法後,也必須重寫hashCode()方法;
  • 以HashSet為例,先使用hashCode()方法判斷,相同再使用equal()方法。如果只重寫了equals()方法而不重寫hashCode()方法,會造成hashCode()的值不同,而equals()方法判斷結果未true的情況,從而違背上述規定。

程式與執行緒

程式是程式的一次執行過程。系統執行一個程式就是一個程式從建立、執行到消亡的過程。

執行緒是比程式更小的執行單位,一個程式中可以有多個執行緒。執行緒共享程式的堆和方法區的資源,同時執行緒還有私有的程式計數器、虛擬機器棧和本地方法棧。

執行緒的基本狀態:NEW、RUNNABLE、BLOCKED、WAITING、TIME_WAITING、TERMINATED

final、finally和finalize

final關鍵字可以用於修飾變數、方法和類。final修飾變數時,如果是基本資料型別變數,則該變數一旦初始化後便不能修改,如果是引用型別變數,則改變了初始化後不能再指向另一物件;final修飾方法會將方法鎖定,防止任何繼承類對方法進行修改;final修飾類時表明該類不能被繼承,final修飾的類中的所有成員方法都會被隱式指定為final方法。

finally常與try-catch程式碼塊一起使用,通常將一定要執行的程式碼放入finally塊中,比如關閉資源的相關程式碼。無論是否捕獲或者處理異常,finally塊裡的語句都會被執行。

finally塊不被執行的情況:

  • finally塊中第一行出現異常;
  • 在前面的程式碼中使用了System.exit(int)已退出程式。
  • 程式所在的執行緒死亡;
  • 關閉CPU。

finalize是屬於Object類的方法,該方法一般由垃圾回收器呼叫。當呼叫System.gc()時,垃圾回收器會呼叫finalize()方法判斷一個物件是否可以回收。

Java中的異常處理

java.lang.Throwable類的兩個重要子類:Error和Exception。Error是程式無法處理的錯誤,主要是JVM出現的問題,比如虛擬機器錯誤(StackOverFlowError和OutOfMemoryError);Exception是程式可以處理的異常,主要有NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException等

異常處理的方法:

  • 指定方法中丟擲指定異常

    throw new ArrayIndexOutOfBoundsException("陣列下標越界了!");
    
  • 在方法後宣告可能的異常

    throws IOException
    
  • 捕獲異常:try-catch-finally

獲取鍵盤輸入(筆試常用)

通過Scanner:

Scanner sc = new Scanner(System.in);
String s = sc.nextLine();

通過BufferedReader:

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s = br.readLine();

Java的常用IO流

位元組輸入/輸出流:FileInputStrean/FileOutputStream

字元輸入/輸出流:BufferedReader/BufferedWriter、InputStreamReader/OutputStreamWriter

淺拷貝與深拷貝

淺拷貝:對基本資料型別進行值傳遞,對引用資料型別進行引用傳遞般的拷貝;

深拷貝:對基本資料型別進行值傳遞,對引用資料型別,建立一個新的物件,並複製其內容。

如何實現深拷貝?

實現Cloneable介面、反序列化方法

相關文章