Java内部类

Java内部类

1.什么是内部类

在java中,可以将一个类定义到另一个类的内部,里边的类就是内部类,外边的类则是外部类。

2.有什么内部类

  • 成员内部类(普通内部类)
  • 静态内部类
  • 局部内部类
  • 匿名内部类

3.成员内部类(普通内部类)

package demo;

/**
 * 普通内部类
 */
public class OutClass {
    public String outA="1";
    String outB="2";
    protected String outC="3";
    private String outD="4";
    public OutClass() {
        //在外部类的构造方法中实例化内部类对象
        InnerClass innerClass = new InnerClass();
        System.out.println("创建"+this.getClass().getSimpleName()+"对象");
        System.out.println("内部类的A为"+innerClass.innerA);
        System.out.println("内部类的B为"+innerClass.innerB);
        System.out.println("内部类的C为"+innerClass.innerC);
        System.out.println("内部类的D为"+innerClass.innerD);

    }
    public class InnerClass{
        public String innerA="5";
        String innerB="6";
        protected String innerC="7";
        private String innerD="8";
        //static String innerF="9";  编译,普通内部类中不允许定义static属性 //Static declarations in inner classes are not supported at language level '8'

        public InnerClass() {
            System.out.println("创建"+this.getClass().getSimpleName()+"对象");
            System.out.println("外部类的A为"+outA);
            System.out.println("外部类的B为"+outB);
            System.out.println("外部类的C为"+outC);
            System.out.println("外部类的D为"+outD);
        }
    }

    public static void main(String[] args) {
        //实例化外部类
        OutClass outClass = new OutClass();
        
        //实例化内部类
        //InnerClass innerClass = outClass.new InnerClass();
    }
}

Java内部类_第1张图片

内部类可以访问外部类的所有访问权限字段,外部类也可以通过内部类的引用访问内部类的所有访问权限字段

4.静态内部类

package demo;

/**
 * 静态内部类
 */
public class StaticOutClass {
    public String outA="1";
    String outB="2";
    protected String outC="3";
    private String outD="4";
    static String outE="5";

    public StaticOutClass() {
        System.out.println("创建"+this.getClass().getSimpleName()+"对象");
        //创建静态内部类
        StaticInnerClass staticInnerClass = new StaticInnerClass();
        System.out.println("静态内部类的A为"+staticInnerClass.innerA);
        System.out.println("静态内部类的B为"+staticInnerClass.innerB);
        System.out.println("静态内部类的C为"+staticInnerClass.innerC);
        System.out.println("静态内部类的D为"+staticInnerClass.innerD);
        System.out.println("静态内部类的E为"+staticInnerClass.innerE);

    }

    public static class StaticInnerClass{
        public String innerA="5";
        String innerB="6";
        protected String innerC="7";
        private String innerD="8";
        static String innerE="9";

        public StaticInnerClass() {
            System.out.println("创建"+this.getClass().getSimpleName()+"对象");
            //System.out.println(outA);  静态内部类访问不到外部类的非静态属性
            System.out.println("外部类的E为"+outE);
        }
    }

    public static void main(String[] args) {
        StaticOutClass staticOutClass = new StaticOutClass();

        //创建静态内部类的方式,无需依赖外部类
        //StaticInnerClass staticInnerClass = new StaticInnerClass();
    }
}

Java内部类_第2张图片

外部类持有静态内部类的引用可以访问所有字段,静态内部类只能访问外部类的静态字段,我觉得是类加载顺序的问题,静态的加载顺序在非静态之前

5.局部内部类

package demo;
/**
*  局部内部类
*/
public class LocalClass {

    public String outA="1";
    String outB="2";
    protected String outC="3";
    private String outD="4";

    public LocalClass() {
        System.out.println("创建"+this.getClass().getSimpleName()+"对象");
    }
    public void method(){
        class  InnerA{
            public InnerA() {
                System.out.println("创建"+this.getClass().getSimpleName()+"对象");
                System.out.println("外部类的A为"+outA);
                System.out.println("外部类的B为"+outB);
                System.out.println("外部类的C为"+outC);
                System.out.println("外部类的D为"+outD);
            }
        }
        boolean flag=true;

        if (flag){
            class  InnerB{
                public InnerB() {
                    System.out.println("创建"+this.getClass().getSimpleName()+"对象");
                    System.out.println("外部类的A为"+outA);
                    System.out.println("外部类的B为"+outB);
                    System.out.println("外部类的C为"+outC);
                    System.out.println("外部类的D为"+outD);
                }
            }
            InnerB innerB = new InnerB(); //实例化B
        }
       // InnerB innerB = new InnerB();  //编译错误,不在B的定义域内
        InnerA innerA = new InnerA();  //实例化A
    }

    public static void main(String[] args) {
        LocalClass localClass = new LocalClass();
        localClass.method();
    }


}

Java内部类_第3张图片

局部内部类可以访问外部类对象的所有访问权限的字段,外部类不能访问局部内部类的字段。因为局部内部类的定义只在其特定的方法体 / 代码块中有效。

6.匿名内部类

package demo;

import javafx.scene.AmbientLight;

/**
 * 匿名内部类
 */
public class AnonymousClass {
    public String outA="1";
    String outB="2";
    protected String outC="3";
    private String outD="4";

    public AnonymousClass() {
        System.out.println("创建"+this.getClass().getSimpleName()+"对象");
    }

    interface Listener{
        void beginListener();
    }


