Java内部类

一、概述

Java 内部类是定义在其他类内部的类,具有一些特殊的特性和用途。以下是 Java 内部类的主要特性和使用。

二、内部类的四种类型

Java 内部类分为四种类型,其中两种是嵌套类,分为静态嵌套类和非静态嵌套类(成员内部类)。

1、非静态内部类(成员内部类)

(1)说明

  1. 依赖外部类的实例:非静态内部类与外部类的实例关联,可以访问外部类的实例变量和方法(包括私有实例变量和方法)。

  2. 不能定义静态成员:不允许在非静态内部类中定义静态变量和静态方法,以维持一致性和可读性。

(2)案例

public class OuterClass1 {
    private int outerField = 1;

    private void outerMethod() {
    }

    public class InnerClass {
        void method() {
            // 1、可以访问外部类的实例变量和实例方法
            System.out.println(outerField);
            outerMethod();
        }

        // 2、不能定义静态变量、静态方法
        /*static int innerStaticField;
        static void innerStaticMethod() {
        }*/
    }

    public static void main(String[] args) {
        OuterClass1 outerClass1 = new OuterClass1();
        InnerClass innerClass = outerClass1.new InnerClass();
    }
}

(3)原理

  • 编译后会生成 OuterClass1.classOuterClass1$InnerClass.class 两个字节码文件。
  • OuterClass1$InnerClass.class 构造函数中有一个参数类型为OuterClass1,也就是说在内部类实例化时,就会将外部类的实例对象传入,并且存储到内部类的实例变量中,这就解释了内部类是如果访问外部类的成员变量和成员方法的。
  • OuterClass1.class 中有2个方法 access$000access$100 ,当内部类需要访问外部类的私有变量和方法时,就是通过这2个方法进行访问的。
Ⅰ、OuterClass1.class
Compiled from "OuterClass1.java"
public class work.vcloud.dc.controller.test.OuterClass1 {
  public work.vcloud.dc.controller.test.OuterClass1();
    Code:
       0: aload_0
       1: invokespecial #3                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: iconst_1
       6: putfield      #2                  // Field outerField:I
       9: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #4                  // class work/vcloud/dc/controller/test/OuterClass1
       3: dup
       4: invokespecial #5                  // Method "<init>":()V
       7: astore_1
       8: new           #6                  // class work/vcloud/dc/controller/test/OuterClass1$InnerClass
      11: dup
      12: aload_1
      13: dup
      14: invokevirtual #7                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      17: pop
      18: invokespecial #8                  // Method work/vcloud/dc/controller/test/OuterClass1$InnerClass."<init>":(Lwork/vcloud/dc/controller/test/OuterClass1;)V
      21: astore_2
      22: return

  static int access$000(work.vcloud.dc.controller.test.OuterClass1);
    Code:
       0: aload_0
       1: getfield      #2                  // Field outerField:I
       4: ireturn

  static void access$100(work.vcloud.dc.controller.test.OuterClass1);
    Code:
       0: aload_0
       1: invokespecial #1                  // Method outerMethod:()V
       4: return
}
Ⅱ、OuterClass1$InnerClass.class
Compiled from "OuterClass1.java"
class work.vcloud.dc.controller.test.OuterClass1$InnerClass {
  final work.vcloud.dc.controller.test.OuterClass1 this$0;

  work.vcloud.dc.controller.test.OuterClass1$InnerClass(work.vcloud.dc.controller.test.OuterClass1);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1                  // Field this$0:Lwork/vcloud/dc/controller/test/OuterClass1;
       5: aload_0
       6: invokespecial #2                  // Method java/lang/Object."<init>":()V
       9: return

  void method();
    Code:
       0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_0
       4: getfield      #1                  // Field this$0:Lwork/vcloud/dc/controller/test/OuterClass1;
       7: invokestatic  #4                  // Method work/vcloud/dc/controller/test/OuterClass1.access$000:(Lwork/vcloud/dc/controller/test/OuterClass1;)I
      10: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
      13: aload_0
      14: getfield      #1                  // Field this$0:Lwork/vcloud/dc/controller/test/OuterClass1;
      17: invokestatic  #6                  // Method work/vcloud/dc/controller/test/OuterClass1.access$100:(Lwork/vcloud/dc/controller/test/OuterClass1;)V
      20: return
}

2、静态内部类

  1. 独立于外部类:静态内部类不依赖于外部类的实例,可以在没有创建外部类实例的情况下使用。

  2. 允许定义静态成员:可以在静态内部类中定义静态变量和静态方法,与普通类类似。

public class OuterClass2 {
    private static int outerField = 1;

    private static void outerMethod() {
    }

    static class InnerClass {
        void method() {
            // 1、可以访问外部类的静态变量和静态方法
            System.out.println(outerField);
            outerMethod();
        }

        // 2、可以定义静态变量、静态方法
        static int innerStaticField;

