【面試】Java後臺面試基礎知識點總結

Java基礎知識點複習與總結

1. Java 基本數據類型

Java中總共有八種基本的數據類型:byte(1)、short(2)、int(4)、long(8)、char(2)、float(4)、double(8)和boolean【PS:括號內的爲所佔的字節數】,每種基本類型都有其對應的包裝類。

1.1 自動類型轉換

自動類型轉換 :低級變量可以直接轉換爲高級變量,轉換規則爲:

byte→short(char)→int→long→float→double

例如,下面的語句可以編譯通過:

byte b;
int i=b;
long l=b;
float f=b;
double d=b;

但是將double型變量賦值給float變量,不加強轉的話會報錯。

1.2 自動裝箱和自動拆箱

簡單的說,裝箱就是就是將基本數據類型轉換爲包裝器類型;拆箱就是將包裝器類型轉換爲基本數據類型。

例如:

Integer i = 10;  //裝箱
int n = i;   //拆箱

注意事項

  1. 對於Integer而言,如果數值在[-128,127]之間,便返回指向IntegerCache.cache中已經存在的對象的引用;否則創建一個新的Integer對象。例如:

    public class Main {
        public static void main(String[] args) {         
            Integer i1 = 100;
            Integer i2 = 100;
            Integer i3 = 200;
            Integer i4 = 200;
            System.out.println(i1==i2);
            System.out.println(i3==i4);
        }
    }
    

    輸出的結果就爲:

    true
    false
    
  2. Integer i = new Integer(xxx)和Integer i =xxx;這兩種方式的區別。

    • 第一種方式不會觸發自動裝箱的過程;而第二種方式會觸發;
    • 在執行效率和資源佔用上的區別。第二種方式的執行效率和資源佔用在一般性情況下要優於第一種情況(注意這並不是絕對的)。
  3. 當 **==**運算符的兩個操作數都是包裝器類型的引用,則是比較指向的是否是同一個對象,而如果其中有一個操作數是表達式(即包含算術運算)則比較的是數值(即會觸發自動拆箱的過程);另外,對於包裝器類型,equals方法並不會進行類型轉換。例如:

    public class Main {
        public static void main(String[] args) {        
            Integer a = 1;
            Integer b = 2;
            Integer c = 3;
            Integer d = 3;
            Integer e = 320;
            Integer f = 320;
            Long g = 3L;
            Long h = 2L;
            // 因爲c、d都是包裝類型,所以返回true
            System.out.println(c==d);
            // 因爲e、f都大於128,屬於不同的對象,所以返回false
            System.out.println(e==f);
            // 因爲含有算數運算符,所以比較的數值,返回true
            System.out.println(c==(a+b));
            // 因爲equals不會類型轉換,所以比較的是類型,返回true
            System.out.println(c.equals(a+b));
            // 比較數值,所以返回true
            System.out.println(g==(a+b));
            // 因爲equals不會類型轉換,a、b爲Integer,所以返回false
            System.out.println(g.equals(a+b));
            // a向上轉型爲Long,所以返回true
            System.out.println(g.equals(a+h));
        }
    }
    

1.3 int 和 和 Integer 有什麼區別?

  1. Integer是int的包裝類;int是基本數據類型;
  2. Integer變量必須實例化後才能使用;int變量不需要;
  3. Integer實際是對象的引用,指向此new的Integer對象;int是直接存儲數據值 ;
  4. Integer的默認值是null;int的默認值是0。

1.4 String、StringBuffer、StringBuilder的區別:

  1. 是否可變:
    • String:字符串常量,在修改時不會改變自身;若修改,等於重新生成新的字符串對象。
    • StringBuffer、StringBuilder:在修改時會改變對象自身,每次操作都是對 StringBuffer 對象本身進行修改,不是生成新的對象;使用場景:對字符串經常改變情況下,主要方法:append(),insert()等。
  2. 是否線程安全:
    • String:對象定義後不可變,線程安全。
    • StringBuilder:是線程不安全的,適用於單線程下操作字符串緩衝區大量數據。
    • StringBuffer:是線程安全的(對調用方法加入同步鎖),執行效率較慢,適用於多線程下操作字符串緩衝區大量數據。
  3. String不可變有什麼好處?
    • 可以緩存 hash 值
    • String Pool 的需要
    • 線程安全

