内部类:
一个类的定义放在另一个类的定义内部,这就是内部类。
内部类的创建:
在创建内部类之前,我们须先了解:内部类自动拥有对其外围类(注意与外部类的区别)所有成员(包括private成员)的访问权,但是这种访问是在内部类定义处。之所以可以访问,是因为在创建一个内部类的对象时,此内部类对象将获取一个指向那个外围类对象的引用,当你想访问外围类对象的成员时,编译器就会调用外围类的引用。
反过来,外围类同样可以访问内部类的所有成员(包括private成员),因为内部类本身就可以看做是外围类的成员。
通过上面内部类与外围类的关系,可以知道,要想创建一个内部类,必须先存在一个外围类的对象,因为这是两者联系的必要条件。
内部类对象的创建分为外围类中创建和外部类中的创建。(说明:这里所说的外围类时指包围内部类的类,外部类是其他外部的类。很多地方都将外围类和外部类看做一样。)
1)外围类(包围内部类的类)
无论是否是静态方法,都不需要具体指明对象的类型:OuterClassName.InnerClassName。直接先创建一个外围类对象,然后在与外围类对象相关联的情况下,才可以被创建。
public class Pacel {
private int i = 1;
class Contents {
public int test() {
return i;//内部类可以访问外围类成员,所以内部类的对象的创建必须和外围类有关联。
}
}
public Contents contents() { //迭代器模式
return new Contents();
}
public static void main(String[] args) {
Pacel2 pacel = new Pacel();
Contents p1 = pacel.new Contents();//使用.new提供对外围类对象的引用
Contents p2 = pacel.contents();//通过使用迭代器设计模式,调用contents方法返回Contents对象。
}
}
2)外部类(不包围该内部类的独立类)
书中说的是,外部类的非静态方法之外的任意位置创建某个内部类的对象,那么必须指明对象的类型:OuterClassName.InnerClassName。但是经检验,即使是静态方法,仍然需要指明。
public class PacelTest {
public void test() {
Pacel pacel = new Pacel();
Pacel.Contents p3 = pacel.new Contents();
}
public static void main(String[] args) {
Pacel pacel2 = new Pacel();
Pacel.Contents p4 = pacel2.new Contents();
//!Contents p3 = pacel.new Contents();
}
}
.this和.new:
.this生成外围类的引用; .new 提供外围类的引用来创建内部类对象,见上面代码示例。
小结:
在拥有外围类对象之前是不可能创建内部类对象的,这是因为内部类对象会暗暗地连接到外围类对象上。这种暗暗其实是我们在定义内部类的构造器时不管有参无参,不管是否使用默认构造器,编译器都会默默添加一个参数用于保存外围类对象引用。但是,如果你创建的是嵌套类(静态内部类),那么就不需要对外围类对象的引用。
内部类与向上转型:
当将内部类向上转型为其基类,尤其是转型为一个接口的时候,内部类就有了其用武之地。因为我们得到的只是指向基类和接口的引用,所以可以更好的隐藏实际细节。参见下面示例代码的29、30行,只是得到两个对象引用 contents和destination,并不知道其真正创建的类型。客户端程序员也不能访问任何新增加的、原本不属于接口的任何方法,所以扩展接口是毫无意义的,从而完全隐藏了实现的细节。
class Parcel4{
private class PContents implements Contents {
private int i = 11;
@Override
public int value() {
return i;
}
}
protected class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
@Override
public String readLabel() {
return label;
}
}
public Destination destination(String s){
return new PDestination(s);
}
public Contents contents(){
return new PContents();
}
}
public class TestParcel {
public static void main(String[] args) {
Parcel4 p = new Parcel4();
Contents contents = p.contents();
Destination destination = p.destination("Tasmania");
//Parcel4.PContents pc = p.new PContents();//PContents是private,只能在Parcel4内部访问,此处报错
}
}
局部内部类:
对于具备内部类和非局部内部类,其标识符依然可以使用,可以将这两种内部类简单看成变量,虽然不是很准确。
注意:局部内部类只能在定义域内使用,定义域之外是无法被访问的。这和非局部内部类不一样。非局部内部类相当于是外围类的成员变量,而局部内部类相当于方法内部的变量。这一点很重要。
1)方法作用域内定义内部类
public class Parcel5 {
public Destination destination(String s){
class PDestination implements Destination{
private String label;
private PDestination(String whereTo){
label = whereTo;
}
@Override
public String readLabel() {
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args) {
Parcel5 p = new Parcel5();
Destination destination = p.destination("Tasmania");
}
}
2)其他任何作用域下的内部类定义。如if作用域内。
public class Parcel6 {
private void internalTracking(boolean b) {
if (b){
class TrackingSlip {
private String id;
public TrackingSlip(String s) {
id = s;
}
String getSlip(){ return id;}
}
TrackingSlip ts = new TrackingSlip("slip");
String s = ts.getSlip();
}
//Can't use it here!Out of scope:
//TrackingSlip ts = new TrackingSlip("x");
}
public void track(){internalTracking(true);}
public static void main(String[] args) {
Parcel6 p = new Parcel6();
p.track();
}
}
啰嗦一句:TrackingSlip这个类并不是说只有if条件成立,才会存在。其实它早已和其他类一起编译过了。其次,在定义TrackingSlip类的定义域之外,它是不可用的。
匿名内部类:
如:new Contents() { private int i = 11;} 大括号里面的匿名内部类其实是Contents的导出类,自动向上转型为对Contents的引用。小括号里面可以传递参数给基类构造器,大括号里面可以实现对该匿名内部类(导出类)进行添加成员、初始化等功能,如果匿名内部类中使用到了外部定义的对象,编译器会要求这个对象的引用必须是final型的,如果没有用到,则不需要。
public class Parcel9 {
public Destination dest(final String dest, final float price) {
return new Destination() {
private int cost;
{
cost = Math.round(price);
if(cost > 100)
System.out.println("Over budget!");
}
private String label = dest;
public String readLabel() { return label; }
};
}
public static void main(String[] args) {
Parcel9 p = new Parcel9();
Destination d = p.dest("Tanzania", 101.395F);
}
}
嵌套类:
如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static。这通常称为嵌套类。嵌套类是没有.this引用的。
普通的内部类对象隐式地保存了一个引用,指向创建它对的外围类对象。然而,当内部类是static时,意味着:
1)要创建嵌套类的对象,并不需要先创建外围类的对象;
2)不能从嵌套类的对象去访问非静态的外围类的对象;
3)普通内部类里面不能包含嵌套类,但是嵌套类可以。
正常情况下,不能在接口内部放置任何代码,但是嵌套类可以作为接口的一部分,放置在接口中的任何类都是自动设置为public和static的。因为类时static的,所以只是占用了接口的命名空间而已,并没有违反接口的规则。这种写法的好处在于,当你想要创建某些公共代码时,使得它们可以被某个接口的所有不同实现所公用,将会更加方便。
一个普通内部类被嵌套多少层,无所谓!编译器通过 .new 语法可以产生正确的作用域。嵌套类直接创建就可以,其实它也没有.new引用。