coreJava_7——static、final

一、static修飾符

static修飾符可以用來修飾類的成員變量、成員方法和代碼塊。

  • 用static修飾的成員變量表示靜態(tài)變量,可以直接通過類名來訪問;
  • 用static修飾的成員方法表示靜態(tài)方法,可以直接通過類名來訪問;
  • 用static修飾的程序代碼表示靜態(tài)代碼塊,當Java虛似機加載類時,就會執(zhí)行該代碼塊;
    被static所修飾的成員變量和成員方法表明歸某個類所有,它不依賴于類的特定實例,被類的所有實例共享。

1、static變量

成員變量:定義在類里面、方法外面的變量, 分兩種:
a. 實例變量;
b. 靜態(tài)變量;形式和實例變量類似,在實例變量前面加static關鍵字;
static變量和實例變量的區(qū)別:
. static變量對于每個類而言在內存中只有一個,能被類的所有實例所共享;實例變量對于每個類的每個實例都有一份,它們之間互不影響;
. Java虛擬機在加載類的過程中為static變量分配內存,實例變量在加載完類后創(chuàng)建對象時分配內存;
. static變量可以直接通過類名訪問,實例變量通過引用類型變量訪問;

例:

public class Counter {
public int count1 = 0;
public static int count2 = 0;
public static void main(String[] args){
Counter counterA = new Counter();
Counter counterB = new Counter();
counterA.count1++;
counterA.count2++;
counterB.count1++;
counterB.count2++;
     }
 }

2、static方法

成員方法分為靜態(tài)方法和實例方法。用static修飾的方法叫靜態(tài)方法,或類方法。靜態(tài)方法也和靜態(tài)變量一樣,不需要創(chuàng)建類的實例,可以直接通過類名來訪問。

public class Sample1 {
 
  public static int add(int x, int y) {
     return x+y;
    }
}
    public class Sample2 {
    public void method() {
    int result = Sample1.add(1,2);
     System.out.println("result= " + result);
   }
}

a. static方法可以直接訪問所屬類的實例變量和實例方法,直接訪問所屬類的靜態(tài)變量和靜態(tài)方法;

注:1、 不能使用this關鍵字;
2、不能使用super關鍵字,super關鍵字用來訪問當前實例從父類中繼承的方法和屬性。super關鍵字與類的特定實例相關;
3、靜態(tài)方法必須被實現。靜態(tài)方法用來表示某個類所特有的功能,這種功能的實現不依賴于類的具體實例,也不依賴于它的子類。既然如此,當前類必須為靜態(tài)方法提供實現。

b. 父類的靜態(tài)方法不能被子類覆為非靜態(tài)方法。以下代碼編譯出錯。

3. static 代碼塊

類中可以包含靜態(tài)代碼塊,它不存于任何方法中。在Java虛擬機中加載類時會執(zhí)行這些靜態(tài)代碼塊。如果類中包含多個靜態(tài)代碼塊,那么Java虛擬機將按照它們在類中出現的順序依次執(zhí)行它們,每個靜態(tài)代碼塊只會被執(zhí)行一次。

    public class Sample {
    static int i = 5;
    static {//第一個靜態(tài)代碼塊
System.out.println("First Static code i="+i++);
    }
    static {//第二個靜態(tài)代碼塊
System.out.println("Second Static code i="+i++);
    }
    public static void main(String[] args) {
Sample s1 = new Sample();
Sample s2 = new Sample();
System.out.println("At last, i= "+i);
    }
}

類的構造方法用于初始化類的實例,而類的靜態(tài)代碼塊則可用于初始化類,給類的靜態(tài)變量賦初始值。

靜態(tài)代碼塊與靜態(tài)方法一樣,也不能直接訪問類的實例變量和實例方法,而必須通過實例的引用來訪問它們。

思考:new一個對象的時候JVM都做了那些事情?

1.之前沒有進行類加載

1.類加載,同時初始化類中靜態(tài)的屬性(賦默認值),查看靜態(tài)變量是否有指定的值,有替換默認值
2.執(zhí)行父類靜態(tài)代碼塊,在執(zhí)行子類靜態(tài)代碼塊
3.分配內存空間,同時初始化非靜態(tài)的屬性(賦默認值)
4.先調用父類的匿名代碼塊

5.父類構造器執(zhí)行完后,如果子類聲明屬性的同時有顯示的賦值,那么進行顯示賦值把默認值覆蓋
6.執(zhí)行子類代碼塊
7.執(zhí)行子類構造器
8.返回內存地址

例子:

package com.briup.ch06;

public class Test {
public static void main(String[] args) {
 
 A a = new B();
}

}

class A{
protected String name = "lisi";
public A() {
 System.out.println("父類構造器A");
 System.out.println("父類構造器A中調用test方法開始,由于子類重寫過test方法所以這里執(zhí)行子類的test方法");
 test();
 System.out.println("父類構造器A中調用test方法結束");
}
public void test(){
 
 }

}

