java基础(十三)-----详解内部类——Java高级开发必须懂的

简介: java基础(十三)-----详解内部类——Java高级开发必须懂的 目录 为什么要使用内部类 内部类基础 静态内部类 成员内部类 成员内部类的对象创建 继承成员内部类 局部内部类 推荐博客 匿名内部类   正文 可以将一个类的定义放在另一个类的定义内部,这就是内部类。

java基础(十三)-----详解内部类——Java高级开发必须懂的

 

正文

可以将一个类的定义放在另一个类的定义内部,这就是内部类。

为什么要使用内部类

  为什么要使用内部类?在《Think in java》中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

      在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。

复制代码
public interface Father {
 
}
 
public interface Mother {
 
}
 
public class Son implements Father, Mother {
 
}
 
public class Daughter implements Father{
 
    class Mother_ implements Mother{
        
    }
}
复制代码

    其实对于这个实例我们确实是看不出来使用内部类存在何种优点,但是如果Father、Mother不是接口,而是抽象类或者具体类呢?这个时候我们就只能使用内部类才能实现多重继承了

      其实使用内部类最大的优点就在于它能够非常好的解决多重继承的问题,但是如果我们不需要解决多重继承问题,那么我们自然可以使用其他的编码方式,但是使用内部类还能够为我们带来如下特性(摘自《Think in java》):

      1、内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。

      2、在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。

      3、创建内部类对象的时刻并不依赖于外围类对象的创建。

      4、内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。

      5、内部类提供了更好的封装,除了该外围类,其他类都不能访问。

内部类基础

  在这个部分主要介绍内部类如何使用外部类的属性和方法,以及使用.this与.new。

       当我们在创建一个内部类的时候,它无形中就与外围类有了一种联系,依赖于这种联系,它可以无限制地访问外围类的元素。

 

复制代码
public class OuterClass {
    private String name ;
    private int age;
 
    /**省略getter和setter方法**/
    
    public class InnerClass{
        public InnerClass(){
            name = "chenhao";
            age = 23;
        }
        
        public void display(){
            System.out.println("name:" + getName() +"   ;age:" + getAge());
        }
    }
    
    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        OuterClass.InnerClass innerClass = outerClass.new InnerClass();
        innerClass.display();
    }
}
--------------
Output:
name:chenhao   ;age:23
复制代码

       在这个应用程序中,我们可以看到内部了InnerClass可以对外围类OuterClass的属性进行无缝的访问,尽管它是private修饰的。这是因为当我们在创建某个外围类的内部类对象时,此时内部类对象必定会捕获一个指向那个外围类对象的引用,只要我们在访问外围类的成员时,就会用这个引用来选择外围类的成员。

       其实在这个应用程序中我们还看到了如何来引用内部类:引用内部类我们需要指明这个对象的类型:OuterClasName.InnerClassName。同时如果我们需要创建某个内部类对象,必须要利用外部类的对象通过.new来创建内部类:OuterClass.InnerClass innerClass = outerClass.new InnerClass();。

       同时如果我们需要生成对外部类对象的引用,可以使用OuterClassName.this,这样就能够产生一个正确引用外部类的引用了。当然这点实在编译期就知晓了,没有任何运行时的成本。

复制代码
public class OuterClass {
    public void display(){
        System.out.println("OuterClass...");
    }
    
    public class InnerClass{
        public OuterClass getOuterClass(){
            return OuterClass.this;
        }
    }
    
    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        OuterClass.InnerClass innerClass = outerClass.new InnerClass();
        innerClass.getOuterClass().display();
    }
}
-------------
Output:
OuterClass...
复制代码

       到这里了我们需要明确一点,内部类是个编译时的概念,一旦编译成功后,它就与外围类属于两个完全不同的类(当然他们之间还是有联系的)。对于一个名为OuterClass的外围类和一个名为InnerClass的内部类,在编译成功后,会出现这样两个class文件:OuterClass.class和OuterClass$InnerClass.class。

       在Java中内部类主要分为静态内部类、成员内部类、局部内部类、匿名内部类。

