Thinking in Java第十章学习笔记----内部类

内部类:

  一个类的定义放在另一个类的定义内部,这就是内部类。

内部类的创建:

  在创建内部类之前,我们须先了解:内部类自动拥有对其外围类(注意与外部类的区别)所有成员(包括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引用。

原文地址:https://www.cnblogs.com/promiseslc/p/8809990.html