Java八股文系列五:内部类

image

一、什么是内部类

将一个类定义置入另一个类定义中,这个类就叫做内部类。简单来说就是在一个类的内部再定义一个类。

二、为什么要用内部类

  1. 内部类与外部类之间可以很方便地访问彼此的私有域。
  2. 内部类是另外的一种封装,对外部的其他类隐藏。
  3. 内部类可以打破Java单继承的局限。

缺点就是结构复杂。

如何使用内部类呢?

  • 在外部类外部生成非静态内部类实例
Outer.Inner in = new Outer().new Inner();
  • 在外部类外部生成静态内部实例
Outer.Inner in = new Outer.Inner();
  • 在外部类内部生成内部类实例
Inner in = new Inner();

三、内部类的分类

Java内部类主要分为四种:成员内部类、静态内部类、方法内部类、匿名内部类。

3.1 成员内部类

成员内部类是内部类中最普通的一种,对比成员方法:

public class Outer1 {
    private String name = "test";
    public static int age = 20;
    class Inner {
        public void getOuter() {
            System.out.println("name: " + name + " age: " + age);
        }
    }
    public static void main(String[] args) {
        Inner inner = new Outer1().new Inner();
        inner.getOuter();
    }
}

输出:

name: test age: 20
  1. 成员内部类中不允许存在static域,正如成员方法中不允许存在static域。
  2. 成员内部类依附于外部类,只有创建了外部类实例才能创建内部类实例。

3.2 静态内部类

static修饰的内部类称之为静态内部类。

public class Outer3 {
    public static String name = "张三";
    private int age = 20;
    static class Inner{
        private int num = 10;
        private static int height = 170;
        public void getOuter() {
            System.out.println("name: " + name + " num: " + num);
        }
    }

    public static void main(String[] args) {
        new Inner().getOuter();
        System.out.println(Inner.height);
    }
}

输出:

name: 张三 num: 10
170
  1. 静态内部类的创建不需要依赖外部的类,可直接创建。
  2. 静态内部类不可以使用外部类的任何非static的域。

3.3 局部内部类

局部内部类就是定义在方法里的类。

public class Outer2 {
    private int num = 10;
    public void getNum(final int tmp) {
        final int b = 100;
        class Inner {
            public void test() {
                System.out.println(b);
                System.out.println(num);
                System.out.println(tmp);
            }
        }
        new Inner().test();
    }

    public static void main(String[] args) {
        new Outer2().getNum(20);
    }
}

输出:

100
10
20
  1. 局部内部类不允许使用任何访问权限修饰符。
  2. 局部内部类对外部完全隐藏,只有定义了这个类的方法才可以访问。
  3. jdk1.8之前局部内部类中的方法如果想使用定义它的方法的形参,则该参数必须使用final声明。

3.4 匿名内部类

匿名内部类就是一个没有名字的局部内部类。

public interface MyInterface {
    void test();
}
public class Outer4 {
    private int age = 10;
    public MyInterface display(final int tmp) {
        final int a = 10;
        return new MyInterface() {
            @Override
            public void test() {
                System.out.println("age: " + age + " tmp: " + tmp);
                System.out.println(a);
            }
        };
    }
    public static void main(String[] args) {
        Outer4 outer4 = new Outer4();
        outer4.display(20).test();
    }
}

输出:

age: 10 tmp: 20
10
  1. 匿名内部类必须继承一个抽象类或者实现一个接口。
  2. 匿名内部类没有类名,因此没有构造方法。
  3. 在JDK1.8之前,如果我们在匿名内部类中需要访问局部变量,那么这个局部变量必须用final修饰符修饰。

四、内部类的相关面试问题

4.1 jdk1.8之前,局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final?

用final修饰实际上就是为了保护数据的一致性。进一步就是为了保证代码执行结果的正确性

Outer2.java:

public class Outer2 {
    private int num = 10;
    public void getNum(final int tmp) {
        final int b = 100;
        class Inner {
            public void test() {
                System.out.println(b);
                System.out.println(num);
                System.out.println(tmp);
            }
        }
        new Inner().test();
    }

    public static void main(String[] args) {
        new Outer2().getNum(20);
    }
}

使用 javac Outer2.java 命令进行编译,得到两个.class文件:Outer2$1Inner.class、Outer2.class。

再用 javap -private Outer2$1Inner 反编译内部类的字节码文件,得到:

class com.example.demo.inner.Outer2$1Inner {
  final int val$tmp;
  final com.example.demo.inner.Outer2 this$0;
  com.example.demo.inner.Outer2$1Inner();
  public void test();
}

可以看到方法的局部变量tmp被复制为内部类的成员变量。

那么问题来了:将局部变量复制为内部类的成员变量时,必须保证这两个变量的值是一样的,也就是如果在内部类中修改了成员变量,方法中的局部变量也要跟着改变,如何解决呢?

解决办法就是将局部变量设置成final的,禁止修改这个变量,从而就保证了内部类的成员变量和方法内的局部变量的一致性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值