一道面試題引起的思考:(1)

這是網易2015校招Java面試題,直接上題目。java

題目

package com.mousycoder.staticTest;

public class HelloB extends HelloA {
    public HelloB() {
        System.out.println("HelloB");
    }

    {
        System.out.println("I’m B class");
    }
    static {
        
        System.out.println("static B");
    }

    public static void main(String[] args) {
        new HelloB();
    }
}

class HelloA {
    public HelloA() {
        System.out.println("HelloA");
    }

    {
        System.out.println("I’m A class");
    }
    static {
        System.out.println("static A");
    }
}

答案

static A
static B
I’m A class
HelloA
I’m B class
HelloB()

考察點

  • static 相關知識面試

  • 非靜態代碼塊相關知識微信

  • 類加載順序函數

解析

  • 存在父子關係,又有靜態代碼塊,先執行父類靜態代碼塊,再執行子類靜態代碼塊,故打印static A static B性能

  • 存在父子關係,又有非靜態代碼塊,先執行父類非靜態代碼塊,父類構造器,再執行子類非靜態代碼塊,子類構造器故打印I'm A class HelloA I'm B class HelloBthis

結論

  • 靜態代碼塊=>非靜態代碼塊=>構造方法spa

  • 父子關係:父類靜態代碼塊=>子類靜態代碼塊=>父類非靜態代碼塊=>父類構造函數=>子類非靜態代碼塊=>子類構造函數code

擴展知識

非靜態代碼塊

{ 
   System.out.println("I'm B class");
}

這個叫作非靜態代碼塊,也叫普通代碼塊,在每一個類建立前(構造函數以前)調用 ,不建立對象的時候,不被調用,代碼塊中定義的變量都是局部變量,若是有父子關係的話,先執行父類的,再執行子類的。非靜態代碼塊屬於對象,靜態代碼塊屬於類。對象

package com.mousycoder.staticTest;

public class Child extends Father{
    
    static {
        System.out.println("child-->static");
    }
    
    private int n = 20;
    
    {
        System.out.println("Child Non-Static");
        n = 30;
    }
    
    public int x = 200;
    
    public Child() {
        this("The other constructor");
        System.out.println("child constructor body: " + n);
    }
    
    public Child(String s) {
        System.out.println(s);
    }
    
    public void age() {
        System.out.println("age=" + n);
    }
    
    public void printX() {
        System.out.println("x=" + x);
    }
    
    public static void main(String[] args) {
        new Child().printX();
    }
}

class Father {
    
    static {
        //System.out.println("n+"+n);
            //當n定義在下面時,會提示Cannot reference a field before it is defined,
            //因此必須把n定義移到上面才能夠輸出
        System.out.println("super-->static");
    }
    
    public static int n = 10;
    public int x = 100;
    
    public Father() {
        System.out.println("super's x=" + x);
        age();
    }
    
    {
        System.out.println("Father Non-Static");
    }
    
    public void age(){
        System.out.println("nothing");
    }
}

結果:blog

super-->static
child-->static
Father Non-Static
super's x=100
age=0
Child Non-Static
The other constructor
child constructor body: 30
x=200

解析:

  • 先執行靜態代碼塊,有父子關係,先執行父類靜態代碼塊,打印super-->static

  • 執行子類靜態代碼塊,打印child-->static

  • 實例化子類的時候,先實例化父類,在調用父類構造方法以前,先調用父類非靜態代碼塊,打印Father Non-Static

  • 實例化父類,調用父類構造方法,執行第一句 System.out.println("super's x=" + x); 在調用構造方法以前,成員變量x 已經被賦值爲100,打印super's x=100

  • 執行age()方法,由於main方法中真正實例化的是子類,子類又重寫了父類的age方法,因此執行子類的age方法,此時子類還沒初始化,默認n=0 ,打印age=0

  • 實例化子類,調用子類構方法以前,調用子類非靜態代碼塊,打印Child Non-Static

  • 調用子類構造方法,執行this("The other constructor"),調用子類的Child(String s),打印The other constructor

  • 執行System.out.println("child constructor body: " + n); 初始化以前 成員變量初始值爲20,執行完非靜態代碼塊後 n被修改爲30 ,因此這裏n =30 打印child constructor body: 30

  • 執行printX()方法,打印x = 200

注意點:

  • 子類重寫父類的方法

  • 雖然非靜態代碼塊裏是局部變量,可是能夠改變類的成員變量的值

靜態代碼塊

static {
  System.out.println("static B");
}

屬於靜態代碼塊,使用場景:1.想用一個存儲區域來保存一個特定的數據,不想建立對象 2.建立一個特殊的方法,與這個類的對象沒有關聯,即便沒建立對象,也能夠調用

靜態變量

靜態變量也叫作static變量,靜態變量和非靜態變量的區別在於靜態變量被全部的對象所共享,在內存中只有一個副本,它當且僅當類初次加載時會被初始化,而非靜態變量是對象所擁有的,在建立對象的時候被初始化,存在多個副本,各個對象擁有的副本互不影響 好處:1.能夠用類名直接訪問(方便),也能夠用對象訪問(不推薦) 2.只分配一次內存,節省空間

靜態方法

靜態方法就是不會被this和super的方法。特色:1.在static方法內部不能調用所屬類的實例變量和實例方法(非靜態方法),可是能夠在沒有建立任何對象的前提下,僅僅經過類自己(類名)來調用static方法 2.main方法就是static 方法,由於執行main方法的時候,沒有建立任何對象,只能經過類名去訪問 3.沒有顯示聲明,類的構造器也是靜態方法,參考:實例構造器是否是靜態方法?
常見的錯誤Cannot make a static reference to the non-static field 就是靜態方法中引用了非靜態的的變量,靜態變量是不依賴對象而存在的,非靜態變量是依賴對象而存在的