2. Java 修飾符

Java修飾符:用來定義類、方法或者變量,通常放在語句的最前端。可分爲:

  • 訪問修飾符
  • 非訪問修飾符

2.1 訪問修飾符

Java中具有四種訪問權限:

  • public : 對所有類可見。使用對象:類、接口、變量、方法

  • protected : 對同一包內的類和所有子類可見。使用對象:變量、方法。 注意:不能修飾類(外部類)

  • default (即缺省,什麼也不寫): 在同一包內可見,不使用任何修飾符。使用對象:類、接口、變量、方法。

  • private : 在同一類內可見。使用對象:變量、方法。 注意:不能修飾類(外部類)

訪問權限之間的對比如下表所示:

在這裏插入圖片描述

2.2 非訪問修飾符

Java中提供的非訪問修飾符有:

  • static:用來修飾類、方法和變量

    • static修飾變量:類所有的實例都共享靜態變量,可以直接通過類名來訪問它;
    • static修飾方法:靜態方法在類加載的時候就存在了,它不依賴於任何實例,所以 static 方法必須實現,也就是說它不能是抽象方法(abstract)。
    • static修飾語句塊:靜態語句塊在類初始化時運行一次。
    • static修飾內部類:靜態內部類不依賴外部類,且不能訪問外部類的非 static 變量和方法。
  • final:用來修飾類、方法和變量:

    • final 修飾類:不能夠被繼承
    • final 修飾方法:不能被繼承類重新定義;
    • final 修飾變量:如果爲基本類型,則數值不可改變,爲常量;若爲引用類型,則引用不能更改,但引用的對象本身的屬性可更改。
  • abstract:用來創建抽象類和抽象方法

  • synchronized 和 volatile:主要用於線程的編程。

注意事項:

  1. 靜態成員函數中不能調用非靜態成員;
  2. 非靜態成員函數可以調用靜態成元;
  3. 常量成員不能修改,靜態成員必須初始化,但可以修改;
  4. 靜態成員和靜態方法可以直接通過類名進行調用,其他成員方法則需要進行實例化後調用。

2.3 Java變量

Java中的變量可分爲三種類型:

  • 成員變量
  • 局部變量
  • 靜態變量

它們三者的區別如下圖所示:

在這裏插入圖片描述

3. Java 繼承

Java面對對象具有三大特性:

  1. 繼承:繼承是從已有類得到繼承信息創建新類的過程。提供繼承信息的類被稱爲父類(超類、基類);得到繼承信息的類被稱爲子類(派生類)。繼承讓變化中的軟件系統有了一定的延續性,同時繼承也是封裝程序中可變因素的
  2. 封裝:通常認爲封裝是把數據和操作數據的方法綁定起來,對數據的訪問只能通過已定義的接口
  3. 多態:多態是指允許不同子類型的對象對同一消息作出不同的響應。要實現多態主要是做兩件事:重寫和重載。

3.1 方法重載和方法重寫

重載:是在一個類裏面,是多態在編譯器的表現形式。判斷方法:

  • 方法名相同
  • 形參列表不同

重寫:是子類對父類的允許訪問的方法的實現過程進行重新編寫, 返回值和形參都不能改變。是多態的運行期間的表現形式。判斷條件:

  • 方法名和形參列表相同
  • 重寫方法的返回值或者拋出的異常類型要與被重寫的方法的返回值和拋出的異常相同,或者爲其子類
  • 重寫方法的訪問修飾符大於等於被重寫方法的訪問修飾符

注意事項:

  • 靜態方法不存在重寫,形式上的重寫只能說是隱藏;
  • 私有方法不存在重寫,父類中的private方法,子類中即使定義了也相當於一個新的方法;

3.2 抽象類和接口

