Android面試之 Java篇

面向對象

Java面向對象的三個特徵與含義

  1. 繼承:繼承是從已有類得到繼承信息創建新類的過程。提供繼承信息的類被稱爲父類(超類、基類);得到繼承信息的類被稱爲子類(派生類)。繼承讓變化中的軟件系統有了一定的延續性,同時繼承也是封裝程序中可變因素的重要手段。
  2. 封裝:通常認爲封裝是把數據和操作數據的方法綁定起來,對數據的訪問只能通過已定義的接口。面向對象的本質就是將現實世界描繪成一系列完全自治、封閉的對象。我們在類中編寫的方法就是對實現細節的一種封裝;我們編寫一個類就是對數據和數據操作的封裝。可以說,封裝就是隱藏一切可隱藏的東西,只向外界提供最簡單的編程接口(可以想想普通洗衣機和全自動洗衣機的差別,明顯全自動洗衣機封裝更好因此操作起來更簡單;我們現在使用的智能手機也是封裝得足夠好的,因爲幾個按鍵就搞定了所有的事情)。

  3. 多態:多態性是指允許不同子類型的對象對同一消息作出不同的響應。簡單的說就是用同樣的對象引用調用同樣的方法但是做了不同的事情。多態性分爲編譯時的多態性和運行時的多態性。如果將對象的方法視爲對象向外界提供的服務,那麼運行時的多態性可以解釋爲:當A系統訪問B系統提供的服務時,B系統有多種提供服務的方式,但一切對A系統來說都是透明的(就像電動剃鬚刀是A系統,它的供電系統是B系統,B系統可以使用電池供電或者用交流電,甚至還有可能是太陽能,A系統只會通過B類對象調用供電的方法,但並不知道供電系統的底層實現是什麼,究竟通過何種方式獲得了動力)。方法重載(overload)實現的是編譯時的多態性(也稱爲前綁定),而方法重寫(override)實現的是運行時的多態性(也稱爲後綁定)。運行時的多態是面向對象最精髓的東西,要實現多態需要做兩件事:

    • 方法重寫(子類繼承父類並重寫父類中已有的或抽象的方法);
    • 對象造型(用父類型引用引用子類型對象,這樣同樣的引用調用同樣的方法就會根據子類對象的不同而表現出不同的行爲)。

Java 多態

什麼是多態

面向對象的三大特性:封裝、繼承、多態。從一定角度來看,封裝和繼承幾乎都是爲多態而準備的。這是我們最後一個概念,也是最重要的知識點。

多態的定義:指允許不同類的對象對同一消息做出響應。即同一消息可以根據發送對象的不同而採用多種不同的行爲方式。(發送消息就是函數調用)

實現多態的技術稱爲:動態綁定(dynamic binding),是指在執行期間判斷所引用對象的實 際類型,根據其實際的類型調用其相應的方法。

多態的作用:消除類型之間的耦合關係。

現實中,關於多態的例子不勝枚舉。比方說按下 F1 鍵這個動作,如果當前在 Flash 界面下彈出的就是 AS 3 的幫助文檔;如果當前在 Word 下彈出的就是 Word 幫助;在 Windows 下彈出的就是 Windows 幫助和支持。同一個事件發生在不同的對象上會產生不同的結果。 下面是多態存在的三個必要條件,要求大家做夢時都能背出來!

多態存在的三個必要條件:
一、要有繼承;
二、要有重寫;
三、父類引用指向子類對象。

多態的好處:

  1. 可替換性(substitutability)。多態對已存在代碼具有可替換性。例如,多態對圓Circle類工作,對其他任何圓形幾何體,如圓環,也同樣工作。
  2. 可擴充性(extensibility)。多態對代碼具有可擴充性。增加新的子類不影響已存在類的多態性、繼承性,以及其他特性的運行和操作。實際上新加子類更容易獲得多態功能。例如,在實現了圓錐、半圓錐以及半球體的多態基礎上,很容易增添球體類的多態性。
  3. 接口性(interface-ability)。多態是超類通過方法簽名,向子類提供了一個共同接口,由子類來完善或者覆蓋它而實現的。如圖8.3所示。圖中超類Shape規定了兩個實現多態的接口方法,computeArea()以及computeVolume()。子類,如Circle和Sphere爲了實現多態,完善或者覆蓋這兩個接口方法。
  4. 靈活性(flexibility)。它在應用中體現了靈活多樣的操作,提高了使用效率。
  5. 簡化性(simplicity)。多態簡化對應用軟件的代碼編寫和修改過程,尤其在處理大量對象的運算和操作時,這個特點尤爲突出和重要。

Java中多態的實現方式:接口實現,繼承父類進行方法重寫,同一個類中進行方法重載。

語法知識

& 和 && 的區別

&和&&都是可以作爲邏輯運算符的,其邏輯運算規則是相同的。

