1. 程式人生 > >Java巢狀類(內部類)總結

Java巢狀類(內部類)總結

巢狀類(nested class)是指被定義在另一個類的內部的類。巢狀類的目的應該只是為它的外圍類(enclosing class)提供服務。如果巢狀類將來可能會用於其他某個環境中,它就應該是頂層類,而不是被設計為巢狀類。巢狀類分如下四種:

靜態成員類(static member class)、非靜態成員類(nostatic member class)、匿名類(anonymous class)和區域性類(local class)。

除了靜態成員類之外,其他三種都被稱為內部類(Inner Class)。靜態成員類和非靜態成員類稱為成員類,後兩個稱為非成員類

靜態成員類

靜態成員類是最簡單的一種巢狀類,最好把它看作是普通的類,只是碰巧被宣告在另一個類的內部而已,它可以訪問外圍類的所有成員(靜態),包括那些宣告為私有的成員。靜態成員類是外圍類的一個靜態成員,與其他的靜態成員一樣,也遵循同樣的可訪問性規則。如果它被宣告為私有的,它就只能在外圍類的內部才能被訪問。

靜態成員類的一種常見用法是作為公有的輔助類,僅當它與外部類一起使用時才有意義。

例如String類的CaseInsensitiveComparator比較器,該靜態成員類提供了String型別忽略字元大小寫的比較方法。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence
{
    public static final Comparator<String> CASE_INSENSITIVE_ORDER
                                         = new CaseInsensitiveComparator();
    private static class CaseInsensitiveComparator
                         implements Comparator<String>, java.io.Serializable {
        // use serialVersionUID from JDK 1.2.2 for interoperability
        private static final long serialVersionUID = 8575799808933029326L;

        public int compare(String s1, String s2) {
            int n1 = s1.length();
            int n2 = s2.length();
            int min = Math.min(n1, n2);
            for (int i = 0; i < min; i++) {
                char c1 = s1.charAt(i);
                char c2 = s2.charAt(i);
                if (c1 != c2) {
                    c1 = Character.toUpperCase(c1);
                    c2 = Character.toUpperCase(c2);
                    if (c1 != c2) {
                        c1 = Character.toLowerCase(c1);
                        c2 = Character.toLowerCase(c2);
                        if (c1 != c2) {
                            // No overflow because of numeric promotion
                            return c1 - c2;
                        }
                    }
                }
            }
            return n1 - n2;
        }
    }
......
}
例項:
public class StaticMemberClassTest {
	public static void main(String[] args) { 
		String[] strs={"ab","Ab","CC","abc"};   
		Arrays.sort(strs);//預設排序規則(大小寫敏感)
		for(String s:strs){
			System.out.print(s+" ");
		} 
		System.out.println();
		Arrays.sort(strs,String.CASE_INSENSITIVE_ORDER);//大小寫不敏感的排序規則
		for(String s:strs){
			System.out.print(s+" ");
		} 
	}
}
輸出:

Ab CC ab abc 
Ab ab abc CC 

再比如HashMap中的靜態成員類Entry,該類代表雜湊對映表的鍵值對,與他的外圍類一起使用。

public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
{
    static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        final int hash;

        /**
         * Creates new entry.
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }

        public final K getKey() {
            return key;
        }

        public final V getValue() {
            return value;
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry)o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2)))
                    return true;
            }
            return false;
        }

        public final int hashCode() {
            return (key==null   ? 0 : key.hashCode()) ^
                   (value==null ? 0 : value.hashCode());
        }

        public final String toString() {
            return getKey() + "=" + getValue();
        }

        /**
         * This method is invoked whenever the value in an entry is
         * overwritten by an invocation of put(k,v) for a key k that's already
         * in the HashMap.
         */
        void recordAccess(HashMap<K,V> m) {
        }

        /**
         * This method is invoked whenever the entry is
         * removed from the table.
         */
        void recordRemoval(HashMap<K,V> m) {
        }
    }