抽象類(abstract)和接口(interface)的區別:

  1. 抽象類中可以有構造方法,接口中不能;
  2. 抽象類中可以有普通成員變量,接口中不能;
  3. 抽象類中可以包含非抽象的普通方法,接口不能;
  4. 抽象類中的訪問類型是:public、protected,接口中默認爲:public abstract;
  5. 抽象類中可以包含靜態方法,接口中不能;
  6. 抽象類中的靜態成員變量類型任意,接口中僅爲:public static final;
  7. 一個類只可以繼承(extends)一個抽象類,但一個類可以繼承(implements)多個接口;

3.3 super和this關鍵字

this關鍵字:是指向對象本身的一個指針。this只能在類中的非靜態方法中使用,靜態方法和靜態的代碼塊中絕對不能出現this。

  • 表示類中的屬性和方法:函數參數或者參數中的局部變量和成員變量同名的情況下,成員變量被屏蔽,此時要訪問成員變量則需要用"this.成員變量名"來訪問成員變量。例如:

    class B{
        private int x = 1;
        public void out(){
            int x = 2;
            System.out.print(x);//打印2
            System.out.print(this.x);//打印1
        }
    }
    
  • 表示當前對象:在函數中,需要引用該函數所屬類的當前對象時候,直接使用this

    class C{
        public static void main(String[] args){
            C c1 = new C();
            c1.tell();
        }
        public static void tell(){
            System.out.print(this);//打印當前對象的字符串表示
        }
    }
    

super關鍵字:

  • 子類調用父類的構造方法:用super(參數列表)的方式調用,參數不是必須的。同時,還要注意super(參數列表)這條語句只能在子類構造方法中的第一行。例如:

    class A{
        public A(){
            System.out.print("A");
        }
    }
    class B extends A{
        public B(){
            super();//調用父類構造方法,打印A
            System.out.print("B");
        }
    }
    
  • 訪問父類中被覆蓋的同名變量或者方法:如果子類覆蓋了父類的中某個方法的實現,可以通過使用 super 關鍵字來引用父類的方法實現。例如:

    class A{
        public int a = 1;//可以直接賦值,不用通過構造函數
        public void say(){
            System.out.print(a);
        }
    }
    
    class B extends A{
        private int a = 2;
        public void say(){
            System.out.print(super.a);//訪問父類的a變量,前提是父類的a變量是公有的
        }
        public void tell(){
            super.say();//調用父類的say()方法而不是子類的say()方法
        }
    }
    

3.4 Java構造器

在Java中,構造方法的主要作用是完後才能對象的初始化工作,把定義對象的參數傳給對象的域。

注意事項:

  1. 構造方法的方法名必須與類名相同;

  2. 構造方法沒有返回類型,也不能定義爲void,在方法名前不聲明方法類型;

  3. 一個類可以定義多個構造方法,如果類在定義時沒有定義構造方法,編譯器會自動插入一個無參數的默認構造器;

  4. 子類不繼承父類的構造器(構造方法或者構造函數)的,它只是調用(隱式或顯式)。如果父類的構造器帶有參數,則必須在子類的構造器中顯式地通過 super 關鍵字調用父類的構造器並配以適當的參數列表。如果父類構造器沒有參數,則在子類的構造器中不需要使用 super 關鍵字調用父類構造器,系統會自動調用父類的無參構造器。

4. Java 異常

Java把異常當作對象來處理,並定義一個基類java.lang.Throwable作爲所有異常的超類。

4.1 Java異常分類

throwable又可以分爲兩類:

  • Error:內部錯誤或者資源耗盡錯誤,當出現這些異常時,Java虛擬機(JVM)一般會選擇終止線程
  • Exception:通常情況下是可以被程序處理的,並且在程序中應該儘可能的去處理這些異常。

Exception又可分爲:

  • UncheckedException:是程序運行時錯誤,常見的異常有:
    • ClassNotFoundException:應用程序試圖加載類時,找不到相應的類,拋出該異常。
    • IllegalAccessException:拒絕訪問一個類的時候,拋出該異常。
    • InterruptedException:一個線程被另一個線程中斷,拋出該異常。
    • NoSuchMethodException:請求的方法不存在
    • NullPointerException:空指針異常
    • IndexOutOfBoundsException: 數組角標越界異常,常見於操作數組對象時發生。
    • ClassCastException: 數據類型轉換異常
    • NumberFormatException: 字符串轉換爲數字異常;出現原因:字符型數據中包含非數字型字符。
  • CheckedException:需要用 try…catch… 語句捕獲並進行處理,並且可以從異常中恢復,常見的有:
    • IOException
    • SQLException

