java中的内部类
本博客参考<< thinking in java >>第10章内部类,总结了本人不熟悉的知识点或者重要的知识点.
创建内部类
- 只需要简单地将类的定义置于外围类内部.
链接到外部类
- 内部类能够访问外围对象的所有成员,而不需要任何条件.而且内部类具有外部类所有成员的访问权(包括private).为什么呢?这是因为内部类对象会秘密地捕获一个指向外围类的引用.
- 非static的内部类只能在外部类对象存在时候才可以被创建.(在下一部分中的代码会说明这一点)
使用.this和.new
-
.this:在内部类中通过使用OuterClass.this.就可以生成外部类对象的引用.
-
.new:具体用法见下
public class OuterClass { class InnerClass{ int val = 1; InnerClass(){ System.out.println("In innerclass's construction"); } } void test(){ InnerClass i = new InnerClass(); System.out.println(i.val); } InnerClass getInnerClass(){ return new InnerClass(); } public static void main(String[] args) { OuterClass o = new OuterClass(); // outerclass must be initialed first InnerClass i = o.new InnerClass(); // .new usage InnerClass i2 = o.getInnerClass(); // another way to get the instance of innerclass }
}
内部类和向上转型
-
当将内部类向上转型为其基类,尤其是一个接口的时候,内部类就有了用武之地,因为这个实现了接口的内部类完全不可见,所得到的只是指向基类或接口的引用,可以很方便地隐藏实现细节.
public class TestCast { private class InnerClass implements Base{ private String str; public void otherMethod(){ System.out.println("hah"); } private InnerClass(String str){ this.str = str; } @Override public void say() { System.out.println("sya something : " + str); } } Base getBase(String str){ return new InnerClass(str); } public static void main(String[] args) { TestCast t = new TestCast(); Base b = t.getBase("hello"); }
}
局部内部类
- 可以在方法内定义一个类.
匿名内部类
-
在匿名内部类中,如果你的基类需要一个有参数的构造器时,只要简单地传递合适的参数给基类的构造器即可.
public class TestPass { public Wrapping wrapping(int x){ return new Wrapping(x){ public int getValue(){ return super.getValue() * 2; } }; } public static void main(String[] args) { TestPass t = new TestPass(); Wrapping w = t.wrapping(2); System.out.println(w.getValue());// output 4 } } public class Wrapping { private int i; public Wrapping(int i){ this.i = i; } public int getValue(){ return i; } }
-
如果要定义一个匿名内部类,并且想使用一个外部定义的对象,那么这个参数引用需要是final的.(貌似jdk8后不用final修饰这个变量,但是这个变量也是不能改变的,这个final称为effectively final)
public void test(String str){ return new A(){ private String word = str; public String say(){ return word;} }; }
-
由于匿名类中不可能有命名的构造器(因为根本没有名字),但是通过实例初始化,可以使得匿名类中创建一个构造器类似的效果.
public class TestInit { public Base getBase(){ return new Base() { { System.out.println("In base init block!"); } @Override public void say() { System.out.println("base say"); } }; } public static void main(String[] args) { TestInit t = new TestInit(); Base b = t.getBase(); // output : In base init block! } }
嵌套类
- 不需要内部类对象和外部类对象之间有联系,可以将内部类声明为static,这通常称为嵌套类.嵌套类意味着:①要创建这个static的内部类并不需要外部类对象.②不能从嵌套类的对象中访问非静态的外围类对象(嵌套类中没有这个特殊的this引用).
- 接口内部的类:正常情况下不可以在接口中放置任何代码,但是嵌套类可以作为接口的一部分,因为接口中的任何域都是static和public的,而放在接口中的这个类会变成static,你甚至可以让这个嵌套类实现这个接口.
内部类的继承
-
指向外部类中的秘密引用必须在构造器中被初始化.
class WithInner{ class Inner{} } public class InheritInner extends WithInner.Inner{ InheritInner(WithInner wi){ wi.super(); } } public static void main(String[] args) { WithInner wi = new WithInner(); InheritInner ii = new InheritInner(wi); }
内部类的覆盖
- 如果A类中有个protected的内部类InnerClass,B类继承了A类并且也定义了一个内部类InnerClass,事实发现后者并不会覆盖前者的类,两个内部类是完全独立的两个实体.当然,如果B类中的InnerClass明确表示extends A.InnerClass, 那么可以覆盖其中的方法.
内部类标识符
- 内部类会生成一个.class文件已包含他们的Class对象信息,这些文件的命名规则:如果是局部内部类,那么命名为OuterClassName$InnerClassName.class;如果是匿名内部类,那么会生成OuterClassName$num.class,其中num为一个数字.