1. 简介与定义
Java中的内部类是指定义在另一个类或方法内部的类。它允许一个类在另一个类的内部定义,这样可以更好地封装和组织代码。内部类可以分为静态内部类和非静态内部类(成员内部类)、局部内部类和匿名内部类,每种类型都有其独特的特性和用途。
内部类:包含自定义数据类型,但是该自定义数据类,不依赖与该类它并没有实际意义,因此就可以不必创建一个新的类,而定义成内部类。
比如:人这个类,其中包含心脏,心脏有自己的属性,但心脏需要依赖于人,失去人将不会有任何意义。
// 外部类:人类
public class Person {
private String name;
private int age;
private Heart heart; // 内部类实例作为外部类的成员
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
this.heart = new Heart(); // 初始化内部类实例
}
// 内部类:心脏类
private class Heart {
private String heartbeat;
// 构造方法
public Heart() {
this.heartbeat = "thump-thump"; // 心跳的初始值
}
// 方法:获取心跳
public String getHeartbeat() {
return heartbeat;
}
}
// 方法:获取人的心脏对象
public Heart getHeart() {
return heart;
}
// 方法:显示人的信息
public void displayInfo() {
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Heartbeat: " + heart.getHeartbeat());
}
// 主方法:测试
public static void main(String[] args) {
Person person = new Person("Alice", 30);
person.displayInfo();
}
}
Person
类代表了一个人,它有姓名和年龄属性,还有一个Heart
内部类。Heart
类代表了人的心脏,它有自己的属性(如心跳),但是它是依赖于人存在的,失去人的实例,心脏也就失去了意义。Person
类在构造方法中初始化了Heart
内部类的实例,并提供了方法来获取心脏对象和显示人的信息。
内部类使用场景:
- 内部类表示的事物是外部类的一部分。
- 内部类单独出现没有任何意义。
2. 内部类的语法和使用方法
声明和初始化
内部类的声明方式如下:
public class OuterClass {
// 成员内部类
class InnerClass {
// 内部类的成员和方法
}
// 静态内部类
static class StaticInnerClass {
// 静态内部类的成员和方法
}
void createInnerClass() {
// 局部内部类
class LocalInnerClass {
// 局部内部类的成员和方法
}
LocalInnerClass localInner = new LocalInnerClass();
}
void createAnonymousClass() {
// 匿名内部类
Runnable r = new Runnable() {
@Override
public void run() {
// 实现Runnable接口的匿名内部类的方法
}
};
Thread t = new Thread(r);
t.start();
}
}
访问控制和作用域
内部类可以访问外部类的私有成员,而外部类也可以访问内部类的成员,通过特殊的语法形式 this
和 外部类名.this
进行区分和访问。
内部类的访问特点:
- 内部类可以直接访问外部类的成员,
- 包括私有外部类要访问内部类的成员,必须创建对象
实际场景:
比如:集合中的迭代器就是内部类,如果集合都没有遍历个毛啊
3. 匿名内部类
匿名内部类是Java中的一种特殊类型的内部类。它没有显式的类名,而是在创建对象的同时定义类的方式。
一般用于:参数需要一个对象的时候,可以直接使用。
匿名内部类使用场景:实现类,我只需要使用一次(创建一次对象),如果单独创建一个类会很麻烦。
反复创建的对象的话,就需要使用外部类。名字的意义:为了让别人可以通过该名字反复的使用该名字所代表的内容。
格式:
匿名内部类和外部类的区别
public class Test {
public static void main(String[] args) {
//创建Cat类的对象,并调用方法使用
Animal a1 = new Cat();
a1.eat();
//使用匿名内部类的方式,创建一个动物对象
/*
new 父类名/接口名(){
//内容
}
*/
/*
new: 申请内存,调用构造方法创建对象
Animal: 用于规定匿名内部类中必须有什么内容
(): 调用匿名内部类的空参构造创建对象
{}: 指定类的内容
*/
Animal a2 = new Animal(){
@Override
public void eat() {
System.out.println("吃草草~");
}
};
a2.eat();
//使用匿名内部类的方式创建另外一个对象
//注意: 使用匿名内部类,定义类的同时可以根据该类创建一个对象,没有办法重复使用
Animal a3 = new Animal() {
@Override
public void eat() {
System.out.println("吃草草~");
}
};
a3.eat();
/*
匿名内部类和传统外部类的区别:
匿名局部内部类:
定义类的同时可以创建一个该类的对象
无需要单独创建文件
内部类在项目编译完毕后,在jvm运行过程中,在jvm内部进行编译,存在.class文件
外部类:
可以根据该类创建无数个对象
每定义一个类,需要创建一个新的文件
外部类在项目编译完毕后,存在.class文件
使用场景:
如果某个类只创建一个对象,建议使用匿名内部类
如果某个类要反复创建对象, 建议使用外部类
*/
}
}
注意事项
匿名内部类不仅可以使用于抽象类和接口,也可以使用在具体类中,但没有实际意义。
如果一个具体类没有抽象方法,你仍然可以使用匿名内部类,但可能没有特别需要重写的方法。匿名内部类的主要用途之一是在需要实现接口或抽象方法的情况下提供一种快速、临时的实现方式。
public class Main {
public static void main(String[] args) {
// 使用匿名内部类扩展具体类的功能
MyConcreteClass obj = new MyConcreteClass() {
@Override
public void additionalMethod() {
System.out.println("Added behavior in anonymous inner class");
}
};
obj.normalMethod(); // 调用具体类的方法
obj.additionalMethod(); // 调用匿名内部类中新增的方法
}
}
// 具体类
class MyConcreteClass {
public void normalMethod() {
System.out.println("Normal behavior in concrete class");
}
}
虽然没有必须要重写的抽象方法,但有时你可能希望定制化地修改具体类的某些行为,或者在实例化时做一些特定的初始化操作。
如果没有重写的方法,那就更没有意义了,这种情况下,使用匿名内部类会显得多余,因为具体类已经提供了完整的实现。匿名内部类的主要用途是在需要实现接口或者抽象方法的情况下提供一种便捷的方式,或者在需要动态实现某些特定逻辑时使用。
4. 内部类使用场景
UI开发中的应用
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 处理按钮点击事件的匿名内部类
}
});
数据结构和算法
public class BinaryTree {
private class TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) {
this.val = val;
left = right = null;
}
}
// BinaryTree的成员和方法
}
线程和并发编程
public class ThreadDemo {
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
// 线程的匿名内部类
}
};
Thread t = new Thread(r);
t.start();
}
}