Java中的内部类
Java中的内部类含义就是在一个Java类A的作用域下定义了一个类B,类B就称作为类A的内部类。内部类的机制很强大,在类B中不仅可以访问自身的作用域的数据,还可以访问外围类的实例域。在Java中,内部类分为四种:普通内部类(成员内部类)、局部内部类、匿名内部类、静态内部类(或嵌套类)
内部类原理
JVM中是不存在内部类的,在编译器中,就将内部类编译成了一个【外部类$内部类】作为类名的类,在JVM中外部类与内部类是两个单独的类:
# 编译下述代码会产生两个文件:Outer.class和Outer$Inner.class
class Outer {
class Inner{}
}
在内部类代码中,编译器为这个类默认生成了一个构造器,将外部类作为参数传递进来,如下图,用javap -private查看所有类和成员,可看到内部类的结构,OuterClass为外部类,FormatHobby为内部类:
在外部类中会生成静态方法,用于内部类进行调用,以下access$000就是提供给内部类调用的静态方法。内部类使用了外部类中的String[] hobby,于是通过access$000返回一个String[],提高访问权限:
普通内部类(或称为成员内部类)
定义一个普通内部类
点击查看代码
/**
* Company: XXXXXX
*
* @description:
* @author: banmao
* @date: 2022/1/10 9:49
*/
public class OuterClass {
private String name;
private String[] hobby;
public OuterClass(String name, String[] hobby) {
this.name = name;
this.hobby = hobby;
}
private void toIntroduceMyself() {
// 自我介绍
FormatHobby formatHobby = new FormatHobby();
System.out.println("My name is " + name + ",My hobbies are " + formatHobby.format());
}
private class FormatHobby {
// 内部类
private String format() {
String hobbyStr = "";
for (String str : hobby) {
hobbyStr += str + ",";
}
return hobbyStr.substring(0, hobbyStr.length() - 1);
}
}
public static void main(String[] args) {
OuterClass outerClass = new OuterClass("outerClass", new String[]{"篮球", "台球"});
outerClass.toIntroduceMyself();
}
}
几个关键的点
1、如上,定义了一个普通内部类FormatHobby,且访问修饰符为private,这也是内部类独有的,外部类只能是public或package的访问权限,内部类的访问修饰符决定了内部类对外部其它类可不可见。private代表仅仅对当前所在的OuterClass可见。
2、普通内部类中不能拥有静态属性,静态方法,例:AB代表外部类,CD代表内部类,A.C与B.D,C与D如果有共享的静态属性,但是这个属性对A,B来说应该是不共享的,因为A与B是单独的两个对象,对象的数据应该隔离,所以C与D不应该有共享的数据。
3、在外部类的main方法中无法实例化内部类,因为内部类实例中默认传的是this关键字进行构造,比如FormatHobby formatHobby = new FormatHobby(this);
,this关键字是属于对象的,不能在static方法中使用。
局部内部类
点击查看代码
/**
* Company: XXXXXX
*
* @description:
* @author: banmao
* @date: 2022/1/10 9:49
*/
public class OuterClass2 {
public String name;
private String[] hobby;
public OuterClass2(String name, String[] hobby) {
this.name = name;
this.hobby = hobby;
}
private void toIntroduceMyself(String appendHobby) {
// 自我介绍
class FormatHobby {
private String format() {
String hobbyStr = "";
for (String str : hobby) {
hobbyStr += str + ",";
}
return hobbyStr + appendHobby;
}
}
FormatHobby formatHobby = new FormatHobby();
System.out.println("My name is " + name + ",My hobbies are " + formatHobby.format());
}
public static void main(String[] args) {
OuterClass2 outerClass = new OuterClass2("outerClass", new String[]{"篮球", "台球"});
outerClass.toIntroduceMyself("乒乓球");
}
}
几个关键点
1、局部类不能用private或private进行声明,局部内部类的访问权限控制在了声明这个局部类的块中
2、局部类可以访问局部变量,但是那些局部变量必须事实上为final,事实上为final的含义是表明这个变量一旦声明则不改变
3、不能定义静态成员与静态方法,不能是public,protected,private,static
匿名内部类
点击查看代码
/**
* Company: XXXXXX
*
* @description:
* @author: banmao
* @date: 2022/1/10 9:49
*/
public class OuterClass3 {
public String name;
private String[] hobby;
public OuterClass3(String name, String[] hobby) {
this.name = name;
this.hobby = hobby;
}
private void toIntroduceMyself(String appendHobby) {
FormatHobbyInterface formatHobby = new FormatHobbyInterface() {
public String format() {
String hobbyStr = "";
for (String str : hobby) {
hobbyStr += str + ",";
}
return hobbyStr + appendHobby;
}
};
System.out.println("My name is " + name + ",My hobbies are " + formatHobby.format());
}
public static void main(String[] args) {
OuterClass3 outerClass = new OuterClass3("outerClass", new String[]{"篮球", "台球"});
outerClass.toIntroduceMyself("乒乓球");
}
}
几个关键点
1、匿名内部类含义是创建一个类或接口的没有类名的,内容包含在{}中的子类或实现类
2、可以用Java8 Lambda表达式来改写,表现的更加简洁
3、常用做法是使用匿名内部类来实现事件监听器和其他回调
4、匿名内部类不能有构造方法(因为没有类名)
5、匿名内部类属于局部内部类,局部内部类的所有限制对它都有效
静态内部类(或称为嵌套类)
点击查看代码
/**
* Company: XXXXXX
*
* @description:
* @author: banmao
* @date: 2022/1/10 9:49
*/
public class OuterClass4 {
public String name;
public OuterClass4(String name) {
this.name = name;
}
private void toIntroduceMyself(String appendHobby) {
System.out.println("My name is " + name + ",My hobbies are " + appendHobby);
}
public static void main(String[] args) {
OuterClass4 outerClass = new OuterClass4("outerClass");
outerClass.toIntroduceMyself("乒乓球");
}
public static class StaticInnerClass {
// 静态内部类
private static String name = "静态内部类";
private String hobby;
public StaticInnerClass(String hobby) {
this.hobby = hobby;
}
public static void toIntroduceMyself() {
System.out.println("我是:" + name);
}
public void printHobby() {
System.out.println("我是:" + name + ",我的爱好是:" + hobby);
}
}
}
几个关键点
1、静态内部类与其他内部类的不同在于,静态内部类其实算做一个顶级类,它对外部类并没有引用关系
2、静态内部类可以有静态属性与静态方法,静态内部类只能访问外部类的静态属性与方法
3、静态内部类的使用场景一般是通过类嵌套的结构来命名控制(比如避免命名冲突)
总结
1、四种内部类,成员内部类、局部内部类、匿名内部类、静态内部类
2、比较常用的为成员内部类与匿名内部类
3、在JVM中是不存在内部类概念,编译器将内部类解析成了顶级类
扩展
1、内部类都可以无限套娃(嵌套定义)吗?都可以
2、在接口、抽象类、枚举、注解中都可以定义内部类吗?
接口中不能有成员内部类,可以有静态内部类,使用java1.8的default关键字的话,可以定义局部内部类与匿名内部类
点击查看代码
/**
* Company: XXXXXX
* Description:
*
* @author banmao
* @date 2022/1/12 8:55
*/
public interface InnerClassInterface {
class InnerClass {
// 静态内部类,public与static为缺省
}
default void print() {
class LocalInnerClass {
// 局部内部类
}
TestInterface testInterface = new TestInterface() {
@Override
public void printTest() {
System.out.println("匿名内部类");
}
};
testInterface.printTest();
}
}
自定义注解中只能允许有静态内部类,default关键字不能使用在注解类中,java中的自定义注解相当于一个接口
点击查看代码
public @interface TestAnnotation {
class InnerClass {
// 静态内部类,public与static为缺省
}
}
枚举类相当于一个类,其中定义的枚举类型就是继承此类的多个实例,所以在枚举类中,四种内部类都可以定义
点击查看代码
public enum TestEnum {
SUCCESS,
FAILURE;
class InnerClass {
// 成员内部类
}
public void print() {
class LocalInnerClass {
// 局部内部类
}
TestInterface testInterface = new TestInterface() {
@Override
public void printTest() {
System.out.println("匿名内部类");
}
};
testInterface.printTest();
}
static class StaticInnerClass {
// 静态内部类
}
}
抽象类中四种内部类都可以定义
代码类似于上述的枚举类,故代码省略
3、内部类还可以继承其它类吗?可以,每个内部类都可以独立的继承其它类,这也是Java中“多重继承”的一种补充