1. 程式人生 > >Java面試複習之Java深入解析四

Java面試複習之Java深入解析四

  1. main方法:main方法是程式執行的入口。可以被過載,但必須要有虛擬機器能自動呼叫的main方法才能編譯通過。main方法也可以被其他方法呼叫。main同樣也可以被繼承和隱藏。
  2. 方法的過載:過載是根據引數列表的不同來區分的。引數列表的不同可以區分為個數、型別、順序等。不能根據返回值型別和方法的異常列表來區分。 void m(List ist){} void m(List<String> list){} 以上兩個方法也不能構成過載,因為引數化型別在經過編譯後會被擦除。這兩個方法擦除後的引數列表時相同的,都是List型別,會出現方法重定義。 在呼叫過載方法的時候,會最先呼叫最“明確”的方法。如實參為byte型別時的呼叫,形參中沒有byte型別的時候,會依次找short、int、long、float、double。實參為char型別時的呼叫,會依次找char、int、long、float、double。找到為止。引數為引用型別時也一樣。先找形參為該類的引用的方法。如果沒有,再找可以賦值的父類的引用的方法。 自動拆裝箱和可變引數型別的過載:優先選可變引數型別
public class Maintest {
    public static void main(String[] args) {
        Maintest maintest=new Maintest();
        int i=10;
        maintest.m(i);
    }
   public void m(Integer integer)
   {
       System.out.println("Integer\t\t"+integer.intValue());
   }
    public void m(long l)
    {
        System.out.println("long\t\t"+l);
    }
}

執行結果為:

long		10

當呼叫方法有有兩個或兩個以上的引數,且多個方法都符合呼叫條件,但是無法從中選擇出一個最明確的方法來呼叫時。就會產生編譯錯誤。

public class Maintest {
    public static void main(String[] args) {
        char ch='a';
        int i=1;
        long l=2;
        float f=3;
      //m(ch,l);
    }
    public static void m(char ch,float f)
    {
        System.out.println("char-----float 方法");
    }
    public static void m(int i,long l)
    {
        System.out.println("int------long 方法");
    }
}

這樣的方法呼叫產生了編譯錯誤。因為兩個方法都不是最明確的,但都可以呼叫。因此不明確該呼叫哪一個。註釋兩個方法中的任意一個,編譯即可通過。 過載方法的呼叫是根據引用的靜態型別來決定的(識別符號左邊的型別),這一點與對方法重寫的呼叫不同(多型,執行時決定)。

public class Maintest {
    public static void main(String[] args) {
       Object o=new String();
       m(o);
    }
    public static void m(String s)
    {
        System.out.println("String");
    }
    public static void m(Object o)
    {
        System.out.println("Object");
    }
}

執行結果為Object。是根據引用的靜態型別來決定的。 3.方法的重寫: 重寫條件:

  • 都要是例項方法,不能是靜態。
  • 子類方法的返回值型別是父類方法的返回值型別的可替換型別。
  • 不能比父類丟擲更多的異常。
  • 子類方法的訪問許可權不能低於父類方法的訪問許可權。

可以在子類中使用非泛型化的方法重寫父類中泛型化的方法,但是不能再子類中使用泛型化的方法重寫父類中非泛型化的方法。父類定義為void m(List<String> list)時,子類可以重寫為void m(List list)。但是反過來不行。要重寫父類的方法,在子類中首先要可以訪問到。父類中私有的方法不能重寫,可以在子類中定義與父類中私有方法同名同參的方法,但這不是重寫,是重新定義了一個方法。 4.隱藏: 方法的隱藏:條件和方法的重寫條件一樣,只不過兩個方法都是靜態的。 成員變數的隱藏:從父類中繼承來了(不是private修飾的)且變數名稱相同就是隱藏。 對於隱藏的變數和方法來說,要呼叫時通過類名點變數名來呼叫。 父類:

public class Super {
    public String s="Super 的成員變數";
    public void m(){
        System.out.println("Super 的m方法");
    }
    public static void method(){
        System.out.println("Super的靜態方法");
    }
}

