天天看點

Java 面向對象簡單十問

0. 目錄

  1. 對面向對象思想的了解
  2. Java 對象初始化順序
  3. Overload 和 Override 的差別
  4. int 和 Integer 的差別
  5. char 型變量中能否存儲一個中文漢字
  6. Java 中 Serializable 和 Externalizable 的差別
  7. 抽象類和接口的差別
  8. String 和 StringBuilder、StringBuffer 的差別
  9. 闡述 final、finally 和 finalize 的差別
  10. Java 中的異常處理機制和簡單的應用

1. 面向對象思想的了解

面向對象的思想是相對于面向過程而言的,兩者都是解決問題的方式,但是它們的出發點不同。

面向過程的方式關注的是通過怎樣的步驟解決問題,一般通過若幹緊密聯系的步驟達到解決問題的目的。面向對象的方式關注的是怎麼樣将問題盡量拆分為互相獨立的任務,然後以不同的身份去解決這些任務,最後組合這些任務輸入達到解決問題的目的。

這樣看來面向過程的方式顯得更直接一些,但是當遇到的問題規模和複雜程度很大時,這樣的處理方式會使得解決的過程變得十分繁瑣,思路不夠清晰,并且當問題有所變化時,解決方案不好及時作出調整。面向對象的方式,首先将大的問題拆分為相關性很小的不同的子任務子產品,這樣就可以将這些子子產品配置設定給不同的對象去解決,各個對象之間不需要知道對方内部的具體工作内容,隻需要知道互相之間可以怎樣互動就可以了,通過子產品之間的互相調用協作完成最終的大問題,當問題需求出現變化時,很可能隻需要調整各個子產品之間的協作方式,就能做出及時的應對,而不需要從頭來過。是以面向對象的思想更加适合真實世界中複雜問題場景的需求。

可以這樣了解,面向過程是面向對象的基礎環節,面向對象是面向過程的上層建築。面向對象的解決思路中,一個個的子子產品中的子問題的解決依然是面向過程的方式,它們通過一層層的包裝實作了子產品化的結構,就像是一個個提前生産好的工具包,這樣可以減少不必要的重複工作,降低任務間的耦合程度,更加靈活地應對不同場景的需求。

2. Java 對象初始化順序

大緻的的順序是靜态資源先于非靜态資源被加載。下面以​

​Person​

​類為例進行叙述。

1. 靜态初始化隻會在首次需要用到時被初始化一次,這個被用到的時機是`Person`類對象被首次建立或者`Person`類中的靜态資源(屬性或方法)被首次通路。
   2. 靜态初始化之後是非靜态初始化。當使用`new Person()`建立`Person`對象時(靜态資源在這之前已經被加載),`Person`對象中的所有基本類型資料被設定為預設值(0或false或'\u0000'),引用被設定為`null`。之後執行出現在代碼中的字段定義處初始化動作。然後執行構造器。
      

3. Overload 和 Override 的差別

重載(Overload)是在一個類中,方法名相同,而參數清單不同。而傳回類型不作為評判标準,即可以相同也可以不同。

重寫(Override)是子類對父類的可繼承方法的重新實作,其參數清單不能改變,即“換心不換殼”。而對于傳回類型,在 Java 5 之前的重寫必須保持傳回類型不變,Java 5 之後引入了協變傳回類型,即子類重寫方法可以傳回父類方法的傳回類型的子類型。是以,如果父類方法的傳回值類型是​

​Object​

​,那麼子類的重寫方法就可以傳回任意類型的值。

4. int 和 Integer 的差別

​int​

​屬于基本資料類型,基本資料類型的變量不需要使用​

​new​

​來建立,而是直接使用一個變量類存儲“值”,由于基本資料類型的使用頻率很高,這樣建立更加友善高效。​

​Integer​

​是基本資料類型​

​int​

​的包裝類型,每個基本資料類型都有自己對應的包裝類型,用于在堆記憶體裡表示基本類型的資料。

5. char 型變量中能否存儲一個中文漢字

Java 中​

​char​

​類型變量使用​

​Unicode​

​編碼存儲字元,是以可以存儲中文漢字。

char c = '中';
      

6. Java 中 Serializable 和 Externalizable 的差別

兩者都是用于對象的序列化,即儲存記憶體中的對象到硬碟中,以供下次直接使用(反序列化)。對象需要序列化就必須使其類實作 Seriablizable 或者 Externalizable。兩者的差別如下:

  1. Serializable 序列化時不會調用預設的構造器(即無參構造器),而 Externalizable 序列化時會調用預設構造器。
  2. 隻實作 Seriabliable 的對象,其所有屬性(包括private屬性、其應用的對象)都會被序列化和反序列化來進行儲存、傳遞(當然可以通過 ​

    ​Transient​

    ​修飾類取消指定屬性的序列化);而​

    ​Externaliable​

    ​在我們不希望序列化那麼多屬性的情況下可以使用,它要求我們實作兩個方法:​

    ​writeExternal()​

    ​和​

    ​readExternal()​

    ​來指定序列化和反序列化那些屬性(此時​

    ​Transient​

    ​關鍵字不再起作用)。

