Java基礎:關鍵字

在平時編碼時,咱們可能只注意到static、final、volatile等關鍵字的使用,而忽略了它們更深層次的意義。html

static關鍵字

概述

當static修飾類的屬性或者方法時,那麼就能夠在沒有建立對象的狀況下使用該屬性或方法。java

靜態塊也是static的一個應用,用於初始化類時的一些操做。react

 

靜態方法和靜態變量

被static修飾後的屬性或者方法,使用時不須要new一個類,用類.屬性名或方法名訪問。dom

好比java.lang.Math就存放了不少靜態資源,能夠直接使用Math.random()來獲取隨機數。工具

注:this

一、非靜態方法是能夠直接訪問靜態資源的,靜態方法是不能直接引用非靜態資源的。編碼

 

二、.靜態屬性和方法能夠經過.屬性名或方法名,並且,該類的對象也是訪問靜態屬性和變量的。spa

 

三、Java的語法規定,static不能修飾局部變量。沒有爲何,這就是規定。線程

 

示例:code

編譯時就會報錯,main方法是靜態方法,變量i是非靜態的。解決方案:一是將變量i加上static修飾,二是如上採用實例訪問非靜態屬性。

  

爲何非靜態方法能夠訪問靜態資源,而靜態方法不能訪問非靜態資源呢?

 從類加載機制上講,靜態資源是類初始化的時候加載的,而後非靜態資源是new一個該類的對象的時候加載的。

 

這就帶來一個疑問:

加載類時默認先加載靜態資源的,當new一個對象以後,纔會加載其餘資源,因此在new對象以前,靜態資源是不知道類有哪些非靜態資源的,

可是當對象new出來以後,該類的全部屬性和方法都知道。

 

靜態塊

靜態塊也是在類加載的時候執行,並且只執行一次。

靜態塊注意要點:

  1. 靜態資源的加載順序嚴格按照靜態資源的定義順序加載的;
  2. 靜態塊,對於定義在它以後的靜態變量,能夠賦值但不能訪問。

示例:

package main.keyword;


public class StaticDemoTest2 extends ParentClass{
    public static String subStaticField = "子類靜態變量";
    public String subField = "子類非靜態變量";
    public static StaticClass staticClass = new StaticClass("子類");

    static {
        System.out.println("子類 靜態塊初始化");
    }

    {
        System.out.println("子類 [非]靜態塊初始化");
    }

    public StaticDemoTest2(){
        System.out.println("子類構造器初始化");
    }
    public static void main(String[] args) {
        new StaticDemoTest2();
    }
}

class ParentClass{
    public static String parentStaticField = "父類靜態變量";
    public String parentField = "父類[非]驚天變量";
    public static StaticClass staticClass = new StaticClass("父類");

    static {
        System.out.println("父類 靜態塊初始化");
    }

    {
        System.out.println("父類 [非]靜態塊初始化");
    }

    public ParentClass(){
        System.out.println("父類 構造器初始化");
    }
}

class StaticClass{
    public StaticClass(String name){
        System.out.println(name+" 靜態變量加載");
    }
}

 

執行結果:

父類 靜態變量加載 父類 靜態塊初始化
子類 靜態變量加載
子類 靜態塊初始化
父類 [非]靜態塊初始化 父類 構造器初始化
子類 [非]靜態塊初始化
子類構造器初始化

 

 

 下面是總結類加載流程,能夠對照着這個流程,能夠再從新看一下上面的例子,會有新的理解。

1. 加載父類靜態
    1.1 爲靜態屬性分配存儲空間並賦初始值
    1.2 執行靜態初始化塊和靜態初始化語句(從上至下)

2. 加載子類靜態
    2.1 爲靜態屬性分配存儲空間
    2.2 執行靜態初始化塊和靜態初始化語句(從上至下)

3. 加載父類非靜態
    3.1 爲非靜態塊分配空間  
    3.2 執行非靜態塊

