1、基本概念
当一个类的定义出现在另外一个类的类体中时,那么这个类叫做内部类(Inner),而这个内部类所在的类叫做外部类(Outer)。
类中的内容:(普通)成员变量、(普通)成员方法、构造方法、静态成员(变量、方法)、构造块和静态代码块、内部类。
当一个类存在的价值仅仅是为某一个类单独服务时,那么就可以将这个类定义为所服务类中的内部类,这样可以隐藏该类的实现细节并且可以方便的访问外部类的私有成员而不再需要提供公有的 get 和 set 方法。
2、内部类的分类
普通内部类(成员内部类):直接将一个类的定义放在另外一个类的类体中。
静态内部类:用
static
修饰的普通内部类,隶属于类层级。局部内部类:与上面的两者不同,局部内部类并不是在类体中,而是在一个方法体中。
3、普通内部类
- 普通内部类格式
访问修饰符 class 外部类的类名 {
······
访问修饰符 class 内部类的类名 {
······
}
······
}
- 简单举例:
public class OuterClass {
private int outerMemberValue = 10;
public class InnerClass {
private int innerMemberValue = 20;
public InnerClass() {
System.out.println("inner class constructor");
}
public void show() {
System.out.println("inner class member value: " + innerMemberValue);
System.out.println("outer class member value: " + outerMemberValue);
}
}
}
public class Main {
public static void main(String[] args) {
// 创建内部类对象之前需要创建外部类对象
OuterClass outerClass = new OuterClass();
// 内部类引用类型为 OuterClass.InnerClass
// 注意写法
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
innerClass.show();
}
}
输出:
inner class constructor
inner class member value: 20
outer class member value: 10
普通内部类和普通类一样可以定义成员变量、成员方法以及构造方法等。
普通内部类和普通类一样可以使用
final
或者abstract
关键字修饰。普通内部类还可以使用
private
或protected
关键字进行修饰,当然,也可以使用默认的访问修饰符。普通内部类需要使用外部类对象来创建对象。
如果内部类访问外部类中与本(内部)类中内部同名的成员变量或方法时,需要使用
外部类名.this.
。
public class OuterClass {
private int x = 0;
public class InnerClass {
private int x = 1;
public InnerClass() {
System.out.println("inner class constructor");
}
public void show() {
// 内部类的 x
System.out.println(x);
// 内部类的 x
System.out.println(this.x);
// 外部类的 x
System.out.println(OuterClass.this.x);
}
public void show2(int x) {
// 传递进来的参数 x
System.out.println(x);
// 内部类的 x
System.out.println(this.x);
// 外部类的 x
System.out.println(OuterClass.this.x);
}
}
}
4、静态内部类
- 静态内部类语法格式:
访问修饰符 class 外部类的类名 {
······
访问修饰符 static class 内部类的类名 {
······
}
······
}
静态内部类中可以有成员变量、成员方法、静态的成员变量、静态的成员方法、构造方法等。
静态内部类是无法访问外部类中的非静态成员变量与成员方法的。
简单举例:
public class OuterClass {
private int x = 1;
private static int y = 2;
/**
* 静态内部类
* 隶属于 类 层级
*/
public static class InnerClass {
private int z = 3;
public InnerClass() {
System.out.println("static inner class constructor");
}
public void show() {
// 可以访问外部类中的静态成员变量、成员方法
System.out.println(y);
}
}
}
public class Main {
public static void main(String[] args) {
// 静态内部类的引用类型为 OuterClass.InnerClass
// 静态内部类不需要通过其外部类的对象来创建
OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
innerClass.show();
}
}
输出:
static inner class constructor
2
静态内部类不能直接访问外部类的非静态成员。
静态内部类访问外部类的非静态成员需要在内部类中创建外部类的对象。
静态内部类可以直接创建对象,不需要通过其外部类的对象。
如果静态内部类访问外部类中与本(内部)类内同名的成员变量或方法时,需要使用
外部类类名.
的方式访问。
5、局部内部类
也称“方法内部类”。只在当前方法体的内部有效。
局部内部类格式:
因为局部内部类在方法体中而不是直接在类体中,所以,没有且不允许有访问修饰符。
局部内部类只能在该方法的内部可以使用。局部内部类可以在方法体内部直接创建对象。
局部内部类不能使用访问控制符和
static
关键字修饰。局部内部类可以使用外部方法(其所在方法)的局部变量,但是必须是这个变量必须是
final
的。由局部内部类和局部变量的声明周期不同所致。注意,一般情况下,都会显式的去将这个变量用final
修饰;从 Java 8 开始,一旦局部内部类中使用了其所在方法中的变量,即使那个变量没有显式被final
修饰,编译的时候,也会默认理解为用了final
修饰(就是说final
允许被省略)。
public class OuterClass {
public void show() {
int a = 10;
final int b = 20;
class InnerClass {
public void test() {
// a = 1; 报错
// b = 2; 报错
}
}
}
}
6、回调
- 回调模式是指:如果一个方法的参数是接口类型,则在调用该方法时,需要创建并传递一个实现此接口类型的对象;而该方法在运行时会调用到参数对象中所实现的方法(接口中定义的方法)。
public interface Service {
void doService();
}
public class ServiceImpl implements Service {
@Override
public void doService() {
System.out.println("do service");
}
}
public class Main {
// 回调
private static void method(Service service) {
service.doService();
}
public static void main(String[] args) {
method(new ServiceImpl());
}
}
- “回”的意思是指,将一个对象传递给一个方法,但是该方法内部又会去调用对象的方法。感觉稍稍有些“套娃”的感觉。
7、匿名内部类
public interface Service {
void doService();
}
public class Main {
private static void method(Service service) {
service.doService();
}
public static void main(String[] args) {
Service s1 = new Service() {
@Override
public void doService() {
System.out.println("service 1");
}
};
method(s1);
}
}
public class Main {
private static void method(Service service) {
service.doService();
}
public static void main(String[] args) {
// lamda 表达式实现匿名内部类
// (参数列表) -> {方法体}
Service s2 = () -> {
System.out.println("service 2");
};
method(s2);
}
}