示例:非靜態方法能夠訪問靜態變量、方法,靜態方法是不能夠訪問非靜態變量,方法

package com.mousycoder.staticTest;

public class Test {
    
    private static String str1 = "111";
    
    private String str2 = "ddd";
    
    
    public Test() {
    }
    
    public  void print1() {
        System.out.println(str1);
        System.out.println(str2);
        print1();
    }
    
    
    public static void print2(){
        System.out.println(str1);
        System.out.println(str2);  //報錯  Cannot make a static reference to the non-static field
        print1(); //報錯
    }

}

靜態代碼塊

由static 關鍵字修飾的代碼塊叫作靜態代碼塊,特色:用於提升程序性能,類在初始化的時候,JVM會按照他們在類中出現的順序加載static 代碼塊,而且只會執行一遍

示例:只生成一個String s 對象和一個String m 對象

package com.mousycoder.staticTest;

import java.util.Date;
public class HelloStatic {
    
    public static String s ;
    
    public static String m ;
    
    static {
        s = new String("hell");
        m = new String("0");
    }
        String hello() {
            String s = "hell";
            String m = "o";
            return s + m;
        }
        
        public static void main(String[] args) {
            Date start = new Date();
            for (int i = 0; i < 1000000000 ; i++) {
                HelloStatic h = new HelloStatic();
                h.hello();
            }
            Date end = new Date();
            System.out.println(end.getTime() - start.getTime());
        }

}

耗時:12ms

示例: 生成N個String s和String m對象

package com.mousycoder.staticTest;

import java.util.Date;

public class Hello {
        
        String hello() {
            String s = new String("hell");
            String m = new String("o");
            return s + m;
        }
        
        public static void main(String[] args) {
            Date start = new Date();
            for (int i = 0; i < 1000000000; i++) {
                Hello h = new Hello();
                h.hello();
            }
        
            Date end = new Date();
            System.out.println(end.getTime() - start.getTime());
        }
        
}

所用時間 9645ms

公共的變量應該放到static裏初始化,有利於提升效率

靜態變量訪問權

static 變量是被全部對象共享的,只要該對象有權限,就能夠訪問該變量

package com.mousycoder.staticTest;

public class Main {

    static int value = 33;
 
    public static void main(String[] args) throws Exception{
        new Main().printValue();
    }
 
    private void printValue(){
        int value = 3;
        System.out.println(this.value);
    }
}

該輸出爲:33 ,new Main()建立了一個對象,經過this.value是訪問的該對象的value 而int value = 3 是局部變量,並非和該對象相關聯的,由於static變量是共享的,因此該對象能夠訪問到value 值爲 33 ,把this 去掉 則結果爲 3

靜態變量相關面試題

package com.mousycoder.staticTest;

public class Test {
    Person person = new Person("Test");
    static{
        System.out.println("test static");
    }
     
    public Test() {
        System.out.println("test constructor");
    }
     
    public static void main(String[] args) {
        new MyClass();
    }
}
 
class Person{
    static{
        System.out.println("person static");
    }
    public Person(String str) {
        System.out.println("person "+str);
    }
}
 
 
class MyClass extends Test {
    Person person = new Person("MyClass");
    static{
        System.out.println("myclass static");
    }
     
    public MyClass() {
        System.out.println("myclass constructor");
    }
}

順序是:

test static
myclass static
person static
person Test
test constructor
person MyClass
myclass constructor

執行順序

test static =>new MyClass() => myclass static => new MyClass() => person static => Person person = new Person("Test") => person Test => Person person = new Person("Test") => test constructro => Person person = new Person("Myclass") => person MyClass => Person person = new Person("MyClass") => myclass contructor

說明:

  • 執行main方法,實例化MyClass

  • 發現MyClass 有父類Test,首先實例化Test

  • 加載Test類,這個時候會執行static方法,打印 test static ,

  • 而後要實例化MyClass 類,加載MyClass類,執行static方法,打印myclass static

  • 在全部類按照順序都加載完畢後,開始一個一個實例化類,先實例化父對象 Test類

  • 在實例化以前,要先初始化成員變量,這個時候會執行Person p = new Person("Test"),這個時候發現Person類尚未被加載過,所以先加載Person類

  • 執行Person類的static方法,打印person static 接着實例化Person p = new Person("Test") 調用Person的構造方法,打印person Test

  • 而後開始調用Test的構造方法,打印test constructor,Test類初始化完畢

  • 開始實例化Myclass類,首先仍是初始化MyClass的成員變量 ,由於Person已經加載過了,因此只執行一遍,只需根據類的模板執行其構造方法,來複製成另一個副本,此時打印person MyClass

  • 最後執行Myclass的構造方法,打印myclass constructor

總結:

非靜態代碼塊=>初始化成員變量=>非靜態代碼塊=>構造器方法


感謝您的耐心閱讀,若是您發現文章中有一些沒表述清楚的,或者是不對的地方,請給我留言,你的鼓勵是做者寫做最大的動力,
若是您認爲本文質量不錯,讀後以爲收穫很大,不妨小額贊助我一下,讓我更有動力繼續寫出高質量的文章。

  • 支付寶

  • 微信

做 者 : @mousycoder

原文出處 : http://mousycoder.com/2015/10/12/thinking-of-interview-question-1/

創做時間:2015-7-1

更新時間:2015-10-14

相關文章
相關標籤/搜索