4. 加載子類非靜態
    4.1 爲非靜態塊分配空間  
    4.2 執行非靜態塊

5. 加載父類構造器
    5.1 爲實例屬性分配存數空間並賦初始值
    5.2 執行實例初始化塊和實例初始化語句
    5.3 執行構造器內容

6. 加載子類構造器
    6.1 爲實例屬性分配存數空間並賦初始值
    6.2 執行實例初始化塊和實例初始化語句
    6.3 執行構造器內容

 

 示例2:

package main.keyword;

public class StaticDemoTest3 {
    Person person = new Person("TestStaticLoad");//4
    static{
         System.out.println("TestStaticLoad static");//1
    }

     public StaticDemoTest3() {
         System.out.println("TestStaticLoad constructor");//5 
     }

     public static void main(String[] args) {
         new God();
     }
}
 class Person{
     static{
        System.out.println("person static");//3
     }
     public Person(String str) {
         System.out.println("person "+str);//4 -TestStaticLoad  6-God
    }
 }

class God extends StaticDemoTest3 {
     Person person = new Person("God"); //6
     static{
         System.out.println("God static");//2
     }

     public God() {
         System.out.println("God constructor");//7
     }
 }

 

執行結果:

TestStaticLoad static
God static
person static
person TestStaticLoad
TestStaticLoad constructor
person God
God constructor

 

 

一步一步地解析:

  • 在StaticDemoTest3的main方法中,執行了new God(),那就就會去加載God類,在這以前會先加載它的父類:TestStaticLoad
  • 第一步:加載父類靜態,執行System.out.println("TestStaticLoad static");  輸出:TestStaticLoad static,
  • 第二步:加載子類靜態,執行System.out.println("God static");,輸出God static
  • 第三步:加載父類非靜態,Person person = new Person("TestStaticLoad");,這裏實例化了Person 對象,那就會去加載Person類。
  • 第四步:加載Person類,首先看有沒有父類,沒有。好,加載靜態塊,執行System.out.println("person static");輸出person static
  • 第五步:Pernson類靜態塊加載完畢,加載構造器,new一個Person對象,輸出person TestStaticLoad。這時TestStaticLoad 類非靜態塊加載完畢
  • 第六步:加載God 父類(TestStaticLoad )構造器,輸出TestStaticLoad constructor
  • 第七步:God父類所有加載完畢,加載God的非靜態塊,Person person = new Person("God");這時又會去加載Person類,須要注意的是,static塊只加載一次,由於以前在父類已經加載過了,這時只加載構造器,輸出person God
  • 最後一步:加載本類God 的構造器,輸出God constructor。

 static關鍵字總結:

  • static關鍵字 能夠再沒有建立對象的時候進行調用類的元素
  • static 能夠修飾類的方法 以及類的變量, 以及靜態代碼塊
  • 被static修飾的成爲靜態方法,靜態方法是沒有this的,靜態方法不能訪問同一個類中的非靜態方法和靜態變量,可是非靜態方法 能夠能夠訪問靜態變量
  • 類的構造器 也是靜態的
  • 靜態變量被全部的內存全部的對象共享,在內存中只有一個副本。非靜態變量是是在建立對象的時候初始化的,存在多個副本,每一個副本不受影響。
  • static 靜態代碼塊,static 代碼塊能夠放在類中的任何地方,類加載的時候會按照static代碼塊的順序來加載代碼塊,而且只會執行一次。
  • 靜態成員變量雖然獨立於對象,可是不表明不能夠經過對象去訪問,全部的靜態方法和靜態變量均可以經過對象訪問。
  • static不能夠修飾局部變量(java語法規定)

 

 final關鍵字