但&作爲邏輯運算符時,即使第一個操作符是false,那麼它仍然會計算第二個操作符。&&短路與,如果第一個操作符爲false,那麼它不會再去計算第二個操作符。

Java中“引用”的幾種類型

  1. 強引用:無論內存是否足夠,不會回收。
  2. 軟引用:內存不足時,回收該引用關聯的對象。
  3. 弱引用:垃圾回收時,無論內存是否足夠,都會回收。
  4. 虛引用:任何時候都可能被垃圾回收器回收。

    在Java中,雖然不需要程序員手動去管理對象的生命週期,但是如果希望某些對象具備一定的生命週期的話(比如內存不足時JVM就會自動回收某些對象從而避免OutOfMemory的錯誤)就需要用到軟引用和弱引用了。

      從Java SE2開始,就提供了四種類型的引用:強引用、軟引用、弱引用和虛引用。Java中提供這四種引用類型主要有兩個目的:第一是可以讓程序員通過代碼的方式決定某些對象的生命週期;第二是有利於JVM進行垃圾回收。下面來闡述一下這四種類型引用的概念:
      
      1.強引用(StrongReference)

      強引用就是指在程序代碼之中普遍存在的,比如下面這段代碼中的object和str都是強引用:

Object object = new Object();
String str = "hello";

只要某個對象有強引用與之關聯,JVM必定不會回收這個對象,即使在內存不足的情況下,JVM寧願拋出OutOfMemory錯誤也不會回收這種對象。比如下面這段代碼:

public class Main {
    public static void main(String[] args) {
        new Main().fun1();
    }

    public void fun1() {
        Object object = new Object();
        Object[] objArr = new Object[1000];
    }
}

  當運行至Object[] objArr = new Object[1000];這句時,如果內存不足,JVM會拋出OOM錯誤也不會回收object指向的對象。不過要注意的是,當fun1運行完之後,object和objArr都已經不存在了,所以它們指向的對象都會被JVM回收。

  如果想中斷強引用和某個對象之間的關聯,可以顯示地將引用賦值爲null,這樣一來的話,JVM在合適的時間就會回收該對象。

  比如Vector類的clear方法中就是通過將引用賦值爲null來實現清理工作的:

/**
     * Removes the element at the specified position in this Vector.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).  Returns the element that was removed from the Vector.
     *
     * @throws ArrayIndexOutOfBoundsException if the index is out of range
     *         ({@code index < 0 || index >= size()})
     * @param index the index of the element to be removed
     * @return element that was removed
     * @since 1.2
     */
    public synchronized E remove(int index) {
    modCount++;
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);
    Object oldValue = elementData[index];

    int numMoved = elementCount - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                 numMoved);
    elementData[--elementCount] = null; // Let gc do its work

    return (E)oldValue;
    }

 2.軟引用(SoftReference)

  軟引用是用來描述一些有用但並不是必需的對象,在Java中用java.lang.ref.SoftReference類來表示。對於軟引用關聯着的對象,只有在內存不足的時候JVM纔會回收該對象。因此,這一點可以很好地用來解決OOM的問題,並且這個特性很適合用來實現緩存:比如網頁緩存、圖片緩存等。

  軟引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果軟引用所引用的對象被JVM回收,這個軟引用就會被加入到與之關聯的引用隊列中。下面是一個使用示例:

import java.lang.ref.SoftReference;

public class Main {
    public static void main(String[] args) {

        SoftReference<String> sr = new SoftReference<String>(new String("hello"));
        System.out.println(sr.get());
    }
}

3.弱引用(WeakReference)

  弱引用也是用來描述非必需對象的,當JVM進行垃圾回收時,無論內存是否充足,都會回收被弱引用關聯的對象。在java中,用java.lang.ref.WeakReference類來表示。下面是使用示例:

import java.lang.ref.WeakReference;

public class Main {
    public static void main(String[] args) {

        WeakReference<String> sr = new WeakReference<String>(new String("hello"));

        System.out.println(sr.get());
        System.gc();                //通知JVM的gc進行垃圾回收
        System.out.println(sr.get());
    }
}

 第二個輸出結果是null,這說明只要JVM進行垃圾回收,被弱引用關聯的對象必定會被回收掉。不過要注意的是,這裏所說的被弱引用關聯的對象是指只有弱引用與之關聯,如果存在強引用同時與之關聯,則進行垃圾回收時也不會回收該對象(軟引用也是如此)。
 
  4.虛引用(PhantomReference)

  虛引用和前面的軟引用、弱引用不同,它並不影響對象的生命週期。在java中用java.lang.ref.PhantomReference類表示。如果一個對象與虛引用關聯,則跟沒有引用與之關聯一樣,在任何時候都可能被垃圾回收器回收。

  要注意的是,虛引用必須和引用隊列關聯使用,當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會把這個虛引用加入到與之 關聯的引用隊列中。程序可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。如果程序發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的內存被回收之前採取必要的行動。 

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class Main {
    public static void main(String[] args) {
        ReferenceQueue<String> queue = new ReferenceQueue<String>();
        PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);
        System.out.println(pr.get());
    }
}

