• JavaSE-13 内部类


    学习要点

    • 内部类的定义
    • 内部类的应用

    内部类

    定义

    Java的一个类中包含着另一类。



    A类和B类是C类的外部类。B类是C类的外部类。A类也称为顶层类。

    如何使用内部类

    public class MyFatherClass {
    
    }
    
    public interface MyInterface {
    
        public abstract int getValue();
    
    }
    
    public class MyClass extends MyFatherClass implements MyInterface {
    
        int value = 0;
    
        public int getValue() {
    
            return value;
    
        }
    
        public String getValue() {
    
            return "这是一个神奇的时代!";
    
        }
    
    }
    

      

    以上代码会存在什么问题?编译器提示出错,提示方法重复。即编译器搞不清楚getValue()是继承接口重写的还是重载的。如何解决?使用内部类。

    public class MyClass extends MyFatherClass {
    
        int value = 0;
    
        public int getValue() {
    
            return value;
    
        }
    
        public class MyInnerClass implements MyInterface {
    
            public int getValue() {
    
                return 0;
    
            }
    
        }
    
    }
    
     
    

      

    内部类可以访问外部类的所有资源,如果内部类是继承其他类或者接口,不影响外部类的继承和接口实现。轻松实现Java的“多继承”。

    例如:

    class A{}
    
     
    
    class B{}
    
     
    
    public class S extends A {
    
        public class E extends B{      
    
        }
    
    }
    

      

    内部类主要作用

    • 访问外部类所有成员,包括private成员。
    • 多重继承。
    • 封装类型。
    • 编写事件驱动程序。
    • 回调。

    内部类的分类

     

    • 内部类主要分为成员内部类、局部内部类。
    • 其中,成员内部类根据数据访问权限的不同,又分为静态内部类和非静态内部类。
    • 还有一种称为匿名内部类,匿名内部类在程序运行过程中只使用一次,用完即销毁。
    • 成员内部类和类的属性、方法、构造方法一样,属于类的成员。而局部内部类或者匿名内部类则不属于类的成员。
    • 因此,成员内部类就同其他成员一样,可以采用private、public等修饰,可以被继承。
    • 例如:直接把抽象类Person在Demo类中实现了。
    abstract class Person {
    
        public abstract void read();
    
    }
    
     
    
    public class MyClass {
    
        public static void main(String[] args) {
    
            Person p = new Person() {//匿名内部类
    
                public void read() {
    
                    System.out.println("读书!");
    
                }
    
            };
    
            p.read();
    
        }
    
    }
    

      

    内部类的语法结构

    [访问修饰符]  外部类类名{
    
       ………
    
       [访问修饰符]  内部类类名{
    
          ……
    
    }
    
    ……
    
    }
    

      

    语法示例

    /**外部类A*/
    
    public class A {
    
        private String mess = "这是外部类私有成员变量mess";
    
        /**内部类B*/
    
        class B {
    
            public String getMess() {
    
                return mess;
    
            }
    
        }
    
        private B b = new B();//类A的私有成员变量
    
        /**通过外部类访问内部类获取信息方法*/
    
        public void showMess() {
    
            System.out.println(b.getMess());
    
        }
    
       
    
        /**测试方法*/
    
        public static void main(String[] args){
    
            A a=new A();
    
            a.showMess();
    
        }
    
    }
    

      

    直接访问内部类

      

      /**测试方法*/
    
        public static void main(String[] args){
    
            //A a=new A();
    
            //a.showMess();
    
            A.B in=new A().new B();//如果内部类不希望被外部访问,使用private修饰
    
            System.out.println(in.getMess());
    
        }
    

      

    非静态内部类

    定义

    非静态内部类不采用static修饰。

    特性

    内部类和外部类同名成员变量的访问

    示例代码:

    class Outer {
    
       int number=2000;
    
       public class Inner{
    
           int number=1888;
    
           public int getNum(){
    
               return number;
    
           }
    
       }
    
    }
    
     
    
    public class Test{
    
       public static void main(String[] args){
    
           Outer.Inner in = new Outer().new Inner();
    
           System.out.println(in.getNum());       
    
       }
    
    }
    

      

    在测试类中,输出的number值是多少?---1888

    当内部类的方法访问同名变量时,采用就近原则,先访问最近的一个成员,如果满足条件,就取最近一个成员变量的值,如果不满足,再依次向外部类寻找同名变量。如果找不到,编译器则报错。

    外部类访问内部类成员

    需要显示的实例化非静态内部类,通过对象名访问内部类成员。例如语法示例中的代码:

    /**外部类A*/
    
    public class A {
    
       private String mess = "这是外部类私有成员变量mess";
    
       /**内部类B*/
    
       class B {
    
           public String getMess() {
    
               return mess;
    
           }
    
       }
    
       private B b = new B();//类A的私有成员变量
    
       /**通过外部类访问内部类获取信息方法*/
    
       public void showMess() {
    
           System.out.println(b.getMess());
    
       }
    
      
    
       /**测试方法*/
    
       public static void main(String[] args){
    
           A a=new A();
    
           a.showMess();
    
       }
    
    }
    

      

    非静态内部类不允许有静态成员:包括静态属性、方法、代码块。

    外部类的静态成员不能访问非静态内部类。

    静态内部类(嵌套内部类)

    定义

    静态内部类采用static修饰,他是属于类成员。而非静态内部类则属于类的某个对象。

    语法结构

    class Outer {
    
       public static class Inner{
    
       }
    
    }
    

      

    特性

    静态内部类不能访问外部非静态成员,只能访问外部类的静态成员,即类成员。

    示例代码:

    class Outer {
    
       static int number=1000;
    
       public static class Inner{
    
           static void printNumber(){
    
               System.out.println(number);
    
           }
    
       }
    
    }
    
     
    
    public class Test{
    
       public static void main(String[] args){
    
           Outer.Inner.printNumber();
    
       }
    
    }
    

      

    实例化静态内部类,不需要创建外部类引用。

    示例代码:

    class Outer {
    
       static int number=1000;
    
       public static class Inner{
    
           static void printNumber(){
    
               System.out.println(number);
    
           }
    
       }
    
    }
    
     
    
    public class Test{
    
       public static void main(String[] args){
    
           //Outer.Inner in = new Outer().new Inner();
    
           Outer.Inner in = new Outer.Inner();
    
           in.printNumber();
    
       }
    
    }
    

      

    局部内部类

    定义

    如果将一个类定义在一个方法内部,则称为局部内部类。和局部变量类似,局部内部类是不允许采用访问修饰符的。(局部变量只允许使用final修饰)。

    特性

    局部内部类不能在外部类以外的地方使用。局部内部类的作用域只在方法内部,方法外部无法访问局部内部类。

    示例代码

    class Outer {
    
       int number = 1000;
    
       public void method(){
    
           class LocalInner{  
    
               int count=10;
    
           }      
    
           LocalInner localInner=new LocalInner();
    
           System.out.println("count="+localInner.count);
    
       }
    
    }
    
     
    
    public class Test {
    
       public static void main(String[] args) {
    
           Outer outer = new Outer();
    
           outer.method();
    
       }
    
    }
    

      

    局部内部类可以访问外部类的所有资源

    局部内部类只能访问所在方法final修饰的局部变量(包括方法的形参)

    示例代码:

    class Outer {
    
       int number = 1000;
    
       public void method(){
    
           int money=100;//局部内部类无法访问局部变量
    
           final int score=168;//局部内部类可以访问final修饰的局部变量
    
           class LocalInner{  
    
               int count=10;
    
               public void printNum(){
    
                   System.out.println(number);//局部内部类访问外部类成员
    
                   System.out.println(money); //无法访问,报错
    
                   System.out.println(score);
    
               }
    
           }      
    
           LocalInner localInner=new LocalInner();
    
           System.out.println("count="+localInner.count);
    
           localInner.printNum();
    
       }
    
    }
    
     
    
    public class Test {
    
       public static void main(String[] args) {
    
           Outer outer = new Outer();
    
           outer.method();
    
       }
    
    }
    

      

    匿名内部类

    定义

    匿名内部类是一种特殊的内部类,没有名字。

    例如:

    public class Test {
    
        public static void main(String[] args) {
    
            Test test = new Test(){        
    
            };
    
        }
    
    }
    

      

    在直接“实例化”接口或者抽象类的时候,可以采用匿名类的方式进行实例化。

    特性

    匿名内部类必须继承一个父类或者实现一个接口。当且仅当只能继承一个父类,或者实现一个接口。

    匿名类定义中的示例,匿名类继承了Test类。

    创建完匿名类对象,匿名类自动消失。

    匿名类本身没有构造方法,但他会调用父类的构造方法。

    代码示例:

    public class Test {
    
       public Test(){
    
           System.out.println("这是父类Test的构造方法!");
    
       }
    
      
    
       public void method(){
    
           System.out.println("这是父类普通方法!");
    
       }
    
      
    
       public static void main(String[] args) {
    
           Test test = new Test(){        
    
           };
    
           test.method();
    
    }
    

      

    以上代码将会输出:

     

    匿名类本身没有构造方法,但可以有初始化代码。

    代码示例:

    public class Test {
    
       public Test(){
    
           System.out.println("这是父类Test的构造方法!");
    
       }
    
      
    
       public void method(){
    
           System.out.println("这是父类普通方法!");
    
       }
    
      
    
       public static void main(String[] args) {
    
           Test test = new Test(){
    
               {System.out.println("匿名内部类的初始化代码块!");}
    
           };
    
           test.method();
    
       }
    
    }
    

      

    初始化代码使用{}标识,其初始化的顺序类似继承关系构造方法的执行顺序,输出结果:

     

    成员变量定义成匿名内部类

    除了可以在外部类的方法中定义匿名内部类,还可以在成员变量定义匿名内部类。

    abstract class AC{}
    
    public class Test {
    
       AC ac = new AC(){};//匿名类成员变量 
    
       public static void main(String[] args) {   
    
       }
    
    }
    

      

    封装数据类型

    常见的顶层类只能用public或者默认访问修饰符,而成员内部类可以使用任意访问级别的修饰符。

    如果一个类仅仅只是为一个方法提供服务,那么这个类就可以定义为方法内的局部内部类,只在该方法内可见。

    面向对象核心思想是封装,我们已经学习了属性和方法的封装,内部类也是一种封装方式。

    示例代码:

    interface Tool{}
    
    public class Outer {  
    
       private class InnerTool implements Tool{//实现了工具接口的内部类 
    
       }  
    
       public Tool getTool(){
    
           return new InnerTool();
    
       }
    
    }
    

      

    测试类访问不了private修饰的InnerTool内部类,但是可以通过Outer外部类提供的getTool()方法访问获得InnerTool对象。从而实现数据类型的封装,即把Tool类型封装成InnerTool类型。

    回调(闭包)

    定义

    某个方法获得内部类对象的引用后,就可以在合适的时候反过来调用外部类对象的引用。即,允许测试类通过内部类引用来调用其外部类的方法。

    示例代码:

    /**修改英语成绩接口*/
    
    public interface ReMarkENAble {
    
        /**修改成绩*/
    
        public abstract void modify(int score);
    
    }
    
     
    
    /** 修改语文成绩类 */
    
    public class ReMarkCN {
    
        private int scoreCN;//语文成绩
    
        /**修改语文分数*/
    
        public void modify(int scoreCN) {
    
            this.scoreCN = scoreCN;
    
        }
    
       
    
        public int getScoreCN(){
    
            return this.scoreCN;
    
        }
    
    }
    
     
    

      

    问题:如果一个学生类既想修改语文成绩又想修改英语成绩要如何实现?定义一个学生类,继承ReMarkCN类,实现ReMarkENAble接口,问题是这两个类中modify方法重名,导致学生类只能实现修改语文成绩的方法。

    如何实现学生类既可以修改语文成绩,又可以修改英语成绩?使用内部类回调。

    示例代码:

    /** 学生类 */
    
    public class Student extends ReMarkCN {
    
        private int scoreEN;//英文成绩
    
     
    
        public void outerModify(int scoreEN) {
    
            this.scoreEN = scoreEN;
    
        }
    
     
    
        /**闭包:接口+内部类*/
    
        private class Inner implements ReMarkENAble {
    
            @Override
    
            public void modify(int scoreEN) {
    
                outerModify(scoreEN);
    
            }
    
        }
    
       
    
        /**获得修改英语成绩内部类对象*/
    
        public ReMarkENAble getReMarkENAble(){
    
            return new Inner();
    
        }
    
       
    
        /**打印成绩单*/
    
        public void print(){
    
            System.out.println("*****成绩单*****");
    
            System.out.println("语文成绩:"+this.getScoreCN());
    
            System.out.println("英语成绩:"+this.scoreEN);
    
        }
    
    }
    
     
    
    /**测试类*/
    
    public class Test {
    
        public static void main(String[] args) {
    
            Student student=new Student();
    
            //修改语文成绩
    
            student.modify(60);
    
            //修改英语成绩
    
            ReMarkENAble reMarkENAble=student.getReMarkENAble();
    
            reMarkENAble.modify(61);
    
            //输出成绩
    
            student.print();
    
        }
    
    }
    

      



    本博客文章未经许可,禁止转载和商业用途!

    如有疑问,请联系: 2083967667@qq.com


  • 相关阅读:
    iOS中 H5的input输入框focus()无法自动拉起键盘(解决方法)
    git提交待审核代码,报错没有change-id的解决方法
    Vue proxyTable 解决开发环境的跨域问题
    JSONP原理实现及url传递参数封装
    vue(v-html)和scss的使用问题
    JS获取IOS版本号
    IOS下移除按钮原生样式 -webkit-appearance
    按键排序JavaScript对象
    input输入框限制20个字符,十个汉字
    移动端小坑:用户长按H5文字出现复制
  • 原文地址:https://www.cnblogs.com/rask/p/8253883.html
Copyright © 2020-2023  润新知