首頁  >  文章  >  Java  >  詳解Java程式設計中protected修飾符與static修飾符的作用

詳解Java程式設計中protected修飾符與static修飾符的作用

高洛峰
高洛峰原創
2017-01-24 15:32:121683瀏覽

protected 
來談談protected存取權限問題。看下面範例1:
Test.java

class MyObject {}
  
public class Test {
  public static void main(String[] args) {
    MyObject obj = new MyObject();
    obj.clone(); // Compile error.
  }
}

此時出現上文提到的錯誤:The method clone from the type Object is not visiuable.
我們已經清楚Object.clone()是protected方法。這說明,該方法可以被同包(java.lang)下和它(java.lang.Object)的子類別存取。這裡是MyObject類別(預設繼承java.lang.Object)。
同樣Test也是java.lang.Object的子類別。但是,不能在一個子類別中存取另一個子類別的protected方法,儘管這兩個子類別繼承自同一個父類別。
再看範例2:
Test2.java

class MyObject2 {
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}
  
public class Test2 {
  public static void main(String[] args) throws CloneNotSupportedException {
    MyObject2 obj = new MyObject2();
    obj.clone(); // Compile OK.
  }
}

這裡,我們在MyObject2類別中覆寫(override)父類別的clone()方法,在另一個類別Test2中呼叫clone()方法,編譯通過。
編譯通過的原因顯而易見,當你在MyObject2類別中覆寫clone()方法時,MyObject2類別和Test2類別在同一個套件下,所以此protected方法對Test2類別可見。
分析到這裡,我們在回憶一下Java中的淺複製與深複製文中,章節2.2中的聲明,②在派生類別中覆蓋基類的clone()方法,並聲明為public。現在明白這句話的原因了吧(為了讓它類別能呼叫這個類別的clone()方法,重載之後要把clone()方法的屬性設為public)。
下面再來看範例3:
Test3.java

package 1
class MyObject3 {
protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}
  
package 2
public class Test3 extends MyObject3 {
  public static void main(String args[]) {
    MyObject3 obj = new MyObject3();
    obj.clone(); // Compile error.
    Test3 tobj = new Test3();
    tobj.clone();// Complie OK.
  }
}

   

這裡我用Test3類別繼承MyObject3,注意這兩個類別是不同包裝的,否則就是範例2的情形。在Test3類別中呼叫Test3類別的實例tobj的clone()方法,編譯通過。而同樣呼叫MyObject3類別的實例obj的clone()方法,編譯錯誤!
意想不到的結果,protected方法不是可以被繼承類別存取嗎?
必須明確,類別Test3確實是繼承了類別MyObject3(包含它的clone方法),所以在類別Test3中可以呼叫自己的clone方法。但類別MyObject3的protected方法對其不同包子類別Test3來說,是不可見的。

方法的存取控制:

詳解Java程式設計中protected修飾符與static修飾符的作用

static
1.關鍵字static(先記住這些,再往下看)
1)靜態方法和靜態變數是屬於某一個類,而不是屬於類別的物件。
2)靜態方法和靜態變數的引用直接透過類別名稱引用。
3)在靜態方法中不能呼叫非靜態的方法和引用非靜態的成員變數。反之,則可以。
4)靜態變數在某種程式上與其他語言的全域變數相類似,如果不是私有的就可以在類別的外部進行存取。
2.何時使用static
在我們創建一個類別的實例時(物件),通常使用new方法,這樣這個類別的資料空間才會被創建,其方法才能被呼叫。
     但是,有時候我們希望一個類別雖然可以被創建n個物件(顯然這n個物件的資料空間是不相同的),但這n個物件的某些資料是相同的,即不管這個類別有多少的實例,這些資料對這些實例而言之有一份記憶體拷貝(見範例1)。這是靜態變數的情形。
另一種情形是,你希望某個方法不與包含它的類別的任何物件關聯在一起。也就是說,即使沒有建立對象,也能夠呼叫這個方法。 static 方法的一個重要用法就是在不建立任何物件的前提下,就可以呼叫它(請參閱範例2)。這是靜態方法的情況。
還有一種特殊的用法出現在內部類別中,通常一個普通類別不允許宣告為靜態的,只有一個內部類別才可以。這時這個宣告為靜態的內部類別可以直接當作一個普通類別來使用,而不需實例一個外部類別(見範例3)。這是靜態類別的情形。
範例1

public class TStatic {
  static int i;
  
