• Java 内部类详解


    什么

    定义在一个类内部的类,称为内部类(累不累),如下:

    public class A {
        private int c = 1;
            
        public class C {
            public void test() {
                System.out.println("c:" + c);
            }
        }
    }

    C称为A的内部类,简称内部类

    A称为C的外部类,简称外部类

    而且内部类能访问外部类的成员(静态成员、实例成员),当然有一些限制,限制如下

    4种声明方式

    按照内部类的声明方式,分为4种内部类:

    1. 静态内部类

      像类的静态成员一样声明的类,就称呼为“静态内部类”

      public class A {
          private static String b = "b";
              private int c = 1;
      
              // B是A的静态内部类
          public static class B {
              public void test() {
                  System.out.println(b);
              }
          }
      }  

      静态内部类,只能访问外部类的静态成员(方法和变量),并且可以像类的成员一样使用修饰符(public/protected/private);

      创建静态内部类对象的方式:A.B b = new A.B();

    2. 成员内部类

      新类的实例成员(未加static修饰)声明的类,称为“成员内部类”

      public class A {
          private static String b = "b";
              private int c = 1;
      
              // C是A的成员内部类
          public class C {
              public void test() {
                  System.out.println(c);
                              System.out.println(b);
              }
          }
      }

      成员内部类,访问外部类的一切(静态,还是实例),就像成员方法一样,并且可以像类的成员一样使用修饰符(public/protected/private)

      创建成员内部类对象的方式:

      A a = new A();
      A.C c = a.new C();
    3. 方法内部类

      在一个代码块声明的类称为方法内部类,代码块包括(方法内、静态代码块内、实例代码块内)

      public class A {
          private static String b;
          private int c;
      
              // 成员方法
          public void test() {
              final int d = 1;
              // 方法内部类
              class D {
                  public void test() {
                      // 访问静态变量
                      System.out.println(b);
                      // 访问实例变量
                      System.out.println(c);
                      // 访问方法final类型的局部变量
                      System.out.println(d);
                  }
              }
          }
      }

      方法内部类,和它所在的方法(代码块),具有相同的访问能力,如果上面代码是在static方法中声明的,那么内部类D不能访问c变量。

      jdk1.8 方法内部类,能够访问非final类型的局部变量,本质相当有在内部类D内保存了副本

    4. 匿名内部类

      匿名内部类也就是没有名字的内部类

      正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写

      但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口

    内部类的本质

    内部类的语法颇为奇怪,我们来看看如下代码,编译后的字节码文件!

    public class A {
        private static String b = "b";
        private int c = 1;
            // 静态内部类
        public static class B {
            public void b() {
                System.out.println(b);
            }
        }
            // 成员内部类
        class C {
            public void c() {
                System.out.println(c);
            }
        }
    }

    字节码文件

    1. A.java文件被编译成了多个class文件
    2. A类对应A.class
    3. B类对应A$B.class
    4. C类对应A$C.class

    内部类会被编译成单独的class文件,那意味JVM解释执行class文件时类“B”和类A是独立的,由此可以见内部类也是一种语法糖!

    对于JVM来说,类A的private b和c 成员,怎么能分别被类B和类C访问到的了!

    用javap命令反编译类A.class来看看:

    秘密就来自,编译器为外部类生的两个静态访问方法,Stinrg access$000()返回b变量的值,int access$100(A a)返回a对象的c成员变量值;

    而在静态内部类B中,编译器将访问静态变量b的地方替换为如上方法:

    // 静态内部类
    public static class A$B {
        public void b() {
            System.out.println(A.access$000());
        }
    }

    在成员内部类C中,原理也是如此,不过增加了更多的东西,反编译A$C.class:

    1. 新增了成员字段final A $this;
    2. 构造方法添加形参 `A$C(A obj);
    3. 访问外部类成员变量的地方会被替换成:System.out.println(A.access$100($this));

    你一定会好奇成员构造方法中的外部类对象的参数从哪里传入的!看看我们是怎么声明内部类的对象的

    A a = new A();
    A.C c = a.new C();

    将会被编译器替换成:

    A a = new A();
    A$C c = new A$C(a);

    内部类的使用时机

    两个类之间紧密联系时,可以使用内部类:

    1. 当一个类需要访问另外一个类的许多属性时,内部类可以简化访问代码
    2. 实现更好封装性,比如:B 类仅仅被A类访问时,可以将B类作为A的私有内部类
    3. 使代码更简洁,匿名内部类
  • 相关阅读:
    排序之选择排序
    排序之冒泡排序
    NOIP 模拟 $22; m d$
    NOIP 模拟 $20; m z$
    NOIP 模拟 $20; m y$
    NOIP 模拟 $20; m 玩具$
    NOIP 模拟 $21; m Median$
    NOIP 模拟 $21; m Park$
    NOIP 模拟 $21; m Game$
    NOIP 模拟 $19; m w$
  • 原文地址:https://www.cnblogs.com/zhaohongtian/p/6807154.html
Copyright © 2020-2023  润新知