class B extends A{
private String name = "tom";
{
 System.out.println("子類匿名代碼塊中:"+name);
}
public B() {
 System.out.println("子類構造器B");
}
public void test(){
 
 System.out.println("test方法中:this.name="+this.name);
 System.out.println("test方法中:super.name="+super.name);
 
  }

}

打印結果:
父類構造器A //子類構造器調用父類構造器
父類構造器A中調用test方法開始,由于子類重寫過test方法所以這里執(zhí)行子類的test方法
test方法中:this.name=null //這個時候父類構造器還沒有執(zhí)行完 所以子類中的屬性不會顯示賦值 所以只有初始的默認值null
test方法中:super.name=lisi //這個時候父類構造器開始調用 了 所以父類中的屬性已經有了顯示賦的值了
父類構造器A中調用test方法結束
子類匿名代碼塊中:tom //這個時候父類構造器已經調用結束 所以子類中的屬性已經有了顯示賦的值了
子類構造器B

結論: 子類中的屬性的顯示賦值的時機 是在 父類構造器執(zhí)行完之后和子類的匿名代碼塊執(zhí)行之前的某個時候

2.之前已經進行了類加載

1.分配內存空間,同時初始化非靜態(tài)的屬性(賦默認值)
2.調用父類構造器
3.父類構造器執(zhí)行完后,如果自己聲明屬性的同時有顯示的賦值,那么進行顯示賦值把默認值覆蓋
4.執(zhí)行匿名代碼塊
5.執(zhí)行構造器
6.返回內存地址

4、靜態(tài)導入

靜態(tài)導入也是JDK5.0引入的新特性。
要使用靜態(tài)成員(方法和變量)我們必須給出提供這個靜態(tài)成員的類。使用靜態(tài)導入可以使被導入類的靜態(tài)變量和靜態(tài)方法在當前類中可以直接使用,使用這些靜態(tài)成員無需再在前面寫上他們所屬的類名。
//例如:

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

public class Test {

 public static void main(String[] args) {

//之前是需要Math.random()調用的
System.out.println(random());
System.out.println(PI);

 }

}

二、final修飾符

final具有"不可改變的"含義,它可以修飾非抽象類、非抽象成員方法和變量。
. 用final修飾的類不能被繼承,沒有子類;
. 用final修飾的方法不能被子類的方法覆蓋;
. 用final修飾的變量表示常量,只能被賦一次值;

final不能用來修飾構造方法,因為"方法覆蓋"這一概念僅適用于類的成員方法,而不適用于類的構造方法,父類的構造方法和子類的構造方法之間不存在覆蓋關系. 因此用final修飾構造方法是無意義的。父類中用private修飾的方法不能被子類的方法覆蓋,因此private類型的方法默認是final類型的。

  1. final類
    繼承關系的弱點是打破封裝,子類能夠訪問父類的方法,而且能以方法覆蓋的方式修改實現細節(jié)。在以下情況下,
    可以考慮把類定義為final類型,使得這個類不能被繼承。
    . 子類有可能會錯誤地修改父類的實現細節(jié);
    . 出于安全,類的實現細節(jié)不允許有任何改動;
    . 在創(chuàng)建對象模型時,確信這個類不會再被擴展;

例如JDK中java.lang.String類被定義為final類型;

  1. final方法;

    某些情況下,出于安全原因,父類不允許子類覆蓋某個方法, 此時可以把這個方法聲明為final類型。例如在
    java.lang.Object類中,getClass()方法為final類型。

  2. final變量:
    final修飾的屬性(成員變量)賦值的位置:
    非靜態(tài)的成員變量
    1.聲明的同時
    2.匿名代碼塊
    3.構造器(類中出現的所有構造器)

靜態(tài)的成員變量
1.聲明的同時
2.static代碼塊

a. final可以修飾靜態(tài)變量、實例變量、局部變量;
b. final變量都必須顯示初始化,否則會導致編譯錯誤;
1) 靜態(tài)變量,定義變量時進行初始化或者static代碼塊中賦值;
2) 實例變量,可以在定義變量時,或者在構造方法中進行初始化;
c. final變量只能賦一次值。

      public class Sample {
  private final int var1 = 1;
  public Sample() {
       var1 = 2;     //編譯出錯,不允許改變var1實例變量的值;
  }

  public void method(final int param) {
       final int var2 = 1;         
       var2++;       //編譯出錯,不允許改變var2局部常量的值
       param++;      //編譯出錯,不允許改變final類型參數的值;
  }
      }

      public class Sample {
  final int var1;    //定義var1實例常量
  final int var2 = 0;           //定義并初始化var2實例常量

  Sample() {
var1 = 1;    //初始化var1實例常量
  }

  Sample(int x) {
var1 = x;     //初始化var1實例常量
  } 
}       
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容