1. 程式人生 > >Java基礎之final、static關鍵字

Java基礎之final、static關鍵字

一、前言

  關於這兩個關鍵字,應該是在開發工作中比較常見的,使用頻率上來說也比較高。介面中、常量、靜態方法等等。但是,使用頻繁卻不代表一定是能夠清晰明白的瞭解,能說出個子醜演卯來。下面,對這兩個關鍵字的常見用法做點總結記錄,方便之後的回顧以及突擊知識點。

二、關鍵字 final

  final,一如字面意思 “最終的”,大體在 Java 中表示 “不可變的”。可用來修飾類、方法、方法引數以及變數。

  1、修飾類

   final 在修飾類的時候,代表的是此類不能被繼承。也就是說如果一個類確定不會被繼承使用,則可以設計成 final型別的。典型的例子就是 String 類。

  2、修飾方法

  final 修飾的方法,能被繼承,但是不能重寫。可以過載。

  3、修飾方法引數

  final 在修飾方法引數的時候,表示的是在執行方法的內部,不能夠去改變引數的值。但是如果是引用物件,是可以改變應用物件的屬性值。

  4、修飾變數

  final 在修飾變數,代表的是不可變,也即是常說的 “常量”。 final 在修飾的時候,允許一次賦值,之後在生命週期類,不允許對其進行修改。

  修飾變數存在兩種情況:基本型別的資料 和  物件資料。在修飾基本型別資料的時候,值是不可變的。在修飾物件資料的是,物件的引用是不可改變的,但是,可以修改物件內部的屬性值。

  final 修飾的變數必須在使用前進行初始化,一種方式是在宣告的時候就給出預設值。還有一種就是通過構造方法去設定。

  5、程式碼示例

package com.cfang;

import java.util.Calendar;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class T3 {
    

    public static void main(String[] args) {
        // -- 修飾變數
        final int b = 0;
        /**
         *     編譯報錯:The final local variable b cannot be assigned. It must be blank and not using a compound assignment
         */
//        b = 2;
        
        // -- 修飾方法引數
        DemoCls cls = new DemoCls();
        cls.setAge(10);
        log.info("democls age : {}", cls.getAge());
        int a = 10;
        sayHello(a, cls);
        log.info("after call sayHello, democls age : {}", cls.getAge());
    }
    
    private static void sayHello(final int a, final DemoCls cls) {
        /**
         *     編譯報錯:The final local variable a cannot be assigned. It must be blank and not using a compound assignment
         */
//        a = 11;
//        cls = new DemoCls();
        
        cls.setAge(11);
    }
}

@Data
class DemoCls{
    private int age;
}

// -- 修飾方法
@Slf4j
class Person {
    public final void saySth() {
        log.info("i am person");
    }
}

@Slf4j
class Male extends Person{
    /**
     *     編譯出錯 : Cannot override the final method from Person
     *     修飾的方法不能夠被重寫
     */
//    public final void saySth() {
//        log.info("i am male");
//    }
    
    public final void saySth(String msg) {
        log.info("i am male");
    }
}

  其中,修飾方法引數,如果是引用物件,是可以改變物件的屬性值,這一點也很好理解:cls 是引用變數,final 修飾引用變數,只是限定了此引用變數 cls 的引用不能改變,而實際引用的物件的本身的值是可以進行修改的。文字語言組織可能不是很清晰,如下圖所示,一目瞭然的就知道說要表述的意思了。

  

三、關鍵字 static

  static,靜態的。在 Java 中,static 通常可被用於修飾 變數、方法以及程式碼塊。

  1、修飾變數

  static 修飾的變數,叫做靜態變數。static 變數被所有類物件共享,在記憶體中僅一份,隨著類的初始化而被載入。與之對應的非靜態變數,是屬於每個例項物件本身,記憶體中存在多份,相互間不影響。

  2、修飾方法

  static 修飾的方法,叫做靜態方法。呼叫靜態方法,不依賴於例項物件就可以進行訪問,所以,靜態方法是沒有 this的。由於此特性以及非靜態方法依賴於例項物件呼叫,所以靜態方法中是不能夠直接使用非靜態的成員屬性和方法。與之相反的是,非靜態方法是可以直接訪問使用靜態成員變數和方法。同樣的,靜態方法也是沒有 super 的。可以一句話總結下:由於 static 和具體的例項物件無關,而 this、super和具體的例項物件息息相關,所以,static 和 this、super 勢如水火,一如白天與黑夜。

  3、修飾程式碼塊

  static 修飾程式碼塊,在類初始化載入的時候,會按照 static 塊的順序進行載入,並且,生命週期內,只加載一次。基於此特性,可以設計優化程式的效能,一些只需要一次性初始化載入的內容,就可以放在 static 塊中進行。

  4、程式碼示例

package com.cfang;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class T4 {
    
    private static int a;
    private static int b;
    private static int c;
    
    static {
        log.info("init value a");
        a = 10;
    }
    
    {
        /**
         *     普通程式碼塊,依賴於例項物件
         */
        log.info("init value c");
        c = 12;
    }

    public static void main(String[] args) {
        // -- 靜態方法呼叫非靜態
        /**
         *     不可直接訪問
         */
//        sayHello1();
        new T4().sayHello1();
        
        // -- 靜態方法直接呼叫
        sayHello();
        
        // -- 靜態程式碼塊初始化資源
        log.info("value a : {}", a);
        log.info("value b : {}", b);
        log.info("value c : {}", c);
    }
    
    private static void sayHello() {
        log.info("say hello");
    }
    
    private void sayHello1() {
        log.info("say hello1");
        // -- 非靜態方法直接呼叫
        sayHello();
    }
    
    static {
        log.info("init value b");
        b = 11;
    }
}

  5、static 常見誤區

package com.cfang;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class T5 {
    
    private static int a = 10;

    public static void main(String[] args) {
        new T5().printVal();
        
        //-- The field DemoCls01.b is not visible
        DemoCls01.b;
    }
    
    private void printVal() {
        int a = 11;
        log.info("value a : {}", this.a);
    }
}

@Data
class DemoCls01{
    private static int b;
}

  5.1、static 的 this

  上述程式碼中,最終執行 printVal 方法,輸出的結果是 :value a : 10  。 其實這也很好理解: this 指代的當前物件,而通過 new T5().printVal() 呼叫的話,this 指代的就是當前新建立的例項物件,static 修飾的變數本身是被所有類物件所共享的,而 printVal 方法中 a 屬於區域性變數,跟 this 例項物件並沒有關聯。所以,輸出的就是 10。

  5.2、static 與 可見性

  static 並不能改變變數或者方法的可見性。上述程式碼中,main 方法中,DemoCls01.b 會編譯報錯。

  5.3、static 與 區域性變數

  Java規範中,明確說明了 :static 關鍵字不可修飾區域性變數。

四、總結

  final 和 static ,聯合使用修飾屬性表示一旦給值,就不可修改,並且可以通過類名訪問;修飾方法,表示該方法不能重寫,可以在不new物件的情況下呼叫。

  突然想到,介面 interface 中,成員變數的預設修飾符為 public static final,方法的預設修飾符 public abstract