1. 程式人生 > >Java中的靜態類以及巢狀類

Java中的靜態類以及巢狀類

原文地址: http://www.geeksforgeeks.org/static-class-in-java/ Java中的靜態類

                      http://tutorials.jenkov.com/java/nested-classes.html   Java中的巢狀類

在Java中類可以是靜態的麼?

在Java中類可以是靜態的。在Java中有靜態例項變數,靜態方法,以及靜態塊,類也可以是靜態的。在Java中我們可以在類中再次定義一個類。這種類被稱為巢狀類。巢狀類外圍的類被稱為外部類。在Java中不能使最頂層類為靜態類,而只有巢狀的類可以是靜態類

靜態類與非靜態類的區別?


下面列出了靜態類與非靜態類的主要區別。非靜態巢狀類通常被稱為內部類。

  1. 巢狀靜態類不需要外部類的引用,但是非靜態巢狀類或者內部類需要外部類的引用。(Nested static class doesn’t need reference of Outer class, but Non-static nested class or Inner class requires Outer class reference.)
  2. 內部類(非靜態巢狀類)可以訪問外部類的靜態與非靜態成員。靜態類不能夠訪問外部類的非靜態成員。
  3. 建立內部類的例項,在沒有外部類的例項時,無法建立。內部類可引用其所在外部類的資料與方法。因此我們不需要傳遞物件的引用給內部類的構造器。因為這一點,內部類使得程式變得更簡潔。
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。