Java-内部类

news2024/11/16 9:19:53

内部类的概念

认识内部类

一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称之为内部类inner class,嵌套该内部类的类称为外部类。就像双重for循环,外部for循环里面包含着另一个内层循环。内部类是类的第5大成员,[属性,方法,构造器,代码块,内部类]
内部类的最大特点就是可以直接访问外部类的私有属性,且可以体现类与类之间的包含关系

基本语法

成员内部类的基本语法演示:
class Outer{//外部类
class inner{//内部类
}
}
class other{}//其他类
代码演示:

public class Outer {//外部类
    private int x = 21;
    class inner{//内部类
        public void innerMethod(){
            System.out.println(x);//直接调用外部类的私有属性
        }
    }
}
class other{//其他类
}

内部类的分类

内部类分为4种:分别在局部位置和成员位置,根据不同的修饰或命名有不同的叫法

  • 定义在外部类的局部位置上 (比如定义在方法体内或者代码块内)
  1. 局部内部类(有类名的)
  2. 匿名内部类(没类名的)
  • 定义在外部类的成员位置上 (和构造方法代码块同一个水平位置上的)
  1. 成员内部类(没static修饰)
  2. 静态内部类(使用static修饰的)

局部内部类

  • 局部内部类是定义在外部类的局部位置的类,比如外部类的方法体中,或者代码块中。且有类名
public class other {//其他类
}
class outer{//外部类
    public void m1(){
        class inner{//局部内部类     
        }
    }
}

局部内部类的特点

  1. 局部内部类可以直接访问外部类的所有成员,包含私有的
  2. 不能添加访问修饰符,因为它相当于一个局部变量。局部变量是不能使用修饰符的,但是可以使用final修饰,防止别人继承它。
  3. 局部内部类的作用域仅仅在定义它的方法或者代码中。
  4. 局部内部类要访问外部类的成员,可以直接访问。不用带前缀
  5. 外部类访问局部内部类的成员,需要创建局部内部类的实例对象。且只能在局部内部类的作用域内访问
  6. 其他类不能直接访问或者创建局部内部类的对象访问局部内部类的成员。
  7. 如果外部类的成员和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用 外部类名.this.成员ming 去访问。this代表谁调用就指向谁,类名顾名思义

特点演示:

public class other {//其他类

    public static void main(String[] args) {
        new outer().m1();
    }
}
class outer{//外部类
    private int num = 21;
    public void m1(){
        final class inner{//局部内部类-2.无法访问修饰符,只能修饰final
            private int num = 888;
            public void m2inner(){
                System.out.println(num);//1.局部内部类直接方法外部类的成员.4。如左
                System.out.println("局部内部类的num"+num+"外部类的num"+outer.this.num);//7.外部类和内部类成员重名怎么区分调用
            }
            public int a = 1;
        }
        //5.外部类访问内部局部类的成员,需要创建内部局部类实例化对象。且需在作用域之内
        new inner().m2inner();
    }
    //3,无法在局部内部类的作用域范围外创建
    //new inner();
}

匿名内部类

匿名内部类本质就是基于接口或者其他类,实现或者继承,然后创建一个新类,且实例化返回一个对象

匿名内部类存在的位置和局部内部类相同,都在外部类的方法中或者代码块中。

匿名内部类的特点

  1. 本质上还是个类
  2. 也是一个内部类
  3. 匿名内部类没有名字,但是编译器会给一个名字,但是不重要
  4. 匿名内部类只能使用一次
  5. 匿名内部类实例化的对象可以重复使用

匿名内部类的语法

new 类或者接口(参数列表){
类体
};

举例说明及引出
在学习接口后,了解了各个实现了同一个接口的类,可以重写接口的方法。
比如现在有一个接口IA 他有一个抽象方法cry,老虎类实现了这个接口
外部类的m1方法中需要调用老虎类的cry方法
在未学习匿名内部类前应该是这样实现的

class outer{//外部类
    public void m1(){
        new tiger().cry();
    } 
}
interface IA{
    void cry();
}
class tiger implements IA{
    @Override
    public void cry() {
        System.out.println("老虎嗷嗷叫...");
    }
}

但是因为就调用一次,但是还得去创建一个新类。有点浪费内存,所以可以使用匿名内部类实现。