4.2 異常處理機制

Java的異常處理機制可分爲:

  • 異常捕獲:try、catch 和 finally
  • 異常拋出:throws、throw

對於異常捕獲,我們需要注意的是一下幾點:

try語句在返回前,將其他所有的操作執行完,保留好要返回的值,而後轉入執行finally中的語句,而後分爲以下三種情況:

  • 情況一:如果finally中有return語句,則會將try中的return語句”覆蓋“掉,直接執行finally中的return語句,得到返回值,這樣便無法得到try之前保留好的返回值。

  • 情況二:如果finally中沒有return語句,也沒有改變要返回值,則執行完finally中的語句後,會接着執行try中的return語句,返回之前保留的值。

  • 情況三:如果finally中沒有return語句,但是改變了要返回的值,這裏有點類似與引用傳遞和值傳遞的區別,分以下兩種情況:

    • 如果return的數據是基本數據類型或文本字符串,則在finally中對該基本數據的改變不起作用,try中的return語句依然會返回進入finally塊之前保留的值。

    • 如果return的數據是引用數據類型,而在finally中對該引用數據類型的屬性值的改變起作用,try中的return語句返回的就是在finally中改變後的該屬性的值

對於異常的拋出,需要注意的是:throws和throw的區別,

throw:

  • throw 語句用在方法體內,表示拋出異常,由方法體內的語句處理。
  • throw 是具體向外拋出異常的動作,所以它拋出的是一個異常實例,執行 throw 一定是拋出了某種異常

throws:

  • throws 語句是用在方法聲明後面,表示如果拋出異常,由該方法的調用者來進行異常的處理。
  • throws 主要是聲明這個方法會拋出某種類型的異常,讓它的使用者要知道需要捕獲的異常的類型。
  • throws 表示出現異常的一種可能性,並不一定會發生這種異常。

5. Java Object

5.1 Object方法概述

Java中的Object類是所有類的父類,它提供了以下11個方法:

  1. public final native Class<?> getClass():返回當前運行時對象的Class對象,getClass方法是一個final方法,不允許子類重寫,並且也是一個native方法。

  2. public native int hashCode(): 返回散列值,而 equals() 是用來判斷兩個實例是否等價。等價的兩個實例散列值一定要相同,但是散列值相同的兩個實例不一定等價。

  3. public boolean equals(Object obj):在非空對象引用上equlas具有以下特性:

    (一)自反性

    x.equals(x); // true
    

    (二)對稱性

    x.equals(y) == y.equals(x) // true
    

    (三)傳遞性

    if(x.equals(y) && y.equals(z)) {
        x.equals(z); // true;
    }
    

    (四)一致性:多次調用 equals() 方法結果不變

    x.equals(y) == x.equals(y); // true
    

    (五)與 null 的比較:對任何不是 null 的對象 x 調用 x.equals(null) 結果都爲 false

    x.euqals(null); // false;
    
  4. protected native Object clone() throws CloneNotSupportedException:是一個protected的native方法。由於Object本身沒有實現Cloneable接口,所以不重寫clone方法並且進行調用的話會拋出異常。且clone函數具有以下特性:

    • x.clone()!=x:x.clone()返回的對象爲新建的對象,與原來的對象地址不同。
    • x.clone().getClass() == x.getClass():克隆出的對象與原對象都是同一個類生成的。
    • x.clone().equals(x):新的對象與原來的對象相同(在equals()函數下是相同的,所以通常需要覆蓋equals()方法)
  5. public String toString():Object對象的默認實現,即輸出類的名字@實例的哈希碼的16進制

  6. public final native void notify():是一個native方法,並且也是final的,不允許子類重寫。喚醒一個在此對象監視器上等待的線程(監視器相當於就是鎖的概念)。如果所有的線程都在此對象上等待,那麼只會選擇一個線程。選擇是任意性的,並在對實現做出決定時發生。

  7. public final native void notifyAll():跟notify一樣,唯一的區別就是會喚醒在此對象監視器上等待的所有線程,而不是一個線程。

  8. public final native void wait(long timeout) throws InterruptedException:是一個native方法,並且也是final的,不允許子類重寫。wait方法會讓當前線程等待直到另外一個線程調用對象的notify或notifyAll方法,或者超過參數設置的timeout超時時間。

  9. public final void wait(long timeout, int nanos) throws InterruptedException:跟wait(long timeout)方法類似,多了一個nanos參數,這個參數表示額外時間(以毫微秒爲單位,範圍是 0-999999)

  10. public final void wait() throws InterruptedException:跟之前的2個wait方法一樣,只不過該方法一直等待,沒有超時時間。

  11. protected void finalize() throws Throwable { }:該方法的作用是實例被垃圾回收器回收的時候觸發的操作finalize方法是一個protected方法,Object類的默認實現是不進行任何操作。

5.2 “==”和equals的區別:

==:如果比較的對象是基本數據類型,則比較的是數值是否相等;如果比較的是引用數據類型,則比較的是對象的地址值是否相等。在不遇到算術運算的情況下,不會自動拆箱。
equals():用來比較方法兩個對象的內容是否相等。
注意:equals 方法不能用於基本數據類型的變量,如果沒有對 equals 方法進行重寫,則比較的是引用類型的變量所指向的對象的地址。

5.3爲什麼重寫equals方法後需要重寫hashcode?

因爲考慮到類似HashMap、HashTable、HashSet的這種散列的數據類型的運用。在Object類中,hashCode方法是通過Object對象的地址計算出來的,因爲Object對象只與自身相等,所以同一個對象的地址總是相等的,計算取得的哈希碼也必然相等,對於不同的對象,由於地址不同,所獲取的哈希碼自然也不會相等。所以如果重寫了equals方法,而不重寫hashcode方法,就可能會造成兩個相同對象的重複插入。

5.4 notify()和notifyAll有什麼區別?

notify()方法不能喚醒某個具體的線程,所以只有一個線程等待的時候纔有用。而notifyAll()喚醒所有的線程,並允許他們爭奪鎖,確保只有一個線程能繼續運行

5.5 爲什麼wait/notify/notifyAll是Object而不是Thread方法?

Java提供的鎖是對象級的而不是線程級。每個對象都有鎖,通過線程獲得。因爲它們屬於鎖的操作,而鎖屬於對象。

5.6 clone一個對象和new 一個對象的區別

new一個對象: 先根據new後面的類型去分配內存,然後再調用構造函數填充對象的各個域,初始化完成後,一個對象創建完畢,可以把他的引用(地址)發佈到外部。

clone一個對象:首先分配與原對象同樣大小的內存空間,然後根據原對象中對應的各個域,填充新對象的域,填充完成之後,clone 方法返回,一個新的相同的對象被創建,同樣可以把這個新對象的引用發佈到外部

5.7 深拷貝和淺拷貝

淺拷貝:拷貝實例和原始實例的引用類型引用同一個對象;

深拷貝:拷貝實例和原始實例的引用類型引用不同對象。

原理圖大概如圖所示:

在這裏插入圖片描述

6. Java反射

Java 中的反射首先是能夠獲取到Java中要反射類的字節碼 ,獲取字節碼有三種方法 :

  • Class.forName(className)
  • 類名.class
  • this.getClass()

然後將字節碼中的方法,變量,構造函數等映射成相應的 Method、Filed、Constructor 等類,這些類提供了豐富的方法可以被我們所使用。

6.1 反射的作用

Java反射機制主要提供一下的功能:

  1. 在運行時判斷任意一個對象所屬的類
  2. 在運行時構造任意一個類的對象
  3. 在運行時判斷任意一個類所具有的成員變量和方法
  4. 在運行時調用任意一個對象的方法
  5. 生成動態代理

6.2 靜態代理和動態代理

靜態代理:通常只代理一個類,事先知道要代理的是什麼。

