内部类
概述: 在一个类的内部又创建了类,创建的类就叫做内部类
特点:
- 内部类会产生独立的class文件
- 外部内的资源可以直接在内部类中使用,而不破坏封装性
- 外部类的一些组件功能,可以交给内部类来完成
- 分类:成员内部类,静态内部类,局部内部类(了解),匿名内部类(重点)
一、成员内部类
在一个类中创建的普通的类
在Java中使用成员内部类时,需要注意以下几点:
- 访问内部类: 成员内部类可以访问外部类的所有成员(变量和方法),包括私有成员。但是,要从外部类外部访问内部类,需要使用语法“OuterClass.InerClass”。
- 实例化内部类: 要创建内部类的实例,首先需要实例化外部类。然后,使用外部类的实例,可以创建内部类的实例。语法为
OuterClass.InnerClass innerObject=outerObject.new InnerClass();
。 - 可见性: 可见性修饰符(public、private、protected)可以像任何其他类一样应用于成员内部类。但是,需要注意的是,如果内部类被声明为私有,则只能在外部类中访问它。
- 生存期: 内部类的实例与外部类的实例绑定。如果没有外部类的实例,它就不可能存在。这意味着内部类可以访问外部类的实例变量,即使它们没有声明为final。
- 命名冲突: 如果内部类的成员与外部类中的成员同名,则内部类成员优先。要显式引用外部类成员,可以使用语法
OuterClass.this.member
。
class Outter{
private String name="凤姐";
public void print() {
new Inner().test(); //外部类的一些功能,可以交给内部类来完成
}
class Inner{ //成员内部类
String name = "芙蓉";
//static int age = 30; //在成员内部类中,不能使用静态变量--加载时机有关
public void test() {
System.out.println(name); //可以调用外部类的属性
System.out.println(Outter.this.name); //调用外部类的属性
}
}
}
public class Test1 {
public static void main(String[] args) {
Outter out = new Outter(); //1.使用外部类来调用内部类方法
out.print();
//2.直接调用内部类方法: 通过外部类对象,产生内部类对象
Outter.Inner inner = new Outter().new Inner();
inner.test();
}
}
二、静态内部类
在一个类的内部创建一个static修饰的内部类
在Java中使用静态内部类时,需要注意以下几点:
- 访问内部类: 静态内部类独立于外部类,可以在不实例化外部类的情况下访问。您可以使用语法“OuterClass.InerClass”访问静态内部类。
- 实例化内部类: 要创建静态内部类的实例,可以使用语法
OuterClass.InnerClass innerObject=new OuterClass.INerClass();
直接实例化它。 - 可见性: 与其他类类似,您可以将可见性修饰符(public、private、protected)应用于静态内部类。但是,请注意,如果内部类被声明为private,则只能在外部类中访问它。
- 生存期: 静态内部类的生存期独立于外部类。即使没有外部类的实例,它们也可以存在。
- 访问外部类成员: 静态内部类不能直接访问外部类的非静态成员(变量或方法)。但是,他们可以通过创建外部类的实例来访问它们。
- 命名冲突: 如果静态内部类有一个成员与外部类中的成员同名,则不会有任何命名冲突,因为它们是独立的实体。
class Outter2{
private String name="凤姐";
private static int age = 30;
static class Inner2{ //静态内部类
public void print() {
//System.out.println(name); //在静态内部类中不能使用成员变量
System.out.println(age); //可以使用外部类的静态成员变量
System.out.println("内部类方法的调用");
}
}
}
public class Test2 {
public static void main(String[] args) {
//调用静态内部类1:通过类名来调资源
Outter2.Inner2 inner2 = new Outter2.Inner2();
inner2.print();
//简化版:直接导包 ...Outter2.Inner2
Inner2 in = new Inner2();
in.print();
}
}
三、局部内部类
在Java中使用本地内部类时,需要注意以下几点:
- 作用域: 本地内部类是在外部类的方法、块或构造函数中定义的。它们的范围有限,只能在定义它们的范围内访问。
- 可访问性: 本地内部类可以访问外部类的所有成员(变量和方法),包括私有成员。但是,如果它们被声明为final或实际上是final,则它们只能访问封闭范围的局部变量和参数。
- 生存期: 与其他类型的内部类相比,本地内部类的生存期更短。它们在执行封闭作用域时创建,在退出作用域时销毁。
- 实例化: 不能从封闭范围之外实例化本地内部类。它们只能在定义它们的范围内实例化。
- 命名冲突: 本地内部类可以与封闭范围内的其他类或变量同名。在命名冲突的情况下,本地内部类在其作用域内具有优先权。
//局部内部类:在外部类的方法中定义的类
//应用:在外部类的方法中,才能使用局部内部类(很少用);不能使用public修饰
class Outter3{
String name = "张三";
public void show() {
int age = 30;
class Inner3{ //局部内部类
public void test() {
System.out.println("调用局部内部类--"+name);
//age = 40; //外部类方法中定义的变量,会自动变为常量 +final
//原因是age的作用域要与局部内部类保持一致
System.out.println(age);
}
}
new Inner3().test(); //在当前方法中,才能调用局部内部类
}
}
public class Test3 {
public static void main(String[] args) {
new Outter3().show();;
}
}
四、匿名内部类
匿名内部类在设计上和局部内部类相似;但是本质为多态,也就是能用多态的案例,肯定能用匿名内部类
回顾多态案例(接口或抽象类都可以),有直接引用,传参多态,返回值多态
匿名内部类是一种特殊的内部类,它没有显式的类名,直接在代码中定义并实例化。使用匿名内部类时,需要注意以下几点:
- 语法: 匿名内部类通常用于创建接口或抽象类的实例,并在实例化时提供具体的实现。语法上,可以在创建对象的地方
使用关键字 new ,后跟要实现的接口或抽象类,并在大括号内提供具体的实现代码
。 - 生命周期: 匿名内部类的生命周期与其创建的位置有关。它们的
作用域仅限于创建它们的方法或代码块
,并且不能被其他方法或代码块访问。 - 访问外部变量: 匿名内部类
可以访问外部类的成员变量和方法,以及外部方法中的 final 或 effectively final 的局部变量
。如果要在匿名内部类中使用非 final 的局部变量,该变量必须是事实上的 final,即在定义后不再修改。 - 限制: 匿名内部类
不能是抽象类
,因为它们是直接实例化的。另外,一个匿名内部类只能继承一个类或实现一个接口
。 - 代码可读性: 由于匿名内部类
没有类名
,因此在代码阅读和维护时可能会变得复杂。因此,建议在简单的情况下使用匿名内部类
,而对于复杂的逻辑,最好使用具名的内部类或独立的类。
直接引用
应用场景:
- 当实例化一次对象,倾向于用匿名内部类;写法上简单,节约资源
- 当实例化多次对象,倾向于用多态;结果清晰,无需创建多个class资源
案例:
- 描述: 喷火娃具有喷火的能力
interface Fireable{
void fire();
}
class WaWa implements Fireable{
@Override
public void fire() {
System.out.println("喷火娃正在喷火...");
}
}
public class Test1 {
public static void main(String[] args) {
//1.接口直接引用的多态
Fireable able = new WaWa();
able.fire();
//2.匿名内部类的方式
Fireable able2 = new Fireable() {
@Override
public void fire() {
System.out.println("匿名内部类的喷火..");
}
};
able2.fire();
}
}
传参多态
案例:
- 描述: 直接调方法,多态传参;接口为USB标准;实现类为Disk
interface USB{
void run();
}
class Disk implements USB{
@Override
public void run() {
System.out.println("硬盘正在运转");
}
}
public class Test2 {
public static void main(String[] args) {
//传参多态:将Test2看成第三方类来使用
Test2.connect(new Disk());
//匿名内部类实现
Test2.connect(new USB() {
@Override
public void run() {
System.out.println("匿名设备正在运转");
}
});
}
private static void connect(USB u) {
u.run(); //接口回调
}
}
结论:多态有什么应用场景,那么匿名内部类就有什么场景