二.進一步理解軟引用和弱引用

  對於強引用,我們平時在編寫代碼時經常會用到。而對於其他三種類型的引用,使用得最多的就是軟引用和弱引用,這2種既有相似之處又有區別。它們都是用來描述非必需對象的,但是被軟引用關聯的對象只有在內存不足時纔會被回收,而被弱引用關聯的對象在JVM進行垃圾回收時總會被回收。

  在SoftReference類中,有三個方法,兩個構造方法和一個get方法(WekReference類似):

  兩個構造方法:
  

public SoftReference(T referent) {
    super(referent);
    this.timestamp = clock;
    }

public SoftReference(T referent, ReferenceQueue<? super T> q) {
    super(referent, q);
    this.timestamp = clock;
    }

get方法用來獲取與軟引用關聯的對象的引用,如果該對象被回收了,則返回null。

  在使用軟引用和弱引用的時候,我們可以顯示地通過System.gc()來通知JVM進行垃圾回收,但是要注意的是,雖然發出了通知,JVM不一定會立刻執行,也就是說這句是無法確保此時JVM一定會進行垃圾回收的。
  

Java 中的 SoftReference 是什麼

Java 中的 SoftReference 即對象的軟引用。如果一個對象具有軟引用,內存空間足夠,垃圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實現內存敏感的高速緩存。使用軟引用能防止內存泄露,增強程序的健壯性。

SoftReference的特點是它的一個實例保存對一個Java對象的軟引用,該軟引用的存在不妨礙垃圾收集線程對該Java對象的回收。也就是說,一旦SoftReference保存了對一個Java對象的軟引用後,在垃圾線程對這個Java對象回收前,SoftReference類所提供的get()方法返回Java對象的強引用。另外,一旦垃圾線程回收該Java對象之後,get()方法將返回null

用Map集合緩存軟引用的Bitmap對象

Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();
//強引用的Bitmap對象
Bitmap bitmap = BitmapFactory.decodeStream(InputStream);
//軟引用的Bitmap對象
SoftReference<Bitmap> bitmapcache = new SoftReference<Bitmap>(bitmap);
//添加該對象到Map中使其緩存
imageCache.put("1",softRbitmap);
// ...
//從緩存中取軟引用的Bitmap對象
SoftReference<Bitmap> bitmapcache_ = imageCache.get("1");
//取出Bitmap對象,如果由於內存不足Bitmap被回收,將取得空
Bitmap bitmap_ = bitmapcache_.get();

談一下對 Java 中的 abstract 的理解

abstract(抽象)可以修飾類、方法

如果將一個類設置爲abstract,則此類必須被繼承使用。此類不可生成對象,必須被繼承使用。 abstract可以將子類的共性最大限度的抽取出來,放在父類中,以提高程序的簡潔性。 abstract雖然不能生成對象,但是可以聲明,作爲編譯時類型,但不能作爲運行時類型。 final和abstract永遠不會同時出現。

當abstract用於修飾方法時,此時該方法爲抽象方法,此時方法不需要實現,實現留給子類覆蓋,子類覆蓋該方法之後方法才能夠生效。

注意比較:
private void print(){};此語句表示方法的空實現。
abstract void print(); 此語句表示方法的抽象,無實現。

如果一個類中有一個抽象方法,那麼這個類一定爲一個抽象類。 反之,如果一個類爲抽象類,那麼其中可能有非抽象的方法。

如果讓一個非抽象類繼承一個含抽象方法的抽象類,則編譯時會發生錯誤。因爲當一個非抽象類繼承一個抽象方法的時候,本着只有一個類中有一個抽象方法,那麼這個類必須爲抽象類的原則。這個類必須爲抽象類,這與此類爲非抽象衝突,所以報錯。

所以子類的方法必須覆蓋父類的抽象方法。方法才能夠起作用。

爲了實現多態,那麼父類必須有定義。而父類並不實現,留給子類去實現。此時可將父類定義成abstract類。如果沒有定義抽象的父類,那麼編譯會出現錯誤。

abstract 和 static 不能放在一起,否則便會出現錯誤。(這是因爲static不可被覆蓋,而abstract爲了生效必須被覆蓋。)

abstract 和 final 不能放在一起,否則便會出現錯誤。
Overload 和 Override

方法的重寫(Overriding)和重載(Overloading)是Java多態性的不同表現。重寫(Overriding)是父類與子類之間多態性的一種表現,而重載(Overloading)是一個類中多態性的一種表現。

如果在子類中定義某方法與其父類有相同的名稱和參數,我們說該方法被重寫 (Overriding)。子類的對象使用這個方法時,將調用子類中的定義,對它而言,父類中的定義如同被”屏蔽”了。