動態代理:動態代理是代理一個接口下的多個實現類,不知道要代理什麼東西,只有在運行時才知道。動態代理是實現 JDK 裏的 InvocationHandler 接口的 invoke 方法,但注意的是代理的是接口,也就是你的業務類必須要實現接口,通過 Proxy 裏的 newProxyInstance 得到代理對象。

例如:下圖爲實現一個ArrayList的動態代理類:

public class proxy {
    public static void main(String[] args) {
        final ArrayList<String> list = new ArrayList<>();
        ClassLoader classLoader = list.getClass().getClassLoader();
        Class<?>[] interfaces = list.getClass().getInterfaces();
        List<String> listProxy=(List<String>) Proxy.newProxyInstance(classLoader,interfaces , new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return method.invoke(list,args);
            }
        });
        listProxy.add("你好") ;
        System.out.println(list);
    }
}

還有一種動態代理 CGLIB,代理的是類,不需要業務類繼承接口,通過派生的子類來實現代理。通過在運行
時,動態修改字節碼達到修改類的目的。

7. Java 註解

Java 註解是附加在代碼中的一些元信息,用於一些工具在編譯、運行時進行解析和使用,起到說明、配置的功能。

其中Java有四種元註解,用來標註其他的註解:

  • @Target: 修飾的對象範圍。
  • @Retention: 定義被保留的時間長短
    • SOURCE:在源文件中有效(即源文件保留)
    • CLASS:在 class 文件中有效(即 class 保留)
    • RUNTIME:在運行時有效(即運行時保留)
  • @Documented: 描述-javadoc
  • @Inherited : 闡述了某個被標註的類型是被繼承的

8. Java 泛型

  1. 更符合面向抽象的編程:與普通的 Object 代替一切類型這樣簡單粗暴而言,泛型使得數據的類別可以像參數一樣由外部傳遞進來。它提供了一種擴展能力。
  2. 是一種安全監測的機制:當具體的類型確定後,只有相匹配的數據才能正常的賦值,否則編譯器就不通過。所以說,它是一種類型安全檢測機制,一定程度上提高了軟件的安全性防止出現低級的失誤。
  3. 提高代碼的可讀性:泛型提高了程序代碼的可讀性,不必要等到運行的時候纔去強制轉換,在定義或者實例化階段,程序員能夠一目瞭然猜測出代碼要操作的數據類型。

出於規範的目的,Java建議使用單個大寫字母來代表類型參數。常見的如:

  1. T 代表:任何類;
  2. E 代表 :泛型方法;
  3. K 代表 :Key 的意思;
  4. V 代表: Value 的意思,通常與 K 一起配合使用;

8.1 泛型的使用情形

  1. 泛型方法****

    public static < E > void printArray( E[] inputArray ){
    	for ( E element : inputArray ){
    		System.out.printf( "%s ", element );
    	}
    }
    
  2. 泛型類****

    public class Box<T> {
    	private T t;
    	public void add(T t) {
    		this.t = t;
    	}
    	public T get() {
    		return t;
    	}
    }
    

8.2 通配符<?>

通配符具有下面三種形式:

  1. <?>:被稱爲無限定通配符,它其中的 ? 其實代表的是未知類型,所以涉及到 ? 時的操作,一定與具體類型無關。
  2. <? extends T>:被稱作有上限的通配符,表示該通配符所代表的類型是 T 類型的子類。
  3. <? super T>:被稱作有下限的通配符,表示該通配符所代表的類型是 T 類型的父類。

8.3 類型擦除

Java 中的泛型基本上都是在編譯器這個層次來實現的。在生成的 Java 字節代碼中是不包含泛型中的類型信息的。類型擦除的基本過程也比較簡單,首先是找到用來替換類型參數的具體類。這個具體類一般是 Object。如果指定了類型參數的上界的話,則使用這個上界。把代碼中的類型參數都替換成具體的類。

注意事項:

  1. 在泛型方法或者泛型類中,不接受8種基本數據類型
  2. Java不能創建具體類型的泛型數組

9. Java內部類

內部類可分爲:成員內部類(類中)、局部內部類(方法中)、匿名內部類、和靜態內部類

9.1 成員內部類

