Java中的靜態類以及巢狀類
原文地址: http://www.geeksforgeeks.org/static-class-in-java/ Java中的靜態類
http://tutorials.jenkov.com/java/nested-classes.html Java中的巢狀類
在Java中類可以是靜態的麼?
在Java中類可以是靜態的。在Java中有靜態例項變數,靜態方法,以及靜態塊,類也可以是靜態的。在Java中我們可以在類中再次定義一個類。這種類被稱為巢狀類。巢狀類外圍的類被稱為外部類。在Java中不能使最頂層類為靜態類,而只有巢狀的類可以是靜態類。
靜態類與非靜態類的區別?
下面列出了靜態類與非靜態類的主要區別。非靜態巢狀類通常被稱為內部類。
- 巢狀靜態類不需要外部類的引用,但是非靜態巢狀類或者內部類需要外部類的引用。(Nested static class doesn’t need reference of Outer class, but Non-static nested class or Inner class requires Outer class reference.)
- 內部類(非靜態巢狀類)可以訪問外部類的靜態與非靜態成員。靜態類不能夠訪問外部類的非靜態成員。
- 建立內部類的例項,在沒有外部類的例項時,無法建立。內部類可引用其所在外部類的資料與方法。因此我們不需要傳遞物件的引用給內部類的構造器。因為這一點,內部類使得程式變得更簡潔。
class OuterClass{ private static String msg = "GeeksForGeeks"; private String nonStaticMsg = "nonStaticGeeksForGeeks"; // 靜態巢狀類 public static class NestedStaticClass{ // 只有外部類的靜態稱為可以在該靜態類中訪問 public void printMessage(){ System.out.println("Message from nested static class: " + msg); // 報錯,因為在靜態類中訪問非靜態成員nonStaticMsg // System.out.println("Message from nested static class: " + nonStaticMsg); } } public class InnerClass{ public void display(){ // 內部類中,靜態與非靜態成員都可以訪問。 System.out.println("Message from nested static class: " + msg); System.out.println("Message from nested static class: " + nonStaticMsg); } } } public class App { public static void main( String[] args ) { // 建立巢狀靜態類的例項 OuterClass.NestedStaticClass printer = new OuterClass.NestedStaticClass(); printer.printMessage(); // 為了建立內部類的例項,我們需要一個外部類的例項 OuterClass outer = new OuterClass(); OuterClass.InnerClass inner = outer.new InnerClass(); // 呼叫內部類的非靜態方法 inner.display(); // 還可以用以一步來建立內部類的例項 OuterClass.InnerClass innerObject = new OuterClass().new InnerClass(); innerObject.display(); } }
class Outer{
public static class StaticInner{
}
public class Inner{
}
public static void test(){
Inner it1 = new Inner(); // 報錯,需要建立Outer,然後通過Outer建立Inner的例項
StaticInner st = new StaticInner();
}
public void test2(){
Inner it2 = new Inner();
}
}
巢狀類 在Java中,巢狀類是定義在另一個類內部的類。
使用巢狀類的目的是將外部類與巢狀類清晰地組織在一起,並表示這兩個類是一起使用的。或者,巢狀類僅僅在內部被外圍類所使用。
Java開發者通常將巢狀類視為內部類,但內部類(非靜態巢狀類)僅僅是Java中幾種不同巢狀類中的一種。
在Java中,巢狀類被看作是外圍類的成員。這樣,巢狀類可以用public,package(無訪問修飾符),protected及private來修飾。因此,Java中的巢狀類可以被子類繼承。
在Java中你可以建立幾種不同型別的巢狀類。它們是:
- 靜態巢狀類
- 非靜態巢狀類
- 區域性類
- 匿名類
靜態巢狀類
靜態巢狀類是以如下形式宣告的:
public class Outer {
public static class Nested {
}
}
為了建立巢狀類的例項,你必須以外部類Outer作為類Nested的字首來引用它。Outer.Nested instance = new Outer.Nested();由於是靜態的,靜態巢狀類只能通過一個其外層類的例項的引用來訪問外部類例項。
非靜態巢狀類(內部類)
在java中非靜態巢狀類被稱為內部類。內部類是與一個外部類例項相關聯。必須先建立外部類例項,隨後才能建立內部類。
public class Outer {
public class Inner {
}
}
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
非靜態巢狀類(內部類)能夠訪問外部類的域,即使它們被宣告為private.
public class Outer {
private String text = "I am private!";
public class Inner {
public void printText() {
System.out.println(text);
}
}
}
//printText中引用了一個Outer類中private成員text.這是可以的。下面是printText的呼叫方式。
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.printText();
內部類作用域遮蔽
如果內部類宣告的域或方法與外圍類有相同的名字,內部域或方法會遮蔽外部類的域或方法。
public class Outer {
private String text = "I am Outer private!";
public class Inner {
private String text = "I am Inner private";
public void printText() {
System.out.println(text);
}
}
}
上面的例子中,Outer與Inner類都包含有名為text的域。當Inner類引用text時,它引用的是自己的text域。外部類引用text時,它引用的也是自己的text域。
Java中,內部類可以引用外部類同名的text域。通過,外部類名.this.變數名 的形式來引用。
public class Outer {
private String text = "I am Outer private!";
public class Inner {
private String text = "I am Inner private";
public void printText() {
System.out.println(text);
System.out.println(Outer.this.text);
}
}
}
區域性類
Java中區域性類與內部類類似,區域性類定義在一個方法或作用域塊的內部。
class Outer {
public void printText() {
class Local {
}
Local local = new Local();
}
}
區域性類只能在定義的作用域內被訪問。
區域性類可以像內部類一樣訪問外圍類的方法或變數。
區域性類可以訪問其所在方法或作用域塊內部的變數,這些變數需要被定義為finnal。
從Java8開始,區域性類可以訪問其所在方法的區域性變數以及方法引數。這些引數必須被宣告為final或是effectually final, effectually final意思是,變數在初始化後再不會被改變。方法引數一般都是 effectually final.
區域性類也可以在靜態方法中宣告。這種情況,區域性類僅僅可以訪問外圍類的靜態部分。區域性類不能包含靜態宣告(常量可以 - 變數被宣告為static final),因為本質上,即使區域性類在靜態方法中被宣告,區域性類也是非靜態的。
區域性類與內部類有著相同的變數遮蔽規則。
匿名類
Java中,如果巢狀類沒有名稱,則為匿名類。通常作為一個已經存在的類的子類來宣告,或是某個介面的實現。匿名類在例項化時定義。如下定義了一個SuperClass的匿名子類。
public class SuperClass {
public void doIt() {
System.out.println("SuperClass doIt()");
}
}
SuperClass instance = new SuperClass() {
public void doIt() {
System.out.println("Anonymous class doIt()");
}
};
instance.doIt();
匿名類子類化SuperClass並且覆蓋doIt()方法。
匿名類可以實現一個介面,而不需要繼承一個類。
public interface MyInterface {
public void doIt();
}
MyInterface instance = new MyInterface() {
public void doIt() {
System.out.println("Anonymous class doIt()");
}
};
instance.doIt();
如你所見,匿名類實現了一個介面與匿名類繼承另一個類十分類似。匿名類可以訪問外圍類的成員,它也可以訪問被宣告為final 或 effectively final(since Java8)的區域性變數。
你可以在匿名類中宣告一個變數或方法,但不能宣告一個構造器,你可以宣告一個靜態初始化器取代構造器。例如:final Strint textToPrint = "Text...";
匿名類與內部類有著同樣的作用域遮蔽規則。
巢狀類的好處
巢狀類的好處是,你可以將相關的類組織在一起。你可以將相關類放入一個包中,但將一個類放入另一個類內部,以加強它們之間的關聯。
巢狀類通過僅僅被外圍類所使用。通常巢狀類僅僅對外圍類可見,僅僅是內部使用,而在對於外圍類之外的類是不可見的。其它情況下,僅當外圍類被使用時,巢狀類才對於外圍類之外的類可見。
一個例項是,Cache類。在Cache類內部,你聲明瞭一個CacheEntry類,這個類包含了關於Cache的資訊。(資訊包括:值,插入時間,訪問次數等)。 如果使用者不需要獲取CacheEntry自身資訊,Cache類的使用者是看不到CacheEntry類的,僅能看到快取的值。然而,Cache類可能會使cacheEntry類對外部可見。這樣使用者可以獲得更多的資訊,而不僅僅是存數的值。
如下是兩個Cache的實現:
public class Cache {
private Map<String, CacheEntry> cacheMap = new HashMap<String, CacheEntry>();
private class CacheEntry {
public long timeInserted = 0;
public object value = null;
}
public void store(String key, Object value){
CacheEntry entry = new CacheEntry();
entry.value = value;
entry.timeInserted = System.currentTimeMillis();
this.cacheMap.put(key, entry);
}
public Object get(String key) {
CacheEntry entry = this.cacheMap.get(key);
if(entry == null) return null;
return entry.value;
}
}
public class Cache {
private Map<String, CacheEntry> cacheMap = new HashMap<String, CacheEntry>();
public class CacheEntry {
public long timeInserted = 0;
public object value = null;
}
public void store(String key, Object value){
CacheEntry entry = new CacheEntry();
entry.value = value;
entry.timeInserted = System.currentTimeMillis();
this.cacheMap.put(key, entry);
}
public Object get(String key) {
CacheEntry entry = this.cacheMap.get(key);
if(entry == null) return null;
return entry.value;
}
public CacheEntry getCacheEntry(String key) {
return this.cacheMap.get(key);
}
}
第一個類隱藏了CacheEntry巢狀類,而第二個類則可以獲取CacheEntry。