    public void anonymousInnerClass(){
        //创建了一个匿名内部类,并且重写方法
        Listener listener=new Listener(){
            //外部类无法获取匿名内部类的字段,因为外部类无法获取匿名内部类的名称,因此无法创建对象
            int innerA=5;

            @Override
            public void beginListener() {
                System.out.println("外部类的A为"+outA);
                System.out.println("外部类的B为"+outB);
                System.out.println("外部类的C为"+outC);
                System.out.println("外部类的D为"+outD);
            }
        };
        listener.beginListener();
    }

    public static void main(String[] args) {
        AnonymousClass anonymousClass = new AnonymousClass();
        anonymousClass.anonymousInnerClass();
    }

}

Java内部类_第4张图片

匿名内部类的两种实现方式

1.直接 new 一个接口,并实现这个接口声明的方法,在这个过程其实会创建一个匿名内部类实现这个接口,并重写接口声明的方法

2.new 一个已经存在的类 / 抽象类,并且选择性的实现这个类中的一个或者多个非 final 的方法,这个过程会创建一个匿名内部类对象继承对应的类 / 抽象类,并且重写对应的方法

7.深入理解内部类

我们观察一下以上的四种内部类代码我们会发现一个问题,除了静态内部类以外,其他内部类可以访问外部类所有访问修饰符修饰的字段(包括private),同时外部类也可以访问内部类也能访问外部类的字段,在我们的印象中,private修饰的字段是只能被当前类访问,然而我们上边的代码却可以访问,那是为什么呢?我们编译一下代码来看一下

package demo;

public class OuterClass {
    private String outA="1";
    public String outB="2";

    public OuterClass() {
        InnerClass innerClass = new InnerClass();
        String a=innerClass.innerA;
    }


    class InnerClass{
        private String innerA="3";
        public String innerB="4";

        public InnerClass() {
            String s=outA;
        }
    }
}

首先使用 javac OuterClass.java 将代码编译一下,编译完以后会看到OuterClass.classOuterClass$InnerClass.class两个文件

在这里插入图片描述

然后通过 javap -c OuterClass反编译OuterClass

Java内部类_第5张图片

我们会发现字节码中多了一个static修饰的**access$100(OuterClass)**方法,接收一个外部类对象为参数,方法内部获取到一个private 修饰的outA字段并返回,现在我们应该想到是如何获取private修饰的字段了,就是通过这个外部类的提供的静态方法

我们在26行 看一下这个 invokestatic,这里标识执行了一个静态方法,后面的注释表明是调用了OuterClass$InnerClass的access$000的方法,接下来我们反编译一下OuterClassInnerClass 来看一下

javap -c OuterClass$InnerClass

Java内部类_第6张图片

我们可以发现同样也有一个静态方法access$000(InnerClass),而且22行 invokestatic 执行了一个静态方法,我们可以看注释发现是调用的外部类OuterClass的**access$100(OuterClass)**方法,这下子我们就可以知道内部类如何获取外部类private修饰的字段

我们还可以发现内部类中有一个接收OuterClass(外部类对象)的构造方法,并且内部类中定义了一个外部类的引用(this$0),而且我们发现这个引用在构造方法中指向了参数所对应的外部类对象。

上边我们对普通内部类进行了分析,但其实匿名内部类和局部内部类的原理和普通内部类是类似的,只是在访问上有些不同:外部类无法访问匿名内部类和局部内部类对象的字段,因为外部类根本就不知道匿名内部类 / 局部内部类的类型信息(匿名内部类的类名被隐匿,局部内部类只能在定义域内使用)。但是匿名内部类和局部内部类却可以访问外部类的私有成员,原理也是通过外部类提供的静态方法来得到对应外部类对象的私有成员的值

接下来我们分析一下静态内部类,静态内部类是独立于外部类的,因此静态内部类不会持有外部类的引用,但是外部类对象还可以访问静态内部类的私有成员,因为外部类可以知道静态内部类的信息,即可以得到静态内部类的对象,那么就可以通过静态内部类的静态方法来获得对应的私有值。

package demo;
/**
 * 静态内部类
 */
public class StaticTestClass {
    private String outA="1";
    public String outB="2";

    public StaticTestClass() {
        StaticClass staticClass = new StaticClass();
        String temp=staticClass.innerA;

    }

    public static class StaticClass{
        private String innerA="3";
    }
}

和上边的命令一样,我们分别来看一下反编译后的代码

外部类

Java内部类_第7张图片

静态内部类

Java内部类_第8张图片

我们会发现一个方法access 000 ( S t a t i c T e s t C l a s s 000(StaticTestClass 000(StaticTestClassStaticClass),外部类正是通过这个方法来获取静态内部类的字段值,静态内部类对象不依赖其外部类对象存在,而其余的内部类对象必须依赖其外部类对象而存在。

8.内部类和多重继承

大家应该都知道Java是单继承,一个子类只能有一个直接父类,那么内部类是否可以弥补单继承的缺陷呢?

package demo;


import java.util.HashMap;
import java.util.Map;

public class Bird {

    public void bird(String name,String fav){
        Fly fly = new Fly();
        Animal animal = new Animal();
        animal.animal(name);
        fly.fly(fav);

    }

    class Fly{
        public void fly(String fly){
            System.out.println(fly);
        }
    }

    class Animal{
        public void animal(String name){
            System.out.println(name);
        }
    }

    public static void main(String[] args) {
        Bird bird = new Bird();
        bird.bird("鹦鹉","会飞");
    }
}

程序运行正确,因为普通内部类的特性,外部类可以访问内部类的所有属性,同时内部类也可以访问外部类的所有属性,这种方式一定程度上弥补了java单继承的局限性,但是也在一定程度上破坏了类的特性,一般来说,一个java文件只包含一个类,除非两个类之间有很明确的依赖关系。

你可能感兴趣的:(java)