内部类 - 将一个类的定义放在另一个内部类里面
1 为什么我们需要内部类
在学习内部类之前我们应该思考一下,Java中为什么会引入内部类这个概念。
因为Java中一个子类只能继承一个父类,没有C++的多重继承,但内部类这个概念可以帮助Java实现多重继承
2 内部类的创建与引用
public class Parcel2 {
class Contents {
private int i = 11;
public int value() { return i; }
}
class Destination {
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel() { return label; }
}
public Destination to(String s) {
return new Destination(s);
}
public Contents contents() {
return new Contents();
}
public void ship(String dest) {
Contents c = contents();
Destination d = to(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcel2 p = new Parcel2();
p.ship("Tasmania");
Parcel2 q = new Parcel2();
// Defining references to inner classes:
Parcel2.Contents c = q.contents();
Parcel2.Destination d = q.to("Borneo");
}
} /* Output:
Tasmania
*///:~
1.如果从外部类的非静态方法之外的任意位置创建某个内部类的对象,那么形如main()中OuterClassName.InnerClassName
2.内部类拥有外部类的所有元素访问权
3.当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密得捕获一个指向那个外围类对象的引用
,然后在访问外部类成员时,就是用那个引用来选择外围类的成员
3 使用.this和.new
3.1内部类中对外部类对象引用
使用外部类的名字后面紧跟圆点和this
,生成对外部类对象的引用
// Qualifying access to the outer-class object.
public class DotThis {
void f() { System.out.println("DotThis.f()"); }
public class Inner {
public DotThis outer() {
return DotThis.this;
// A plain "this" would be Inner's "this"
}
}
public Inner inner() { return new Inner(); }
public static void main(String[] args) {
DotThis dt = new DotThis();
DotThis.Inner dti = dt.inner();
dti.outer().f();
}
} /* Output:
DotThis.f()
*///:~
3.2 内部类对象的创建
在拥有外部类对象之前,是不可能创建内部类对象的
,所以不能通过直接引用外部类的名字来创建
public class DotNew {
public class Inner {}
public static void main(String[] args) {
DotNew dn = new DotNew();
DotNew.Inner dni = dn.new Inner();
}
}
4 内部类与向上类型
5 内部类的种类
5.1 局部内部类
在方法和作用域内的内部类
/*方法中嵌入内部类*/
public class Parcel5 {
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 static void main(String[] args) {
Parcel5 p = new Parcel5();
Destination d = p.destination("Tasmania");
}
} ///:~
/*作用域中的内部类*/
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("slip");
String s = ts.getSlip();
}
// 在TrackingSlip 外面的{}外,它是不可用的
//! TrackingSlip ts = new TrackingSlip("x");
}
public void track() { internalTracking(true); }
public static void main(String[] args) {
Parcel6 p = new Parcel6();
p.track();
}
} ///:~
5.2 匿名内部类
将返回值的生成与表示这个返回值的类的定义结合在一起,且没有“姓名”
public class Parcel7 {
public Contents contents() {
return new Contents() { // 插入一个类定义
private int i = 11;
public int value() { return i; }
}; // 分号required in this case
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Contents c = p.contents();
}
} ///:~
/*创建一个来自Contents的匿名类对象,通过new表达式返回的引用被自动向上转型为对Contents的引用
上述代码可以简化为如下*/
public class Parcel7b {
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) {
Parcel7b p = new Parcel7b();
Contents c = p.contents();
}
} ///:~
如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器要求
其参数引用是final
public class Parcel9 {
// Argument must be final to use inside
// anonymous inner class:
public Destination destination(final String dest) {
return new Destination() {
private String label = dest;
public String readLabel() { return label; }
};
}
public static void main(String[] args) {
Parcel9 p = new Parcel9();
Destination d = p.destination("Tasmania");
}
} ///:~
5.3 嵌套(静态内部)类
普通的内部类保存了外围对象的引用,但嵌套类不需要,类似于外部类的一个static方法
1.要创建嵌套类的对象,并不需要外围类的对象
2.不能从嵌套类的对象访问非静态的外围类对象
普通类不能包含static数据和字段以及嵌套类,但嵌套类都可以包含
public class Parcel11 {
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 ParcelDestination(String whereTo) {
label = whereTo;
}
public String readLabel() { return label; }
// Nested classes can contain other static elements:
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("Tasmania");
}
} ///:~
5.3.1 嵌套类可以作为接口内部的类
正常情况下,不能在接口中放置任何代码,但嵌套类可以作为接口的一部分
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();
}
}
} /* Output:
Howdy!
*///:~
6 内部类不可以被覆盖但可以被继承
6.1 内部类不可以被覆盖
以下示例告诉我们不同作用域的同名内部类是完全独立的两个实体,各自在自己的命名空间里
class Egg {
private Yolk y;
protected class Yolk {
public Yolk() { print("Egg.Yolk()"); }
}
public Egg() {
print("New Egg()");
y = new Yolk();
}
}
public class BigEgg extends Egg {
public class Yolk {
public Yolk() { print("BigEgg.Yolk()"); }
}
public static void main(String[] args) {
new BigEgg();
}
} /* Output:
New Egg()
Egg.Yolk()
*///:~
6.2 内部类可以被继承
class Egg2 {
protected class Yolk {
public Yolk() { print("Egg2.Yolk()"); }
public void f() { print("Egg2.Yolk.f()");}
}
private Yolk y = new Yolk();
public Egg2() { print("New Egg2()"); }
public void insertYolk(Yolk yy) { y = yy; }
public void g() { y.f(); }
}
public class BigEgg2 extends Egg2 {
public class Yolk extends Egg2.Yolk {
public Yolk() { print("BigEgg2.Yolk()"); }
public void f() { print("BigEgg2.Yolk.f()"); }
}
public BigEgg2() { insertYolk(new Yolk()); }
public static void main(String[] args) {
Egg2 e2 = new BigEgg2();
e2.g();
}
} /* Output:
Egg2.Yolk()
New Egg2()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk.f()
*///:~
7 内部类标识符
由于每个类都会产生一个.class文件,内部类也必须生成一个.class文件以包含它们的Class对象信息
内部类的命名规则是:外围类的名字+$
+内部类的名字+.class
如上
BigEgg2.class,内部类为BigEgg2$Yolk.class
Egg2.class 内部类为Egg2$Yolk.class
如果内部类是匿名的,编译器会产生一个数字作为标识符