1. 程式人生 > 實用技巧 >Java基礎語法:static修飾符

Java基礎語法:static修飾符


一、靜態變數

描述:

  • 在類中,使用'static'修飾的成員變數,就是靜態變數,反之為非靜態變數。

區別:

  • 靜態變數屬於類的,可以使用類名來訪問;非靜態變數是屬於物件的,必須使用物件來訪問。
  • 靜態變數對於類而言在記憶體中只有一個,能被類的所有例項所共享;例項變數對於類的每個例項都有一份,它們之間互不影響。
  • 靜態變數在載入類的過程中分配記憶體,例項變數在建立物件時分配記憶體,所以靜態變數可以使用類名來直接訪問,而不需要使用物件來訪問。

示例:

public class Student {
    private static int age = 20;
    private double score = 100;

    public static void main(String[] args) {
        Student student = new Student();
        System.out.println(age);//20
        System.out.println(Student.age);//20
        /* 不建議使用物件訪問靜態成員 */
        System.out.println(student.age);//20
        System.out.println(student.score);//100.0
    }
}




二、靜態方法

描述:

  • 在類中,使用'static'修飾的成員方法,就是靜態方法,反之為非靜態方法。

區別:

  • 靜態方法數屬於類的,可以使用類名來呼叫;非靜態方法是屬於物件的,必須使用物件來呼叫。
  • 靜態方法不可以直接訪問類中的非靜態變數和非靜態方法,但是可以直接訪問類中的靜態變數和靜態方法;非靜態方法可以直接訪問類中的非靜態變數和非靜態方法,也可以直接訪問類中的靜態變數和靜態方法。

注意:

  • 'this'和'super'在類中屬於非靜態的變數,靜態方法中不能使用
  • 父類的靜態方法可以被子類繼承,但是不能被子類重寫。
  • 父類的非靜態方法不能被子類重寫為靜態方法。

示例:

public class Student {
    private static int age = 20;
    private double score = 100;

    public static void staticSay() {
        System.out.println(age);
    }

    public void say() {
        System.out.println(age + " " + score);
    }

    public static void main(String[] args) {
        Student.staticSay();//20
        Student student = new Student();
        /* 不建議使用物件呼叫靜態方法 */
        student.staticSay();//20
        student.say();//20 100.0
    }
}




三、靜態程式碼塊

描述:

  • 在類中,使用'static'修飾的匿名程式碼塊,就是靜態程式碼塊
  • 因為沒有名字,在程式中並不能主動呼叫這些匿名程式碼塊。

區別:

  • 靜態程式碼塊是在類載入完成之後就自動執行,並且只執行一次;匿名程式碼塊是在每次建立物件的時候自動執行的,並且在構造器執行之前執行。
  • 靜態程式碼塊不可以直接訪問類中的非靜態變數和非靜態方法,但是可以直接訪問類中的靜態變數和靜態方法;匿名程式碼塊可以直接訪問類中的非靜態變數和非靜態方法,也可以直接訪問類中的靜態變數和靜態方法。
  • 靜態程式碼塊的作用是給類中的靜態成員變數初始化賦值;匿名程式碼塊的作用是給物件的成員變數初始化賦值,但是因為構造器也能完成這項工作,所以匿名程式碼塊使用的並不多。

注意:

  • 'this'和'super'在類中屬於非靜態的變數,靜態程式碼塊中不能使用
  • 在構造器中給靜態變數賦值,並不能保證能賦值成功,因為構造器是在建立物件的時候才執行,但是靜態變數可以不建立物件而直接使用類名來訪問。

示例:

public class Student {
    /* 靜態程式碼塊 */
    static {
        staticSay();
        age = 10;
        staticSay();
    }
    /* 匿名程式碼塊 */
    {
        say();
        age = 30;
        score = 80.0;
        say();
    }
    /* 構造器 */
    Student() {
        say();
        age = 40;
        score = 90.0;
        say();
    }
    /* 靜態成員變數賦初始值,定義在靜態程式碼塊之下,會比靜態程式碼塊後執行 */
    private static int age = 20;
    /* 非靜態成員變數賦初始值,定義在匿名程式碼塊之下,會比匿名程式碼塊後執行 */
    private double score = 100.0;
    /* 靜態方法 */
    public static void staticSay() {
        System.out.println(age);
    }
    /* 非靜態方法 */
    public void say() {
        System.out.println(age + " " + score);
    }

    public static void main(String[] args) {
        /*
            本次執行順序為:靜態程式碼塊-->靜態成員變數賦初始值-->匿名程式碼塊-->非靜態成員變數賦初始值-->構造器
            所以建立物件會在控制檯依次列印:
                0
                10
                20 0.0
                30 80.0
                30 100.0
                40 90.0
        */
        Student student = new Student();
        student.say();//40 90.0
    }
}




四、建立和初始化物件的過程

步驟:

  1. 類載入,同時初始化類中靜態變數賦預設值
  2. 靜態變數有指定初始值,且程式碼定義在靜態程式碼塊之上,對靜態變數進行顯式賦值
  3. 執行靜態程式碼塊
  4. 靜態變數有指定初始值,且程式碼定義在靜態程式碼塊之下,對靜態變數進行顯式賦值
  5. 建立物件,分配記憶體空間,同時初始化成員變數賦預設值
  6. 呼叫父類構造器
  7. 成員變數有指定初始值,且程式碼定義在匿名程式碼塊之上,對成員變數進行顯式賦值
  8. 執行匿名程式碼塊
  9. 成員變數有指定初始值,且程式碼定義在匿名程式碼塊之下,對成員變數進行顯式賦值
  10. 執行構造器
  11. 若有變數引用,返回記憶體地址賦給變數。

示例:

public class Person {
    
    private int age = 10;

    public Person() {
        System.out.println("Person構造器");
        print();
    }

    public void print() {
        System.out.println("Person print方法: age = " + age);
    }
}

class Student extends Person {
    /* 賦初始值定義在匿名程式碼塊之上,會比匿名程式碼塊先執行 */
    private int age = 20;

    static {
        System.out.println("Student靜態程式碼塊");
    }

    {
        System.out.println(age);
        System.out.println("Student匿名程式碼塊");
    }

    public Student() {
        System.out.println(age);
        System.out.println("Student構造器");
    }

    public void print() {
        System.out.println("student print方法: age = " + age);
    }

    public static void main(String[] args) {
        /*
            結果為在控制檯依次列印:
                Student靜態程式碼塊
                Person構造器
                student print方法: age = 0
                20
                Student匿名程式碼塊
                20
                Student構造器
         */
        new Student();
    }
}




五、靜態導包

描述:

  • 靜態導包就是java包的靜態匯入,用'import static'代替'import'來靜態匯入包,是JDK1.5中的新特性,意思是匯入這個類裡的靜態方法
  • 靜態導包的好處就是在有很多重複呼叫靜態方法的時候可以簡化一些操作,如果僅有一到兩次呼叫,不如直接寫來的方便。

示例:

import static java.lang.Math.PI;
import static java.lang.Math.random;

public class Test {
    public static void main(String[] args) {
        /* 非靜態匯入需要使用Math.random()的方式呼叫 */
        System.out.println(random());//0.05775943381246629
        System.out.println(PI);//3.141592653589793
    }
}