        static void innerStaticMethod() {
        }
    }

    public static void main(String[] args) {
        System.out.println(InnerClass.innerStaticField);
        InnerClass.innerStaticMethod();
    }
}

3、局部内部类

(1)说明

  1. 定义在方法内部:局部内部类是定义在方法内部的类,其作用域仅限于包含它的方法。

  2. 可以访问方法的局部变量,但要求局部变量为 final 或 effectively final。

(2)案例

public class OuterClass3 {
    public void outerMethod() {
        String str = "abc";
        final String methodField = str;
        class InnerClass {
            void method() {
                // 1、可以访问外部方法中的变量,但是变量必须是final修饰的(jdk 1.8之后会自动添加final修饰的)
                System.out.println(methodField);
            }
        }
        new InnerClass().method();
    }
}

(3)原理

编译后会生成 OuterClass3.classOuterClass3$InnerClass.class 两个字节码文件。

Ⅰ、OuterClass3.class
Compiled from "OuterClass3.java"
public class OuterClass3 {
  public OuterClass3();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void outerMethod();
    Code:
       0: ldc           #2                  // String abc
       2: astore_1
       3: aload_1
       4: astore_2
       5: new           #3                  // class OuterClass3$1InnerClass
       8: dup
       9: aload_0
      10: aload_2
      11: invokespecial #4                  // Method OuterClass3$1InnerClass."<init>":(LOuterClass3;Ljava/lang/String;)V
      14: invokevirtual #5                  // Method OuterClass3$1InnerClass.method:()V
      17: return
}
Ⅱ、OuterClass3$1InnerClass.class
Compiled from "OuterClass3.java"
class OuterClass3$1InnerClass {
  final java.lang.String val$methodField;

  final OuterClass3 this$0;

  OuterClass3$1InnerClass();
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1                  // Field this$0:LOuterClass3;
       5: aload_0
       6: aload_2
       7: putfield      #2                  // Field val$methodField:Ljava/lang/String;
      10: aload_0
      11: invokespecial #3                  // Method java/lang/Object."<init>":()V
      14: return

  public void method();
    Code:
       0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_0
       4: getfield      #2                  // Field val$methodField:Ljava/lang/String;
       7: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      10: return
}

我们看下 OuterClass3$InnerClass.class 的构造器字节码指令。

  1. aload_0(指令0):将局部变量0的值加载到操作数栈中。这个值是当前对象引用(即this)。
  2. aload_1(指令1):将局部变量1的值加载到操作数栈中。这个值通常是外部类 OuterClass3 的引用。
  3. putfield(指令2):将操作数栈顶的值(即局部变量1中的OuterClass3 引用)赋值给当前对象(即this$0字段)。#1 表示常量池中的索引,用于查找字段。
  4. aload_0(指令5):再次将局部变量0的值加载到操作数栈中。这个值是当前对象引用(即this)。
  5. aload_2(指令6):将局部变量2的值加载到操作数栈中。这个值通常是外部传递的字符串参数。
  6. putfield(指令7):将操作数栈顶的值(即局部变量2中的字符串引用)赋值给当前对象的 val$methodField 字段。#2 表示常量池中的索引,用于查找字段。
  7. aload_0(指令10):再次将当前对象引用加载到局部变量0中。
  8. invokespecial(指令11):调用 java/lang/Object 类的构造函数,执行对象的初始化。

通过上面的指令说明,可以看出内部类访问外部方法的局部变量,其实就是将方法的局部变量,通过内部类构造器传入后,保存在了内部类实例的属性 val$methodField 中。

为什么要使用 final 修饰局部变量呢?

现在我们从语义上来理解下Java设计者的考虑:假如传递到匿名内部类的局部变量,不加 final 修饰,那么意味着局部变量可以改变,这就意味着匿名内部类里面值的变更和外部的变量的变更不能同步,,虽然内部类持有的是局部变量值的拷贝,但是语义应该保持一致,语义保持一致的前提是值要能同步,因为java编译器的设计无法提供两边同步变更的机制,所以直接锁死,不允许内外变更。

4、匿名内部类

  1. 通常用于创建实现某接口或继承某类的临时对象,不需要单独的类定义。

  2. 常见于事件处理和线程创建等场景。

public class OuterClass4 {

    public static void main(String[] args) {
        final int methodField = 1;
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 1、可以访问外部方法中的变量,但是变量必须是final修饰的(jdk 1.8之后会自动添加final修饰的)
                System.out.println(methodField);
            }
        });
    }

}

三、总结

内部类可以增加封装性、组织性和代码复用性。它们允许将相关的类放在一起,并隐藏一些实现细节。

总之,Java 内部类提供了一种将类组织在一起的机制,以及在某些情况下实现更优雅和灵活的设计。使用内部类时,需要根据具体需求选择不同类型的内部类,并理解它们的特性和限制。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值