如果在一個類中定義了多個同名的方法,它們或有不同的參數個數或有不同的參數類型或有不同的參數次序,則稱爲方法的重載(Overloading)。不能通過訪問權限、返回類型、拋出的異常進行重載.

方法重載規則

被重載的方法必須改變參數列表(參數個數或類型或順序不一樣);
被重載的方法可以改變返回類型;
被重載的方法可以改變訪問修飾符;
被重載的方法可以聲明新的或更廣的檢查異常;
方法能夠在同一個類中或者在一個子類中被重載。
無法以返回值類型作爲重載函數的區分標準。
方法的重寫規則

參數列表必須完全與被重寫方法的相同;
返回類型必須完全與被重寫方法的返回類型相同;
訪問權限不能比父類中被重寫的方法的訪問權限更低。例如:如果父類的一個方法被聲明爲public,那麼在子類中重寫該方法就不能聲明爲protected。
父類的成員方法只能被它的子類重寫。
聲明爲final的方法不能被重寫。
聲明爲static的方法不能被重寫,但是能夠被再次聲明。
子類和父類在同一個包中,那麼子類可以重寫父類所有方法,除了聲明爲private和final的方法。
子類和父類不在同一個包中,那麼子類只能夠重寫父類的聲明爲public和protected的非final方法。
重寫的方法能夠拋出任何非強制異常,無論被重寫的方法是否拋出異常。但是,重寫的方法不能拋出新的強制性異常,或者比被重寫方法聲明的更廣泛的強制性異常,反之則可以。
構造方法不能被重寫。
如果不能繼承一個方法,則不能重寫這個方法。
Overload 和 Override 的區別

這裏寫圖片描述

java中abstract,interface,final,static的總結

抽象類:abstract

只要有一個或一個以上抽象方法的類,必須用abstract聲明爲抽象類;
抽象類中可以有具體的實現方法;
抽象類中可以沒有抽象方法;
抽象類中的抽象方法必須被它的子類實現,如果子類沒有實現,則該子類繼續爲抽象類
抽象類不能被實例化,但可以由抽象父類指向的子類實例來調用抽象父類中的具體實現方法;通常作爲一種默認行爲;
要使用抽象類中的方法,必須有一個子類繼承於這個抽象類,並實現抽象類中的抽象方法,通過子類的實例去調用;

接口:interface

接口中可以有成員變量,且接口中的成員變量必須定義初始化;
接口中的成員方法只能是方法原型,不能有方法主體;
接口的成員變量和成員方法只能public(或缺省不寫),效果一樣,都是public
實現接口的類必須全部實現接口中的方法(父類的實現也算,一般有通過基類實現接口中個異性不大的方法來做爲適配器的做法)

關鍵字:final

可用於修飾:成員變量,非抽象類(不能與abstract同時出現),非抽象的成員方法,以及方法參數
final方法:不能被子類的方法重寫,但可以被繼承;
final類:表示該類不能被繼承,沒有子類;final類中的方法也無法被繼承.
final變量:表示常量,只能賦值一次,賦值後不能被修改.final變量必須定義初始化;
final不能用於修飾構造方法;
final參數:只能使用該參數,不能修改該參數的值;

關鍵字:static

可以修飾成員變量和成員方法,但不能修飾類以及構造方法;
被static修飾的成員變量和成員方法獨立於該類的任何對象。也就是說,它不依賴類特定的實例,被類的所有實例共享
static變量和static方法一般是通過類名直接訪問,但也可以通過類的實例來訪問(不推薦這種訪問方式)
static變量和static方法同樣適應java訪問修飾符.用public修飾的static變量和static方法,在任何地方都可以通過類名直接來訪問,但用private修飾的static變量和static方法,只能在聲明的本類方法及靜態塊中訪問,但不能用this訪問,因爲this屬於非靜態變量.
static和final同時使用

static final用來修飾成員變量和成員方法,可簡單理解爲“全局常量”!
對於變量,表示一旦給值就不可修改,並且通過類名可以訪問。
對於方法,表示不可覆蓋,並且可以通過類名直接訪問。

switch 能否用 String 做參數?

從 Java 7開始 switch 就能使用 String 做爲參數。

在Java 5以前,switch(expr)中,expr只能是byte、short、char、int。從Java 5開始,Java中引入了枚舉類型,expr也可以是enum類型,從Java 7開始,expr還可以是字符串(String),但是長整型(long)在目前所有的版本中都是不可以的。

Object有哪些公用方法?

1.clone方法

保護方法,實現對象的淺複製,只有實現了Cloneable接口纔可以調用該方法,否則拋出CloneNotSupportedException異常。

