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

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介面、反序列化方法