内部类精要
内部类的分类
内部类分为普通内部类,局部内部类,匿名内部类,嵌套类,接口内部类。比较陌生的就是接口内部类了,顾名思义就是在接口中定义一个内部类,这个内部类默认是public static的,用处不大。
1 public interface Appendable { 2 void append(); //接口方法 3 class Main implements Appendable { //接口内部类 4 public static void main(String[] args) { 5 new Main().append(); 6 } 7 @Override 8 public void append() { 9 System.out.println("public void append()"); 10 } 11 } 12 } 13 //在其他地方可以这么调用接口中的内部类 14 Appendable.Main.main(null);
内部类的创建需要外部类的实例的引用
我们都知道,内部类可以肆无忌惮的访问外部类的成员,这是怎么办到的?可见内部类内部肯定隐藏了外部类实例的引用,创建内部类时,往往需要隐式或者显示的将外部类实例的引用传入内部类中。就非静态方法而言,非静态方法内部隐藏含有this引用,所以在非静态方法中创建内部类就和创建普通的类一样,直接new就可以了,编译器会隐式将这个隐藏的this引用传递给内部类对象。可是就静态方法而言,方法是属于类的而不是属于实例对象的,方法内部不可能含有this引用,所以在静态方法中构建内部类对象就要显示的传入this引用,语法如下。
1 public class Test { 2 public static void main(String[] args) { 3 //new Inner(); compile error 4 Test t = new Test(); 5 t.new Inner(); // 在静态方法中,显示传递一个外部类引用 6 } 7 public void foo() { 8 new Inner(); // 在非静态方法中,直接new内部类 9 } 10 /** 11 * 内部类 12 */ 13 private class Inner { 14 public Inner() { 15 System.out.println("public Inner()"); 16 } 17 } 18 } 19 /* 20 报的编译错误是:No enclosing instance of type Test is accessible. Must qualify the allocation with an enclosing instance of type Test (e.g. x.new A() where x is an instance of Test).大意是:没有无法访问Test的实例,必须分配一个Test实例,比如x.new A(),其中x是Test类的实例 21 */
【外部类名】.this 获取外部类的this引用
既然内部类可以访问外部类的成员,那内部类中也肯定可以方便的使用外部类的this引用啊,但是,这肯定会和自己的this引用相冲突,那么怎么访问外部类的this引用呢?语法如下。
1 public class Test { 2 private int bar = 0; 3 public void foo() { 4 System.out.println("Test.foo()"); 5 } 6 /** 7 * 内部类 8 */ 9 private class Inner { 10 /** 11 * 外部类中也有个bar数据域 12 */ 13 private int bar = 1; 14 public Inner() { 15 System.out.println("public Inner()"); 16 System.out.println(bar); //Inner中的bar 17 System.out.println(this.bar); //仍然是Inner中的bar 18 System.out.println(Test.this.bar); //Test中的bar,【外部类名】.this获取外部类的this引用 19 foo(); //调用Inner中的foo 20 this.foo(); //仍然调用Inner中的foo 21 Test.this.foo(); //调用Test中的foo,【外部类名】.this获取外部类的this引用 22 } 23 /** 24 * 外部类中也有个foo方法 25 */ 26 public void foo() { 27 System.out.println("Inner.foo()"); 28 } 29 } 30 }
特殊的内部类:嵌套类
嵌套类是指用static修饰的内部类,它很特殊
- 无法访问外部类的非静态成员(非静态数据域+非静态方法)
- 创建嵌套类不需要外部类的对象
- 普通内部类内部不可定义静态成员,而嵌套类内部可以定义静态成员
其中,1、2点相辅相成,由于无法访问外部类的非静态成员,那创建嵌套类时还要外部类引用干嘛儿使?由于创建嵌套类不传递一个外部类引用,那还咋访问外部类的非静态成员?而对于第3点,是由于静态成员的访问都只需通过类来访问,而普通内部类的初始化又必须需要外部类的实例,所以普通内部类内部不能定义静态成员(静态数据域+静态方法+静态代码块都不可以),具体见引用【2】
局部内部类与匿名内部类的对比
局部内部类和匿名内部类都是在方法中定义的内部类,目的就是为了少些一些类...吧?
局部内部类相对于匿名内部类的优势
- 当需要不止一个内部类对象时,使用局部内部类就很方便了
- 局部内部类具有已命名的构造方法,可以进行构造方法的重载
匿名内部类相对于局部内部类的优势
- 简单粗暴
1 /** 2 * 仅返回一个Comparable对象,使用匿名内部类更加简洁 3 * @return 4 */ 5 public Comparable getComparable() { 6 return new Comparable() { 7 @Override 8 public int compareTo(Object o) { 9 return 0; 10 } 11 }; 12 } 13 /** 14 * 需要多个Comparable对象,使用局部内部类更加方便 15 * @return 16 */ 17 public Comparable[] getComparableArr() { 18 class MyComparable implements Comparable { 19 @Override 20 public int compareTo(Object o) { 21 return 0; 22 } 23 } 24 Comparable[] comparableArr = new Comparable[5]; 25 for(int i=0;i<comparableArr.length;i++) { 26 comparableArr[i] = new MyComparable(); 27 } 28 return comparableArr; 29 }
为什么需要内部类
为什么需要内部类,使用内部类能给程序员带来什么好处?首先,众所周知,Java类是无法多继承的,但某些场景使用多继承更加方便,怎么办?这时内部类就可以登场了,由于内部类实际上就是一个普通的类,它的字节码文件不会编译进外部类的字节码文件中合适独立存在的,所以每个内部类都是独自存在的,它们可以独立的继承其他类而不受外部类的影响,这就是内部类实现多次继承的先决条件。
1 public class Person { 2 3 } 4 public class Student extends Person { 5 6 } 7 /** 8 * 有很多大学老师,他们既是老师又是学生, 9 * 所以Teacher仅继承Person是不够的,还要想办法继承Student 10 */ 11 public class Teacher extends Person{ 12 /** 13 * 老师到学生的身份转变 14 * @return 15 */ 16 public Student toStudent() { 17 return new TeaStudent(); 18 } 19 private class TeaStudent extends Student { 20 } 21 }
其次,内部类可以提高封装性,见下面这段代码
1 public static final Comparator<String> CASE_INSENSITIVE_ORDER 2 = new CaseInsensitiveComparator(); 3 private static class CaseInsensitiveComparator 4 implements Comparator<String>, java.io.Serializable { 5 // use serialVersionUID from JDK 1.2.2 for interoperability 6 private static final long serialVersionUID = 8575799808933029326L; 7 public int compare(String s1, String s2) { 8 int n1 = s1.length(); 9 int n2 = s2.length(); 10 int min = Math.min(n1, n2); 11 for (int i = 0; i < min; i++) { 12 char c1 = s1.charAt(i); 13 char c2 = s2.charAt(i); 14 if (c1 != c2) { 15 c1 = Character.toUpperCase(c1); 16 c2 = Character.toUpperCase(c2); 17 if (c1 != c2) { 18 c1 = Character.toLowerCase(c1); 19 c2 = Character.toLowerCase(c2); 20 if (c1 != c2) { 21 // No overflow because of numeric promotion 22 return c1 - c2; 23 } 24 } 25 } 26 } 27 return n1 - n2; 28 } 29 /** Replaces the de-serialized object. */ 30 private Object readResolve() { return CASE_INSENSITIVE_ORDER; } 31 }
这是JDK1.8 String的部分源码,业务逻辑不用管,只要看懂结构就够就行。当我们使用String.CASE_INSENSITIVE_ORDER时,仅仅知道这是个Comparator,其他的什么都不知道,这就是良好的封装性的体现。假设如果不使用内部类,那么类库中就会多一个名叫CaseInsensitiveComparator的类, 使用时还得new CaseInsensitiveComparator(),这么麻烦简直要了程序员的命呀!
最后,当外部类需要以多种方式实现某一接口时,内部类成为了一个方案。考虑这么一场场景,有一个Sequence类,包装了一个数组,有一个Selector接口,功能是迭代序列(比如数组),现在Sequence想要一个正向迭代的Selector和一个反向迭代的Selector。如果没有掌握内部类,就要编写两个Sequence类,一个实现正向迭代Selector,一个实现反向迭代Selector,如果Sequence类代码量很大的话还得用继承,这样问题一下就复杂了。下面用内部类解决这个问题。
public class Sequence { public static void main(String[] args) { Sequence seq = new Sequence(10); select(seq.getSelector()); System.out.println(); //换行 select(seq.getReverseSelector()); } private static void select(Selector selector) { while(!selector.end()) { System.out.print(selector.current()+" "); selector.next(); } } private Object[] seq; public Sequence(int size) { seq = new Object[size]; for (int i = 0; i < seq.length; i++) { seq[i] = new Integer(i); } } /** * 获取正向迭代器 * @return */ public Selector getSelector() { return new PosSelector(); //内部类 } /** * 获取泛型迭代器 * @return */ public Selector getReverseSelector() { return new RevSelector(); //内部类 } /** * 正向Selector(Positive Selector) */ private class PosSelector implements Selector { private int cursor; // 当前元素下标 public PosSelector() { cursor = 0; } @Override public boolean end() { return cursor >= seq.length; } @Override public Object current() { if (end()) { throw new RuntimeException("Selector已到末尾"); } return seq[cursor]; } @Override public void next() { if (cursor < seq.length) { cursor++; } } } /** * 反向Selector(Reverse Selector) */ private class RevSelector implements Selector { private int cursor; // 当前元素下标 public RevSelector() { cursor = seq.length - 1; } @Override public boolean end() { return cursor < 0; } @Override public Object current() { if(end()) { throw new RuntimeException("Selector已到末尾"); } return seq[cursor]; } @Override public void next() { if(cursor>=0) cursor--; } } }
总之,所以内部类的作用有
1. 多重继承
2. 增强封装性
3. 多方式实现某一接口
当然,个人认为,仅仅是个人认为,内部类最大的好处就是增加了代码的紧凑性有效减少了类的数量。
总结
本篇Blog讲述了内部类的分类,内部类的创建,内部类的作用和一个较特殊的内部类:嵌套类。虽然在内部类在实际编码过程中用的少,但是鄙人在看源码时常常遇到,所以还是要掌握的:)
By the way,转眼大三就结束了,人已身在帝都,身边的大佬也陆陆续续开始投简历了,我也差不多要投了,老天保佑找个好工作=w=
引用
1.《Thinking in Java》
2.http://blog.csdn.net/u013257679/article/details/52053567