7. 抽象類和接口的差別

  1. 抽象類是包含抽象方法的類。抽象類和抽象方法使用

    abstract

    修飾。抽象方法是隻有方法聲明沒有方法體的方法,如下:

    abstract void func();

    如果一個類包含一個或多個抽象方法,那麼該類本身也必須定義為抽象的,否則編譯不通過,如下:

    abstract class Person { abstract void speak(); }

    編譯器不允許建立抽象類的對象,是以必須在繼承并實作其所有抽象方法之後的子類中建立對象。抽象允許同時擁有任何非抽象方法的一般成員,且抽象類的抽象方法可以使用權限修飾符設定通路權限,但禁止被修飾為

    private

    ,因為這樣的話該抽象方法就永遠不能被實作。
  2. 接口是相對于抽象類更進一步的抽象。接口使用

    interface

    關鍵字來建立,它的所有方法必須是抽象的,并且它們是預設以

    public abstract

    修飾的,是以不需要在方法前添加任何修飾,并且也不能以任何其它方式修飾,它們隻能是

    publid abstract

    的,如下:

    public interface Person { void speak(); int func(); }

    接口通過使用

    implements

    在新的類中實作它的所有抽象方法,之後才能被執行個體化,如下:

    public class PersonImpl implements Person { public void speak() { System.out.println("你好!"); } public int func() { return 1; } }

    Java 8 之前的接口隻允許包含抽象方法,Java 8 之後的接口允許包含預設方法、靜态方法以及屬性。預設方法使用關鍵字

    default

    修飾,這樣就可以在接口中提供方法的實作,并且可以在實作類中調用它。接口中的屬性被隐式地修飾為為

    static final

    ,且不能以其他方式修飾。

8. String 和 StringBuilder、StringBuffer 的差別

  1. 可變性:​

    ​String​

    ​類是不可變的,​

    ​String​

    ​類中每個看起來會修改本身值得方法,都會建立一個新的​

    ​String​

    ​對象來存儲修改後得字元串内容;​

    ​StringBuilder​

    ​StringBuffer​

    ​都是可變的。是以,當代碼中涉及到頻繁更改變換字元串的行為時,使用​

    ​String​

    ​的速度比​

    ​StringBuilder​

    ​要慢得多。
  2. 線程安全:​

    ​String​

    ​不可變,是以是線程安全地,​

    ​StringBuilder​

    ​是可變的,不是線程安全的。​

    ​StringBuffer​

    ​雖然也是可變的,但它内部的很多方法可以帶有 ​

    ​synchronized​

    ​關鍵字進行同步,是以可以保證線程安全。是以如果要進行的操作是多線程的,那麼就要使用​

    ​StringBuffer​

    ​,但在單線程運作的情況下,​

    ​StringBuider​

    ​更快。

9. 闡述 final、finally 和 finalize 的差別

  1. ​final​

    ​是修飾符,用于修飾類、方法或者屬性。​

    ​final​

    ​修飾類時,它意味着該類不能被繼承,是以不能使用​

    ​final​

    ​修飾抽象類或接口。​

    ​final​

    ​修飾方法時,意味着該方法不能被重寫,隻能被使用,是以同樣地,不能使用​

    ​final​

    ​修飾抽象方法。​

    ​final​

    ​修飾屬性或局部變量時,它隻有一次被初始化地機會,且必須在其被使用之前,之後就不能對其進行任何改動,對于對象屬性或變量來說,這意味着它不能再引用任何其它的對象,但對象本身的内部屬性是可以更改的。
  2. ​finally​

    ​是關于異常進行中的關鍵字,在提供了執行清理操作的辦法。不管有沒有異常被捕獲,即不管​

    ​try​

    ​中代碼塊正常執行或者異常觸發被抛出還是異常被捕獲進而執行​

    ​catch​

    ​代碼塊,​

    ​finally​

    ​代碼塊中的内容都會被執行,即使​

    ​try/catch​

    ​代碼塊中包含​

    ​return​

    ​語句,​

    ​finally​

    ​代碼塊同樣會執行。
  3. ​finalize​

    ​是方法名。Java 中可以使用​

    ​finalize()​

    ​方法在垃圾收集器将對象從記憶體中清楚出去之前做必要的清理工作。該方法是在​

    ​Object​

    ​類中定義的,是以所有的類都繼承了它。子類通過重寫​

    ​finalize()​

    ​方法以整理系統資源或者執行其它清理工作。它是由垃圾回收器,在确定該對象沒有被引用,需要被删除之前調用的。

10. Java 中的異常處理機制和簡單的應用

Java 中以​

​Throwable​

​類為任何可以作為異常類型的基類,進一步分為兩種類型:​

​Error​

​Exception​

​。其中​

​Error​

​用來表示 JVM 無法處理的錯誤,​

​Exception​

​表示程式執行中的異常。​

​Exception​

​也分為兩種:受檢異常(Checked Exception)和非受檢異常(Unchecked Exception)。受檢異常是從文法角度必須被處理的異常,如果不處理,程式就不能編譯通過。不受檢異常包含的是​

​Exception​

​中的一大分支​

​RuntimeException​

​(運作時異常),這些異常一般是由程式邏輯錯誤引起的,應該從邏輯角度盡可能地避免這類異常發生,可以選擇捕獲處理,也可以不處理。

Java 的異常處理實際上就是抛出異常和捕獲異常。

  1. 抛出異常:抛出異常的前提是異常情形(exception condition)的發生,異常情形指的是目前環境下無法獲得足夠的資訊來解決的問題。此時程式能做的就是跳出目前環境,把問題交給上一級環境,此時就發生了抛出異常。抛出異常會首先建立出異常對象,然後目前程式被終止,并從目前環境中彈出對異常對象的引用。此時,異常處理機制接管程式,并轉到異常處理程式中繼續執行。
  2. 捕獲異常:當異常被抛出之後,運作時系統會開始尋找合适的異常處理器(exception handler),合适的異常處理器所能處理的異常和方法抛出的異常類型相符。當找到合适的異常處理器時,開始執行其中處理邏輯,其任務是企圖将程式從異常狀态中恢複。當未找到合适的異常處理器時,整個 Java 程式就會終止。

所有的異常實際上都是以對象的形式存在的,我們也可以編寫自定義的異常類型,并在合适的地方抛出自定義異常對象或者在并合适的地方編寫處理程式。