總結:

  • 在java中final能夠用來修飾類、方法、和變量(包括成員變量和局部變量)
  • final修飾類的時候,這個類將永遠不會被繼承,類中的成員方法也會被隱式的修飾爲final(儘可能不要用final修飾類)
  • final修飾的方法不可重寫,final修飾的屬性不能繼承
  • 若是不想方法被繼承,能夠用final修飾,private也會隱式的將方法指定爲final
  • final修飾變量的時候,若是是基本類型的變量,那麼他的值在初始化以後就不能更改
  • final在修飾對象的時候,在其初始化以後就不能指向其餘對象
  • 被static和final修飾的變量,將會佔據一段不能改變的存儲空間,將會被看作編譯期常量
  • 不可變的是變量的引用而非引用指向對象的內容

 示例:

public class TestFinal {
    public static void main(String args[]){
        String a = "test1";
        final String b = "test";
        String d = "test";
        String c = b + 1; 
        String e = d + 1;
        System.out.println((a == c));
        System.out.println((a.equals(e)));
    }
}

 

執行結果:

 

true
true

 

 由於final變量是基本類型以及String時,在編譯期的時候就把它當作常量來使用,不須要在運行時候使用。「==」是對比兩個對象基於內存引用,若是兩個對象的引用徹底相同,則返回true,因此這裏b是用訪問常量的方式去訪問,d是連接的方式,因此a的內存引用和c的內存引用是相等的,因此結果爲true,a和e兩個對象的值是相等的,因此結果爲true。

 

示例:

在編譯的時候,或報錯, 不能指向一個final對象。

 

volatile關鍵字

具體定義請看這裏

 應用

線程池(ThreadPoolExecutor)中一些變量的定義:

 

   private volatile ThreadFactory threadFactory;

    private volatile RejectedExecutionHandler handler;

    private volatile long keepAliveTime;

    private volatile boolean allowCoreThreadTimeOut;

    private volatile int corePoolSize;

    private volatile int maximumPoolSize;

 

 

能夠看到線程工廠threadFactory,拒絕策略handler,沒有任務時的活躍時間keepAliveTime,keepAliveTime的開關allowCoreThreadTimeOut,核心池大小corePoolSize,最大線程數maximumPoolSize

都是被volatile修飾中,由於在線程池中有若干個線程,這些變量必需保持對線程可見性,否則會引發線程池運行不正確。

 

assert關鍵字

assert斷言

在目前的java編碼中,是不推薦使用的,這裏只是稍微瞭解一下:

使用方式:

 
一、assert <boolean表達式>
若是<boolean表達式>爲true,則程序繼續執行。
若是爲false,則程序拋出AssertionError,並終止執行。
 
二、assert <boolean表達式> : <錯誤信息表達式>
若是<boolean表達式>爲true,則程序繼續執行。
若是爲false,則程序拋出java.lang.AssertionError,並輸入<錯誤信息表達式>。
 
若是要開啓斷言檢查,則須要用開關-enableassertions或-ea來開啓,java中IDE工具默認支持開啓-ea
 
示例:
public class AssertDemoTest {
    public static void main(String[] args) {
        assert true;
        System.out.println("斷言1成功執行");
        System.out.println("-----------");
        assert false:"error";
        System.out.println("斷言2成功執行");
    }
}

 執行結果:

斷言1成功執行
-----------
斷言2成功執行

 

synchronized關鍵字

關於鎖關鍵字,有如下幾個總結:

  • 不管synchronized關鍵字加在方法上仍是對象上,若是它做用的對象是非靜態的,則它取得的鎖是對象;若是synchronized做用的對象是一個靜態方法或一個類,則它取得的鎖是對類,該類全部的對象同一把鎖。
  • 每一個對象只有一個鎖(lock)與之相關聯,誰拿到這個鎖誰就能夠運行它所控制的那段代碼。
  • 實現同步是要很大的系統開銷做爲代價的,甚至可能形成死鎖,因此儘可能避免無謂的同步控制。 

 

transient關鍵字

 Java中,一個類想要序列化,能夠經過實現Serilizable接口的方式來實現,實現該接口以後,該類全部屬性和方法都會自動序列化。

可是若是屬性或方法被transient修飾,那麼將不會被序列化。

相關文章
相關標籤/搜索