8.1 概述
8.1.1 什么是内部类
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类(InnerClass)
,类B则称为外部类(OuterClass)
。
8.1.2 为什么要声明内部类呢
具体来说,当一个事物A的内部,还有一个部分需要一个完整的结构B进行描述,而这个内部的完整的结构B又只为外部事物A提供服务,不在其他地方单独使用,那么整个内部的完整结构B最好使用内部类。
总的来说,遵循高内聚、低耦合
的面向对象开发原则。
8.1.3 内部类的分类
根据内部类声明的位置(如同变量的分类),我们可以分为:成员内部类以及局部内部类。
8.2 成员内部类
8.2.1 概述
如果成员内部类中不使用外部类的非静态成员,那么通常将内部类声明为静态内部类,否则声明为非静态内部类。
语法格式:
[修饰符] class 外部类{ [其他修饰符] [static] class 内部类{ } }
成员内部类的使用特征,概括来讲有如下两种角色:
-
成员内部类作为
类的成员的角色
:-
和外部类不同,Inner class还可以声明为private或protected;
-
可以调用外部类的结构。(注意:在静态内部类中不能使用外部类的非静态成员)
-
Inner class 可以声明为static的,但此时就不能再使用外层类的非static的成员变量;
-
-
成员内部类作为
类的角色
:-
可以在内部定义属性、方法、构造器等结构
-
可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
-
可以声明为abstract类 ,因此可以被其它的内部类继承
-
可以声明为final的,表示不能被继承
-
编译以后生成OuterClass$InnerClass.class字节码文件(也适用于局部内部类)
-
注意点:
-
外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式
-
成员内部类可以直接使用外部类的所有成员,包括私有的数据
-
当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的
8.2.2 创建成员内部类对象
-
实例化静态内部类
外部类名.静态内部类名 变量 = 外部类名.静态内部类名(); 变量.非静态方法();
-
实例化非静态内部类
外部类名 变量1 = new 外部类(); 外部类名.非静态内部类名 变量2 = 变量1.new 非静态内部类名(); 变量2.非静态方法();
8.2.3 举例
public class TestMemberInnerClass { public static void main(String[] args) { //创建静态内部类实例,并调用方法 Outer.StaticInner inner = new Outer.StaticInner(); inner.inFun(); //调用静态内部类静态方法 Outer.StaticInner.inMethod(); System.out.println("*****************************"); //创建非静态内部类实例(方式1),并调用方法 Outer outer = new Outer(); Outer.NoStaticInner inner1 = outer.new NoStaticInner(); inner1.inFun(); //创建非静态内部类实例(方式2) Outer.NoStaticInner inner2 = outer.getNoStaticInner(); inner1.inFun(); } } class Outer{ private static String a = "外部类的静态a"; private static String b = "外部类的静态b"; private String c = "外部类对象的非静态c"; private String d = "外部类对象的非静态d"; static class StaticInner{ private static String a ="静态内部类的静态a"; private String c = "静态内部类对象的非静态c"; public static void inMethod(){ System.out.println("Inner.a = " + a); System.out.println("Outer.a = " + Outer.a); System.out.println("b = " + b); } public void inFun(){ System.out.println("Inner.inFun"); System.out.println("Outer.a = " + Outer.a); System.out.println("Inner.a = " + a); System.out.println("b = " + b); System.out.println("c = " + c); // System.out.println("d = " + d);//不能访问外部类的非静态成员 } } class NoStaticInner{ private String a = "非静态内部类对象的非静态a"; private String c = "非静态内部类对象的非静态c"; public void inFun(){ System.out.println("NoStaticInner.inFun"); System.out.println("Outer.a = " + Outer.a); System.out.println("a = " + a); System.out.println("b = " + b); System.out.println("Outer.c = " + Outer.this.c); System.out.println("c = " + c); System.out.println("d = " + d); } } public NoStaticInner getNoStaticInner(){ return new NoStaticInner(); } }
8.3 局部内部类
8.3.1 非匿名局部内部类
语法格式:
[修饰符] class 外部类{ [修饰符] 返回值类型 方法名(形参列表){ [final/abstract] class 内部类{ } } }
-
编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名、$符号、编号。
-
这里有编号是因为同一个外部类中,不同的方法中存在相同名称的局部内部类
-
-
和成员内部类不同的是,它前面不能有权限修饰符等
-
局部内部类如同局部变量一样,有作用域
-
局部内部类中是否能访问外部类的非静态的成员,取决于所在的方法
举例:
/** * ClassName: TestLocalInner * @Author 尚硅谷-宋红康 * @Create 17:19 * @Version 1.0 */ public class TestLocalInner { public static void main(String[] args) { Outer.outMethod(); System.out.println("-------------------"); Outer out = new Outer(); out.outTest(); System.out.println("-------------------"); Runner runner = Outer.getRunner(); runner.run(); } } class Outer{ public static void outMethod(){ System.out.println("Outer.outMethod"); final String c = "局部变量c"; class Inner{ public void inMethod(){ System.out.println("Inner.inMethod"); System.out.println(c); } } Inner in = new Inner(); in.inMethod(); } public void outTest(){ class Inner{ public void inMethod1(){ System.out.println("Inner.inMethod1"); } } Inner in = new Inner(); in.inMethod1(); } public static Runner getRunner(){ class LocalRunner implements Runner{ @Override public void run() { System.out.println("LocalRunner.run"); } } return new LocalRunner(); } } interface Runner{ void run(); }
8.3.2 匿名内部类
因为考虑到这个子类或实现类是一次性的,那么我们“费尽心机”的给它取名字,就显得多余。那么我们完全可以使用匿名内部类的方式来实现,避免给类命名的问题。
new 父类([实参列表]){ 重写方法... }
new 父接口(){ 重写方法... }
举例1:使用匿名内部类的对象直接调用方法:
interface A{ void a(); } public class Test{ public static void main(String[] args){ new A(){ @Override public void a() { System.out.println("aaaa"); } }.a(); } }
举例2:通过父类或父接口的变量多态引用匿名内部类的对象
interface A{ void a(); } public class Test{ public static void main(String[] args){ A obj = new A(){ @Override public void a() { System.out.println("aaaa"); } }; obj.a(); } }
举例3:匿名内部类的对象作为实参
interface A{ void method(); } public class Test{ public static void test(A a){ a.method(); } public static void main(String[] args){ test(new A(){ @Override public void method() { System.out.println("aaaa"); } }); } }
个人思考:
-
封装和隐藏实现细节:内部类可以用于隐藏一个类的实现细节,使其对外部世界不可见。这有助于提高代码的模块化和封装性。
-
实现多重继承:在某些编程语言中,内部类可以用于实现多重继承,因为一个类可以包含多个内部类,从而获得多个类的功能。
-
回调函数:内部类常常用于实现回调函数或事件处理程序。一个外部类可以定义一个内部类来实现某种接口或抽象类,然后在适当的时候创建内部类的实例并将其用作回调函数。
-
访问外部类成员:内部类可以访问外部类的成员,包括私有成员,这对于实现某些特定功能非常有用。
-
代码组织和可读性:内部类可以帮助组织代码,使其更具可读性。相关的类可以放在一起,减少类的数量,从而简化代码结构。
-
局部性和作用域:内部类的作用域通常被限制在包含它的外部类中,这可以防止内部类的名称与其他部分的代码发生冲突。
-
单例模式:内部类可以用于实现懒汉式单例模式,确保只有在需要时才会创建类的实例。
-
事件处理:在图形用户界面(GUI)开发中,内部类通常用于处理事件,例如按钮点击事件或鼠标事件。