先收藏!關於Java類、接口、枚舉的知識點大彙總

摘要:Java知識點精選之類、接口、枚舉30問,算是比較基礎的,希望大家一起學習進步。

整理了一些JAVA語言的在類、接口、枚舉等方面的知識點以及大家常遇到的問題。希望能幫助到大家。

Q: 各修飾符所代表的可見性?
public: 可被所有使用
protect: 只能被自己和子類使用,或者同一個包路徑
private: 只能自己使用,兒子都不行
不加修飾符即default權限: 包訪問權限,和他在同一包內的類都可以訪問他,包外的則都不能訪問

Q: 外部類可以用private或者protect修飾嗎?
A: 不能,只能用public或者包訪問權限。 內部類可以。

解釋以下final的作用
Q: final 成員?
A: 如果是基本類型,則指值不能被改變。 如果是對象,指對象的引用不可改變,但是引用處的內容可改變。

Q: final 參數?
A: 參數不可變,只能讀不能修改,同上

Q: final方法
A: 方法不能被子類重寫。

Q: final類
A: 該類不能被繼承。

Q:final局部變量可以作爲非final的參數傳入嗎?會編譯報錯嗎?

public static void main(String[] args){
	final A a = new A();
	changeA(a);
}
public void changeA(A a) {
	// change A...
}

A:可以作爲非final的參數傳入,不會編譯報錯。

Q: 重載和重寫的區別?
A:重載是方法名相同,參數不同。
重寫是方法參數等都一致的情況下重寫父類的方法。

Q: 如果子類重載了父類中的方法, 那麼子類中還能調用父類中的同名方法嗎?
A: 可以(C++中不可以調用父類中的同名重載方法)。

Q: 怎樣能避免子類在重寫父類的方法,不小心弄成了重載?
(即你想重寫父類的f(int), 卻不小心寫成了f(int,int),導致調用f(int)時還是調用了父類的f ,怎麼能避免這種失誤?)
A: 加個@Override關鍵字即可,原文解釋:

Q:父類的成員變量能被重寫/覆蓋嘛?

class A{
	public String name = "A";
}
class B extends A{
	public String name = "B";
}
public static void main{
	A a = new B();
   System.out.println(a.name);
}

A:輸出A。
注意成員變量不具有多態性,因此你定義的是A,賦值的是B, 那麼輸出的依舊是A裏的成員。
如果是被重寫的方法的話,那會用B裏的方法。

Q:內部類是啥,內部類能訪問外部類的成員嗎?
A:
內部類概念:

class A {
	class B{
   		...
   }
}

B就是A的內部類,B能訪問A的所有成員

Q: A中有1個內部類C, A的子類B中也有1個內部類C, B中的C會覆蓋A中的C嗎?
A: 不會, 因爲使用時是通過B.C或者A.C去調用的,存在命名空間的關係。

Q:可以在內部類中定義靜態成員嗎?

class A {
	class B{
    	static int b;
   		...
   }
}

A:不可以。 除非在class B前面加static變爲靜態類

Q: 匿名類是啥, 匿名類能訪問外面的變量或者對象嗎?
A: 匿名類概念:

return new A(構造參數){
   {構造器內容}
   類定義 
}

匿名類如果要用外面的對象, 外面的對象必須要定義爲final。

Q: 嵌套類是啥,能訪問外部類的成員嗎?
A:

class A {
   static int sa;
   int a;
	static class B{}
}

B只能訪問A中的靜態成員sa, 而不能訪問a。

§ 接口

類是單繼承,接口可以多繼承

Q: 接口中如果要定義成員變量,那成員的默認修飾符是什麼?
A: public static final

Q: 接口中各方法的默認修飾符是什麼?
A: public abstract

Q: 接口中可以定義實現具體方法嘛?
A:java8以上版本可以。
引入了default關鍵字,在接口中用default關鍵字修飾接口,就可以在接口中去實現這個接口了。

§ 枚舉

Q: enum可以被繼承嗎?
像下面這樣:

enum A extend B{
...
}

A: 不可以。enum標識符本身被編譯器處理過,自身就繼承自Enum類,而java不支持多重繼承。但支持實現接口

Q: switch(enum)時需要加default嗎?
A: 可以不需要。

Q: Enum基類裏實現了values()方法嗎?
A: 沒有實現, values方法是編譯器加的。因此從List<Enum>裏取出的對象,是不能調用values()的。

Q:enum裏的枚舉的默認修飾符默認是?
A:static final

§ 靜態分派和動態分派

