java第八篇:Java内部类全解析
前言
一、什么是内部类
What:什么是内部类
Why:内部类的好处
二 、成员内部类
what:什么是成员内部类
Why:成员内部类的好处:
how:如何使用成员内部类
how:成员内部类如何创建对象
三、静态内部类:
What:什么是静态内部类
Why:为什么要用静态内部类
how:如何使用静态内部类
how:静态内部类如何创建对象
成员内部类和静态内部类对比:
四、局部内部类:
What:什么是局部内部类
Why:为什么要用局部内部类
How:如何使用局部内部类
how:局部内部类如何创建对象
五、匿名内部类(!!!):
What:什么是匿名内部类
Why:为什么要用匿名内部类
How:如何使用匿名内部类
匿名类的类名
匿名内部类的两种一般写法:
六、匿名内部类的lambada简化形式
适用条件:
简化规则:
总结
前言
今天我们学习内部类的相关内容。包括成员内部类、静态内部类、局部内部类、匿名内部类。我们将重点主要放在匿名内部类,并学习他的lambada简化的表达方式。
一、什么是内部类
What:什么是内部类
内部类(Inner Class)是指一个类定义在另一个类内部的类。内部类可以访问外部类的所有成员,包括私有成员,这使得它成为封装相关功能的强大工具。
在Java中,内部类主要分为四种类型:
- 成员内部类(Member Inner Class)
- 静态内部类(Static Nested Class)
- 局部内部类(Local Inner Class)
- 匿名内部类(Anonymous Inner Class)
Why:内部类的好处
- 增强封装性:将只在特定上下文中使用的类隐藏在外部类内部,减少命名冲突,提高代码的模块化程度。
- 访问外部类私有成员:内部类可以直接访问外部类的所有成员,包括私有字段和方法,这在实现某些设计模式时非常有用。
- 逻辑分组:当一个类只对另一个类有意义时,将其作为内部类可以使代码结构更加清晰,符合"高内聚"的设计原则。
- 实现多继承效果:Java不支持多继承,但内部类可以继承一个类,同时外部类继承另一个类,从而在某种程度上模拟多继承。
- 事件驱动编程:在GUI编程和回调机制中,匿名内部类提供了简洁的事件处理方式。
二 、成员内部类
what:什么是成员内部类
成员内部类是定义在一个外部类的成员位置的的类,没有 static 修饰。
Why:成员内部类的好处:
- 完全访问权限:可以访问外部类的所有字段和方法,包括私有成员
- 强大的封装:将相关功能紧密绑定,提高代码可读性
- 状态共享:内部类可以持有外部类的状态,实现紧密协作
how:如何使用成员内部类
//外部一个class类 class Outer { private String a = "hello"; //内部嵌套一个class类 class Inner { // 成员内部类 public void sys() { System.out.println(a); // 直接使用成员名访问 System.out.println(Outer.this.a);// 使用外部类的this引用访问,两个等效 } } }当变量名相同的时候如何输出?
//当变量名相同的时候如何输出 class People { private int heartBeat = 100; public class Heart { private int heartBeat = 80; public void show() { int heartBeat = 200; System.out.println(heartBeat);// 200 System.out.println(this.heartBeat);// 80 System.out.println(People.this.heartBeat);// 100 } }how:成员内部类如何创建对象
外部类名.内部类名.变量名=new 外部类名().new 内部类名();
class People { private int heartBeat = 100; public class Heart { private int heartBeat = 80; public void show() { int heartBeat = 200; System.out.println(heartBeat);// 200 System.out.println(this.heartBeat);// 80 System.out.println(People.this.heartBeat);// 100 } } public static void main(String[] args) { People.Heart heart = new People().new Heart(); heart.show(); }输出结果:
三、静态内部类:
What:什么是静态内部类
静态内部类是使用static关键字修饰的类,定义在另一个类的内部。它与外部类的实例无关,可以直接通过外部类名访问,而无需创建外部类的对象。
Why:为什么要用静态内部类
内存效率高:不会意外持有外部类引用,避免内存泄漏,特别适合工具类、辅助类
独立性强,解耦合:静态内部类可以独立于外部类使用,降低代码耦合度
命名空间管理:逻辑分组常量和工具类,避免命名冲突,提高代码可读性
设计模式支持:Builder设计模式等需要独立内部类的场景的最佳选择
how:如何使用静态内部类
public class Outer { private static String staticField = "静态"; private String instanceField = "实例"; // 静态内部类不能访问 // 静态内部类定义 public static class Inner { private String innerField = "内部字段"; private static String staticInnerField = "静态内部字段"; // 可以有静态成员 public void show() { System.out.println(staticField); // 可以访问外部类静态字段 // System.out.println(instanceField); // 编译错误!不能访问实例字段 System.out.println(innerField); System.out.println(staticInnerField); } } }how:静态内部类如何创建对象
外部类名.内部类名 对象名 = new 外部类名.内部类名();
public static void main(String[] args) { // 创建对象:外部类名.内部类名 对象名 = new 外部类名.内部类名(); outer.Inner inner = new outer.Inner(); inner.show(); }成员内部类和静态内部类对比:
| 特性 | 静态内部类 | 成员内部类 |
|---|---|---|
| 定义 | static class Inner | class Inner |
| 创建对象 | new Outer.Inner() | new outer.new Inner() |
| 持有外部引用 | 不持有 | 持有 |
| 访问外部实例成员 | 不能 | 能 |
| 访问外部静态成员 | 能 | 能 |
| 定义静态成员 | 能 | 不能(除常量) |
| 内存开销 | 小 | 大(持有引用) |
四、局部内部类:
What:什么是局部内部类
局部内部类是定义在方法或代码块内部的类,它的作用域仅限于该方法或代码块。局部内部类可以访问外部类的所有成员,也可以访问方法中的final或effectively final(实际上不可变的)局部变量,但它不能有访问修饰符(public、private等),也不能定义静态成员(除了常量)。
Why:为什么要用局部内部类
- 作用域限制:只能在定义它的方法内使用,封装性更好,避免类被误用
- 访问权限强大:可以同时访问方法局部变量和外部类的所有成员,实现紧密协作
- 代码组织清晰:将只在特定方法中使用的类定义在该方法内,提高代码可读性和维护性
- 临时性设计:适合创建只在特定上下文中使用的对象,避免创建不必要的顶级类
How:如何使用局部内部类
局部内部类是定义在方法内部的内部类,只能在该方法内部使用,它的定义方式如下
class OuterClass { public OuterClass() { class LocalInnerClassA { // 在构造器中定义局部内部类 } } { class LocalInnerClassB { // 在实例代码块中定义局部内部类 } } static { class LocalInnerClassC { // 在静态代码块中定义局部内部类 } } public void fun() { int localVar; class LocalInnerClassD { // 在成员函数中定义局部内部类 } } }例如:
public class Outer { private int data; public void method() { class Inner { public void print() { System.out.println(data); } } Inner inner = new Inner(); inner.print(); } }how:局部内部类如何创建对象
- 创建对象:在定义类之后,但在作用域结束之前,使用
new关键字实例化该类的对象。- 使用对象:创建对象后,可以调用其方法或访问其属性,但仅限于当前作用域内。
public class Calculator { public void calculate(int a, int b) { final int multiplier = 10; // 定义局部内部类 class Operation { public int add() { return a + b; } public int multiply() { return (a + b) * multiplier; } } // 必须在定义它的方法内创建对象 Operation op = new Operation(); System.out.println("Sum: " + op.add()); System.out.println("Multiplied: " + op.multiply()); // 无法在方法外创建:Operation op2 = new Operation(); // 编译错误! } } // 使用 Calculator calc = new Calculator(); calc.calculate(5, 3);五、匿名内部类(!!!):
What:什么是匿名内部类
匿名内部类是没有名字的内部类,只能一次性使用,它通常用于实现接口和抽象类的方法。
匿名内部类本质上是一个局部内部类的简化形式,它只能在声明时创建一个实例,可以直接访问外部类的所有成员以及方法中final或effectively final的局部变量,但不能定义构造方法、静态成员或任何静态代码块。
Why:为什么要用匿名内部类
代码简洁:在需要一次性使用的类时,避免单独定义类文件,减少代码量
即时接口实现:当需要快速实现接口或抽象类且仅需单次使用时,匿名内部类无需显式定义子类
Lambda的过渡方案:在Java 8之前,它是函数式编程的主要实现方式(现可被Lambda替代):
访问权限强:可以访问外部类成员和方法局部变量,实现更紧密的协作
更方便的创建一个子类对象
How:如何使用匿名内部类
先定义一个类,然后内部类进行new类或接口(参数值){一般是外部类的方法重写}
public abstract class Animal { public abstract void cry(); } //匿名内部类,直接方法重写,本质是子类 public class test { public static void main(String[] args){ Animal animal=new Animal() { @Override public void cry() { System.out.println("小猫喵喵"); } }; animal.cry(); } }匿名类的类名
匿名内部类只是没有显式的类名,但实际上有隐式的名字,一般命名规则是外部类名$1,这里的 1 是指在这个外部类内定义的第一个匿名内部类,我们可以通过 getClass() 方法获取类名:
public class AnonymousClassNameDemo { public static void main(String[] args) { // 创建匿名内部类实例 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("Running..."); } }; // 获取并打印类名 String className = runnable.getClass().getName(); System.out.println("匿名内部类的类名: " + className); }匿名内部类的一般写法:
平时:Swim a=new Swim(){
匿名内部类:swim(new Swim(){
package itheima.innerclass2; public class test2 { public static void main(String[] args){ //第一种--正常创建对象方式 Swim a=new Swim(){ @Override public void swimming() { System.out.println("学生游泳很快"); } }; swim(a); //第二种--匿名内部类方式(去掉对象名,直接new对象) swim(new Swim(){ public void swimming() { System.out.println("老师游泳很慢"); } } );} public static void swim(Swim s){ System.out.println("开始~~~~~~~~"); s.swimming(); System.out.println("结束~~~~~~~~"); } interface Swim{ void swimming(); } }六、匿名内部类的lambada简化形式
适用条件:
仅适用于函数式接口(有且仅有一个抽象方法的接口)
简化规则:
1. 当接口只有一个抽象方法时,可以省略接口名和方法名
2. 参数类型可以省略(编译器自动推断)
3. 单行代码可以省略大括号和return关键字
4. 无参数时保留空括号,单参数时可以省略括号
如何写:(被重写方法的形参列表)->被重写的代码,删掉new后面到方法前面的内容,只保留参数
//两者效果等同 swim(new Swim(){ public void swimming() { System.out.println("老师游泳很慢"); } } );} swim(() -> System.out.println("老师游泳很慢")总结
内部类的出现进一步扩展了类的功能,使得类的功能更加丰富,内部类的种类繁多,但我们对于这一块只要求掌握就可以了,不必做过深的研究。
Tips:开发中并不是主动去写匿名内部类,而是函数方法需要我们写才写,是被动地写
今天我们学习了内部类的相关知识。本次分享到此结束,感谢大家的观看与支持!如果本内容对您有帮助,欢迎点赞、收藏,您的支持将是我持续创作的最大动力,谢谢!
