zoukankan      html  css  js  c++  java
  • 内部类精要

    内部类的分类

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

  • 相关阅读:
    JVM-堆内存
    生产者消费者模式-基于线程池
    nginx 499错误
    thrift入门
    RPC-基于原生java实现
    rocketMQ入门
    跟着刚哥深入学maven(通俗易懂)
    跟着刚哥学习Spring框架--AOP(五)
    跟着刚哥学习Spring框架--通过注解方式配置Bean(四)
    跟着刚哥学习Spring框架--Spring容器(二)
  • 原文地址:https://www.cnblogs.com/fudashi/p/7171539.html
Copyright © 2011-2022 走看看