  public TStatic() {
    i = 4;
  }
  
  public TStatic(int j) {
    i = j;
  }
  
  public static void main(String args[]) {
    System.out.println(TStatic.i);
    TStatic t = new TStatic(5); // 声明对象引用,并实例化。此时i=5
    System.out.println(t.i);
    TStatic tt = new TStatic(); // 声明对象引用,并实例化。此时i=4
    System.out.println(t.i);
    System.out.println(tt.i);
    System.out.println(t.i);
  }
}

結果:

0
5
4
4
4

static變數在類別被載入時創建,只要類別存在,static變數就存在。它們在定義時必須進行初始化。上例中沒有初始化i,所以會得到預設的初始值0。 static的變數的初始化僅能一次,static變數只是接受了最後一次的初始化。
實際這還是多個實例共享一個靜態的變數的問題。
 
範例2
未宣告為static

class ClassA {
  int b;
  
  public void ex1() {}
  
  class ClassB {
    void ex2() {
      int i;
      ClassA a = new ClassA();
      i = a.b; // 这里通过对象引用访问成员变量b
      a.ex1(); // 这里通过对象引用访问成员函数ex1
    }
  }
}

   

 
聲明為static

class ClassA {
  static int b;
  
  static void ex1() {}
}
  
class ClassB {
  void ex2() {
    int i;
    i = ClassA.b; // 这里通过类名访问成员变量b
    ClassA.ex1(); // 这里通过类名访问成员函数ex1
  }
}

   

在使用静态方法时要注意,在静态方法中不能调用非静态的方法和引用非静态的成员变量(在static方法中也不能以任何方式引用this或super)。理由很简单,对于静态的东西,JVM在加载类时,就在内存中开辟了这些静态的空间(所以可以直接通过类名引用),而此时非静态的方法和成员变量所在的类还没有实例化。
所以如果要使用非静态的方法和成员变量,可以直接在静态方法中实例化该方法或成员变量所在的类。public static void main就是这么干的。
 
示例3

public class StaticCls {
  public static void main(String[] args) {
    OuterCls.InnerCls oi = new OuterCls.InnerCls();// 这之前不需要new一个OuterCls
  }
}
  
class OuterCls {
  public static class InnerCls {
    InnerCls() {
      System.out.println("InnerCls");
    }
  }
}

 
结果:

InnerCls

3.静态初始化
static定义的变量会优先于任何其它非static变量,不论其出现的顺序如何。静态代码块(在“static{”后面跟着一段代码),是用来进行显式的静态变量初始化,这段代码只会初始化一次,且在类被第一次装载时。看下面示例。

class Value {
  static int c = 0;
  
  Value() {
    c = 15;
  }
  
  Value(int i) {
    c = i;
  }
  
  static void inc() {
    c++;
  }
}
  
class Count {
  public static void prt(String s) {
    System.out.println(s);
  }
  
  Value v = new Value(10);
  
  static Value v1, v2;
  static {
    prt("in the static block of calss Count v1.c=" + v1.c + " v2.c="
       + v2.c);
    v1 = new Value(27);
    prt("in the static block of calss Count v1.c=" + v1.c + " v2.c="
       + v2.c);
    v2 = new Value();
    prt("in the static block of calss Count v1.c=" + v1.c + " v2.c="
       + v2.c);
  }
}
  
public class TStaticBlock {
  public static void main(String[] args) {
    Count ct = new Count();
    Count.prt("in the main:");
    Count.prt("ct.c=" + ct.v.c);
    Count.prt("v1.c=" + Count.v1.c + " v2.c=" + Count.v2.c);
    Count.v1.inc();
    Count.prt("v1.c=" + Count.v1.c + " v2.c=" + Count.v2.c);
    Count.prt("ct.c=" + ct.v.c);
  }
}

 

 
结果:

in the static block of calss Count v1.c=0 v2.c=0
in the static block of calss Count v1.c=27 v2.c=27
in the static block of calss Count v1.c=15 v2.c=15
in the main:
ct.c=10
v1.c=10 v2.c=10
v1.c=11 v2.c=11
ct.c=11

   

不管是v,v1还是v2,它们操作的成员变量都是同一个静态变量c。
在类Count中先初始化v1,v2(static Value v1, v2;),再初始化静态代码块(static{}),最后初始化v。

更多詳解Java程式設計中protected修飾符與static修飾符的作用相关文章请关注PHP中文网!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn