Java - 内部类(非常重要)
一、介绍
一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。
类的五大成员:属性、方法、构造器、代码块、内部类。
内部类的最大特点是可以直接访问私有属性,并且可以体现类与类之间的包含关系。
注意:
内部类是学习的难点,同时也是重点,后面看底层源码时,有大量的内部类。
二、基础语法
class Outer{ // 外部类
class inner{ // 内部类
}
}
class Other{ // 外部其他类
}
三、内部类的分类
定义在外部类局部位置上(比如方法内)
1. 局部内部类(有类名)
说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
-
可以直接访问外部类的所有成员,包含私有的。
-
不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用 final 修饰,因为局部变量也可以使用 final。
-
作用域:仅仅在定义它的方法或代码块中。
-
局部内部类 --- 访问 ---> 外部类的成员 【访问方式:直接访问】
-
外部类 --- 访问 ---> 局部内部类的成员 【访问方式:创建对象,再访问(注意:必须在作用域内】
-
外部类其他类 --- 不能访问 ---> 局部内部类(因为 局部内部类地位是一个局部变量)
-
如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(
外部类名.this.成员
)去访问
2. 匿名内部类(没有类名,重点!!!!)
说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名。
- 匿名内部类的基本语法
new 类或接口(参数列表){
类体
};
- 匿名内部类的本质
package innerclass;
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04{ // 外部类
private int n1 = 10; // 属性
public void method() { // 方法
// 基于接口的匿名内部类
// 1. 需求:想使用 IA 接口,并创建对象
// 2. 传统方式,是写一个类,实现该接口,并创建对象
// 3. 我们此次代码的需求是:Tiger/Dog 类只是使用一次,后面不再使用
// 4. 可以使用匿名内部类来简化开发
// 5. tiger 的编译类型 ? IA
// 6. tiger 的运行类型 ? 就是匿名内部类 Outer04$1
/*
我们看底层 会分配 类名 Outer04$1
class Outer04$1 implements IA {
@Override
public void cry(){
System.out.println("老虎叫唤...");
}
}
*/
// 7. jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并把地址
// 返回给 tiger
// 8. 匿名内部类使用一次,就不能再使用,但是对象可以使用多次
IA tiger = new IA() {
@Override
public void cry(){
System.out.println("老虎叫唤...");
}
};
System.out.println("tiger的运行类型 = " + tiger.getClass());
tiger.cry();
}
}
interface IA{
public void cry();
}
//class tiger implements IA{
// @Override
// public void cry() {
// System.out.println("老虎叫唤...");
// }
//}
//
//class Dog implements IA {
// @Override
// public void cry() {
// System.out.println("狗叫唤");
// }
//}
- 匿名内部类的细节
- 匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象。因此从语法上看,它既有定义类的特征,也有创建对象的特征。
- 可以直接访问外部类的所有成员,包括私有的。
- 不能添加访问修饰符,因为它的地位就是一个局部变量。
- 作用域:仅仅在定义它的方法或代码块中。
- 匿名内部类 --- 访问 ---> 外部类成员 【访问方式:直接访问】
- 外部其他类 --- 不能访问 ---> 匿名内部类 (因为 匿名内部类地位是一个局部变量)
- 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用
外部类名.this.成员
去访问。
注意:代码中 Outer05.this 就是调用 f1() 的对象。
- 匿名内部类的最佳实践
- 当做实参直接传递,简洁高效
先看下传统的写法,需要先写出一个类,然后再传参,这是种垃圾写法。
package innerclass;
public class InnerClassExercise01 {
public static void main(String[] args) {
//传统写法
f1(new Picture());
}
//静态方法,形参是接口类型
private static void f1(IL il) {
il.show();
}
}
//接口
interface IL {
void show();
}
//类 -> 实现IL =》编程实现(硬编码)
class Picture implements IL {
@Override
public void show() {
System.out.println("这是一幅名画...");
}
}
下面的是高效的写法:值得学习
package innerclass;
public class InnerClassExercise01 {
public static void main(String[] args) {
//当做实参直接传递,简洁高效
f1(new IL() {
@Override
public void show() {
System.out.println("这是一幅名画...");
}
});
}
//静态方法,形参是接口类型
private static void f1(IL il) {
il.show();
}
}
//接口
interface IL {
void show();
}
定义在外部类的成员位置上
1. 成员内部类(没用 static 修饰)
说明:成员内部类是定义在外部类的成员位置,并且没有 static 修饰。
- 可以直接访问 外部类的所有成员,包含私有。
class Outer08 { // 外部类
private int n1 = 10;
public String name = "张三";
// 注意:成员内部类,是定义在外部类内的成员位置上的
class Inner08 {
public void say() {
//可以直接访问外部类的所有成员,包含私有
System.out.println("n1 = " + n1 + " name = " + name);
}
}
//写方法
public void t1() {
Inner08 inner08 = new Inner08();
inner08.say();
}
}
-
可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
-
作用域:和外部类其他成员一样,为整个类体。
-
成员内部类 --- 访问 ---> 外部类 【访问方法:直接访问】
-
外部类 --- 访问 ---> 成员内部类 【访问方式:创建对象,再访问】
-
外部其他类 --- 访问 ---> 成员内部类
示例:
package innerclass;
public class MemberInnerClass01 {
public static void main(String[] args) {
//外部其他类,使用成员内部类的两种方式
//第一种
Outer08 outer08 = new Outer08();
// outer08.new Inner08(); 相当于把 new Inner08() 当做是outer08的成员
// 这就是和语法,不用太纠结
Outer08.Inner08 inner08 = outer08.new Inner08();
// 第二种:在外部类中,编写一个方法,可以返回 Inner08 的对象
Outer08.Inner08 inner08__ = outer08.getInner08Instance();
inner08__.say();
}
}
class Outer08 { // 外部类
private int n1 = 10;
public String name = "张三";
// 注意:成员内部类,是定义在外部类内的成员位置上的
class Inner08 {
public void say() {
//可以直接访问外部类的所有成员,包含私有
System.out.println("n1 = " + n1 + " name = " + name);
}
}
//方法:返回一个Inner08的实例
public Inner08 getInner08Instance(){
return new Inner08();
}
//写方法
public void t1() {
Inner08 inner08 = new Inner08();
inner08.say();
}
}
- 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用
外部类名.this.成员
去访问。
2. 静态内部类 (使用 static 修饰)
说明:静态内部类是定义在外部类的成员位置,并且有 static 修饰
-
可以直接访问外部类的所有静态成员,包含私有,但不能直接访问非静态成员。
-
可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
-
作用域:同其他的成员,为整个类体。
-
静态内部类 --- 访问 ---> 外部类 【访问方式:直接访问所有的静态成员】
-
外部类 --- 访问 ---> 静态内部类 【访问方式:创建对象,再访问】
-
外部其他类 --- 访问 ---> 静态内部类
示例:
package innerclass;
public class StaticInnerClass01 {
public static void main(String[] args) {
//外部其他类,使用静态内部类
Outer10 outer10 = new Outer10();
//方式1:
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
//方式2:
//调用的普通方法
Outer10.Inner10 inner101 = outer10.getInner10();
inner101.say();
//调用的静态方法
Outer10.Inner10 inner102 = Outer10.getInner10_();
inner102.say();
}
}
class Outer10{
private static String name = "张三";
static class Inner10 {
public void say() {
System.out.println(name);
}
}
public void m1() { // 外部类---访问--->静态内部类,方法方式:创建对象,再访问
Inner10 inner10 = new Inner10();
inner10.say();
}
public Inner10 getInner10(){
return new Inner10();
}
public static Inner10 getInner10_(){
return new Inner10();
}
}
- 如果外部类和静态内部类的成员重名时,静态内部类访问时,默认遵守就近原则,如果想访问外部类的成员,则可以使用
外部类名.成员
去访问。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY