java 内部类理解

一、简介

在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。下面就先来了解一下这四种内部类的用法。

二、成员内部类

成员内部类是最普通的内部类,它的定义为位于另一个类的内部,形如下面的形式:

package com.tfq.innerclass.member;/*** @author: fqtang* @date: 2024/05/07/10:11* @description: 成员内部类--定义四边形类* */
public class Quadrilateral {private int length = 0;private int width = 0;private Draw draw = null;public Quadrilateral(int length, int width) {this.length = length;this.width = width;//必须先创建成员内部类的对象,再进行访问//getDrawInstance().drawShape();}/*** 虽然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。* 在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问:* @return 新创建的内部类Draw*/public Draw getDrawInstance(){if(draw == null)draw = new Draw();return draw;}/*** 这样看起来,类Draw像是Quadrilateral类的一个成员,Quadrilateral称为外部类。* 成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。*/class Draw {public void drawShape() {System.out.println("drawShape =" + (length == width?"正方形":"长方形"));}}
}

测试类

package com.tfq.innerclass.member;/*** @author: fqtang* @date: 2024/05/07/10:25* @description: 描述*/
public class TestInnerClass {public static void main(String[] args) {// 成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,// 前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下://第一种方式:Quadrilateral quadrilateral = new Quadrilateral(2, 2);Quadrilateral.Draw draw = quadrilateral.new Draw();draw.drawShape();//第二种方式:Quadrilateral.Draw draw1 = new Quadrilateral(2, 3).getDrawInstance();draw1.drawShape();}
}

三、局部内部类

局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。

package com.tfq.innerclass.local;/*** @author: fqtang* @date: 2024/05/07/10:36* @description: 局部内部类讲解*/
public class People {private String name;public People() {}}class Man{public Man(){}public People getWoman(){/*** 注意,局部内部类就像是方法里面的一个局部变量一样,* 是不能有public、protected、private以及static修饰符的。*/class Woman extends People{   //局部内部类int age =0;}return new Woman();}
}

四、匿名内部类

匿名内部类也就是没有名字的内部类

正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写

但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口

实例1:不使用匿名内部类来实现抽象方法

abstract class Person {public abstract void eat();
}class Child extends Person {public void eat() {System.out.println("eat something");}
}public class Demo {public static void main(String[] args) {Person p = new Child();p.eat();}
}

运行结果:eat something

可以看到,我们用Child继承了Person类,然后实现了Child的一个实例,将其向上转型为Person类的引用

但是,如果此处的Child类只使用一次,那么将其编写为独立的一个类岂不是很麻烦?

这个时候就引入了匿名内部类

实例2:匿名内部类的基本实现

package com.tfq.innerclass.anonymous.basedemo;/*** @author: fqtang* @date: 2024/05/07/11:35* @description: 匿名内部类的基本实现*/
abstract class Person {public abstract void eat();
}package com.tfq.innerclass.anonymous.basedemo;/*** @author: fqtang* @date: 2024/05/07/11:36* @description: 描述*/
public class AbstractDemo {public static void main(String[] args) {Person p = new Person() {/*** 可以看到,我们直接将抽象类Person中的方法在大括号中实现了* 这样便可以省略一个类的书写** 并且,匿名内部类还能用于接口上*/public void eat() {System.out.println("eat something");}};p.eat();}
}

运行结果:eat something

可以看到,我们直接将抽象类Person中的方法在大括号中实现了

这样便可以省略一个类的书写

并且,匿名内部类还能用于接口上

实例3:在接口上使用匿名内部类

package com.tfq.innerclass.anonymous.interfacedemo;/*** @author: fqtang* @date: 2024/05/07/11:38* @description: 在接口上使用匿名内部类*/
public interface Person {void eat();
}package com.tfq.innerclass.anonymous.interfacedemo;/*** @author: fqtang* @date: 2024/05/07/11:39* @description: 描述*/
public class InterfaceDemo {public static void main(String[] args) {Person p = new Person() {/*** 由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现** 最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口*/public void eat() {System.out.println("eat something");}};p.eat();}
}

运行结果:eat something

由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现

最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口。

实例4:Thread类的匿名内部类实现

package com.tfq.innerclass.anonymous.basedemo;/*** @author: fqtang* @date: 2024/05/07/11:44* @description: 描述*/
public class RunnableDemo {public static void main(String[] args) {Runnable r = new Runnable() {public void run() {for(int i=0;i<2;i++){System.out.println(i);}}};Thread thread = new Thread(r);thread.start();}
}package com.tfq.innerclass.anonymous.basedemo;/*** @author: fqtang* @date: 2024/05/07/11:41* @description: 描述*/
public class ThreadDemo {public static void main(String[] args) {Thread t = new Thread(){public void run(){for(int i=0;i<10;i++){System.out.println(i);}}};t.start();}
}

运行结果:0 1 2 3 4 5 6 7 8 9

在使用匿名内部类的过程中,我们需要注意如下几点:

a、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。

b、匿名内部类中是不能定义构造函数的。

c匿名内部类中不能存在任何的静态成员变量和静态方法。

d、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。

e匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

f、匿名内部类访问局部变量,jdk 8之前必须加final 关键字。

五、静态内部类