主要是JAVA裏除了8種基本類型傳參數是值傳遞,其他的類對象傳參數都是引用傳遞,我們有時候不希望在方法裏講參數改變,這是就需要在類中複寫clone方法。

2.getClass方法

final方法,獲得運行時類型。

3.toString方法

該方法用得比較多,一般子類都有覆蓋。

4.finalize方法

該方法用於釋放資源。因爲無法確定該方法什麼時候被調用,很少使用。

5.equals方法

該方法是非常重要的一個方法。一般equals和==是不一樣的,但是在Object中兩者是一樣的。子類一般都要重寫這個方法。

6.hashCode方法

該方法用於哈希查找,可以減少在查找中使用equals的次數,重寫了equals方法一般都要重寫hashCode方法。這個方法在一些具有哈希功能的Collection中用到。

一般必須滿足obj1.equals(obj2)==true。可以推出obj1.hashCode()==obj2.hashCode(),但是hashCode相等不一定就滿足equals。不過爲了提高效率,應該儘量使上面兩個條件接近等價。

如果不重寫hashCode(),在HashSet中添加兩個equals的對象,會將兩個對象都加入進去。

7.wait方法

wait方法就是使當前線程等待該對象的鎖,當前線程必須是該對象的擁有者,也就是具有該對象的鎖。wait()方法一直等待,直到獲得鎖或者被中斷。wait(long timeout)設定一個超時間隔,如果在規定時間內沒有獲得鎖就返回。

調用該方法後當前線程進入睡眠狀態,直到以下事件發生。

(1)其他線程調用了該對象的notify方法。

(2)其他線程調用了該對象的notifyAll方法。

(3)其他線程調用了interrupt中斷該線程。

(4)時間間隔到了。

此時該線程就可以被調度了,如果是被中斷的話就拋出一個InterruptedException異常。

8.notify方法

該方法喚醒在該對象上等待的某個線程。

9.notifyAll方法

該方法喚醒在該對象上等待的所有線程。

foreach與正常for循環效率對比。

直接for循環效率最高,其次是迭代器和 ForEach操作。作爲語法糖,其實 ForEach 編譯成 字節碼之後,使用的是迭代器實現的,反編譯後,testForEach方法如下:

public static void testForEach(List list) {  
    for (Iterator iterator = list.iterator(); iterator.hasNext();) {  
        Object t = iterator.next();  
        Object obj = t;  
    }  
}

可以看到,只比迭代器遍歷多了生成中間變量這一步,因爲性能也略微下降了一些。

基本數據類型

基本數據類型 int char long 等各佔多少字節

這裏寫圖片描述

PS:單個的 boolean 類型變量在編譯的時候是使用的 int 類型。而對於 boolean 類型的數組時,在編譯的時候是作爲 byte。

swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?

switch語句中的表達式只能是byte,short,char ,int以及枚舉(enum),所以當表達式是byte的時候可以隱含轉換爲int類型,而long字節比int字節多,不能隱式轉化爲int類型,所以switch語句可以用在byte上而不可以用在long上。
由於在JDK7.0中引入了新特性,所以switch語句可以接收一個String類型的值,switch語句可以作用在String上。

常量final string str=“ab”可不可以變成”abd”,爲什麼?

不能,因爲使用 final 修飾的變量不可改變。

StringBuffer的作用?

StringBuffer類和String一樣,也用來代表字符串,只是由於StringBuffer的內部實現方式和String不同,所以StringBuffer在進行字符串處理時,不生成新的對象,在內存使用上要優於String類。

所以在實際使用時,如果經常需要對一個字符串進行修改,例如插入、刪除等操作,使用StringBuffer要更加適合一些。

String StringBuffer StringBuilder 的區別

三者在執行速度方面的比較:StringBuilder > StringBuffer > String

String 字符串常量,不可變
StringBuffer 字符串變量(線程安全)
StringBuilder 字符串變量(非線程安全)
和 String 類不同的是,StringBuffer 和 StringBuilder 類的對象能夠被多次的修改,並且不產生新的未使用對象。

StringBuilder 類在 Java 5 中被提出,它和 StringBuffer 之間的最大不同在於 StringBuilder 的方法不是線程安全的(不能同步訪問)。

由於 StringBuilder 相較於 StringBuffer 有速度優勢,所以多數情況下建議使用 StringBuilder 類。然而在應用程序要求線程安全的情況下,則必須使用 StringBuffer 類。

簡要的說, String 類型和 StringBuffer 類型的主要性能區別其實在於 String 是不可變的對象, 因此在每次對 String 類型進行改變的時候其實都等同於生成了一個新的 String 對象,然後將指針指向新的 String 對象,所以經常改變內容的字符串最好不要用String ,因爲每次生成對象都會對系統性能產生影響,特別當內存中無引用對象多了以後,JVM 的 GC 就會開始工作,那速度是一定會相當慢的。

而如果是使用 StringBuffer 類則結果就不一樣了,每次結果都會對 StringBuffer 對象本身進行操作,而不是生成新的對象,再改變對象引用。所以在一般情況下我們推薦使用 StringBuffer ,特別是字符串對象經常改變的情況下。而在某些特別情況下, String 對象的字符串拼接其實是被 JVM 解釋成了 StringBuffer 對象的拼接,所以這些時候 String 對象的速度並不會比 StringBuffer 對象慢,而特別是以下的字符串對象生成中, String 效率是遠要比 StringBuffer 快的:

String S1 = “This is only a” + “ simple” + “ test”;
StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);

你會很驚訝的發現,生成 String S1 對象的速度簡直太快了,而這個時候 StringBuffer 居然速度上根本一點都不佔優勢。其實這是 JVM 的一個把戲,在 JVM 眼裏,這個 String S1 = “This is only a” + “ simple” + “test”; 其實就是: String S1 = “This is only a simple test”; 所以當然不需要太多的時間了。但大家這裏要注意的是,如果你的字符串是來自另外的 String 對象的話,速度就沒那麼快了,譬如:

String S2 = “This is only a”; 
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;

這時候 JVM 會規規矩矩的按照原來的方式去做。在大部分情況下 StringBuffer > String。

StringBuffer

Java.lang.StringBuffer線程安全的可變字符序列。一個類似於 String 的字符串緩衝區,但不能修改。雖然在任意時間點上它都包含某種特定的字符序列,但通過某些方法調用可以改變該序列的長度和內容。

可將字符串緩衝區安全地用於多個線程。可以在必要時對這些方法進行同步,因此任意特定實例上的所有操作就好像是以串行順序發生的,該順序與所涉及的每個線程進行的方法調用順序一致。

StringBuffer 上的主要操作是 append 和 insert 方法,可重載這些方法,以接受任意類型的數據。每個方法都能有效地將給定的數據轉換成字符串,然後將該字符串的字符追加或插入到字符串緩衝區中。append 方法始終將這些字符添加到緩衝區的末端;而 insert 方法則在指定的點添加字符。

例如,如果 z 引用一個當前內容是“start”的字符串緩衝區對象,則此方法調用 z.append(“le”) 會使字符串緩衝區包含“startle”,而 z.insert(4, “le”) 將更改字符串緩衝區,使之包含“starlet”。

在大部分情況下 StringBuilder > StringBuffer

java.lang.StringBuilder

java.lang.StringBuilder一個可變的字符序列是5.0新增的。此類提供一個與 StringBuffer 兼容的 API,但不保證同步。該類被設計用作 StringBuffer 的一個簡易替換,用在字符串緩衝區被單個線程使用的時候(這種情況很普遍)。如果可能,建議優先採用該類,因爲在大多數實現中,它比 StringBuffer 要快。兩者的方法基本相同

內部類

Static Inner Class 和 Inner Class 的不同

靜態內部類不持有外部類的引用;非靜態內部類持有外部類的引用。
靜態內部類可以有靜態成員(方法、屬性),而非靜態內部類則不能有靜態成員(方法、屬性)。
靜態內部類只能訪問外部類的靜態成員和靜態方法,而非靜態內部類則可以訪問外部類的所以成員(方法、屬性)。
實例化一個靜態內部類不依賴於外部類的實例,直接實例化內部類對象;實例化一個非靜態內部類依賴於外部類的實例,通過外部類的實例生成內部類的實例。
調用靜態內部類的方法或靜態變量,直接通過類名調用。

內部類機制

爲什麼內部類擁有外部類的所有元素的訪問權?
當某個外圍類對象創建一個內部連對象時,內部類對象必定會捕獲一個指向那個外圍類對象的引用。內部類對象只能在與其外部類對象關聯的情況下才能被創建(在內部類非static時),構建內部類需要一個外部類的引用,內部類正是利用這個引用去訪問外部類的。

內部類的種類

按照內部類所在的位置不同,內部類可以分爲以下幾種:

成員內部類
方法內部類
匿名內部類
靜態內部類

內部類的作用?

  • 內部類可以用多個實例,每個實例都有自己的狀態信息,並且與其他外圍對象的信息相互獨立。
  • 在單個外圍類中,可以讓多個內部類以不同的方式實現同一個接口,或者繼承同一個類。
  • 創建內部類對象的時刻並不依賴與外圍類對象的創建。
  • 內部類並沒有令人迷惑的“is-a”關係,它就是一個獨立的實體。
  • 內部類提供了更好的封裝,除了外圍類,其他類都不能訪問。

靜態內部類、內部類、匿名內部類,爲什麼內部類會持有外部類的引用?持有的引用是this?還是其它?

靜態內部類:使用static修飾的內部類

匿名內部類:使用new生成的內部類

因爲內部類的產生依賴於外部類,持有的引用是類名.this。

繼承

父類的靜態方法能否被子類重寫

