Java 中的内部类

前言

在第一次把Java 编程思想中的内部类这一章撸完后,有点印象。大概知道了什么时内部类,局部内部类,匿名内部类,嵌套内部类。随着时间的推移,自己慢慢的就忘记了,总感觉自己思考的东西不多,于是
看了第二遍,并把自己的想法和一些笔记写下来。供以后参考。

内部类

定义:如果把A类定义再B类里面,那么把A类叫做 内部类

代码如下:


public class B {
    public class A{}
}

这样看内部类是不是感觉很简单?定义确实很简单,但是思考一下,这样定义一个内部类有什么意义吗?或者说能带来什么好处? 上面那样定义,个人感觉是意义不大。所以 ,我们一般定义内部类,都是需要内部类实现一个接口或着抽象类。有实际意义的代码如下(例子来自,java编程思想):

/**
 * @ClassName Selector
 * @Description 选择器
 * @Author ouyangkang
 * @Date 2019-03-12 14:21
 **/
public interface Selector {

    // 是否结束
    boolean end();

    // 当前数据
    Object current();

    // 下一个节点
    void next();
}
/**
 * @ClassName Squence
 * @Description TODO
 * @Author ouyangkang
 * @Date 2019-03-12 14:24
 **/
public class Squence {

    private Object[] items;

    private int next;

    public Squence(int size) {
        items = new Object[size];
    }

    public void add(Object x) {
        if (next < items.length) {
            items[next++] = x;
        }
    }

    private class SequceneSelector implements Selector {

        private int i;

        @Override
        public boolean end() {
            return i == items.length;
        }

        @Override
        public Object current() {
            return items[i];
        }

        @Override
        public void next() {
            if (i < items.length) {
                i++;
            }
        }
    }

    public Selector selector(){
        return new SequceneSelector();
    }
}

class Test{
    public static void main(String[] args) {
        Squence squence = new Squence(10);
        for (int i = 0; i < 10 ; i++) {
            squence.add(i);
        }
        // 调用内部类  这是迭代器模式的一个例子。 通过内部类 ,访问类中的私有属性。
        Selector selector = squence.selector();
        while (!selector.end()){
            System.out.print(selector.current()+" ");
            selector.next();
        }
    }
}

输出: 0 1 2 3 4 5 6 7 8 9

请仔细查看上面代码。 这是个非常好的例子。访问权限为 private 的内部类 SequceneSelector 实现了 Selector 接口 ,该内部类可以访问外部类 Squence 中私有属性 items 。 并提供一个公开的方法 selector ,返回一个,向上转型为 Selector 类型。 在测试代码中。先创建 squence 对象。 往里面添加10个元素。 并调用该对象的中的 selector() 方法,返回一个 Selector 类型的对象。 根据我们定义的 Selector 接口中方法的含义,编码。打印输出。

上面代码说明了内部类的几个好处:

  1. 隐藏了细节,实现一个接口,向上转型。
  2. 可以访问外部类中的所有私有属性,方法。就像是拥有他们一样。但是不是拥有(你可以把它想成一个成员方法)

我觉得第一点没什么好说的,反倒是第二点,自己是这样理解的:外部类就像是一个房子,里面的成员变量,方法,内部类。就像是房子里面的人。可以相互通信。而内部类实现了一个接口或着抽象类后,就有点像细作一样,表面看起来是房子里面的人,其实真正是外面的人。只要我创建它,并通过向上转型,就可以到外面去通信。

局部内部类

定义: 如果把A类定义再B类的方法中,那么把A类叫做局部内部类

代码如下:

public class A {
    private void getB(){
        class B{}
    }

}

其实上面代码意义并不大。 下面看下一些有意义的代码。代码如下:

/**
 * @InterfaceName Destination
 * @Description TODO
 * @Author ouyangkang
 * @Date 2019-03-12 19:59
 **/
public interface Destination {
    String readLabel();
}
/**
 * @ClassName Parcel
 * @Description TODO
 * @Author ouyangkang
 * @Date 2019-03-12 20:00
 **/
public class Parcel {
    public Destination destination(String str){
        class PDestination implements Destination{

            private String label = str;

            public PDestination(String label){
                this.label = label;
            }
            @Override
            public String readLabel() {
                return label;
            }
        }
        return new PDestination(str);
    }

    public static void main(String[] args) {
        Parcel parcel = new Parcel();
        Destination destination = parcel.destination("hello");
        System.out.println(destination.readLabel());
    }
}

输出 hello

定义一个为 Destination 的接口,方法为 readLabel() 。 Parcel 类中定义了一个 返回 Destination 类型的方法。 该方法中定义了一个 PDestination 类,并实现了 Destination 接口。 在最后返回PDestination 的对象。 上面局部内部类很熟悉把。下面,我们看下匿名内部类。

匿名内部类

将上面Parcel类修改 ,代码如下

/**
 * @ClassName Parcel1
 * @Description TODO
 * @Author ouyangkang
 * @Date 2019-03-13 15:33
 **/
public class Parcel1 {

    public Destination destination(final String str){
        return new Destination() {
            private String label = str;
            @Override
            public String readLabel() {
                return label;
            }
        };
    }

    public static void main(String[] args) {
        Parcel1 parcel1 = new Parcel1();
        Destination ouyangkang = parcel1.destination("hello");
        System.out.println(ouyangkang.readLabel());
    }
}

输出:hello

Parce1 中的 destination() 方法直接 new 了一个 Destination 对象,重写该 readLabel() 方法。 这样返回没有名字的局部内部类类,称为匿名内部类。

局部内部类 VS 匿名内部类

在使用局部内部类或着匿名内部类的时候,要使用外部类中的局部变量是,该变量要为final ,要问为为什么,因为局部内部类或着匿名内部类内部对局部变量操作并不会改变改内容,所以为了防止使用错误。就用final修饰。不可变。其根本原因就是不会有任何的变化。
那么什么时候用局部内部类,什么时候用匿名内部类?
在你需要一个已命名的构造器,或着需要重构构造器。需要的不止是该一个内部类对象。就是你要定义多个构造器的时候用局部内部类。如果不需要就用匿名内部类。
JAVA 8 可以用Lambda 表达式表示
上面 Parcel1 类 用 JAVA 8 编码如下

public class Parcel2 {

    public Destination destination(final String str) {
        return ()->{
            String label = str;
            return label;
        };
    }

    public static void main(String[] args) {
        Parcel2 parcel2 = new Parcel2();
        Destination ouyangkang = parcel2.destination("hello");
        System.out.println(ouyangkang.readLabel());
    }

   
}

输出:hello

嵌套类

如果把A类定义在B类中,并A类用static关键字修饰,那么把A叫做嵌套类。
代码如下:

publc class B{
    static class A{}
}

如果不需要内部类和外部类有关系,就把该内部类声明为 static
创建嵌套类代表:

  1. 并不需要外围类对象
  2. 不能从嵌套类的对象中访问外部类中的 非静态的东西。

我一般用嵌套类来做测试类。嵌套类理解就到此为止了

总结

总的来说,Java 中的 内部类并没有想象的的那么难理解和认知,但是内部类使用起来就比较深奥了,其中多态这一特性在,配合着内部类,可谓说贼强了。隐藏细节,关注接口中方法本身的意思。

上一篇:redis集合数据类型---SET


下一篇:【Java编程高级进阶】java 获取 string 字符串的编码详解