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引用。

  • 相关阅读:
    Java实现 LeetCode 382 链表随机节点
    Java实现 LeetCode 382 链表随机节点
    Java实现 LeetCode 381 O(1) 时间插入、删除和获取随机元素
    Java实现 LeetCode 381 O(1) 时间插入、删除和获取随机元素
    Java实现 LeetCode 381 O(1) 时间插入、删除和获取随机元素
    Java实现 LeetCode 380 常数时间插入、删除和获取随机元素
    Java实现 LeetCode 380 常数时间插入、删除和获取随机元素
    Linux下的iwpriv(iwlist、iwconfig)的简单应用
    OCX控件的注册卸载,以及判断是否注册
    .OCX、.dll文件注册命令Regsvr32的使用
  • 原文地址:https://www.cnblogs.com/promiseslc/p/8809990.html
Copyright © 2011-2022 走看看