【Java】山外有山,类外还有类
内部类是Java语言中的一种特性,它允许在另一个类中定义一个类。
内部类可以是静态的(不依赖于外部类的实例),也可以是非静态的(依赖于外部类的实例)。
在本篇博客中,我们将首先介绍内部类的使用特点,而后介绍几种内部类:
静态成员内部类
非静态成员内部类
局部内部类
匿名内部类
首先来看看内部类的使用特点:
内部类的使用特点
- 访问控制:内部类可以直接访问外部类的成员,包括私有成员。
- 封装性:内部类可以隐藏在外部类中,对外不可见,增加了封装性。
- 继承:内部类可以继承外部类的属性和方法,也可以继承其他类或实现接口。
- 多态:内部类可以实现外部类不能实现的接口,实现多态。
- 生命周期:内部类的生命周期与创建它的外部类对象的生命周期相关联。
- 作用域:内部类的作用域限定在外部类中,只能在外部类中被访问。
什么时候使用内部类
当一个事物内部,还有一个部分需要完整的结构去描述,而这个内部的完整结构又仅为外部事物提供服务,则整个内部的完整结构,最好使用内部类。
例如:人类都有心脏,人类本身需要用属性去描述,人类内部的心脏,也需要特殊的属性和行为去描述,则心脏可以被定义成内部类,人类中的一个心脏类。
在实际的开发场景中,可能会有以下情形:
- 实现回调:使用匿名内部类来实现一个回调接口。
- 创建辅助类:创建一些仅供外部类使用的辅助类。
- 实现多态:通过内部类实现多态,特别是在设计模式中,如适配器模式、观察者模式等。
格式例如:
public class Outer {class Inner {// ...}
}
静态成员内部类
格式:直接在定义内部类的时候加上static关键字
public class A{static class B{}
}
注意:
- 内部类可以定义属性,方法,构造等
- 静态内部类可以被final或者abstract修饰,被final修饰之后,不能被继承,被abstract修饰之后,不能new
- 静态内部类,不能调用外部类的非静态成员
- 内部类还可以被四种权限修饰符修饰
调用静态内部类成员:
外部类.内部类 对象名 = new 外部类.内部类()
使用示例:
public class OuterClass {public static int staticField = 10;static class StaticInnerClass {void display() {System.out.println(staticField);}}public static void main(String[] args) {StaticInnerClass inner = new StaticInnerClass();inner.display();//或者OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();inner.display();}
}
非静态成员内部类
格式:
public class A{class B{}
}
调用非静态内部类成员:
外部类.内部类 对象名 = new 外部类().new 内部类()
使用示例:
public class OuterClass {private String outerField = "外部类字段";class NonStaticInnerClass {void display() {System.out.println(outerField);}}public void createInner() {NonStaticInnerClass inner = new NonStaticInnerClass();inner.display();}
}OuterClass outer = new OuterClass();
outer.createInner();
//或者
OuterClass.NonStaticInnerClass inner = new OuterClass().new NonStaticInnerClass();
局部内部类
局部内部类可以定义在方法中,代码块中,构造中。
例如:
- 辅助计算类
public class Calculator {public void calculate() {class Helper {int add(int a, int b) {return a + b;}}Helper helper = new Helper();int result = helper.add(5, 10);System.out.println("计算结果是: " + result);}
}
- 假如要完成临时任务
public class TaskRunner {public void runTask() {class Task {void execute() {System.out.println("执行临时任务");}}Task task = new Task();task.execute();}
}
内部类、接口、抽象类、普通类作方法参数
接口作为方法参数返回值,传递实参时,传递实现类对象。
接口作为返回值类型返回,实际返回的是实现类对象。
示例代码如下:
package com.hamburger.innerclass;public class main {public static void main(String[] args) {Mouse mouse = new Mouse();method(mouse);USB usb = method01();}/*接口作为方法参数,传递实参时,传递实现类对象*/public static void method(USB usb){usb.open();}public static USB method01(){Mouse mouse = new Mouse();return mouse;}
}
package com.hamburger.innerclass;public interface USB {public abstract void open();
}
package com.hamburger.innerclass;public class Mouse implements USB{public void open(){System.out.println("haha");}
}
抽象类
- 作为方法参数传递,传递实参时,传递的是其子类对象。
- 作为方法返回值类型返回,实际返回的是其子类对象。
普通类
- 作为方法参数传递,传递的是对象
- 作为方法返回值返回,返回的是对象
匿名内部类
匿名内部类,可以理解为没有显式声明出类名的内部类。它没有类名,通常用于创建一次性使用的类对象,特别是当需要快速实现某个接口或继承某个类时。
使用场景:我们如果想要实现接口,简单使用一次抽象方法,就需要:
创建一个实现类
实现这个接口
重写抽象方法
new实现类方法
如果我们只想单纯使用一次接口中的方法,能不能不要这么麻烦?
使用匿名内部类的方法:
- 创建实现类,实现接口
- 重写方法
- 创建实现类对象
- 调用方法
匿名内部类格式:
new 接口/抽象类(){重写方法
}.重写的方法();
===============================
类名 对象名 = new 接口/抽象类(){重写方法
}
对象名.重写的方法();
匿名内部类作为参数传递
package com.ham.niminginnerclass;
public class test {public static void main(String[] args){method(new USB() {@Overridepublic void open() {System.out.println("lzy");}@Overridepublic void close() {System.out.println("dyp");}}); }public static void method(USB usb){usb.open();usb.close();}
}package com.ham.niminginnerclass;public interface USB {public abstract void open();public abstract void close();
}
匿名内部类的使用场景:
- 事件监听器:在需要为事件添加监听器时,可以利用匿名内部类快速实现接口中的方法。
- 线程的实现:创建线程时,可以匿名内部类实现
Runnable
接口。- 临时任务:当需要临时实现某个接口,且该实现不需要复用时。
- 回调函数:在需要提供回调函数时,可以使用匿名内部类快速实现。
- 实现策略模式:在实现策略模式时,可以使用匿名内部类来定义不同的策略。
使用示例
- 当需要创建一个线程来执行特定的任务时,可以使用匿名内部类实现
Runnable
接口:
// 使用匿名内部类创建线程
new Thread(new Runnable() {@Overridepublic void run() {System.out.println("线程运行中");}
}).start();
- 在对对象数组进行排序时,可能需要提供一个自定义的比较器,这时可以使用匿名内部类:
// 使用匿名内部类实现比较器
String[] strings = {"banana", "apple", "orange"};
Arrays.sort(strings, new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return o1.compareTo(o2);}
});
- 当需要临时实现一个接口,并且这个实现不需要在其他地方复用时,可以使用匿名内部类:
// 使用匿名内部类实现临时任务
public void performTask() {Task task = new Task() {@Overridepublic void execute() {System.out.println("执行临时任务");}};task.execute();
}
结语
参考文档如下:
【Java】内部类的使用方法和使用特点-CSDN博客
Java内部类详解(含:成员内部类、局部内部类、匿名内部类、静态内部类)-CSDN博客
36.内部类介绍_哔哩哔哩_bilibili
本篇作为复习内部类时的一部分笔记与总结,在之后仍会对本文不够详细之处作修订与补充。