子類:

public class Maintest extends Super{
    public String s="Maintest 的成員變數";
    @Override public void m() {
        System.out.println("Maintest 的方法");
    }
    public static void method()
    {
        System.out.println("Maintese的靜態方法");
    }
    public static void main(String[] args) {
        Super s=new Maintest();
        System.out.println(s.s);
        s.m();
        s.method();
    }
}

執行結果:

Super 的成員變數
Maintest 的方法
Super的靜態方法

對於隱藏,是根據引用的靜態型別來決定呼叫相關類的成員。

5.構造器:構造器在建立物件之後呼叫,對物件進行初始化工作。所以不是構造器建立物件,而是new關鍵字建立了物件。構造器可以顯示呼叫,用this(呼叫當前類的構造)或者super(呼叫父類構造)。但是this或super必須出現在構造器中的第一行。所以this和super不能同時出現在一個構造方法中。如果類內沒有構造方法時,系通或生成無參構造。這個無參構造會呼叫父類的構造,一直遞迴,直到呼叫到Object類。 父類:

public class Super {
   public Super()
   {
       System.out.println("This is Super's constructor");
   }
}

子類,沒有寫構造方法。系統自動生成無參構造。但是如果有有參構造,系統不會自動生成無參構造。所以如果寫了有參構造,最好順便新增上無參構造。

public class Maintest extends Super{
    public static void main(String[] args) {
     Maintest maintest=new Maintest();
    }
}

執行結果:

This is Super's constructor

6.成員變數的初始值: 基本資料型別:數字型別的均為0,char為空字元,boolean為false。 引用資料型別:陣列和物件的引用均為null。 在方法中宣告的區域性變數沒有預設值,使用前一定要賦初始值。但是陣列有值,值為0。 例項變數初始化方式:

  • 在宣告處初始化。
  • 在例項化塊中初始化。
  • 在構造方法中初始化。

靜態變數的初始化方式:

  • 在宣告處初始化。
  • 在靜態初始化塊中初始化

五種初始化的先後順序:先靜態,後例項,最後剩下構造器。靜態和例項中方法和變數的順序是以定義順序為初始化順序。

public class Maintest {
    public static int i=Super.init2();
    public Maintest()
    {
        System.out.println("構造器初始化");
    }
    static {
       System.out.println("靜態塊的初始化");
   }
    {
        System.out.println("例項塊初始化");
    }
    public int j=Super.init1();
    public static void main(String[] args) {
        Maintest maintest=new Maintest();
    }
}

不管怎樣調整調整以上五段初始化程式碼的順序,構造器都是在最後,靜態都是在例項之前。JVM虛擬機器試圖呼叫該類的main方法,發現類沒有被載入。因此會先載入類,為類的靜態變數分配空間,並設定預設值,隨後初始化執行。靜態初始化部分只會在載入類的時候進行執行。例項初始化部分會在建立物件的時候執行。 執行結果:

靜態變數初始化
靜態塊的初始化
例項塊初始化
例項變數初始化
構造器初始化

當存在繼承關係時,先呼叫父類的靜態,後呼叫子類。當存在引用時。 向前引用:在定義變數之前就引用了該變數。變數的值為預設值。訪問時要加this。 在構造器中呼叫了例項方法,該例項方法用到了例項成員變數。由於構造器是對例項變數的初始化,因此這種情況也被視為向前引用。

public class Maintest {
   private int area=this.side*4;
   private int side=5;
   public static void main(String[] args) {
       Maintest maintest=new Maintest();
        System.out.println("side:"+maintest.side+"\tside:"+ maintest.area);
    }
}

由於是在同一個類,公有可以訪問私有。成員變數數字基本型別中數字型別的預設值為0。

side:5	side:0

如果有final修飾的變量出現,則該變數即為常量。在編譯成位元組碼的時候值就已經定了,且不可更改。所以在程式執行的時候,根本看不見final修飾的變數的預設值。就算是向前引用也看不到。