• Java基础之:OOP——内部类


    Java基础之:OOP——内部类

    在一个类A中又完整的声明了另一个类B,那么类A我们就称为外部类,类B就称为内部类。

    内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。

    内部类的分类:

    1. 创建在外部类的方法内部:

      • 局部内部类

      • 匿名内部类(实际也可以看作是,匿名局部内部类)

    2. 创建在外部类的属性位置:

      • 成员内部类

      • 静态内部类(实际也可以看作是,静态成员内部类)

     

    局部内部类

    局部内部类是定义在外部类的方法体中的类,具体使用方法与使用细节如下:

    • 1.可以直接访问外部类的所有成员,包含私有的

    • 2.不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的,但是可以使用final 修饰,因为局部变量也可以使用final

    • 3.作用域仅仅在定义它的方法或代码块中,并且遵循前向引用原则

    • 4.外部类---访问------>局部内部类,创建对象调用

    • 5.外部其他类---不能访问----->局部内部类 (因为 局部内部类地位是一个局部变量),这点实际上是可以访问的,不过要用到多态的性质。看下面思考题

    • 6.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问

    package class_inner;
    /**
     * 
     *  1.可以直接访问外部类的所有成员,包含私有的
     *  2.不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。
     *      但是可以使用final 修饰,因为局部变量也可以使用final 
     *  3.作用域仅仅在定义它的方法或代码块中,并且遵循前向引用原则
     *  4.外部类---访问------>局部内部类,创建对象调用
     *  5.外部其他类---不能访问----->局部内部类 (因为 局部内部类地位是一个局部变量)
     *      这点实际上是可以访问的,不过要用到多态的性质。看下面思考题。
     *  6.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则
     *      如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问 
     */
    public class LocalInnerDetail {
    ​
        public static void main(String[] args) {
            //细节4:创建Outer对象,调用say()方法,即调用了Inner局部内部类
            new Outer().say();
        }
    }
    ​
    ​
    class Outer{
        private int a;
        
        public void say() {
            //细节3:此时Inner类还没有执行到,不满足向前引用原则,所以不能创建对象
    //      Inner inner = new Inner();
            
            class Inner{
                private int b;
                public void innerShow() {
                    //细节1:可以直接访问外部类的所有成员,包含私有的
                    System.out.println(a);
                }
            }
            
            //细节2:可以使用fanal修饰,因为局部内部类,地位就相当于局部变量
            final class InnerA{
                private int a;
                public void innerShow() {
                    //细节6:就近原则 ,访问到内部类的属性a
                    System.out.println(a);
                    //细节6:访问外部成员属性,外部类名.this.成员
                    System.out.println(Outer.this.a);
                }
            }
            
            Inner inner = new Inner();
            inner.innerShow();
        }
        
        //细节3:
    //  Inner i = new Inner(); //报错:Inner cannot be resolved to a type
        
    }

    关于细节2的补充说明:

    局部内部类中只能访问使用final修饰的局部变量(并且在同一个作用域内):

    jdk7 final必须手动添加! jdk8 final是隐式加入,不用手动添加。

    原因:防止局部变量访问范围扩大!

     

    匿名内部类(重要!!!)

    匿名内部类也可以看作是匿名局部内部类,这种内部类在实际开发中经常用到,所以比较重要。

    匿名内部类的使用方式以及使用理解如下:

    package class_inner.Anonymous;
    ​
    public class AnonymousInner {
    ​
        public static void main(String[] args) {
            new XX().m1();  //输出:AnonymousInner......
        }
    }
    ​
    class XX{
    ​
        public void m1() {
            /*
             * 对于a而言,编译类型为A
             * 运行类型为:new 后面一直到 ";" 前面这一部分代码
             *  就像是一个类的声明  但这个类并没有名字,声明出来之后直接通过new产生一个对象返回给A接口的引用a
             * a的运行类型就是一个匿名内部类
             */
            A a  = new A() {
                @Override
                public void show() {
                    System.out.println("AnonymousInner......");
                }
            }; 
            
            /*
             * 对于show方法调用而言,它会先看编译类型A接口中有没有show方法,如果有编译器通过。
             * 然后在运行时,通过动态绑定找到a的运行类型即匿名内部类中实现了的show方法,再执行。
             */
            a.show();
            
            //还可以使用匿名对象来创建匿名内部类,我们之前会使用匿名对象调用方法,例如: new A().show
            new A() {
                @Override
                public void show() {
                    System.out.println("Other_AnonymousInner......");
                }
            }.show();
        }
    }
    ​
    //不仅可以使用接口,也可以使用父类创建匿名内部类
    interface A{
        public void show();
        
    }

     

    匿名内部类的使用细节与局部内部类,基本相同。但由于匿名内部类的使用方式让我们看起来很"奇特",所以在这里把两种调用方式列出来做一个对比:

    package class_inner.Anonymous;
    ​
    public class AnonymousInner {
    ​
        public static void main(String[] args) {
            
            //可以使用匿名内部类,作为参数传入方法中,调用顺序于局部内部类相同
            say(new B(){
                public void show() {
                    System.out.println("interface_B.....");
                }
            });
            
            //打印匿名内部类的运行类型,可以看到是没有名字的。
            System.out.println(new B() {
                @Override
                public void show() {
                }
            }.getClass());
            
            System.out.println(new Father("jack").getClass());   //匿名对象
            System.out.println(new Father("jack") {}.getClass());//匿名内部类
            
            //使用匿名内部类打印输出信息
            Father f = new Father("jack") {
                @Override
                public void show() {
                    System.out.println("AnonymousExtends_Class_Father_name:" + getName());
                }
            };
            f.show();
            
        }
        
        public static void say(B b) {
            b.show();
        } 
    }
    ​
    ​
    interface B{
        public void show();
    }
    ​
    class Father{
        private String name;
    ​
        public String getName() {
            return name;
        }
    ​
        public void setName(String name) {
            this.name = name;
        }
    ​
        public Father(String name) {
            super();
            this.name = name;
        }
        
        public void show() {
            System.out.println("Class_Father:" + name);
        }
    }

    程序输出:

    interface_B.....

    class class_inner.Anonymous.AnonymousInnerDetail$2

    class class_inner.Anonymous.Father

    class class_inner.Anonymous.AnonymousInnerDetail$3

    AnonymousExtends_Class_Father_name:jack

     

    匿名内部类的应用案例

    有一个铃声接口Bell,里面有个ring方法。 有一个手机类Cellphone,具有闹钟功能alarmclock,参数是Bell类型 测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了 再传入另一个匿名内部类(对象),打印:小伙伴上课了

    package class_inner.Anonymous;
    
    public class AnonymousInnerClassWork {
    ​
        public static void main(String[] args) {
            
            //方式一:匿名对象调用方法
            new Cellphone().alarmclock(new Bell(){
                @Override
                public void ring() {
                    System.out.println("懒猪起床了");
                }
            }); 
            
            //方式二:实名对象调用方法
            Cellphone cellphone = new Cellphone();
            cellphone.alarmclock(new Bell() {
                @Override
                public void ring() {
                    System.out.println("小伙伴上课了");
                }
            });
            
        }
    }
    ​
    class Cellphone {
        public void alarmclock(Bell b) {
            b.ring();
        }
    }
    ​
    interface Bell{
        public void ring();
    }
    

      


     

    成员内部类

    成员内部类是声明在外部类的成员属性位置上的类,具体使用如下:

    package class_inner;
    ​
    import class_inner.OuterM.InnerM;
    ​
    public class MemberInner {
    ​
        public static void main(String[] args) {
            
            //外部其他类---访问---->成员内部类:
            //方式一:匿名对象,访问成员内部类,调用方法
            new OuterM().new InnerM().show2();
            
            //方式二:
            OuterM outerM = new OuterM();
            //如果想要接收InnerM对象,必须要引包
            //import class_inner.OuterM.InnerM;
            InnerM i = outerM.gerInnnerM(); 
            i.show();
            
            //方式三:
            InnerM i2 = new OuterM().gerInnnerM();  
            i2.show2();
        }
    ​
    }
    class OuterM{
        private String name = "小范";
        private int age = 20;
        //可以使用所有的访问修饰符来修饰成员内部类
        public  class InnerM{
            private String name = "小黄";
            public void show() {
                //可以访问外部类所有属性与方法,因为成员内部类本质地位就是成员属性
                //如果外部类和内部类的成员重名时,如果想访问外部类的成员,则可以使用(外部类名.this.成员)
                System.out.println("name:" + OuterM.this.name + "  age: " + age);
            }
            public void show2() {
                System.out.println("name:" + name + "  age: " + age);
            }
        }
        
        public InnerM gerInnnerM() {
            return new InnerM();
        }
    }

    程序输出:

    name:小黄 age: 20

    name:小范 age: 20

    name:小黄 age: 20

     

    静态内部类

    静态内部类又可能看作是静态成员内部类,因为它就是成员内部类加上了static修饰。

    具体使用如下:

    package class_inner;
    ​
    import class_inner.OuterM.InnerM;
    ​
    public class MemberInner {
    ​
        public static void main(String[] args) {
            
            //外部其他类---访问---->成员内部类:
            //方式一:匿名对象,访问成员内部类,调用方法
            new OuterM().new InnerM().show2();
            
            //方式二:
            OuterM outerM = new OuterM();
            //如果想要接收InnerM对象,必须要引包
            //import class_inner.OuterM.InnerM;
            InnerM i = outerM.gerInnnerM(); 
            i.show();
            
            //方式三:
            InnerM i2 = new OuterM().gerInnnerM();  
            i2.show2();
        }
    ​
    }
    class OuterM{
        private String name = "小范";
        private int age = 20;
        //可以使用所有的访问修饰符来修饰成员内部类
        public  class InnerM{
            private String name = "小黄";
            public void show() {
                //可以访问外部类所有属性与方法,因为成员内部类本质地位就是成员属性
                //如果外部类和内部类的成员重名时,如果想访问外部类的成员,则可以使用(外部类名.this.成员)
                System.out.println("name:" + OuterM.this.name + "  age: " + age);
            }
            public void show2() {
                System.out.println("name:" + name + "  age: " + age);
            }
        }
        
        public InnerM gerInnnerM() {
            return new InnerM();
        }
    }

    程序输出:

    name:小范

    OuterS_phone:123

    InnerS_Sphone:321

    特别注意:

    1. 使用静态内部类,只需要使用类名调用即可,就不会生成OuterS外部类的对象

    2. 出现重名的属性时,只需要 (外部类名.属性)就可以访问

    3. 静态内部类只能访问外部静态的内容

     

    思考题

    在局部内部类的细节5中提到外部其他类不能访问局部内部类,但可以通过多态的特性将其实现。

    具体方式是:

    1. 定义一个局部内部类可以访问到的接口或父类

    2. 让局部内部类继承此接口或父类

    3. 在外部其他可以使用接口或父类的类中,就可以通过多态的方式使用到局部内部类。

    4. 这种方式主要就是使用了多态的动态绑定机制

    package test;
    ​
    public class Test {
        public static void main(String[] args) {
            A a = new A();
            System.out.println("a--hashCode= " + a.hashCode());
            
            A a2 = a.say();
            a2.show();
            
    //      X x = a.say();
    //      x.show();
            
            
        }
    }
    ​
    interface X{
        public void show();
    }
    ​
    class A{
        public void show() {
            
        }
        private int n1 = 10;
        private int n3 = 330;
    ​
        public A /* X */ say() {
            int n2 = 20;
            
            class InnerA extends A /* implements X */{
                int n3 = 30;
                
                public void show() {
                    System.out.println("A--n3=" + A.this.n3 + "	InnerA--n3= " + n3);
                    
                    System.out.println("A--hashCode=" + A.this.hashCode());
                }
            }
            InnerA innerA = new InnerA();
            innerA.show();
            return innerA;
        }
    }
    

      

    程序输出:

    a--hashCode= 366712642

    A--n3=330 InnerA--n3= 30

    A--hashCode=366712642

    A--n3=330 InnerA--n3= 30

    A--hashCode=366712642

    说明

    在程序中可以使用接口 X 将局部内部类"接出来",也可以使用父类 A 将局部内部类"接出来"。

    这种思路需要灵活应用动态绑定机制,考虑局部内部类的运行类型问题。

     

     

     

  • 相关阅读:
    Java程序执行超时——Future接口介绍
    JENKINS 打包发布脚本
    获取servletContext springMvc获取servletContext
    14.19 InnoDB and MySQL Replication InnoDB 和MySQL 复制:
    14.18.1 The InnoDB Recovery Process InnoDB 恢复进程:
    perl 获取文件内容里第一个AAA和最后一个AAA
    14.18 InnoDB Backup and Recovery 备份和恢复:
    职业素养与职业声誉——北漂18年(62)
    Openstack组件实现原理 — Nova 体系结构
    Openstack组件实现原理 — Nova 体系结构
  • 原文地址:https://www.cnblogs.com/SongHai/p/14144469.html
Copyright © 2020-2023  润新知