静态内部类

静态内部类: 一般也称”静态嵌套类“,在类中用static声明的内部类。

因为是static,所以不依赖于外围类对象实例而独立存在,静态内部类的可以访问外围类中的所有静态成员,包括private的静态成员,但是它不能使用任何外围类的非static成员变量和方法。

同时静态内部类可以说是所有内部类中独立性最高的内部类,其创建对象、继承(实现接口)、扩展子类等使用方式与外围类并没有多大的区别。

复制代码
public class OuterClass {//外围类
    public int aa; //实例成员
    private static float f = 1.5f;//private的静态成员
    static void println() {
        System.out.println("这是静态方法");
    }
    protected static class StaticInnerClass{//protected的静态内部类
        float a;
        public StaticInnerClass() {
             a = f;// 外围类的private静态变量
             println();//外围类的静态方法
        }
    }
}
class OtherClass{
   public static void main(String[] args) {
       //创建静态内部类的对象
    OuterClass.StaticInnerClass staticInnerClass = new OuterClass.StaticInnerClass(); 
   } 
}
复制代码

成员内部类

成员内部类: 定义在类的内部,而且与成员方法、成员变量同级,即也是外围类的成员之一,因此 成员内部类 与 外围类 是紧密关联的。

注意:
这种紧密关联指的是,成员内部类的对象的创建必须依赖于外围类的对象(即没有外围类对象,就不可能创建成员内部类)。因此,成员内部类有以下3个特点:

  • 成员内部类可以访问外围类的所有成员,包括私有成员;

  • 成员内部类是不可以声明静态成员(包括静态变量、静态方法、静态成员类、嵌套接口),但有个例外---可以声明 static final的变量, 这是因为编译器对final类型的特殊处理,是直接将值写入字节码;

  • 成员内部类对象都隐式地保存了一个引用,指向创建它的外部类对象;或者说,成员内部类的入口是由外围类的对象保持着(静态内部类的入口,则直接由外围类保持着)

成员内部类中的 this,new关键字:

    • 获取外部类对象:OuterClass.this

    • 明确指定使用外部类的成员(当内部类与外部类的名字冲突时):
      OuterClass.this.成员名

    • 创建内部类对象的new:外围类对象.new

//先创建外围类对象
OuterClass outer=new OuterClass();
//创建成员内部类对象
OuterClass.InnerClass inner=outer.new InnerClass();

成员内部类的对象创建

  我们知道,成员内部类就像外围类的实例成员一样,一定要存在对象才能访问,即成员内部类必须绑定一个外围类的对象。上面已经介绍了成员内部类的创建格式了,我们直接看一个例子

复制代码
public class OuterClass {//外围类
    public int aa; //实例成员
    private static float f = 1.5f;//private的静态成员
    public void initInnerClass() {
        System.out.println("内部类的初始化方法");
    }
    public void createInnerClass() {//
        //外围类的成员方法中创建成员内部类对象
        InnerClass innerClass = new InnerClass();
    }
    class InnerClass{//成员内部类
        private double aa; //与围类的变量aa的名字重复
        public InnerClass(){
            this.aa = OuterClass.this.aa + f;//明确指定两个aa的所属
            initInnerClass();
        }
    }   
}
//其他类
class OtherClass{
    public static void main(String[] args) {
        //其他类中创建成员内部类
        OuterClass oc = new OuterClass();//外部类对象
        //创建内部类对象
        OuterClass.InnerClass innerClass = oc.new InnerClass();
    }
}
复制代码

补充几点:

  • 成员内部类可以继续包含成员内部类,而且不管一个内部类被嵌套了多少层,它都能透明地访问它的所有外部类所有成员;

  • 成员内部可以继续嵌套多层的成员内部类,但无法嵌套静态内部类;静态内部类则都可以继续嵌套这两种内部类。

