学习要点

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

内部类

定义

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();

    }

}

  

JavaSE-13 内部类的更多相关文章

  1. javase(13)_网络编程

    一.概述 1.网络编程的核心是IP.端口(表示应用程序).协议三大元素 2.网络编程的本质是进程间通信 3.网络编程的2个主要问题:1是定位主机,2是数据传输 二.网络通信的概念 1.网络通信协议 计 ...

  2. JavaSE自学笔记

    ch03 [Thu Aug 18 2016 11:22:26 GMT+0800] 对象变量与对象之间是指代关系,对象变量并不能完全说明有无对象可用.这种指代关系是通过赋值运算建立起来的.对象变量保存的 ...

  3. java学习札记

    java学习札记 0x0 学习原因  本来打算大三再去跟着课程去学习java的,但是现在题目越来越偏向java,所以迫于无奈开启了java的学习篇章,同时也正好写个笔记总结下自己学习一门语言的流程. ...

  4. JAVA(一)JAVA基础/面向对象基础/高级面向对象

    成鹏致远 | lcw.cnblog.com |2014-01-23 JAVA基础 1.开发环境搭建 JAVA程序的执行流程 JAVA命令->要使用一个*.class文件(类文件)->通过c ...

  5. 黑马程序员_java基础笔记(03)...面向对象

    ——————————  ASP.Net+Android+IOS开发..Net培训.期待与您交流!—————————— 1:面向对象的概念,2 : 类和对象的关系,3 : 封装,4 : 构造函数,5 : ...

  6. spring-framework-core-ioc Container

    阅读须知 实例化bean xml方式实例化bean 注解方式实例化bean java方式实例化bean ClassPathXmlApplication和AnnotationConfigApplicat ...

  7. 玩好JDK[转]

    ref: https://www.cnblogs.com/zuoxiaolong/p/life53.html java-reference:https://docs.oracle.com/en/jav ...

  8. CMS总结

    过程 初始标记 从roots(例如:thread stack引用的对象,static对象),新生代对象,标记直接引用的老年代对象. 并发标记 利用初始标记阶段标记的对象,递归标记整个老年代. 该阶段与 ...

  9. 为实践javaweb项目,搭建了相应环境

    为实践javaweb项目,搭建了相应环境,现总结一下. JDK与JRE的安装与配置 前提准备: 1.我们下载的JDK安装包里面既包含JDK又包含JRE: 2.要确认你的电脑里面没有JDK和JRE的残留 ...

  10. Java常用的文档地址

    https://docs.oracle.com/en/ https://docs.oracle.com/en/java/javase/13/   specifications--->langua ...

随机推荐

  1. bzoj 3991 寻宝游戏

    题目大意: 一颗树 有一个点的集合 对于每个集合的答案为 从集合内一个点遍历集合内所有点再返回的距离最小值 每次可以选择一个点 若在集合外便加入集合 若在集合内就删除 求每次操作后这个集合的答案 思路 ...

  2. BZOJ1266:上学路线route (最短路+最小割)

    可可和卡卡家住合肥市的东郊,每天上学他们都要转车多次才能到达市区西端的学校.直到有一天他们两人参加了学校的信息学奥林匹克竞赛小组才发现每天上学的乘车路线不一定是最优的. 可可:“很可能我们在上学的路途 ...

  3. 解决axios IE11 Promise对象未定义

    在你的项目中安装polyfill Babel Polyfill 按照官网方法安装并引入即可 http://blog.csdn.net/panyox/article/details/76377248

  4. jQuery笔记之animate中的queue

    队列 队列的执行顺序 queue() dequeue() 输出对象里面的内容 依次出队 不过这样写太麻烦了,因为每次都要输出,所以我们看下面的方法 运用到队列输出的 <!DOCTYPE html ...

  5. Golang bash alias 自动配置GOPATH并运行项目

     BASH代码: source ~/.bash_profile; export GOPATH=$GOPATH:`cd ..; pwd`; echo -e "* GOPATH: $GOPATH ...

  6. python之排序算法-冒泡、选排、快排

    影响内排序算法性能的三个因素: 时间复杂度:即时间性能,高效率的排序算法应该是具有尽可能少的关键字比较次数和记录的移动次数 空间复杂度:主要是执行算法所需要的辅助空间,越少越好. 算法复杂性.主要是指 ...

  7. 通过IDEA制作包含Java应程序的Docker镜像

    IDEA官网在IDEA中把Java App制作成Docker镜像并启动一个容器运行 在idea上使用docker作为java的开发环境[][] ubuntu+docker+docker-compose ...

  8. 数据结构RMQ

    RMQ算法介绍 RMQ算法全称为(Range Minimum/Maximum Query)意思是给你一个长度为n的数组A,求出给定区间的最值的下标.当然我们可以采用枚举,但是我们也可以使用线段树来优化 ...

  9. 窗口Dialog

    Dialog是窗口的意思,它是Window的子类.与frame相比,frame是我们大的窗口,而dialog便是那种弹出来和你说话的对话框. Dialog类的默认布局是BorderLayout Dia ...

  10. Volley的初步了解

    Volley的介绍 Volley是什么? 2013年Google I/O大会上推出的网络请求和图片加载框架 其优点是api简单,性能优秀 非常适合数据量不大但是通信频繁的网络请求,而对于大数据量的操作 ...