不能。

父類的靜態方法是不能被子類重寫的,其實重寫只能適用於實例方法,不能用於靜態方法,對於上面這種靜態方法而言,我們應該稱之爲隱藏。

Java靜態方法形式上可以重寫,但從本質上來說不是Java的重寫。因爲靜態方法只與類相關,不與具體實現相關。聲明的是什麼類,則引用相應類的靜態方法(本來靜態無需聲明,可以直接引用)。並且static方法不是後期綁定的,它在編譯期就綁定了。換句話說,這個方法不會進行多態的判斷,只與聲明的類有關。

抽象類和接口

抽象類總結:

  • 抽象類不能被實例化(初學者很容易犯的錯),如果被實例化,就會報錯,編譯無法通過。只有抽象類的非抽象子類可以創建對象。
  • 抽象類中不一定包含抽象方法,但是有抽象方法的類必定是抽象類。
  • 抽象類中的抽象方法只是聲明,不包含方法體,就是不給出方法的具體實現也就是方法的具體功能。
  • 構造方法,類方法(用static修飾的方法)不能聲明爲抽象方法。
  • 抽象類的子類必須給出抽象類中的抽象方法的具體實現,除非該子類也是抽象類。

接口與類的區別

  • 接口不能用於實例化對象。
  • 接口沒有構造方法。
  • 接口中所有的方法必須是抽象方法。
  • 接口不能包含成員變量,除了 static 和 final 變量。
  • 接口不是被類繼承了,而是要被類實現。
  • 接口支持多重繼承。

接口特性

  • 接口中每一個方法也是隱式抽象的,接口中的方法會被隱式的指定爲 public abstract(只能是 public
    abstract,其他修飾符都會報錯)。
  • 接口中可以含有變量,但是接口中的變量會被隱式的指定爲 public static final 變量(並且只能是 public,用
    private 修飾會報編譯錯誤)。
  • 接口中的方法是不能在接口中實現的,只能由實現接口的類來實現接口中的方法。

抽象類和接口的區別

  • 抽象類中的方法可以有方法體,就是能實現方法的具體功能,但是接口中的方法不行。
  • 抽象類中的成員變量可以是各種類型的,而接口中的成員變量只能是 public static final 類型的。
  • 接口中不能有靜態代碼塊以及靜態方法(用 static 修飾的方法),而抽象類中可以有靜態代碼塊和靜態方法。注意:Java 8的新特性允許接口中存在靜態方法。
  • 一個類只能繼承一個抽象類,而一個類卻可以實現多個接口。
  • 接口中不能有構造函數和main方法;而抽象類中可以有。
  • 默認的方法實現。抽象類可以有默認的方法實現完全是抽象的。接口根本不存在方法的實現。
  • 實現。使用extends關鍵字來繼承抽象類。如果子類不是抽象類的話,它需要提供抽象類中所有聲明的方法的實現。使用關鍵字implements來實現接口。它需要提供接口中所有聲明的方法的實現。
  • 構造器。抽象類可以有構造器。接口不能有構造器。
  • 與正常Java類的區別。除了你不能實例化抽象類之外,它和普通Java類沒有任何區別 接口是完全不同的類型
  • 訪問修飾符。抽象方法可以有public、protected和default這些修飾符
    接口方法默認修飾符是public。你不可以使用其它修飾符。
  • main方法。抽象方法可以有main方法並且我們可以運行它。接口沒有main方法,因此我們不能運行它。
  • 多繼承。抽象類在java語言中所表示的是一種繼承關係,一個子類只能存在一個父類,但是可以存在多個接口。
  • 速度。抽象類比接口速度要快。接口是稍微有點慢的,因爲它需要時間去尋找在類中實現的方法。
  • 添加新方法。如果你往抽象類中添加新的方法,你可以給它提供默認的實現。因此你不需要改變你現在的代碼。如果你往接口中添加方法,那麼你必須改變實現該接口的類。

語法層面上的區別

  • 抽象類可以提供成員方法的實現細節,而接口中只能存在public abstract 方法;
  • 抽象類中的成員變量可以是各種類型的,而接口中的成員變量只能是public static final類型的;
  • 接口中不能含有靜態代碼塊以及靜態方法,而抽象類可以有靜態代碼塊和靜態方法;
  • 一個類只能繼承一個抽象類,而一個類卻可以實現多個接口。 設計層面上的區別

