请求提供JDK、JRE中内部类、嵌套类等的实际使用示例
嘿,这问题提得太实在了!很多人学内部类的时候光抠语法,却不知道JDK/JRE里其实到处都是它们的身影——毕竟这些特性就是为了更好地封装逻辑、优化代码结构而生的。我整理几个最典型的官方实际案例,都是你日常写代码可能天天打交道的:
最经典的例子就是java.util.HashMap里的Node类,用来存储HashMap的键值对:
// 来自java.util.HashMap的核心实现 static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; Node(int hash, K key, V value, Node<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } // 省略getKey、getValue、equals等核心方法 }
为什么这么用?
Node只负责存储键值对的核心数据,不需要访问HashMap的非静态成员(比如size、modCount这些实例变量)。静态嵌套类不会持有外部类的引用,更节省内存,同时把Node的实现完全封装在HashMap内部,对外隐藏底层细节,符合面向对象的封装原则。
java.util.ArrayList的迭代器Itr就是典型的成员内部类:
// 来自java.util.ArrayList的迭代器实现 private class Itr implements Iterator<E> { int cursor; // 下一个要返回的元素的索引 int lastRet = -1; // 上一个返回的元素的索引 int expectedModCount = modCount; public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); // 直接访问外部类的非静态成员elementData Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } // 省略remove、forEachRemaining等方法 }
为什么这么用?
迭代器需要直接访问ArrayList的非静态成员(比如存储元素的elementData数组、集合大小size、修改计数器modCount),成员内部类可以直接引用外部类的实例变量,不需要额外传参。同时把迭代器的实现隐藏在ArrayList内部,对外只暴露Iterator接口,简化了API的复杂度。
java.util.Collections的checkedCollection方法里就定义了一个局部类,用来给集合做类型校验:
// 来自java.util.Collections的工具方法 public static <E> Collection<E> checkedCollection(Collection<E> c, Class<E> type) { // 局部类:只在这个方法内部使用,对外完全不可见 class CheckedCollection implements Collection<E>, Serializable { private static final long serialVersionUID = 1578914078182001775L; final Collection<E> c; final Class<E> type; CheckedCollection(Collection<E> c, Class<E> type) { this.c = Objects.requireNonNull(c); this.type = Objects.requireNonNull(type); } public int size() { return c.size(); } public boolean isEmpty() { return c.isEmpty(); } // 所有添加元素的方法都会先做类型检查 public boolean add(E e) { type.cast(e); // 校验元素类型,不符合会抛出ClassCastException return c.add(e); } // 省略其他方法 } return new CheckedCollection(c, type); }
为什么这么用?
这个CheckedCollection类只在checkedCollection方法内部有用,不需要对外暴露。用局部类可以把类型校验的逻辑封装起来,同时不会污染外部的命名空间,而且可以直接访问方法的参数c和type,非常方便。
匿名类在JDK里随处可见,比如java.util.Collections的reverseOrder方法,用来生成一个反转顺序的比较器:
// 来自java.util.Collections public static <T> Comparator<T> reverseOrder() { return new Comparator<T>() { public int compare(T o1, T o2) { return ((Comparable<T>)o2).compareTo(o1); } }; }
再比如AWT/Swing里的事件监听(JRE GUI场景):
// 来自JRE的AWT事件处理示例 Button submitBtn = new Button("提交"); submitBtn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("按钮被点击了!"); } });
为什么这么用?
匿名类适合只需要使用一次的场景,比如临时的比较器、事件监听器。不需要单独定义一个类,简化了代码结构,而且可以直接访问外部方法的局部变量(只要是final或者Java 8+里的effectively final变量)。
这些案例都是JDK/JRE源码里的真实实现,理解它们的使用场景,比死记硬背内部类的语法规则有用多了!
内容的提问来源于stack exchange,提问作者koniga-ganica