复制代码
class InnerClass{//成员内部类
    private double aa; //与围类的变量aa的名字重复
    public InnerClass(){
        this.aa = OuterClass.this.aa + f;//明确指定两个aa的所属
        initInnerClass();
    }
    public  class InnerInnerCalss2{//成员内部类中的成员内部类
        protected double aa = OuterClass.this.aa;//最外层的外围类的成员变量
    }//InnerInnerCalss2
}//InnerClass
复制代码

继承成员内部类

在内部类的访问权限允许的情况下,成员内部类也是可以被继承的。由于成员内部类的对象依赖于外围类的对象,或者说,成员内部类的构造器入口由外围类的对象把持着。因此,继承了成员内部类的子类必须要与一个外围类对象关联起来。同时,子类的构造器是必须要调用父类的构造器方法,所以也只能通过父类的外围类对象来调用父类构造器。

下面的例子也是基于上面的例子的,只贴出多出的部分代码。

复制代码
class ChildClass extends OuterClass.InnerClass{
    //成员内部类的子类的构造器的格式
    public ChildClass(OuterClass outerClass) {
        outerClass.super();//通过外围类的对象调用父类的构造方法
    }
}
复制代码

局部内部类

局部内部类: 就是在方法、构造器、初始化块中声明的类,在结构上类似于一个局部变量。因此局部内部类是不能使用访问修饰符

局部内部类的两个访问限制

  • 对于局部变量,局部内部类只能访问final的局部变量。不过,后期JDK(忘了是JDK几了)局部变量可不用final修饰,也可以被局部内部类访问,但你必须时刻记住此局部变量已经是final了,不能再改变。

  • 对于类的全局成员,局部内部类定义在实例环境中(构造器、对象成员方法、实例初始化块),则可以访问外围类的所有成员;但如果内部类定义在静态环境中(静态初始化块、静态方法),则只能访问外围类的静态成员。

复制代码
public class OuterClass {
    private int a = 21;
    static {//静态域中的局部内部类
       class LocalClass1{
              //  int z = a; //错误,在静态的作用域中无法访问对象成员
       }
    }
    {//实例初始化块中的局部内部类
        class localClass2{          
        }
    }
    public OuterClass(){
        int x = 2;
        final int y = 3;
        // x = 3;//若放开此行注释,编译无法通过,因为局部变量x已经是final类型
        //构造器中的局部内部类
        class localClass3{
            int z = y; //可以访问final的局部变量
            int b = a;//可以访问类的所有成员
             //访问没有用final修饰的局部变量
            int c = x;
        }
    }
    public void createRunnable() {
        final int x = 4;
        //方法中的局部内部类
        class LocalClass4 implements Runnable {//
            @Override
            public void run() {
                System.out.println("局部final变量:"+x);
                System.out.println("对象成员变量:"+a);
            }
        }
    }
}
复制代码

推荐博客

  程序员写代码之外,如何再赚一份工资?

匿名内部类

匿名内部类: 与局部内部类很相似,只不过匿名内部类是一个没有给定名字的内部类,在创建这个匿名内部类后,便会立即用来创建并返回此内部类的一个对象引用。

作用:匿名内部类用于隐式继承某个类(重写里面的方法或实现抽象方法)或者实现某个接口。

匿名内部类的访问限制: 与局部内部类一样,请参考局部内部类;

匿名内部类的优缺点:
优点: 编码方便快捷;
缺点:

  • 只能继承一个类或实现一个接口,不能再继承其他类或其他接口。

  • 只能用于创建一次对象实例;

下面的例子是我们创建线程时经常用到的匿名内部类的方式来快速地创建一个对象的例子:

复制代码
class MyOuterClass {
    private int x = 5;
    void createThread() {
        final int a = 10;
        int b = 189;
        // 匿名内部类继承Thread类,并重写Run方法
        Thread thread = new Thread("thread-1") {
            int c = x;  //访问成员变量
            int d = a;  //final的局部变量
            int e = b; //访问没有用final修饰的局部变量
            @Override
            public void run() {
                System.out.println("这是线程thread-1");
            }
        };
        // 匿名内部类实现Runnable接口
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("线程运行中");
            }
        };
    }
}
复制代码
原文地址 https://www.cnblogs.com/java-chen-hao/p/10412677.html
相关文章
|
4天前
|
IDE Java 数据库连接
使用 Java 进行桌面应用开发
【4月更文挑战第19天】Java 是一款广泛应用于企业级、网络和桌面应用开发的编程语言。其跨平台特性使Java程序能在不同操作系统上运行,而JDK提供了开发所需工具和库。使用Swing等GUI库构建用户界面,结合JDBC进行数据库操作,Socket实现网络通信。虽然面临性能和用户体验的挑战,但通过优化和选用合适的IDE,Java仍能开发出高效稳定的桌面应用。
|
5天前
|
前端开发 Java Go
开发语言详解(python、java、Go(Golong)。。。。)
开发语言详解(python、java、Go(Golong)。。。。)
|
6天前
|
人工智能 前端开发 Java
Java语言开发的AI智慧导诊系统源码springboot+redis 3D互联网智导诊系统源码
智慧导诊解决盲目就诊问题,减轻分诊工作压力。降低挂错号比例,优化就诊流程,有效提高线上线下医疗机构接诊效率。可通过人体画像选择症状部位,了解对应病症信息和推荐就医科室。
48 10
|
6天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
8天前
|
存储 Java
Java基础教程(7)-Java中的面向对象和类
【4月更文挑战第7天】Java是面向对象编程(OOP)语言,强调将事务抽象成对象。面向对象与面向过程的区别在于,前者通过对象间的交互解决问题,后者按步骤顺序执行。类是对象的模板,对象是类的实例。创建类使用`class`关键字,对象通过`new`运算符动态分配内存。方法包括构造函数和一般方法,构造函数用于对象初始化,一般方法处理逻辑。方法可以有0个或多个参数,可变参数用`类型...`定义。`this`关键字用于访问当前对象的属性。
|
9天前
|
Java 索引
Java基础教程(6)-Java中的流程控制语句
【4月更文挑战第6天】Java流程控制包括选择(if, switch)、重复(while, do-while, for)和跳转(break, continue, return)语句。选择语句根据条件执行不同路径,if和switch用于单条件和多条件分支。重复语句用于循环,如for循环的初始化、条件和迭代部分,以及while和do-while循环。跳转语句中,break用于立即退出循环,continue结束当前循环迭代,return则从方法中返回。此外,Java的for each循环简化了数组或集合的遍历,但不能控制遍历顺序或索引。
|
11天前
|
存储 Java 编译器
Java基础教程(4)-Java中的操作符
【4月更文挑战第4天】Java中的String是常用类,字符串是不可变对象,用双引号表示。String对象在编译期长度受限于65535,运行期不超过Int范围。字符串方法如length()、substring()、replace()、equals()等提供了多种操作。可变字符串可使用StringBuffer或StringBuilder。String对象通过字符串池优化内存,池中已有相同内容字符串则返回其引用。
|
11天前
|
安全 Java 编译器
接口之美,内部之妙:深入解析Java的接口与内部类
接口之美,内部之妙:深入解析Java的接口与内部类
34 0
接口之美,内部之妙:深入解析Java的接口与内部类
|
13天前
|
运维 NoSQL 算法
Java开发-深入理解Redis Cluster的工作原理
综上所述,Redis Cluster通过数据分片、节点发现、主从复制、数据迁移、故障检测和客户端路由等机制,实现了一个分布式的、高可用的Redis解决方案。它允许数据分布在多个节点上,提供了自动故障转移和读写分离的功能,适用于需要大规模、高性能、高可用性的应用场景。
16 0
|
13天前
|
Java API
Java基础—笔记—内部类、枚举、泛型篇
本文介绍了Java编程中的内部类、枚举和泛型概念。匿名内部类用于简化类的创建,常作为方法参数,其原理是生成一个隐含的子类。枚举用于表示有限的固定数量的值,常用于系统配置或switch语句中。泛型则用来在编译时增强类型安全性,接收特定数据类型,包括泛型类、泛型接口和泛型方法。
9 0