抽象類是對一種事物的抽象,即對類抽象,而接口是對行爲的抽象。抽象類是對整個類整體進行抽象,包括屬性、行爲,但是接口卻是對類局部(行爲)進行抽象。舉個簡單的例子,飛機和鳥是不同類的事物,但是它們都有一個共性,就是都會飛。那麼在設計的時候,可以將飛機設計爲一個類Airplane,將鳥設計爲一個類Bird,但是不能將 飛行 這個特性也設計爲類,因此它只是一個行爲特性,並不是對一類事物的抽象描述。此時可以將 飛行 設計爲一個接口Fly,包含方法fly( ),然後Airplane和Bird分別根據自己的需要實現Fly這個接口。然後至於有不同種類的飛機,比如戰鬥機、民用飛機等直接繼承Airplane即可,對於鳥也是類似的,不同種類的鳥直接繼承Bird類即可。從這裏可以看出,繼承是一個 “是不是”的關係,而 接口 實現則是 “有沒有”的關係。如果一個類繼承了某個抽象類,則子類必定是抽象類的種類,而接口實現則是有沒有、具備不具備的關係,比如鳥是否能飛(或者是否具備飛行這個特點),能飛行則可以實現這個接口,不能飛行就不實現這個接口。
設計層面不同,抽象類作爲很多子類的父類,它是一種模板式設計。而接口是一種行爲規範,它是一種輻射式設計。什麼是模板式設計?最簡單例子,大家都用過ppt裏面的模板,如果用模板A設計了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它們的公共部分需要改動,則只需要改動模板A就可以了,不需要重新對ppt B和ppt C進行改動。而輻射式設計,比如某個電梯都裝了某種報警器,一旦要更新報警器,就必須全部更新。也就是說對於抽象類,如果需要添加新的方法,可以直接在抽象類中添加具體的實現,子類可以不進行變更;而對於接口則不行,如果接口進行了變更,則所有實現這個接口的類都必須進行相應的改動。

接口的意義

  • 提供一個規範,要求類必須實現指定的方法。
  • 解決 Java 中的單繼承問題,可以用接口來實現多繼承的功能,簡單化多重繼承中繼承樹的複雜程度。
  • 增強程序的擴展性。

抽象類的意義

爲其子類提供一個公共的類型、封裝子類中的重複內容、定義抽象方法,子類雖然有不同的實現,但是定義是一致的。

接口是否可繼承接口? 抽象類是否可實現(implements)接口? 抽象類是否可繼承實體類(concreteclass)?

  • 接口可以繼承接口。但是要使用extends,而不是用implements。
  • 抽象類可以實現接口。比如java.util中的AbstractCollection類就是實現的Collection接口。
  • 抽象類可以繼承實體類。

下面這段執行無誤的代碼說明的所有的問題:

interface MyInterface {
}
interface AnotherInterface extends MyInterface {
}
class EntityClass {
}
abstract class AbstractClass extends EntityClass implements MyInterface {
}

接口的繼承

一個接口能繼承另一個接口,和類之間的繼承方式比較相似。接口的繼承使用extends關鍵字,子接口繼承父接口的方法。

下面的Sports接口被Hockey和Football接口繼承:

// 文件名: Sports.java
public interface Sports
{
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}

// 文件名: Football.java
public interface Football extends Sports
{
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}

// 文件名: Hockey.java
public interface Hockey extends Sports
{
   public void homeGoalScored();
   public void visitingGoalScored();
   public void endOfPeriod(int period);
   public void overtimePeriod(int ot);
}

Hockey接口自己聲明瞭四個方法,從Sports接口繼承了兩個方法,這樣,實現Hockey接口的類需要實現六個方法。

相似的,實現Football接口的類需要實現五個方法,其中兩個來自於Sports接口。

abstract的method是否可同時是static,是否可同時是native,是否可同時時final,是否可同時是synchronized?

都不行

abstract的method不可以是static的,因爲抽象的方法是要被子類實現的,而static與子類扯不上關係!

native方法表示該方法要用另外一種依賴平臺的編輯語言實現的,不存在者被子類實現的問題,所以,他也不能是抽象的,不能與abstract混用。

關於synchronized中abstract合用的問題,我覺得也不行,因爲我覺得 synchronized應該是作用在一個具體的方法上纔有意義。而且,方法上的synchronized同步所使用的同步鎖對象是this,而抽象方法 上無法確定this是什麼。

類使用 final 修飾符的用處?

當用final修飾一個類時,表明這個類不能被繼承。也就是說,如果一個類你永遠不會讓他被繼承,就可以用final進行修飾。final類中的成員變量可以根據需要設爲final,但是要注意final類中的所有成員方法都會被隱式地指定爲final方法。

在使用final修飾類的時候,要注意謹慎選擇,除非這個類真的在以後不會用來繼承或者出於安全的考慮,儘量不要將類設計爲final類。

finally final finalize的作用?

final用於聲明屬性,方法和類,分別表示屬性不可交變,方法不可覆蓋,類不可繼承。

finally是異常處理語句結構的一部分,表示總是執行。

finalize是Object類的一個方法,在垃圾收集器執行的時候會調用被回收對象的此方法,供垃圾收集時的其他資源回收,例如關閉文件等。

未完待續

發佈了15 篇原創文章 · 獲贊 9 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章