语法解释:

public void m1(){
        IA tiger = new IA() {
            @Override
            public void cry() {
                System.out.println("老虎嗷嗷叫");
            }
        };
        tiger.cry();
    }

这一段就是在外部类的方法中使用了匿名内部类,在这一段代码中做了这么几件事:

  1. 底层创建了一个类,且实现了IA接口,只不过这个类,不同于正常类,有点虚幻

class 匿名 implement IA{}

  1. 在{}中的是这个类的类体,这里跟其他类一样,这个类在类体中实现了IA接口的cry方法
  2. 以此类为模板,实例化了一个对象。并且将此对象的地址给到 tiger

根据之前学到的知识,等号左边是编译类型,等号右边是运行类型。
根据上面的代码可以看到 编译类似是IA接口类型,但是运行类型却不能直接认为是IA接口。运行类型是底层创建的匿名内部类
因为这个匿名内部类,没有我们定义的类名。所以它只能使用一次,而我们将他实例化的对象地址给到了tiger。后面也无法再实例化一个对象了。所以说是匿名内部类只能使用一次,但是它已经实例化的对象,我们可以通过它的引用tiger,反复调用。
此致就解决了只需要使用一次还要创建一个类的问题。

完整代码:

public class other {//其他类
    public static void main(String[] args) {
        new outer().m1();
    }
}
class outer{//外部类
    public void m1(){
        IA tiger = new IA() {//匿名内部类
            @Override
            public void cry() {
                System.out.println("老虎嗷嗷叫");
            }
        };
        tiger.cry();
        tiger.cry();
        tiger.cry();
    }
}
interface IA{
    void cry();
}

上面是基于接口举例的匿名内部类,下面用基于普通类的案例演示下
基于接口的匿名类,可以看作是匿名内部类实现了接口。那么基于普通类,可以看作是匿名内部类继承于普通类extends

public class other {//其他类
    public static void main(String[] args) {
        new outer().m1();
    }
}
class outer{//外部类
    public void m1(){
        Father father = new Father(){//匿名内部类
            
        };
    }
}
class Father{
    public Father() {
        System.out.println("父类的构造器被调用");
    }
}

通过运行后输出-父类的构造器被调用,这说明匿名内部类确实会和子类一样调用父类的构造器的。

匿名内部类的使用注意点

  1. 匿名内部类的语法看起来比较复杂,但是本质上其实就是一个类的定义且返回一个对象,也就是说匿名内部类的语法是定义类和返回一个对象。这两者是一体的
    演示:类定义理解比较简单,返回一个对象可以这样理解,不管匿名内部类的语法多长多复杂,直接看作是一个临时对象即可。直接调用需要的东西
class outer{//外部类
    public void m1(){
        new Father(){
        }.hi();//因为是定义和返回对象一体的,所以可以直接看作是临时对象,直接调用
    }
}
class Father{
    public Father() {
        System.out.println("父类的构造器被调用");
    }
    public void hi(){
        System.out.println("hi,");
    }
}

其他特点和注意事项与局部内部类差不多

  1. 不能添加访问修饰符,因为它相当于一个局部变量
  2. 可以直接访问外部类的成员
  3. 作用域只在定义它的方法或代码块中‘
  4. 外部类其他类不能访问匿名内部类
  5. 外部类和匿名内部类的成员重名时,默认是就近原则。想访问外部类的成员可以 类名.this.成员

匿名内部类的实践

第一题

  • 将匿名内部类 作为参数使用(因为匿名内部类即是类也是一个对象)
class outer{//外部类
    public void test(IA ia){
        ia.print();
    }
    public void m1(){
        test(new IA() {//传入了一个匿名内部类
            @Override
            public void print() {
                System.out.println("匿名内部类的print");
            }
        });
    }
}
interface IA{
    void print();
}

只是看起来理解性不太好,但是解决了只使用一次还要创建类的麻烦。
在这里插入图片描述
把这一部分看作是一个整体的参数就行

第二题

  • 有一个铃声接口Bell 它有ring方法。
  • 有一个手机类CallPhone,它有闹钟功能 AlarmClock方法,参数是Bell
  • 测试手机类的闹钟功能,通过匿名内部类作为参数打印“快点起床”
  • 再传入另一个匿名内部类打印“再不起床,扣积分”

比较简单,直接上代码

public class testinners2 {
    public static void main(String[] args) {
    new CallPhone().AlarmClock(new Bell() {//临时对象调用ALarm方法,传入匿名对象(实现了Bell接口)
        @Override
        public void ring() {
            System.out.println("快点起床");
        }
    });
    new CallPhone().AlarmClock(new Bell() {
        @Override
        public void ring() {
            System.out.println("再不起床,扣积分");
        }
    });
    }
}
interface Bell{
    void ring();
}
class CallPhone{
    public void AlarmClock(Bell bell){
        bell.ring();
    }
}

成员内部类

成员内部类定义在外部类的成员位置,和属性 方法 构造器是同级别,且没有static修饰

  1. 成员内部类和其他内部类一样可以直接访问外部类的所有成员。
  2. 因为成员内部类也是外部类的一个成员,跟属性方法一样,所以成员内部类可以添加任意访问修饰符
  3. 成员内部类的作用域和其他外部类的成员一样,是外部类的整个类体
  4. 外部类访问内部类,需要创建对象再访问
  5. 外部类和成员内部类成员重名,和前面一样,默认就近原则。区分直接外部类名.this.成员名
  6. 外部类其他类访问成员内部类,有两种方法

成员内部类演示:

public class other {//外部其他类

    public static void main(String[] args) {
        //外部其他类访问成员内部类 方法一:直接创建
        outer1 ou2 = new outer1();
        outer1.Inner2 inner3 = ou2.new Inner2();
        inner3.innerme();
        //外部其他类访问成员内部类 方法二:使用外部类返回成员内部类的对象
        outer1.Inner2 inner4 = ou2.getinner2();
        inner4.innerme();
    }
}
class outer1{
    private int num = 90;
    public class Inner2{//成员内部类
        public void innerme(){
            System.out.println(num);//直接使用外部类的成员
        }
    }
    //外部类访问成员内部类
    public Inner2 getinner2(){
        Inner2 inner2 = new Inner2();
        inner2.innerme();
        return inner2;
    }
}

注意点:外部类其他类访问成员内部类的方法:
第一种:先创建外部类的对象 outer1 ou2 = new outer1();
然后通过类名.成员内部类名作为编译类型 且使用外部类的对象去创建成员内部类的对象 outer1.Inner2 inner3 = ou2.new Inner2();
第二种:就是外部类直接将创建成员内部类封装在方法里,返回创建好的对象,给个引用即可。

静态内部类

静态内部类也是定义在外部类的成员位置上,但是与成员内部类的区别就是静态内部类加了static修饰

  1. 静态内部类,因为是静态的所以要遵守静态只能直接访问静态的原则。它只能直接访问外部类的所有静态成员
  2. 同理静态内部类也是个成员,可以添加访问修饰符
  3. 作用域也是整个外部类体
  4. 外部类访问静态内部类,创建对象再访问
  5. 外部其他类访问静态内部类,有两种访问方式{直接创建和封装方法创建}
  6. 静态内部类和外部类成员重名时,默认就近原则,要区分可以使用外部类.属性名。因为只能调用静态所以可以直接类名.不用加this

静态内部类演示:

public class other {//外部其他类
    public static void main(String[] args) {
        //外部其他类访问静态内部类  第一种:直接创建对象
        outer1.Inner5 inner88 = new outer1.Inner5();
        inner88.print1();
        //外部其他类访问静态内部类  第二种:使用外部类封装返回的对象
        outer1.Inner5 inner99 = outer1.getInner5();
        inner99.print1();
    }
}
class outer1{
    private static int num = 90;
    public static class Inner5{//静态内部类
        private static int num = 88;
        public void print1(){
            System.out.println(num +" "+outer1.num);//直接调用和重名问题
        }
    }
    public void m1(){
        Inner5 inner5 = new Inner5();
        inner5.print1();
    }
    public static Inner5 getInner5(){
        return new Inner5();
    }
}

注意点:静态内部类和成员内部类在外部其他类的调用语法稍有不同,且都要注意修饰符范围
静态内部类也是静态,需要遵守静态只能直接调用静态的规定。

重点:匿名内部类要理解透彻,它既是一个类的定义同时也是一个对象。

内部类练习

  1. 下列代码会输出什么?
public class Test1 {
    public Test1(){
        inner s1 = new inner();
        s1.a = 10;
        inner s2 = new inner();
        System.out.println(s2.a);
    }
    public static class inner{
        public int a = 5;
    }

    public static void main(String[] args) {
        Test1 t1 = new Test1();
        inner s3 = new inner();
        System.out.println(s3.a);
    }
}

5
5

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/22991.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

让我们进入面向对象的世界(一)

让我们进入面向对象的世界 文章目录让我们进入面向对象的世界开场白一.面向对象概述二.认识对象和类2.1什么是类2.2 什么是对象呢?2.3 让我们来创建第一个对象2.3 让我们进一步了解,我们针对对象的操作,是怎样的开场白 大家好!&a…

SpringMVC请求、响应与异步请求

文章目录SpringMVC核心架构的具体流程步骤一、SpringMVC请求与响应1、SpringMVC Handle原理与应用1.1 概念1.2 Spring MVC Handler的三种写法2、SpringMVC 视图解析器2.1 概念2.2 配置视图解析器二、SpringMVC异步请求1、 Ajax基本配置2、 异步与同步优缺点:如何设置…

基于TMI8421的3D打印机步进电机解决方案

打印机一直是工作中不可缺少的一部分,当下,随着3D打印技术的推广,3D打印机逐渐进入我们的生活与工作当中。每个人都期望可以在办公室环境下安静快速的打印,更高效地完成每项打印工作;更生动逼真的重现理想的3D模型。而…

实战:如何优雅地扩展Log4j配置?

前言 Log4j 日志框架我们经常会使用到,最近,我就遇到了一个与日志配置相关的问题。简单来说,就是在原来日志配置的基础上,指定类的日志打印到指定的日志文件中。 这样讲述可能不是那么好理解,且听我从需求来源讲起。…

【10】leetcode note

题目: 799. 香槟塔 个人总结 799. 香槟 我们把玻璃杯摆成金字塔的形状,其中 第一层 有 1 个玻璃杯, 第二层 有 2 个,依次类推到第 100 层,每个玻璃杯 (250ml) 将盛有香槟。 从顶层的第一个玻璃杯开始倾倒一些香槟&…

基于铁犀牛ironrhino平台的税务档案管理系统

目录 摘要 2 引言 5 1.1 选题背景 6 1.2 国内外研究现状 7 1.3课题研究的重要性 8 2. 系统的需求与分析 8 2.1 系统的功能介绍 9 2.1.1 业务信息描述 9 2.1.2 系统功能说明 10 2.1.3 系统的开发目标 11 2.2 系统分析 12 2.2.1 铁犀牛的功能 12 2.2.2 铁犀牛工作原理 13 2.2.3 铁…

翻阅必备----Java窗口组件,容器,布局,监听,事件 API大全

---------------------------------------------------------------------------------------- 👨‍💻个人主页:元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 收录于专栏 java ⭐Jav…

将存在课题的过程可视化,丰田的“自工程完结”是什么?

将存在课题的过程可视化,丰田的“自工程完结”是什么? “全日本的公司是不是都发生了奇怪的事情呢?人们常说日本人很勤劳。所以要拼命努力。但是,有很多人拼命努力却毫无成果。(中略)这样才不会有动力。明明很努力却没有成果,我认为这才是奇…

将SpringBOOT项目 打成 war 包 并 部署到 Tomcat

当前环境&#xff1a;Windows Tomcat版本&#xff1a;tomcat8.5 SpringBoot版本&#xff1a; 2.2.3 1. pom.xml 修改打包方式 <packaging>war</packaging> 2.加入SpringBoot打包插件&#xff08;pom.xml&#xff09; <build><plugins><plugin&g…

Jmeter的使用说明

一、安装Jmeter工具 链接&#xff1a;https://pan.baidu.com/s/1ZYc15eq9DO-r0ChKHxMXlg?pwdckcd 提取码&#xff1a;ckcd --来自百度网盘超级会员V5的分享二、Jmeter的常用元器件说明 jmeter八大元件件&#xff1a;取样器&#xff0c;前置处理器&#xff0c;后置处理器&a…

计算机网络——第六章笔记(1)

传输层 传输层是层是整个协议栈(TCP/IP)的核心。 任务&#xff1a;是提供可靠的、高效的数据传输。 面向连接的服务 1、端到端的连接管理 建立连接 数据传输 释放连接 2、流控制 3、差错控制 传输环境&#xff1a;通信子网、物理信道。 传输服务和网络服务的两个主…

网络了解编程五层协议

一:了解 1.了解一下网络: 局域网(LAN),一个上课的机房,多个连在同一个路由器上的设备,就是在一个局域网中---打游戏 (局域网内的主机之间能方便的进行网络通信&#xff0c;又称为内网&#xff1b;局域网和局域网之间在没有连接的情况下&#xff0c;是无法通信的) 广域网(WAN) ,…

无线 LAN 服务概述

无线 LAN 服务是 Windows Server 2008 R2 和 Windows Server 2008 中的一项功能&#xff0c;可用于启用无线 WLAN 自动配置服务&#xff0c;以及配置 WLAN 自动配置以自动启动。一旦启用后&#xff0c;WLAN 自动配置会动态选择计算机将自动连接的无线网络&#xff0c;并配置无线…

项目管理的四大模型,PM必懂的事半功倍模型!

瀑布模型、迭代模型、增量模型、原型模型&#xff0c;是项目管理常见的四种模型。每种模型都有其优缺点和适用的项目类型。项目经理针对不同的项目用对模型&#xff0c;才能起到事半功倍的作用。 今天就讲讲这四种模型及其优缺点&#xff1a; 如果你需要项目管理相关资料可拉…

代码质量与安全 | “吃狗粮”能够影响到代码质量?来了解一下!

“dogfooding”是什么&#xff1f;乍一看&#xff0c;这就是“吃狗粮”的意思&#xff0c;但其实这来源于一句俚语&#xff1a;“Eat your own dog food”&#xff0c;直译过来就是“吃自己的狗粮”&#xff0c;常用于描述公司使用自己产品的这一种情况。 “吃自己的狗粮”实践…

(更新中)【后端入门到入土!】Java+Servlet+JDBC+SSM+SpringBoot+SpringCloud 基础入门

目录 第一部分&#xff1a;Java 基础语法&#xff08;已完结&#xff09; 第二部分&#xff1a;Java 高级&#xff08;已完结&#xff09; 第三部分&#xff1a;Servlet&#xff08;待更新……&#xff09; 第四部分&#xff1a;JDBC&#xff08;待更新……&#xff09; 第…

如何搭建一套指标体系?

一、引言 (1)为什么指标体系这么重要? (2)什么是指标体系? (3)指标体系的衡量标准是什么? (4)如何去搭建一套好好的指标体系? 只要弄清楚了这4个问题,指标体系的搭建工作就迅速地开展、快速地落地,精准地产生业务价值。以上是对于数据同学而言的工作。此外,对于…

漏洞练习环境搭建笔记

Docker 安装&#xff08;ubuntu&#xff09; 1.常归删除操作 sudo apt-get autoremove docker docker-ce docker-engine docker.io containerd runc 2. 删除docker其他没有没有卸载 dpkg -l | grep docker dpkg -l |grep ^rc|awk ‘{print $2}’ |sudo xargs dpkg -P # 删除无…

雷神MixBook Air笔记本系统故障怎么重装?

雷神MixBook Air笔记本系统故障怎么重装&#xff1f;有用户使用的雷神MixBook Air笔记本系统出现了故障&#xff0c;导致无法正常的使用电脑了。这个情况我们可以使用U盘来重装一个系统&#xff0c;恢复正常的使用。那么具体要怎么去进行操作&#xff0c;看看具体的方法吧。 准…

python多维数组切片

1、数组a第0个元素&#xff08;二维数组&#xff09;下的所有子元素&#xff08;一维数组&#xff09;的第一列 import numpy as np bnp.arange(24) ab.reshape(2,3,4) print a print a[0,:,0] 2、取所有二维数组下的每个二维数组的第0个元素&#xff08;一维数组&#xff09; …