内部类 --- (寄生的哲学)

内部类总共有 4 种(静态内部类、非静态内部类、局部内部类、匿名内部类)

作用:

一:内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。

二:内部类可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以互相访问,但是外部类不能访问内部类的实现细节,例如内部类的成员变量。

三:匿名内部类适合用于创建那些只需要创建一次使用的类。

四:内部类比外部类可以多使用三个修饰符:private、protected、static

五:非静态内部类不能拥有静态成员。

1.非静态内部类:

非静态内部类定义在外部类里面

public class Demo {private String name;private Integer age;//定义构造器重载public Demo(){}public Demo(String name, Integer age) {this.name = name;this.age = age;}//定义一个非静态内部类private class InnerDemo {//非静态内部类private double length;private String color;public InnerDemo(){}public InnerDemo(double length, String color) {this.length = length;this.color = color;}// 此处省略 length  的getter 和 setter 方法.....................//非静态内部类实例方法public void info() {System.out.print("名字是:" + name + ": " + "年龄是: " + age +"肤色是: " + color + "身高是: " + length);}}public void test() {var innerDemo = new InnerDemo(180, "黄");innerDemo.info();}public static void main(String[] args) {Demo demo = new Demo("李白", 30);demo.test();}}

当非静态成员内部类的方法访问某个变量的时候,系统优先在该方法内查找是否存在该名字的局部变量,如果存在就使用该变量;如果不存在则查找该方法所在的内部类中是否存在该名字的成员变量, 如果不存在,则到外部类中查找是否存在该名字的成员变量,如果依然不存在,则会报编译错误:提示找不到该变量。

因此,如果外部类成员变量、内部类成员变量与内部类里面的方法同名,则可以通过使用 this、外部类类名.this 作为限定区分。

可以参考如下模板:

public class Demo {private String name = "外部类的成员变量";private class InClass {private String name = "内部类的成员变量";public void info(){var name = "局部变量";System.out.print("外部类的实例变量值:" + Demo.this.name);//通过 this.varName 访问内部类的成员变量System.out.print("内部类的成员变量:" + this.name);//直接访问局部变量System.out.print("局部变量的值:" + name);}}public void test() {var inClass = new InClass();inClass.info();}public static void main(String[] args) {Demo demo = new Demo();demo.test();}}

2. 静态内部类

使用 static 修饰的内部类被称为静态内部类,它属于外部类本身,不属于外部类某个实例对象。

静态内部类可以包含静态成员,也可以包含非静态成员。根据静态成员不能访问非静态成员的规则,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。即使是静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员。

public class Demo {private String name = "李白";private static int age = 2;static class StaticInnerClass {//静态内部类可以包含静态成员private static String womanName = "李清照";public void info() {//下面代码出现错误,静态内部类不能访问外部类的实例变量System.out.print(name);//下面代码正常System.out.print(age);}}}

静态内部类是外部类的一个成员,因此外部类的所有方法、所有初始化块中可以使用静态内部类来定义变量、创建对象等

public class Demo {static class StaticInnerClass {private static int age = 10;private String name = "李白";}public void info() {//System.out.print(age)//上面代码出现错误,应改为如下格式//通过类名访问静态内部类的类成员System.out.print(StaticInnerClass.age);//System.out.print(name);//上面代码出现错误,应改为如下格式//通过实例访问静态内部类的实例成员System.out.print(new StaticInnerClass().name);}}

Java 还允许在接口里定义内部类,接口里定义的内部类默认使用 public static 修饰,也就是说接口内部类只能是静态内部类。

3.使用内部类:

1.在外部类内部使用内部类

在外部类内部使用内部类的时,与平常使用普通类没有太大的区别。一样可以直接通过内部类类名来定义变量,通过 new 调用内部类构造器来创建实例。

唯一的区别是:不要在外部类的静态成员(包括静态方法和静态初始化)中去使用非静态内部类,因为静态成员不能访问非静态成员。

在外部类内部定义内部类的子类与平常定义子类也没有太大的区别。

2.在外部类外部使用非静态内部类

如果想在外部类外部使用非静态内部类,则内部类不能使用 private 访问控制权限,private 修饰的内部类只能在外部类内部使用。对于使用其他访问控制符修饰的内部类,则能在访问控制符对应的访问权限内使用。

由于非静态内部类的对象必须寄生在外部类的对象里,因此创建非静态内部类对象之前,必须先创建其外部类对象。在外部类外面创建内部类实例的语法如下:

outerInstance.new InnerContructor();

当创建一个子类的时候,子类构造器总会调用父类的构造器,因此在创建非静态内部类的子类时,必须保证让子类构造器可以调用非静态内部类的构造器,调用非静态内部类的构造器时,必须存在一个外部类对象

class Out  {class Inner {public Inner(String name) {System.out.print(name);}}
}public class Demo extends Out.Inner {//显式定义构造器public Demo(Out out) {out.super("Hello")}}

非静态内部类的子类不一定是内部类,它也可以是外部类。但非静态内部类的子类实例一样需要保留一个引用,该引用指向其父类所在外部类的对象。也就是说,如果有一个内部类子类的对象存在,则一定存在与之对应的外部类对象。

3.在外部类以外使用静态内部类。

因为静态内部类是外部类类相关的,因此创建静态内部类对象时无需创建外部类对象。创建语法如下:

new OutClass.InnerClass();

相比之下,使用静态内部类比使用非静态内部类要简单很多,只要把外部类当成静态内部类的包空间即可。因此当程序需要使用内部类时,应该优先考虑使用静态内部类。

4.局部内部类

把内部类定义在方法里面,这个就是局部内部类。局部内部类因为只能在方法里面有效,不能在外部类方法以外使用。所以不能使用访问控制符和 static 修饰符修饰。

局部内部类是一份非常鸡肋的语法,在实际开发中很少定义局部内部类,这是因为局部内部类的作用域太小了;只能在当前方法中使用。大部分时候,定义一个类之后,当然希望多次复用这个类,但局部内部类无法离开它所在的方法,因此在实际开发中很少使用。

5.匿名内部类

匿名内部类适合创建那种只需要创建一次使用的类,匿名内部类不能重复使用。

匿名内部类必须继承一个父类或者实现一个接口,但最多只能继承一个父类,或实现一个接口。

关于匿名内部类还有如下两条规则:

1.匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象,所以不能把不允许将匿名内部类定义成抽象类。

2.匿名内部类不能定义构造器。由于匿名内部类没有类名,所以无法定义构造器,但可以定义初始化块,可以通过初始化块来完成构造器需要完成的事。

interface Animal {double getPrice();String getName();
}public class Demo {public void test(Animal a) {System.out.print("购买了一个: " + a.getName() + "价格是:" + a.getPrice());}public static void main(String[] args) {var tar = new Demo ();//调用 test()方法时需要传入一个 Animal 参数//此处传入匿名实现类的实例tar.test(new Animal() {public double getPrice() {return 567.9;}public String getName() {return "二哈";}});}}

上面程序中的 Animal 类定义了一个 test()方法,该方法需要一个 Animal 对象作为参数,但 Animal 只是一个接口,无法直接创建对象,因此此处创建一个 Animal 接口实现类的对象传入该方法----如果这个 Animal 接口实现类需要重复用,则应该将实现类定义为一个独立类:如果这个 Animal 接口实现类只需一次使用,则可以采用上面程序中的方式,定义一个匿名内部类。

定义匿名内部类无需 class 关键字,而是在定义匿名内部类的时候直接生成该匿名内部类的对象。

由于匿名内部类不能是抽象类,所以匿名内部类必须实现它的抽象类或者接口里面包含的所有抽象方法。

当通过实现接口创建匿名内部类时,匿名内部类不能显式地定义构造器,因此匿名内部类只有一个隐式的无参构造器,故 new 接口名后的括号里不能传入参数值。

但如果是通过继承父类来创建匿名内部类时,匿名内部类将拥有和父类相似的构造器,此处的相似指的时拥有相同的形参列表。

abstract class Device {private String name;public abstract double getPrice();public Device(){}public Device(String name) {this.name = name;}//此处省略 setter 和 getter 方法...}public class AnonymousInner {public void test(Device d) {System.out.print("购买了一个"+d.getName() + ", 花掉了"+d.getPrice());}public static void main(String[] args) {var ai = new AnonymousInner();ai.test(new Device("电饭煲") {public double getPrice(){return 500.2;}});//调用无参数的构造器创建Device 匿名实现类的对象var d = new Device(){//初始化块{System.out.print("匿名内部类的初始化块....");}//实现抽象方法public double getPrice() {return 56.2;}//重写父类的实例方法public String getName() {return "鼠标";}};ai.test(d);}}

上面的程序创建了一个抽象父类 Device 类,这个抽象父类包含两个构造器:一个无参数的,一个有参数的。当创建以Device 为父类的匿名内部类时,既可以传入参数,也可以不传入参数。

当创建匿名内部类时,必须实现接口或抽象类里面的所有抽象方法。如果有需要,也可以重写父类中的普通方法。

在 Java 8 之前,Java 要求被局部内部类、匿名内部类访问的局部变量必须使用 final 修饰,从 Java 8 开始这个限制被取消了,Java 8更加智能,如果局部变量被匿名内部类访问,那么该局部变量相当于自动加上了 final 修饰。

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

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

相关文章

电脑里msvcr120.dll文件丢失怎样修复?

电脑里msvcr120.dll文件丢失的修复指南 在电脑的日常使用中,我们可能会遇到各种各样的系统文件丢失问题,其中msvcr120.dll文件的丢失就是较为常见的一种。作为一名在软件开发领域深耕多年的从业者,我将为大家详细解析msvcr120.dll文件的重要…

今日头条ip属地根据什么显示?不准确怎么办

在今日头条这样的社交媒体平台上,用户的IP属地信息对于维护网络环境的健康与秩序至关重要。然而,不少用户发现自己的IP属地显示与实际位置不符,这引发了广泛的关注和讨论。本文将深入探讨今日头条IP属地的显示依据,并提供解决IP属…

理解linux内核中的几种地址

1. 前言 《Linux内核完全注释》这本书提到了几种Linux内核中的几种地址,实地址,有虚拟地址,逻辑地址,线性地址,物理地址。除了物理地址以外,其他几种容易弄混淆。这里做一下笔记,讲一下我的理解…

【Rust自学】10.3. trait Pt.1:trait的定义、约束与实现

喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 题外话:trait的概念非常非常非常重要!!!整个第10章全都是Rust的重难点!&#x…

Java List 集合详解:基础用法、常见实现类与高频面试题解析

正文 在 Java 集合框架中,List 是一个非常重要的接口,广泛用于存储有序的元素集合。本文将带你深入了解 List 接口的基本用法、常见实现类及其扩展,同时通过实际代码示例帮助你快速掌握这些知识。 👉点击获取2024Java学习资料 1…

大白话拆解——多线程中关于死锁的一切(七)(已完结)

前言: 25年初,这个时候好多小伙伴都在备战期末 小编明天还有一科考试,日更一篇,今天这篇一定会对小白非常有用的!!! 因为我们会把案例到用代码实现的全过程思路呈现出来!&#xff…

ROS节点架构设计:提高模块化与可扩展性

在 ROS2(Robot Operating System 2)的开发中,节点的架构设计是构建高效、稳定和可扩展机器人系统的基石。一个设计良好的节点架构不仅有助于提升系统的模块化水平,还能极大地增强代码的可维护性。本文将深入探讨 ROS2 中的三种常见…

GitLab集成Runner详细版--及注意事项汇总【最佳实践】

一、背景 看到网上很多用户提出的runner问题其实实际都不是问题,不过是因为对runner的一些细节不清楚导致了误解。本文不系统性的介绍GitLab-Runner,因为这类文章写得好的特别多,本文只汇总一些常几的问题/注意事项。旨在让新手少弯路。 二、…

《数据结构》期末考试测试题【中】

《数据结构》期末考试测试题【中】 21.循环队列队空的判断条件为?22. 单链表的存储密度比1?23.单链表的那些操作的效率受链表长度的影响?24.顺序表中某元素的地址为?25.m叉树第K层的结点数为?26. 在双向循环链表某节点…

「Mac畅玩鸿蒙与硬件54」UI互动应用篇31 - 滑动解锁屏幕功能

本篇教程将实现滑动解锁屏幕功能,通过 Slider 组件实现滑动操作,学习事件监听、状态更新和交互逻辑的实现方法。 关键词 滑动解锁UI交互状态管理动态更新事件监听 一、功能说明 滑动解锁屏幕功能包含以下功能: 滑动解锁区域:用…

螺栓松动丢失腐蚀生锈检测数据集VOC+YOLO格式504张4类别

数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):504 标注数量(xml文件个数):504 标注数量(txt文件个数):504 标注…

Postman测试big-event

报错500。看弹幕,知道可能是yml或sql有问题。 所以检查idea工作台, 直接找UserMapper检查,发现完全OK。 顺着这个error发现可能是sql有问题。因为提示是sql问题,而且是有now()的那个sql。 之后通过给的课件,复制课件…

如何使用大语言模型进行事件抽取与关系抽取

诸神缄默不语-个人CSDN博文目录 文章目录 1. 什么是事件抽取与关系抽取?2. 示例:使用大语言模型进行事件抽取与关系抽取 1. 什么是事件抽取与关系抽取? 事件抽取是指从文本中识别出与某些“事件”相关的信息。这些事件通常包括动作、参与者、…

NAT网络技术

NAT(Network Address Translation,网络地址转换)是一种常用的网络技术,主要用于在私有网络和公共网络之间转换IP地址。在家庭和小型企业网络当中用的比较多。它的主要功能有IP地址重用和增强网络的安全性。   NAT允许一个整个网…

SpringBoot框架开发中常用的注解

文章目录 接收HTTP请求。RestController全局异常处理器Component依赖注入LombokDataBuildersneakyThrowsRequiredArgsConstructor 读取yml文件配置类注解 接收HTTP请求。 RequestMapping 接收HTTP请求。具体一点是 GetMapping PostMapping PutMapping DeleteMapping 一共…

FFmpeg(音视频处理的瑞士军刀)开发实战指南

【欢迎关注编码小哥,学习更多实用的编程方法和技巧】 FFmpeg开发实战指南 1. FFmpeg简介 开源多媒体处理框架支持音视频编解码跨平台(Windows/Linux/Mac) 2. 环境准备 2.1 安装FFmpeg # Ubuntu sudo apt-get install ffmpeg libavcodec-…

洛谷B4071 [GESP202412 五级] 武器强化

题目传送门! 思路 我愿称之为gesp5史上最难想。。。 做法:贪心模拟(or二分) 对于贪心算法来说,最最最无法理解的地方:选择价格最低的配件来转换,还是选择拥有最多配件的其他武器来转换。 选…

TVS二极管选型【EMC】

TVS器件并联在电路中,当电路正常工作时,他处于截止状态(高阻态),不影响线路正常工作,当线路处于异常过压并达到其击穿电压时,他迅速由高阻态变为低阻态,给瞬间电流提供一个低阻抗导通…

qt鼠标右键菜单

来看一个小例子 // 重写鼠标右键事件 void QtGuiApplication2::mousePressEvent(QMouseEvent* event) {if (event->button() Qt::RightButton){m_pMenuRD->exec(QCursor::pos());} } 鼠标右键启动菜单,菜单里面有啥呢? // 先来声明一下 // 右键…

用C++使用带头单向非循环链表,实现一个哈西桶

用C使用带头单向非循环链表,实现一个哈西桶 在C中使用带头单向非循环链表来实现哈希桶是一种常见的做法,特别是在实现哈希表时。哈希桶是哈希表的基本存储单元,用于存储具有相同哈希值的元素。以下是一个简单的示例,展示了如何使用…