Q: 下面輸出什麼,屬於什麼分派?

 void test() {
        Father father = new Son();          //靜態分派
        print(father);
    }
 
    void print(Father father) {
        System.out.println("this is father");
    }
 
    void print(Son son) {
        System.out.println("this is son");
    }

A:輸出this is father。 屬於靜態分派。
靜態分派概念: 編譯器就能確定調用哪個方法。
這裏2個print屬於重載方法,通過輸入參數的定義類型即立刻確定調用哪個
靜態分派取決於靜態類型

靜態類型概念: 編譯期寫在java文件裏能馬上看到的類型
例如 A a = Factory.create(args);
那麼左邊的A就是靜態類型, 而右邊的類型取決於運行期,是不確定的。

Q: 上一題的進階:

  public class Overload {
    private static void sayHello(char arg){
      System.out.println("hello char");
    }
    private static void sayHello(Object arg){
      System.out.println("hello Object");
    }
    private static void sayHello(int arg){
      System.out.println("hello int");
    }
    private static void sayHello(long arg){
      System.out.println("hello long");
    }
    // 測試代碼
    public static void main(String[] args) {
      sayHello('a');
    }
  }

輸出什麼?
A:輸出 hello char

因爲‘a’是一個char類型數據(即靜態類型是char),所以會選擇參數類型爲char的重載方法。
若註釋掉sayHello(char arg)方法,那麼會輸出 hello int
因爲‘a’除了可代表字符串,還可代表數字97。因此當沒有最合適的sayHello(char arg)方式進行重載時,會選擇第二合適(第二優先級)的方法重載,即sayHello(int arg)

總結:當沒有最合適的方法進行重載時,會選優先級第二高的的方法進行重載,如此類推。

優先級順序爲:char>int>long>float>double>Character>Serializable>Object>…
其中…爲變長參數,將其視爲一個數組元素。變長參數的重載優先級最低。
因爲 char 轉型到 byte 或 short 的過程是不安全的,所以不會選擇參數類型爲byte 或 short的方法進行重載,故優先級列表裏也沒有。

Q: 下面輸出什麼,屬於什麼分派:

   void test() {
        Father father = new Son();       
        father.name();    
    }
 
    class Son extends Father {
        void name(){
            System.out.println("son");
        }
    }
 
    class Father {
        void name(){
            System.out.println("father");
        }
    }

A:輸出son,屬於動態分派。運行的時候根據所指向的具體對象才確定調用哪個方法

Q:靜態分派屬於單分派還是多分派?動態分派屬於單分派還是多分派?
A:靜態分派是多分派。動態分派是單分派。

多分派概念: 分派時即要考慮調用者的類型,也要考慮參數類型。
而單分派只考慮調用者的類型。

動態分派原理:

Q:類方法在class文件中是什麼樣的? 是符號引用還是直接引用?

A:class文件中, 所定義的方法 都只是符號引用,即只是個符號,知道方法名字, 但是不知道方法的實際指令運行地址
符號引用如下,不過我這展示的時class_info即類的符號引用, 實際上還會有method_info即方法的引用:

然後方法在class文件中時這樣存放的, 先是一個method_count數量,接着再存儲方法。

此時是不知道方法的指令地址的。 除非從符號引用轉爲直接引用。

Q:什麼時候方法的符號引用會轉爲實際方法區中的直接引用?
A:類加載的解析階段會把滿足「編譯期可知,運行期不可變」的方法的符號引用替換爲指向方法區的直接引用,不會延遲到運行時再去完成。

  • 構造
  • 私有
  • 靜態
  • final修飾

上面這4類方法在類加載時都會被識別出來,並轉成 指向方法區的直接引用(即能知道了指令地址了,而不是字節碼的符號)

Q:動態分派(即多態), 虛擬機裏是怎麼確定調用哪個方法的?
如下, 他怎麼確定調用的是Son實現的do, 還是father實現的do?

int a = 1;
Father f = new Son()
f.do(a);

A:首先,通過靜態分派, 他知道一定選用的是f(int a) 這個方法,但是他不知道選用哪個類的do(int a)方法。
而你執行f.do(a)時, 操作數棧上會存放一個對象引用

那麼執行f方法的虛擬機指令就會通過這個對象引用,找到他的實際類型的class

他會在這個實際類中查找是否有實現這個方法,具體看class文件中有沒有這個方法的定義

如果沒有找到,他就去父類找,父類的關係class文件中就可以知道

如果父類沒有,就接着往上找,直到找到實現了的。

本文分享自華爲雲社區《Java知識點問題精選之類、接口、枚舉》,原文作者:breakDraw 。

 

點擊關注,第一時間瞭解華爲雲新鮮技術~

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