Beispiel einer inneren Klasse

Die AbstractList- Klasse verfügt über eine innere Itr- Klasse. Es handelt sich um eine Implementierung der Iterator- Schnittstelle, die es ermöglicht, Elemente von Sammlungen einzeln abzurufen:

private class Itr implements Iterator<E> {
	int cursor = 0;
	int lastRet = -1;
	int expectedModCount = modCount;

	public boolean hasNext() {
    		return cursor != size();
	}

	public E next() {
    	checkForComodification();
    	try {
        	int i = cursor;
        	E next = get(i);
        	lastRet = i;
        	cursor = i + 1;
        	return next;
    	} catch (IndexOutOfBoundsException e) {
        	checkForComodification();
        	throw new NoSuchElementException(e);
    	}
	}

	public void remove() {
    	if (lastRet < 0)
        	throw new IllegalStateException();
    	checkForComodification();

    	try {
        	AbstractList.this.remove(lastRet);
        	if (lastRet < cursor)
            	cursor--;
        	lastRet = -1;
        	expectedModCount = modCount;
    	} catch (IndexOutOfBoundsException e) {
   	     throw new ConcurrentModificationException();
    	}
	}

	final void checkForComodification() {
    	if (modCount != expectedModCount)
        	throw new ConcurrentModificationException();
	}
}

Es wird in der Iterator- Methode verwendet:

public Iterator<E> iterator() {
	return new Itr();
}

Auf diese Weise erhält jeder Nachkomme von AbstractList einen vorgefertigten Iterator. Und wenn Sie den Iterator anpassen müssen, können Sie Ihre eigene Klasse implementieren, die Iterator oder Itr erbt , und dann die Iterator- Methode überschreiben. Dies ist beispielsweise die Aufgabe der ArrayList- Klasse.

Die Itr- Klasse ist nicht statisch. Dadurch verfügt das Itr- Objekt über einen Verweis auf die AbstractList- Instanz und kann auf deren Methoden ( size , get , remove ) zugreifen.

Beispiel einer statischen verschachtelten Klasse

Die Integer- Klasse verfügt über eine verschachtelte IntegerCache- Klasse.

private static class IntegerCache {
	static final int low = -128;
	static final int high;
	static final Integer[] cache;
	static Integer[] archivedCache;

	static {
    	int h = 127;
    	String integerCacheHighPropValue =
        	VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
    	if (integerCacheHighPropValue != null) {
        	try {
            	h = Math.max(parseInt(integerCacheHighPropValue), 127);
            	h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
        	} catch( NumberFormatException nfe) {
        	}
    	}
    	high = h;

    	VM.initializeFromArchive(IntegerCache.class);
    	int size = (high - low) + 1;

    	if (archivedCache == null || size > archivedCache.length) {
        	Integer[] c = new Integer[size];
        	int j = low;
        	for(int i = 0; i < c.length; i++) {
            	c[i] = new Integer(j++);
        	}
        	archivedCache = c;
    	}
    	cache = archivedCache;
    	assert IntegerCache.high >= 127;
}

	private IntegerCache() {}
}

IntegerCache kapselt Funktionen, die einen Cache erstellen und die Cache-Bereiche sowie die zwischengespeicherten Werte selbst speichern. Somit wird alles, was mit dem Cache zu tun hat, in einer separaten Klasse gehalten. Dies erleichtert das Lesen und Ändern des Codes. Code, der die Klasse verwendet:

public static Integer valueOf(int i) {
	if (i >= IntegerCache.low && i <= IntegerCache.high)
    		return IntegerCache.cache[i + (-IntegerCache.low)];
	return new Integer(i);
}

Die IntegerCache- Klasse greift nicht auf nicht statische Felder und Methoden der Integer- Klasse zu. Darüber hinaus wird nur in der statischen valueOf- Methode darauf zugegriffen. Das heißt, es ist an die Integer- Klasse selbst gebunden, nicht an ihre einzelnen Instanzen. Und das bedeutet, dass IntegerCache statisch ist.

Beispiel einer anonymen inneren Klasse

Als Beispiel für eine anonyme Klasse nehmen wir InputStream und seine statische nullInputStream- Methode:

public static InputStream nullInputStream() {
    return new InputStream() {
    	private volatile boolean closed;

    	private void ensureOpen() throws IOException {
        	if (closed) {
            		throw new IOException("Stream closed");
        	}
    	}

    	@Override
    	public int available () throws IOException {
        	ensureOpen();
        	return 0;
    	}

    	@Override
    	public int read() throws IOException {
        	ensureOpen();
        	return -1;
    	}

    	@Override
    	public int read(byte[] b, int off, int len) throws IOException {
        	Objects.checkFromIndexSize(off, len, b.length);
        	if (len == 0) {
            		return 0;
        	}
        	ensureOpen();
        	return -1;
    	}

    	@Override
    	public byte[] readAllBytes() throws IOException {
        	ensureOpen();
        	return new byte[0];
    	}

    	@Override
    	public int readNBytes(byte[] b, int off, int len)throws IOException {
        	Objects.checkFromIndexSize(off, len, b.length);
        	ensureOpen();
        	return 0;
    	}

    	@Override
   	 public byte[] readNBytes(int len) throws IOException {
        	if (len < 0) {
            		throw new IllegalArgumentException("len < 0");
        	}
        	ensureOpen();
        	return new byte[0];
    	}

    	@Override
    	public long skip(long n) throws IOException {
        	ensureOpen();
        	return 0L;
    	}

    	@Override
    	public void skipNBytes(long n) throws IOException {
        	ensureOpen();
        	if (n > 0) {
            		throw new EOFException();
        	}
    	}

    	@Override
    	public long transferTo(OutputStream out) throws IOException {
        	Objects.requireNonNull(out);
        	ensureOpen();
        	return 0L;
    	}

    	@Override
    	public void close() throws IOException {
        	closed = true;
    	}
    };
}

Die Methode gibt einen leeren InputStream zurück , der von einer anonymen Klasse implementiert wird. Da die Klasse keine Nachkommen haben soll, haben wir sie anonymisiert.

Mit der Hinzufügung der Java Stream API sind anonyme Klassen allgegenwärtig geworden: Alle Lambda-Ausdrücke sind anonyme Klassen, die eine funktionale Schnittstelle implementieren. Betrachten Sie einige Beispiele.

Die AbstractStringBuilder- Klasse enthält das übergeordnete Element der berühmten StringBuilder- und StringBuffer- Klassen:

@Override
public IntStream chars() {
	return StreamSupport.intStream(
        	() -> {
            	byte[] val = this.value;
            	int count = this.count;
            	byte coder = this.coder;
            	return coder == LATIN1
                   	? new StringLatin1.CharsSpliterator(val, 0, count, 0)
                   	: new StringUTF16.CharsSpliterator(val, 0, count, 0);
        	},
        	Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED,
        	false);
}

Die Files- Klasse verfügt über eine zum Konvertieren eines Closeable in ein Runnable :

private static Runnable asUncheckedRunnable(Closeable c) {
	return () -> {
    	try {
        	c.close();
    	} catch (IOException e) {
        	throw new UncheckedIOException(e);
    	}
	};
}

Die Class- Klasse verfügt über eine Methode zum Abrufen einer Zeichenfolgendarstellung einer Methode:

private String methodToString(String name, Class<?>[] argTypes) {
	return getName() + '.' + name +
        	((argTypes == null || argTypes.length == 0) ?
        	"()" :
        	Arrays.stream(argTypes)
        	        .map(c -> c == null ? "null" : c.getName())
                	.collect(Collectors.joining(",", "(", ")")));
}