定義在類內部的非靜態類,就是成員內部類:它具有以下幾個特點:

  1. 可以訪問外部類的所有成員屬性和方法
  2. 不能存在任何Static(靜態)變量和方法(因爲在類的初始化過程中,會先加載靜態成員,而如果內部類是非靜態的,如果含有靜態成員,會產生歧義)
  3. 先創建外部類,才能創建內部類

例如:

public class Out {
    private static int a;
    private int b;
    public class Inner {
    	public void print() {
   			System.out.println(a);
    		System.out.println(b);
    	}
    }
}

9.2 局部內部類

嵌套在方法和作用域中,不能使用publicprivateprotect

public class Out {
	private static int a;
	private int b;
	public void test(final int c) {
		final int d = 1;
		class Inner {
			public void print() {
				System.out.println(c);
			}
		}
	}
}

9.3 匿名內部類

匿名內部類我們必須要繼承一個父類或者實現一個接口,當然也僅能只繼承一個父類或者實現一個接口。同時它也是沒有class關鍵字,這是因爲匿名內部類是直接使用new來生成一個對象的引用。

具有以下的特性:

  1. 創建的對象沒有名字
  2. 不可以家訪問修飾符
  3. 當所在方法的形參需要被內部類裏使用時,該形參必須被final修飾
  4. 不可引用在接口中定義的方法

例如:

abstract class Person {
    public abstract void eat();
}
 
public class Demo {
    public static void main(String[] args) {
        Person p = new Person() {
            public void eat() {
                System.out.println("eat something");
            }
        };
        p.eat();
    }
}

9.4 靜態內部類

定義在類內部的靜態類,就是靜態內部類。具有以下特點:

  1. 創建不需要依賴外部類
  2. 不能使用任何外部類的非static成員和方法
  3. 可以聲明靜態方法

例如:

public class Out {
	private static int a;
	private int b;
	public static class Inner {
		public void print() {
			System.out.println(a);
		}
	}
}

10. Java 枚舉(Enum)

Enum 是jdk1.5以後引入的一個新特性。這個特性可以使得我們的代碼變得更加的簡潔、安全以及便捷。

10.1 Enum的簡單使用

  • 常量的使用:以前我們定義常量會選擇這樣的方式:public static final,而當枚舉出現後,我們可以使用這樣的方式實現常量的定義:

    public enum Day {
        MONDAY, TUESDAY, WEDNESDAY,
        THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }
    
  • 自定義方法:使用Enum實現對異常的統一定義:

    public enum ResultEnum {
        /**
         * 系統未知錯誤
         */
        UNKONW_ERROR(-1,"未知錯誤:"),
         /**
          * 請求成功
          */
        SUCCESS(0,"success"),
        /**
         * 請求缺少參數
         */
        LACK_PARAMETER(1,"缺少參數");
        /**
         * 錯誤碼
         */
        private Integer errorCode;
        /**
         * 錯誤提示信息
         */
        private String message;
    
        ResultEnum(Integer errorCode, String message) {
            this.errorCode = errorCode;
            this.message = message;
        }
    
        public Integer getErrorCode() {
            return errorCode;
        }
        
        public String getMessage() {
            return message;
        }
    }
    
  • switch使用:JDK1.6之前的switch語句只支持int,char,enum類型,使用枚舉,能讓我們的代碼可讀性更強。(jdk1.7以後Switch補充支持了string類型)

    enum Color {GREEN,RED,BLUE}
    
    public class EnumDemo4 {
    
        public static void printName(Color color){
            switch (color){
                case BLUE: //無需使用Color進行引用
                    System.out.println("藍色");
                    break;
                case RED:
                    System.out.println("紅色");
                    break;
                case GREEN:
                    System.out.println("綠色");
                    break;
            }
        }
    
        public static void main(String[] args){
            printName(Color.BLUE);
            printName(Color.RED);
            printName(Color.GREEN);
        }
    }
    

10.2 Enum實現原理

實際上在使用關鍵字enum創建枚舉類型並編譯後,編譯器會爲我們生成一個相關的class,這個類繼承了Java API中的java.lang.Enum類,也就是說通過關鍵字enum創建枚舉類型在編譯後事實上也是一個class類型而且該類繼承自java.lang.Enum類。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章