内部类精要

内部类的分类

内部类分为普通内部类局部内部类匿名内部类嵌套类接口内部类。比较陌生的就是接口内部类了,顾名思义就是在接口中定义一个内部类,这个内部类默认是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. 普通内部类内部不可定义静态成员,而嵌套类内部可以定义静态成员

其中,1、2点相辅相成,由于无法访问外部类的非静态成员,那创建嵌套类时还要外部类引用干嘛儿使?由于创建嵌套类不传递一个外部类引用,那还咋访问外部类的非静态成员?而对于第3点,是由于静态成员的访问都只需通过类来访问,而普通内部类的初始化又必须需要外部类的实例,所以普通内部类内部不能定义静态成员(静态数据域+静态方法+静态代码块都不可以),具体见引用【2】

 

局部内部类与匿名内部类的对比

局部内部类和匿名内部类都是在方法中定义的内部类,目的就是为了少些一些类...吧?
局部内部类相对于匿名内部类的优势

  1. 当需要不止一个内部类对象时,使用局部内部类就很方便了
  2. 局部内部类具有已命名的构造方法,可以进行构造方法的重载

匿名内部类相对于局部内部类的优势

  1. 简单粗暴
 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

posted @ 2017-07-14 17:25  付大石  阅读(242)  评论(0编辑  收藏  举报