1.内部类访问外围类的成员方法
- 内部类可以访问其外围类的方法和字段,就像他们自己的一样。
- 当某个外围类的对象创建一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用。
在访问此外围类成员的时候,就用那个引用去访问外围类的成员,内部类对象只能在与其外围类的对象相关联
的时候才能被创建。编译器会处理这些细节。
2.使用.this和.new
- .this生成一个外部类对象的引用。
public class DotThis { void f(){System.out.println("DotThis.f()");} public class Inner{ public DotThis outer(){ //.this 返回外部类对象 return DotThis.this; } } public Inner inner(){return new Inner();} public static void main(String[] args) { DotThis dt = new DotThis(); Inner dti = dt.inner(); dti.outer().f(); } }
- .new 生成内部类的对象。
可以看到上面的代码必须生成一个外部类对象,然后才能生成内部类对象。public class DotNew { public class Inner{} public static void main(String[] args) { DotNew dn = new DotNew(); //注意这里 你不能new DotNew.Inner(); Inner dni = dn.new Inner(); } }
因为在拥有外部类对象之前是不可能创建内部类对象的。这是因为内部类对象会隐式的连接到创建它的外部类对象上。
3.内部类和向上转型
- 当内部类向上转型为其父类的时候,得到的只是指向父类或接口的引用,所以只能调用父类或接口中所声明的方法(子类会重写这些方法),所以就隐藏了实现细节
而声明private或protected(除非它的子类或同一包下的或外部类能访问)的内部类可以实现外部类外的类不能声明这个内部类的具体引用,所以内部类就实现了细节的隐藏。
interface Destination{ String readLabel(); } interface Contents{ int value(); } class Parcel{ //内部类 private class PContents implements Contents{ private int i = 11; public int value() {return i;} } //内部类 protected class PDestination implements Destination{ private String label; private PDestination(String whereTo){label = whereTo;} public String readLabel() {return null;} } public Destination destination(String s){return new PDestination(s);} public Contents contents(){return new PContents();} } public class TestParcel { public static void main(String[] args) { Parcel p = new Parcel(); //通过p的方法获得,进行了向上转型 Contents c = p.contents(); // c = p.new PContents();这种是错误的行为,因为PContents是Parcel的私有内部类 Destination d = p.destination("哈哈"); } }
4.内部类的复杂使用
- 内部类语法 覆盖了大量其他的更加难以理解的技术。
- 创建一个类,但又不希望这个类是公共可用的。像3一样,private的内部类。
- 一个定义在方法中的类。
- 一个定义在作用域的类,此作用域在方法的内部。
- 一个实现了接口的匿名类。
- 一个匿名类,它扩展了有非默认构造器的类。
- 一个匿名类,他执行字段初始化。
- 一个匿名类,他通过实例化实现构造器(匿名类不可能有构造器)。
4.1.一个定义在方法(域)中的内部类
- 定义在方法中的内部类在方法外是不可以访问这个内部类的。
interface Destination{ String readLabel(); } class Parcel{ //方法 public Destination destination(String s){ //内部类 class PDestination implements Destination{ private String label; private PDestination(String whereTo){label = whereTo;} public String readLabel() {return label;} } //返回这个对象,可以写一个引用获得这个对象 return new PDestination(s); } } public class TestParcel { public static void main(String[] args) { Parcel p = new Parcel(); Destination d = p.destination("呵呵"); } }
4.2.定义在作用域内的类
- 定义在作用域内的类,在作用域外是不能够访问这个类的。
public class Parcel6 { private void internalTracking(boolean b){ if (b) { //定义在作用域内的类,作用域外不可访问 class TrackingSlip{ private String id; TrackingSlip(String s){ id = s; } String getSlip(){return id;} } TrackingSlip ts = new TrackingSlip("x"); String s = ts.getSlip(); } } public void track(){internalTracking(true);} public static void main(String[] args) { Parcel6 p = new Parcel6(); p.track(); } }
4.3实现了接口匿名内部类
- 匿名内部类一定伴随着抽象类或接口的继承。
上面是下面简化形式:??有问题interface Contents{ int value(); } public class TestParcel { public Contents contents(){ //可以看到下面这个实现了Contents接口的类并没有名字,它看起来像是创建了一个Contents对象。 return new Contents() { private int i = 11; public int value() {return i;} }; } public static void main(String[] args) { TestParcel p = new TestParcel(); Contents c = p.contents(); } }
interface Contents{ int value(); } public class TestParcel { class MyContents implements Contents{ private int i = 11; public int value() {return i;} } public Contents contents(){return new MyContents();} public static void main(String[] args) { TestParcel p = new TestParcel(); Contents c = p.contents(); } }
4.4扩展了非默认构造器的匿名内部类
class Wrapping{//这是一个普通的类 private int i; public Wrapping(int x){i = x;} public int value(){return i;} } public class Parcel7 { public Wrapping wrapping(int x){ //也可以这样 return new Wrapping(x){ public int value() { return super.value()*47; } };//此分号和其他return表达式的分号一样,代表表达式结束。 } }
4.5执行字段初始化的内部类
- 如果一个匿名内部类希望使用一个在其外定义的对象,那么编译器会要求其参数引用是final的。
interface Destination{ String readLabel(); } public class TestParcel { //final的参数 public Destination destination(final String dest){ return new Destination() { //初始化 private String label = dest; public String readLabel() {return label;} }; } public static void main(String[] args) { TestParcel p = new TestParcel(); Destination d = p.destination("哈哈"); } }
4.6实例化实现构造的匿名内部类
- 匿名内部类中不可能有命名构造器(因为它们根本就没有名字),但可以通过实力初始化,就能够达到为匿名内部类创建一个构造器的效果,但并不是一个构造器,只是达到效果。
abstract class Base{ public Base(int i){ System.out.println("Base constructor, i = " + i); } public abstract void f(); } public class AnonymousConstructor { //参数i不必为final ,因为没有在匿名内部类中使用 public static Base getBase(int i){ return new Base(i) { //使用这种方式实现实例化构造, 实例初始化 {System.out.println("Inside instance initializer");} public void f() { System.out.println("In anonymous f()"); } }; } public static void main(String[] args) { Base base = getBase(47); base.f(); } }
- 实例初始化的实际效果就是构造器。但它受到了限——你不能重载实例初始化方法,所以你仅有一个这样的构造器。
4.7匿名内部类的限制
- 匿名内部类既可以扩展类,也可以实现接口,但不能两者兼备。而且如果是实现接口,也只能实现一个。
- 匿名内部类可以实现工厂模式。
5.嵌套类
- 如果不需要内部类对象与外部类对象之间有联系,就将内部类声明为static。通常被称为嵌套类。
- 普通的内部类对象是隐式的保存了一个指向外围类对象的引用。当内部类是static意味着
要创建嵌套类的对象,并不需要依赖其外围类的对象
不能从嵌套类的对象中访问非静态的外围类对象。 - 普通内部类的字段和方法,只能放在类的外部层次上,所以普通的内部类不能有static数据和static字段,也不能包含嵌套类。但嵌套类可以。
interface Destination{ String readLabel(); } interface Contents{ int value(); } public class TestParcel { //嵌套类 private static class ParcelContents implements Contents{ private int i = 11; public int value() {return i;} } //嵌套类 protected static class ParcelDestination implements Destination{ private String label; //private的构造方法 这表示外面是无法初始化的 private ParcelDestination(String whereTo){ label = whereTo; } public String readLabel() {return label;} //定义一些静态方法 public static void f(){} static int x = 10; //嵌套类在内部再次嵌套一个类 static class AnotherLevel{ public static void f(){} static int x = 10; } } public static Destination destination(String s){ return new ParcelDestination(s); } public static Contents contents(){ return new ParcelContents(); } public static void main(String[] args) { //可以看到,这里获取嵌套的内部类并不需要外部类的支持了,内部类不需要与外部类关联才能创建了。 Contents c = contents(); Destination d = destination("呵呵"); } }
6.接口内部的类
- 放到接口中的任何类都是public和static的。因为是static的,只是将嵌套类置于接口的命名空间内。
public interface ClassInInterface { void howdy(); //内部类中实现外围接口。 class Test implements ClassInInterface{ public void howdy() { System.out.println("Howdy!"); } public static void main(String[] args) { new Test().howdy(); } } }
- 如果你想要创建某些公共方法,使得他们能够被某个接口的所有不同实现公用,那么使用接口嵌套类会更方便。
7.从多层嵌套类中访问外部类的成员
- 一个内部类被嵌套多少层并不重要——它能够透明的访问所有它嵌入的外围类的所有成员。
class MNA{ private void f(){} //一层嵌套 class A{ private void g(){} //二层嵌套 public class B{ void h(){ //调用外部类的方法,不需要任何条件 g(); f(); } } } } public class MultiNestingAccess { public static void main(String[] args) { MNA mna = new MNA(); //通过外部类创建内部类 MNA.A mnaa = mna.new A(); //二层嵌套 MNA.A.B mnaab = mnaa.new B(); mnaab.h(); } }