zoukankan      html  css  js  c++  java
  • 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引用。

  • 相关阅读:
    分类问题的评价指标
    29 畅游 x86 分页机制(中)
    高手进阶,终极内存技术指南——完整/进阶版
    从ST官网获取STM32 AD封装库(包含原理图库和PCB库)详细教程
    标准SPI、DUAL SPI、Quad SPI;NorFlash、NandFlash、eMMC闪存的比较与区别
    ARM 之七 主流编译器(armcc、iar、gcc for arm)详细介绍
    STM32中ARM系列编译工具链的编译宏选择(__CC_ARM、__ICCARM__、__GNUC__、__TASKING__)
    每日总结
    每日总结
    每日总结
  • 原文地址:https://www.cnblogs.com/promiseslc/p/8809990.html
Copyright © 2011-2022 走看看