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來說,是不可見的。
方法的存取控制:
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中文网!