  静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。

public class Test {public static void main(String[] args)  {Outter.Inner inner = new Outter.Inner();}
}class Outter {public Outter() {}static class Inner {public Inner() {}}
}

六、内部类的使用场景和好处

总结一下主要有以下四点:

  1.每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整,

  2.方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。

  3.方便编写事件驱动程序

  4.方便编写线程代码

参考链接地址:

https://www.cnblogs.com/dolphin0520/p/3811445.html

https://www.cnblogs.com/nerxious/archive/2013/01/25/2876489.html

https://www.cnblogs.com/chenssy/p/3390871.html

java 详解匿名内部类与函数式接口_函数式接口和匿名内部类-CSDN博客

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

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

相关文章

代码随想录day55 | 动态规划P15 | ● 392.判断子序列 ● 115.不同的子序列

392.判断子序列 给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以不删除&#xff09;字符而不改变剩余字符相对位置形成的新字符串。&#xff08;例如&#xff0c;"ace"是"abcde"的…

国内小白用什么方法充值使用ChatGPT4.0?

首先说一下IOS礼品卡订阅&#xff0c;目前最经济实惠的订阅方式&#xff0c;具体操作步骤 使用IOS设备充值&#xff0c;用 App Stroe 兑换券 1、支付宝地址切换旧金山&#xff0c;在里面买app store 的兑换卷 2、美区Apple ID登陆app store &#xff0c;充值兑换券 3、IOS设…

JavaScript中的事件模型

JavaScript中的事件模型分为&#xff1a;事件和事件流、原始事件、标准事件和IE事件。 事件与事件流 JavaScript中的事件&#xff0c;可以理解为HTML文档或者浏览器中发生的一种交互操作&#xff0c;让网页有互动的功能。常见的事件就是加载事件、鼠标事件和自定义事件。 因…

Find My资讯|苹果设备在修复期间可以保持启用“Find My“功能

iOS 17.5 中有一项名为"维修状态"的新功能&#xff0c;可让送修的设备保持启用"查找我的"&#xff08;Find My&#xff09;功能。此前&#xff0c;用户在送修设备时必须禁用跟踪设备位置的"查找我的"功能&#xff0c;但iOS 17.5发布后&#xff0…

vscode远程免密ssh原理与实操方法

什么是SSH SSH是一种加密协议&#xff0c;全称为Secure Shell&#xff0c;用于安全地远程登录到服务器或其他远程设备上执行命令或传输文件。它提供了一种安全的加密通信机制&#xff0c;使得远程登录和文件传输等操作不会被恶意攻击者窃取或篡改&#xff0c;确保了数据的保密…

Jackson系统开发方法

1、Jackson分析方法是面向数据流的分析方法。这一方法从目标系统的输入、输出数据结构入手&#xff0c;导出程序框架结构&#xff0c;再补充其它细节&#xff0c;就可得到完整的程序结构图。 2、应用场景&#xff1a;这一方法对输入、输出数据结构明确的中小型系统特别有效&am…

数据库(MySQL)—— 初识和创建用户

数据库&#xff08;MySQL&#xff09;—— 初识 什么是数据库数据库的种类创建用户mysql -h 主机名或IP地址 -u 用户名 -p 登录mysqlSELECT USER(); 查看当前用户切换用户GRANT ALL PRIVILEGES ON 赋予用户权限 REVOKE 撤销权限示例注意事项 MySQL的图形化界面工具查看所有用户…

【oracle数据库安装篇三】Linux6.8单机环境oracle11g容灾ADG搭建

说明 DataGuard 是在主节点与备用节点间通过日志同步来保证数据的同步&#xff0c;可以实现数据库快速切换与灾难性恢复。用户能够在对主数据库影响很小的情况下&#xff0c;实现主备数据库的同步。 关联文章 【oracle数据库安装篇一】Linux5.6基于LVM安装oracle11gR2单机 【…

追踪攻击数据包中的真实IP地址:方法与技巧

在网络安全领域&#xff0c;追踪攻击数据包中的真实IP地址是一项至关重要的任务。通过确定攻击者的真实IP地址&#xff0c;可以有效地识别和阻止网络攻击行为&#xff0c;提高网络安全防御水平。IP数据云IP地址查询将介绍几种常用的方法和技巧&#xff0c;帮助安全人员有效追踪…

嵌入式C语言教程:实现气压监测系统

气压监测在气象学、航空和户外活动装备中非常重要。本教程将介绍如何在STM32微控制器上使用数字气压传感器实现实时气压监测系统。 一、开发环境准备 硬件要求 微控制器&#xff1a;STM32L476RG&#xff0c;具备低功耗特性和足够的处理能力。开发板&#xff1a;STM32L4 Disc…

CAXA 3D实体设计2024:塑造未来的创新引擎

在数字化时代的浪潮中&#xff0c;3D CAD实体建模设计正成为推动工业创新的核心动力。CAXA 3D实体设计2024&#xff0c;以其卓越的性能和丰富的功能&#xff0c;为企业和个人用户带来了前所未有的设计体验。 CAXA 3D实体设计2024不仅拥有直观易用的界面&#xff0c;还配备了强…

【Osek网络管理测试】[TG4_TC5]唤醒条件

&#x1f64b;‍♂️ 【Osek网络管理测试】系列&#x1f481;‍♂️点击跳转 文章目录 1.环境搭建2.测试目的3.测试步骤4.预期结果5.测试结果 1.环境搭建 硬件&#xff1a;VN1630 软件&#xff1a;CANoe 2.测试目的 验证DUT验证DUT睡眠后被唤醒&#xff0c;并再次睡眠能否…

LINQ 提供了两种主要的查询语法:方法语法和查询表达式

LINQ 提供了两种主要的查询语法&#xff1a;方法语法和查询表达式。这两种语法在功能上是等价的&#xff0c;只是语法形式不同&#xff0c;开发人员可以根据个人偏好选择使用哪种。下面分别介绍这两种语法&#xff1a; 1. 方法语法&#xff1a; 方法语法是使用 LINQ 扩展方法来…

第七十三章 Apache (UNIX® Linux macOS) 的替代选项 - 替代选项 2:带有 NSD 的 CGI 模块 (nph-CSPcgi)

文章目录 第七十三章 Apache (UNIX Linux macOS) 的替代选项 - 替代选项 2&#xff1a;带有 NSD 的 CGI 模块 (nph-CSPcgi)替代选项 2&#xff1a;带有 NSD 的 CGI 模块 (nph-CSPcgi)映射其他文件类型使用 CGI 和 NSD 操作和管理 Web 网关 第七十三章 Apache (UNIX Linux macOS…

ZoomIt快捷键

ZoomIt快捷键 ZoomIt是一款非常实用的屏幕放大镜软件&#xff0c;可以帮助用户更好地查看电脑屏幕上的细节。一些主要功能包括: 放大镜功能 - 可以实时放大屏幕的任何部分&#xff0c;并支持多种视图模式如正常、灰度、反色等。取色器 - 可以精确获取屏幕上任何颜色的RGB值。…

关于二手车系统学习--登录模块

1.样式1-17行 <div class"cheader"><div style"width: 80%;margin: 0 auto;line-height: 50px;padding-top: 10px"><el-row><el-col:span"5"style"font-size: 20px;cursor: pointer;color: #00ae66;font-weight: bold…

typescript 不是特别常用,容易忘的知识点

1、花括号对象通过方括号字符串形式取值 let obj { name: asd, age: 21, salary: 400, desc: "asdasd", op: [asd, as, qwe] };for (let i in obj) {console.log(obj[i as keyof typeof obj]); }let key name; console.log(obj[key as name]); console.log(obj[ke…

富格林:正确杜绝欺诈实现出金

富格林悉知&#xff0c;现货黄金一直以来都是投资者们追逐的热门品种之一。其安全性和避险特性吸引着广大投资者。但在现货黄金市场中要想实现出金其实并不简单&#xff0c;是需要我们通过一定的技巧和方法去正确杜绝欺诈套路。下面为了帮助广大投资者正确杜绝欺诈实现出金&…

九、redis过期策略

目录 一、设置redis键的过期时间 1、删除过期时间 2、查看过期时间 二、过期删除策略 1、定时删除&#xff1a; 2、惰性删除&#xff1a; 3、定期删除&#xff1a; 4、惰性删除和定期删除配合使用 三、 1、设置redis最大内存 2、设置内存的淘汰方式 总结&#xff1…

运维自动化之 ansible

目录 一 常见的自动化运维工具 &#xff08;一&#xff09;有哪些常见的 自动化运维工具 &#xff08;二&#xff09;为什么后面都变成用 ansible 二 ansible 基本介绍 1&#xff0c; ansible 是什么 2&#xff0c;ansible能干什么 3&#xff0c;ansible 工作原…