....
}
例項:
public class EntryStaticMemberClass { 
	public static void main(String[] args) { 
		int[] nums={1,3,3,2,1,5,4,3,8,2};
		Map<Integer,Integer> countMap=new HashMap<Integer,Integer>();
		for(int i:nums){
			if(countMap.get(i)==null){
				countMap.put(i, 1);
			}else{
				countMap.put(i, countMap.get(i)+1);
			}
		}
		for(Entry<Integer, Integer> entry:countMap.entrySet()){
			System.out.println("元素:"+entry.getKey()+" 出現了:"+entry.getValue()+"次");
		} 
	} 
}
輸出:

元素:1 出現了:2次
元素:2 出現了:2次
元素:3 出現了:3次
元素:4 出現了:1次
元素:5 出現了:1次
元素:8 出現了:1次

非靜態成員類

從語法上將,靜態成員類和非靜態成員類之間唯一的區別是,靜態成員類的宣告中包含修飾符static。儘管語法上非常相似,但是這兩種巢狀類有很大的不同。非靜態成員類的每個例項都隱含著與外圍類的一個例項相關聯。在非靜態成員類的例項方法內部,可以呼叫外圍例項上的方法,或者獲取外圍例項的引用。

如果巢狀的的例項可以在外圍類的例項之外獨立存在,即不需要與外圍類的特定例項關聯,這個巢狀類應該宣告為靜態成員類。

在沒有外圍例項的情況下,要想建立非靜態成員類的例項是不可能的。當非靜態成員類的例項被建立的時候,它和外圍例項之間的關聯關係就隨之建立起來了。而且這種關聯關係以後不會被修改。通常情況下,在外圍類的某個例項方法的內部呼叫非靜態成員類的構造器時,這種關聯關係就建立起來了。

非靜態成員類的一種常見的用法是定義一個Adapter(介面卡),提供類的各種檢視。例如,Map介面的實現往往使用非靜態成員類來實現它們的集合檢視(Collection View),這些集合檢視由Map的keySet、entrySet和values方法返回。

public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
{
    // Views
    transient volatile Set<K>        keySet = null;//父類AbstractMap中
    transient volatile Collection<V> values = null;//父類AbstractMap中

    private transient Set<Map.Entry<K,V>> entrySet = null;

    public Set<K> keySet() {
        Set<K> ks = keySet;
        return (ks != null ? ks : (keySet = new KeySet()));
    }

    private final class KeySet extends AbstractSet<K> {
        public Iterator<K> iterator() {
            return newKeyIterator();
        }
        public int size() {
            return size;
        }
        public boolean contains(Object o) {
            return containsKey(o);
        }
        public boolean remove(Object o) {
            return HashMap.this.removeEntryForKey(o) != null;
        }
        public void clear() {
            HashMap.this.clear();
        }
    }

    public Collection<V> values() {
        Collection<V> vs = values;
        return (vs != null ? vs : (values = new Values()));
    }

    private final class Values extends AbstractCollection<V> {
        public Iterator<V> iterator() {
            return newValueIterator();
        }
        public int size() {
            return size;
        }
        public boolean contains(Object o) {
            return containsValue(o);
        }
        public void clear() {
            HashMap.this.clear();
        }
    }

    public Set<Map.Entry<K,V>> entrySet() {
        return entrySet0();
    }

    private Set<Map.Entry<K,V>> entrySet0() {
        Set<Map.Entry<K,V>> es = entrySet;
        return es != null ? es : (entrySet = new EntrySet());
    }

    private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public Iterator<Map.Entry<K,V>> iterator() {
            return newEntryIterator();
        }
        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<K,V> e = (Map.Entry<K,V>) o;
            Entry<K,V> candidate = getEntry(e.getKey());
            return candidate != null && candidate.equals(e);
        }
        public boolean remove(Object o) {
            return removeMapping(o) != null;
        }
        public int size() {
            return size;
        }
        public void clear() {
            HashMap.this.clear();
        }
    }
....
}
我們可以看到,每個非靜態成員類,都可以直接訪問外圍類的例項屬性(keySet、values和entrySet)。
public class NoStaticMemberClassTest {
	public static void main(String[] args) { 
		int[] nums={1,3,3,2,1,5,4,3,8,2};
		Map<Integer,Integer> countMap=new HashMap<Integer,Integer>();
		for(int i:nums){
			if(countMap.get(i)==null){
				countMap.put(i, 1);
			}else{
				countMap.put(i, countMap.get(i)+1);
			}
		}
		System.out.print("鍵集合:");
		for(Integer i:countMap.keySet()){
			System.out.print(i+" ");
		}
		System.out.println();
		System.out.print("值集合:");
		for(Integer i:countMap.values()){
			System.out.print(i+" ");
		}
		System.out.println();
		System.out.println("建值對集合:");
		for(Entry<Integer, Integer> entry:countMap.entrySet()){
			System.out.println("建:"+entry.getKey()+" 值:"+entry.getValue());
		} 
	} 
}
輸出:

鍵集合:1 2 3 4 5 8 
值集合:2 2 3 1 1 1 
建值對集合:
建:1 值:2
建:2 值:2
建:3 值:3
建:4 值:1
建:5 值:1
建:8 值:1

此外,Set和List這種集合介面的實現往往也使用非靜態成員類來實現它們的迭代器(Iterator)。

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    private class ListItr implements ListIterator<E> {
        private Node<E> lastReturned = null;
        private Node<E> next;
        private int nextIndex;
        private int expectedModCount = modCount;

        ListItr(int index) {
            // assert isPositionIndex(index);
            next = (index == size) ? null : node(index);
            nextIndex = index;
        }

        public boolean hasNext() {
            return nextIndex < size;
        }

        public E next() {
            checkForComodification();
            if (!hasNext())
                throw new NoSuchElementException();

            lastReturned = next;
            next = next.next;
            nextIndex++;
            return lastReturned.item;
        }

        public boolean hasPrevious() {
            return nextIndex > 0;
        }

        public E previous() {
            checkForComodification();
            if (!hasPrevious())
                throw new NoSuchElementException();

            lastReturned = next = (next == null) ? last : next.prev;
            nextIndex--;
            return lastReturned.item;
        }

        public int nextIndex() {
            return nextIndex;
        }

        public int previousIndex() {
            return nextIndex - 1;
        }

        public void remove() {
            checkForComodification();
            if (lastReturned == null)
                throw new IllegalStateException();

            Node<E> lastNext = lastReturned.next;
            unlink(lastReturned);
            if (next == lastReturned)
                next = lastNext;
            else
                nextIndex--;
            lastReturned = null;
            expectedModCount++;
        }

        public void set(E e) {
            if (lastReturned == null)
                throw new IllegalStateException();
            checkForComodification();
            lastReturned.item = e;
        }

        public void add(E e) {
            checkForComodification();
            lastReturned = null;
            if (next == null)
                linkLast(e);
            else
                linkBefore(e, next);
            nextIndex++;
            expectedModCount++;
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
    private class DescendingIterator implements Iterator<E> {
        private final ListItr itr = new ListItr(size());
        public boolean hasNext() {
            return itr.hasPrevious();
        }
        public E next() {
            return itr.previous();
        }
        public void remove() {
            itr.remove();
        }
    }

......
}
例項:
public class IteratorTest {
	public static void main(String[] args) {  
		LinkedList<Integer> arr=
				new LinkedList<Integer>(Arrays.asList(1,2,3,4,5,6));
		 Iterator iter = arr.iterator();
		 while(iter.hasNext()){ 
		  System.out.print((Integer) iter.next()+" ");
		 }
		 System.out.println();
		 iter = arr.descendingIterator();
		 while(iter.hasNext()){ 
		  System.out.print((Integer) iter.next()+" ");
		 }
	}
}
輸出:

1 2 3 4 5 6 
6 5 4 3 2 1 
私有靜態成員類
私有靜態成員類的一種常見用法就是來代表外圍類的元件。例如,Map中的Entry,Entry代表鍵值對(key-value)。雖然每個Entry都與一個Map關聯,但是entry上的方法並不需要訪問該Map。因此,使用非靜態成員來表示Entry是非常浪費的,而私有的靜態成員類是最佳的選擇。如果不小心漏掉了Entry宣告中的static修飾符,該map仍然可以工作,但是每個Entry中將會包含一個指向該Map的引用,這樣就浪費了空間和時間。因此,如果成員類不需要訪問外圍類例項,我們應該適用靜態成員類,而不是非靜態成員類。

匿名類

匿名類沒有名字,匿名類在使用的同時被宣告和初始化,匿名類可以出現在任何允許存在表示式的地方。

匿名類的一種常見用法是動態的建立函式物件。

例如,利用匿名的Comparator例項,根據字串長度對字串陣列進行排序。

public class AnonymousClassTest {
	public static void main(String[] args) { 
		String[] strs={"ab","aaaa","CC","abc"}; 
		Arrays.sort(strs,new Comparator<String>(){
			public int compare(String s1,String s2){
				return s1.length()-s2.length();
			}
		});  
		for(String s:strs){
			System.out.print(s+" ");
		} 
	}
}
輸出:

ab CC abc aaaa 

另一種常見用法是建立過程物件,比如Runnable、Thread例項等。

public class AnonymousClassTest {
	static class Anonymous implements Runnable{
		@Override 
		public void run(){
			System.out.println("do something only once");
		}
	}
	public static void main(String[] args) { 
		//常規方式(定義一個靜態成員類,也可以單獨定義)
		new Thread(new Anonymous()).start(); 
		
		//實現介面的匿名類
		new Thread(new Runnable(){
			@Override 
			public void run(){
				System.out.println("do something only once");
			}
		}).start(); 
		//繼承基類的匿名類
		new Thread(){
			@Override 
			public void run(){
				System.out.println("do something only once");
			}
		}.start();
	}
}

第三種常見用法就是靜態工廠方法的內部。

public class ListFactory {
	//將int[]轉換成的List<Integer>的靜態工廠方法
	static List<Integer> intArrayAsList(final int[] a ){
		if(a==null){
			throw new NullPointerException();
		}
		return new AbstractList<Integer>(){
			@Override
			public Integer get(int i){
				return a[i];
			}
			@Override
			public Integer set(int i,Integer value){
				int oldValue=a[i];
				a[i]=value;
				return oldValue;
			}
			@Override
			public int size(){
				return a.length;
			}
		} ;
	}
	public static void main(String[] args) {  
		int[] a={1,2,3,45,2,1};
		List<Integer> list=ListFactory.intArrayAsList(a);
		Collections.sort(list);
		for(int i:list){
			System.out.print(i+" ");
		}
	}
}
從上面三個例子,不難看出匿名類僅限於只例項化一次的內部類。這樣可以防止產生很多短小且複用率不高的類

匿名類的適用性受到諸多的限制:

1、你無法宣告一個匿名類來同時擴充套件基類和實現介面。匿名類要麼實現一個介面要麼繼承一個父類。

2、由於匿名類出現在表示式中,它們必須保持簡短,否則會影響程式的可讀性。

3、除了宣告它們的地方,你無法再次對它們例項化。

區域性類

區域性類是四種巢狀類中用得最少的。在任何“可以宣告區域性變數”的地方,都可以宣告區域性類,並且區域性類也遵循同樣的作用域規則,同樣要求必須簡短。

巢狀類的選擇

四種不同的巢狀類,都有各自的適用場景。

如果一個巢狀類需要在單個方法之外仍然是可見的,或者它太長,不適合放在方法內部,就應該適用成員類(靜態/非靜態)。

如果成員類的每個例項都需要一個指向其外圍例項的引用,就要設計為非靜態成員類。否則,設計為靜態成員類。

假設,這個巢狀類屬於單個方法的內部,如果你只需要在一個地方建立例項,並且已經有了一個預知的型別(類或介面)可以說明這個類的特徵,就要把它設計為匿名類。否則,就做成區域性類。

為什麼需要巢狀類

咋一看,巢狀類就像是一種程式碼隱藏機制,我們將巢狀類置於外圍類的內部,從而允許你把一些邏輯相關的類組織在一起,並控制巢狀類的可見性。但是,巢狀類遠非如此。

1、恰當的使用匿名類和區域性類,可以讓程式碼更加清晰而優雅。

2、我們可以利用私有靜態成員類,將外圍類的具體實現以及內部結構封裝在內部,增加內聚度。(Map的Entry)

3、非靜態成員類,每個非靜態成員類的例項可以直接操作相關的外圍類例項,不同的非靜態成員類可以提供外圍類的不同視窗(Iterator)。

4、每個非靜態成員類都能獨立的繼承父類和實現介面,使得在單繼承體系的Java語言中實現“多繼承變得可能。

參考:

《java程式設計思想》

《Effective Java》