Java SE基礎鞏固(三):經常使用的關鍵字

Java中的關鍵字不少,至少有50個左右,常見的new,final,try,catch等等,其中大多數關鍵字的意義都很簡單,基本上根據英文意思就能知道其功能,本文不會對那些簡單的關鍵字作介紹,僅挑選了幾個使用頻率較高,但又可能致使「迷惑」的關鍵字來討論。java

1 transient

transient關鍵字可修飾於類成員變量,做用是當類的對象發生序列化的時候,最終的序列化內容不包括被修飾的成員變量。下面的代碼演示了transient的功能:編程

public class User implements Serializable {

    private String username;

    private transient String password;
    
    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
    //setter and getter
}
public class TransientTest {

    public static void main(String[] args) {
        User user = new User();

        user.setUsername("yeonon");
        user.setPassword("admin");

        System.out.println("在序列化以前:");
        System.out.println(user);
        System.out.println("----------------------");

        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream(
                        "E:\\Java_project\\effective-java\\src\\top\\yeonon\\ch11\\user.txt"))){
            oos.writeObject(user);
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("E:\\Java_project\\effective-java\\src\\top\\yeonon\\ch11\\user.txt"))){
            User user1 = (User) ois.readObject();
            System.out.println("序列化以後讀取到的:");
            System.out.println(user1);
        } catch (IOException  | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
複製代碼

User類首先得實現Serializable接口,而後我在password成員變量上增長了transient關鍵字,在主類中,用ObjectOutputStream和ObjectInputStream來作序列化和反序列化。安全

執行後的輸出結果以下:併發

在序列化以前:
User{username='yeonon', password='admin'}
----------------------
序列化以後讀取到的:
User{username='yeonon', password='null'}
複製代碼

發現序列化以後,再讀取時,password是null,這說明序列化的內容沒有包括password。ide

順便說一下,在實際寫代碼的時候最好不要使用user1這種變量名,這裏只是爲了方便,隨手寫的。學習

2 instanceof

這是一個二元操做符,也算是關鍵字,做用是判斷左邊的對象是不是右邊類的實例。以下所示:spa

User user = new User();
if (user instanceof User) {
    System.out.println("user object is instance of User class");
} else {
    System.out.println("user object is'n instance of User class");
}
複製代碼

這裏確定會輸出user object is instance of User class。若是咱們將User替換成Object,也同樣會輸出這段話,這說明instanceof還能夠用於繼承體系,便可以用來判斷繼承體系中子類的實例是不是父類的實現。線程

3 volatile

這個多用於併發環境下,功能有兩個:code

  • 禁止指令重排
  • 保證可見性

關於volatile的介紹,在我以前的文章 Java虛擬機(二):Java內存模型 有詳細解釋,在此再也不贅述。對象

4 synchronized

和volatile同樣,多用於併發環境下,其做用就是給某個代碼段或者方法加上內置鎖。拿併發編程中的「Hello,World」來舉個例子:

public class SyncTest {

    private int count = 0;

    public void add() {
        count += 1;
    }

    public int getCount() {
        return count;
    }

    public static void main(String[] args) throws InterruptedException, TimeoutException, ExecutionException {
        ExecutorService service = Executors.newFixedThreadPool(4);
        SyncTest syncTest = new SyncTest();
        for (int j = 0; j < 4; j++) {
            service.execute(() -> {
                for (int i = 0; i < 10000; i++) {
                    syncTest.add();
                }
            });
        }
        service.shutdown();
        service.awaitTermination(1000, TimeUnit.SECONDS);
        System.out.println(syncTest.getCount());
    }
}
複製代碼

這裏輸出的結果會是什麼呢?40000?答案是不必定,多是40000,也多是比40000小的數,但確定不會比40000大,由於這裏至少有4個線程在併發的對count進行修改,而又沒有什麼同步措施,故答案不對。若是在add方法上加入synchronized關鍵字,就能夠保證線程安全了,以下所示:

public synchronized void add() {
    count += 1;
}
複製代碼

這樣以後,不管運行多少次代碼,結果都會是40000。由於synchronized其實是內置鎖,同一時刻僅有一個線程能獲取到鎖,並對其進行修改,最後執行完畢釋放鎖,其餘線程可再次競爭鎖,而後如此往復,知道任務完成。

那synchronized除了做用在方法還能做用在哪呢?下面是synchronized的使用方式:

  • 做用在實例方法上(沒有static修飾的方法),至關於給對應的對象加鎖,即也不能訪問該對象其餘的有synchronized修飾的方法,其餘實例對象不受影響。

  • 做用靜態方法上,至關於給類加鎖,此時的做用範圍就是該類的全部實例對象,即該類的全部對象同一時刻只能訪問有一個對象能訪問到synchronized靜態方法,並且不能訪問該類的其餘synchronized靜態方法。

  • 做用在代碼塊中,以下所示:

    private String lock = "lock";
    synchronized(lock) {
        //do something
    }
    //或者
    synchronized(Test.class) {
        //do something
    }
    複製代碼

    這又有兩種狀況,一種是括號裏的是對象實例,這種狀況是對對象實例加鎖,對其餘對象沒有影響。另外一種是括號裏的是類對象,這種狀況是對類加鎖,該類的其餘對象都會受到影響。

關於synchronized的其餘內容(例如在虛擬機裏是如何實現的?有哪些相應的指令?)就很少說了,比較本文不是專門講併發的。

5 final

final最容易讓人記住的功能就是將一個變量聲明成常量了,但實際上它的做用不只僅是這個,還能夠防止指令重排,在我以前的文章Java虛擬機(二):Java內存模型有比較詳細的介紹,在此再也不贅述。

6 static

static的做用也比較明顯,就是將類、方法、成員變量聲明成靜態的。

  • 做用在類上。不能做用在外部類,只能做用在內部類上。代表該類是外部類所擁有的,而不是外部類的對象實例擁有的。能夠在類裏定義靜態成員,而非靜態內部類則不行。
  • 做用在方法上,代表該方法是一個類方法,和實例對象沒有關係,能夠直接經過類.方法的形式調用。在靜態方法內部,能直接調用靜態方法,但不能直接調用實例方法。
  • 做用在成員變量上,代表該成員變量是一個類成員變量,訪問規則同靜態方法。

靜態類做用就是方便使用,若是一個類不依賴外部類的成員變量、方法等,那麼最好將其聲明成靜態類。

靜態方法其實也是爲了方便使用,在調用的時候能夠直接經過類名.方法的形式調用而不須要建立一個新的實例對象,靜態工廠模式就很是依賴這個特性。

靜態成員變量仍是爲了方便使用,咱們常常能在程序源代碼中看到相似public static final String XXX = "YYY"的聲明,這是由於靜態變量能夠直接訪問,不管是在實例方法裏,仍是靜態方法裏都同樣,這樣就能避免經過參數傳遞了。

7 小結

本文簡單的介紹了幾個經常使用的關鍵字,但實際上它們的功能或者原理都遠遠不止於此,若是想深刻了解,建議到網上搜索資料進行學習。

相關文章
相關標籤/搜索