面向对象的陷阱——非静态内部类的陷阱

来源:互联网 发布:mysql查询面试题及答案 编辑:程序博客网 时间:2024/06/06 19:38

3、非静态内部类的陷阱

       内部类是Java提供的一个常用语法。内部类能提供更好的封装,而且它可以直接访问外部类的private成员,因此在一个特殊场合下更常用。

3、1 非静态内部类的构造器

public class Outer {public static void main(String[] args) throws Exception {new Outer().test();}private void test() throws Exception {System.out.println(new Inner());  //①System.out.println(Inner.class.newInstance());  //②}public class Inner {public String toString() {return "Inner对象";}}}
输出结果为:
Inner对象
Exception in thread "main" java.lang.InstantiationException: Outer$Inner
at java.lang.Class.newInstance(Unknown Source)
at Outer.test(Outer.java:10)
at Outer.main(Outer.java:5)
       代码①行运行正常,代码②行运行报错。这符合静态内部类的规则:非静态内部类必须寄生在外部类的实例中,没有外部类的对象,就不可能产生非静态内部类的对象。因此,非静态内部类不可能有无参数的构造器——即使系统为非静态内部类提供一个默认的构造器,这个默认的构造器也需要一个外部类形参。对于①行代码,程序表面上调用Inner无参数的构造器创建实例,实际上虚拟机会将this(代表当前默认的Outer对象)作为实参传入Inner构造器。至于程序②行代码的效果则不同,程序通过反射指定调用Inner类无参数的构造器,所以引发了运行时异常。
注意:系统在编译阶段总会为非静态内部类的构造器增加一个参数,非静态内部类的构造器的第一个形参总是外部类。因此调用非静态内部类的构造器时必须传入一个外部类对象作为参数,否则程序将会引发运行时异常。

3、2 非静态内部类的子类

class Out {class In {public void test() {System.out.println("In的test()方法");}}class A extends In {}}public class OutTest extends Out.In {public static void main(String[] args) {System.out.println("Hello World!");}}
报错:No enclosing instance of type Out is available due to some intermediate constructor invocation
       上面程序错误的关键在于,由于非静态内部类In必须寄生在Out对象之内,因此父类Out.In根本没有无参数的构造器。而程序定义其Out.In时,也没有定义构造器,那么系统会为它提供一个无参数的构造器。在OutTest无参数的构造器中,编译器会增加代码super(),对于super()调用,指定调用父类Out.In无参数的构造器,必然导致编译错误。为了解决这个问题,应该为OutTest显示定义一个构造器,在该构造器中显示调用Out.In父类对应的构造器,也就是OutTest类的代码改为如下所示。
public class OutTest extends Out.In {public OutTest() {new Out().super();}public static void main(String[] args) {System.out.println("Hello World!");}}
输出结果为:
Hello World!

总结:由于非静态内部类必须寄生在外部类的实例之中,程序创建非静态内部类对象的实例,派生非静态内部类的子类时都必须特别小心,否则很容易引入陷阱。如果条件允许,推荐多使用静态内部类,而不是非静态内部类。对于静态内部类来说,外部类相当于它的一个包,因此静态内部类的用法就简单多了,限制也会少很多,但是静态内部类不能访问外部类的非静态成员。





1 0
原创粉丝点击