【码农每日一题】Java 内部类(Part 4)相关面试题

270 阅读5分钟
关注一下嘛,又不让你背锅!

问:非静态内部类里面为什么不能有静态属性和静态方法?

答:static 类型的属性和方法在类加载的时候就会存在于内存中,要使用某个类的 static 属性或者方法的前提是这个类已经加载到 JVM 中,非 static 内部类默认是持有外部类的引用且依赖外部类存在,所以如果一个非 static 的内部类一旦具有 static 的属性或者方法就会出现内部类未加载时却试图在内存中创建内部类的 static 属性和方法,这自然是错误的,类都不存在(没被加载)却希望操作它的属性和方法。从另一个角度讲非 static 的内部类在实例化的时候才会加载(不自动跟随主类加载),而 static 的语义是类能直接通过类名来访问类的 static 属性或者方法,所以如果没有实例化非 static 的内部类就等于非 static 的内部类没有被加载,所以无从谈起通过类名访问 static 属性或者方法。

问:Java 匿名内部类为什么不能直接使用构造方法,匿名内部类有没有构造方法?

答:因为类是匿名的(相当于没有名字),而且每次创建的匿名内部类同时被实例化后只能使用一次,所以就无从创建一个同名的构造方法了,但是可以直接调用父类的构造方法(譬如 new InnerClass(xxx, xxx) {})。

实质上匿名内部类是有构造方法的,是通过编译器在编译时帮忙生成的,如下代码:

  1. class InnerClass {}

  2. public class OutClass {

  3.    InnerClass clazz = new InnerClass(){};

  4. }

通过编译后生成了 InnerClass.class、OutClass$1.class、OutClass.class,可以看见 OutClass$1.class 就是我们匿名内部类的字节码名字,我们通过 javap -v OutClass$1.class 可以看到如下:

  1. ......

  2. {

  3.  final OutClass this$0;

  4.  ......

  5.  OutClass$1(OutClass);

  6.    descriptor: (LOutClass;)V

  7.    flags:

  8.    Code:

  9.      stack=2, locals=2, args_size=2

  10.         0: aload_0

  11.         1: aload_1

  12.         2: putfield      #1    // Field this$0:LOutClass;

  13.         5: aload_0

  14.         6: invokespecial #2    // Method InnerClass."<init>":()V

  15.         9: return

  16.      LineNumberTable:

  17.        line 8: 0

  18. }

  19. ......

可以很明显看到内部类的字节码中编译器为我们生成了参数为外部类引用的构造方法,其构造方法和普通类的构造方法没有区别,都是执行 <init> 方式。

问:Java 中非静态内部类和静态内部类有什么区别?

答:常见的区别如下。

  • 非静态内部类默认持有外部类的引用,静态内部类不存在该特性。

  • 非静态内部类中不能定义静态成员或者方法,静态内部类中可以随便定义。

  • 非静态内部类可以直接访问外部类的成员变量或者方法,静态内部类只能直接访问外部类的静态成员或者方法(实质是持有外部类名)。

  • 非静态内部类可以定义在外部类的任何位置(方法里外均可,在方法外面定义的内部类的 class 访问类型可以是 public、protected 等,方法里的只能是默认 class,类似局部变量),静态内部类只能定义在外部类中最外层,class 修饰符可以是 public、protected 等。

  • 非静态内部类创建实例时必须先创建外部类实例,静态内部类不依赖外部类实例。

  • 静态方法中定义的内部类是静态内部类(这时不能在类前面加 static 关键字),静态方法中的静态内部类与普通方法中的内部类使用类似,除了可以直接访问外部类的 static 成员变量或者方法外还可以访问静态方法中的局部变量(java 1.8 以前局部变量前必须加 final 修饰符)。

问:Java 中内部类有什么好处(即 Java 的内部类有什么作用)?

答:具体好处如下。

  • 内部类可以很好的实现隐蔽,一般的非内部类,是不允许有 private 与 protected 等权限的,但内部类(除过方法内部类)可以通过这些修饰符来实现隐藏。

  • 内部类拥有外部类的的访问权限(分静态非静态情况),通过这一特性可以比较好的处理类之间的关联性,将一类事物的流程放在一起内部处理。

  • 通过内部类可以实现多重继承,java 默认是单继承,我们可以通过多个内部类继承实现多个父类,接着由于外部类完全可访问内部类,所以就实现了类似多继承的效果。

  • 通过内部类可以避免修改接口而实现同一个类中两种同名方法的调用(譬如你的类 A 中有一个参数为 int 的 func 方法,现在类 A 需要继承实现一个接口 B,而接口 B 中也有一个参数为 int 的 func 方法,此时如果直接继承实现就会出现同名方法矛盾问题,这时候如果不允许修改 A、B 类的 func 方法名则可以通过内部类来实现 B 接口,因为内部类对外部类来说是完全可访问的)。

老铁们,别嫌短,长了你肯定不会看完的,所以这就是码农每日一题的宗旨(其他历史文章请查看公众号历史记录)~

看完分享一波嘛,和你的小伙伴一起讨论才更加有意思,右上角分享 666~

看个笑话放松一下

提问:布和纸怕什么?

回答:布怕一万,纸怕万一。

原因:不(布)怕一万,只(纸)怕万一。

Talent is God given. Be humble. Fame is man-given. Be grateful. Conceit is self-given. Be careful. 

天赋是上帝给予的,要谦虚。名声是别人给予的,要感激。自负是自己给的,要小心。