java-内部类详解
内部类概念
- 定义:把类定义在其他类的内部,这个类被称为内部类(嵌套类)。
- 分类:根据其所在位置及形式的不同,分为成员内部类、静态内部类、局部内部类、匿名内部类。
成员内部类
-
联想到成员变量和成员方法,内部类所在位置和类的成员同级
-
// 外部类 class OuterClass01 {// 成员内部类class InnerClass01 {}}
-
观察一下生成的字节码文件,有两个:OuterClass01.class、OuterClass01KaTeX parse error: Expected 'EOF', got '&' at position 74: …so/search?q=外部类&̲spm=1001.2101.3…成员内部类名称.class。
创建方式:
-
创建成员内部类对象的方式 1
-
通过调用成员内部类的构造函数。
-
格式:外部类名.内部类名 成员内部类对象名 = 外部类对象.new 内部类构造函数();
-
方法1、OuterClass01.InnerClass01 innerClass01 = (new OuterClass01()).new InnerClass01();
-
方法2、OuterClass01.InnerClass01 innerClass01 = new OuterClass01().new InnerClass01();
-
package cn.temptation;public class Test {public static void main(String[] args) {//创建出外部类对象OuterClass01 outerClass01 = new OuterClass01();OuterClass01.InnerClass01 innerClass01 = outerClass01.new InnerClass01();System.out.println(innerClass01);// cn.temptation.OuterClass01$InnerClass01@15db9742}}
-
理解:OuterClass01.InnerClass01 innerClass01 = outerClass01.new InnerClass01();
-
可以理解为:OuterClass01.InnerClass01 innerClass01 = outerClass01.(new InnerClass01());(这种写法语法有错,只是为了方便理解才这样写)即,通过对象名.这样的方式调用类的成员,这里的成员内部类也是这个外部类的成员,也就考虑使用对象名.这样的方式进行调用且这里要获取的是成员内部类对象,所以通过new 成员内部类的构造函数获取成员内部类对象
-
创建成员内部类对象的方式2
-
通过调用成员内部类所在的外部类的成员方法,在外部类中创建出成员内部类的对象
-
// 外部类 class OuterClass02 {// 成员变量private InnerClass02 innerClass02;// 成员方法// 多例创建方法(在类的内部,成员方法可以访问成员)public InnerClass02 getInnerClass02() {return new InnerClass02();}// 单例创建方法(懒汉式,在类的内部,成员方法可以访问成员)public InnerClass02 getInstance() {if (innerClass02 == null) {innerClass02 = new InnerClass02();}return innerClass02;}// 成员内部类class InnerClass02 {// 设置构造函数为私有private InnerClass02() {}}}
-
package cn.temptation;public class Test {public static void main(String[] args) {OuterClass02 outerClass02 = new OuterClass02();// 创建成员内部类对象的写法(多例)OuterClass02.InnerClass02 innerClass02 = outerClass02.getInnerClass02();System.out.println(innerClass02); //cn.temptation.OuterClass02$InnerClass02@15db9742OuterClass02.InnerClass02 innerClass02Ex = outerClass02.getInnerClass02();System.out.println(innerClass02Ex); //cn.temptation.OuterClass02$InnerClass02@6d06d69cSystem.out.println("----------------------");// 创建成员内部类对象的写法(单例)OuterClass02.InnerClass02 innerClass02Demo = outerClass02.getInstance();System.out.println(innerClass02Demo); //cn.temptation.OuterClass02$InnerClass02@7852e922OuterClass02.InnerClass02 innerClass02DemoEx = outerClass02.getInstance();System.out.println(innerClass02DemoEx); //cn.temptation.OuterClass02$InnerClass02@7852e922}}
特点:
-
成员内部类可以直接访问其所在的外部类的非静态成员变量和静态的成员变量
-
// 外部类class OuterClass04 {// 非静态的成员变量public int i = 2;private int j = 3;// 静态的成员变量public static int x = 4;private static int y = 5;// 成员内部类class InnerClass04 {// 成员方法public void showByInner() {// 成员内部类对于其所在的外部类的非静态成员变量均可以访问(不论使用public修饰,还是private修饰)System.out.println(i);System.out.println(j);// 成员内部类对于其所在的外部类的静态成员变量均可以访问(不论使用public修饰,还是private修饰)System.out.println(x);System.out.println(y);}}
-
外部类和内部类成员变量相同时: 获取的变量值采取的是就近原则
-
// 外部类class OuterClass05 {// 非静态的成员变量public int i = 2;// 外部类的成员变量和内部类的成员变量同名public int k = 4;// 静态的成员变量public static int x = 6;public static int z = 8;// 成员内部类class InnerClass05 {// 非静态的成员变量public int j = 3;// 外部类的成员变量和内部类的成员变量同名public int k = 5;// 静态的成员变量:在非静态的成员内部类中定义静态的成员变量,语法出错// 语法错误:The field y cannot be declared static in a non-static inner type, unless initialized with a constant expression// public static int y = 7;// 语法错误:The field z cannot be declared static in a non-static inner type, unless initialized with a constant expression// public static int z = 9;// 成员方法public void showByInner() {System.out.println(i); // 2System.out.println(j); // 3System.out.println(k); // 5 --- 内部类的成员变量(就近原则)System.out.println(this.k); // 5 --- 内部类的成员变量(this指的是当前这个成员内部类对象)System.out.println(this); // cn.temptation.OuterClass05$InnerClass05@15db9742// 问题:就想在成员内部类的成员方法中使用其所在的外部类的同名成员变量,怎么办?// 思路1、考虑创建外部类对象,再通过外部类对象.成员变量获取System.out.println((new OuterClass05()).k); // 4 --- 外部类的成员变量// 思路2、考虑成员内部类对象创建出来时,其所在的外部类对象是否创建了,如果创建了,是什么样的访问形式// 答:创建了,访问形式为外部类类名.thisSystem.out.println(OuterClass05.this); // cn.temptation.OuterClass05@6d06d69cSystem.out.println(OuterClass05.this.k); // 4 --- 外部类的成员变量// 访问外部类的静态成员变量,直接通过外部类类名.静态成员变量System.out.println(OuterClass05.x); // 6System.out.println(OuterClass05.z); // 8}}
-
成员内部类的成员方法可以访问其所在的外部类的所有成员方法(不论是否公有,不论是否静态)
-
// 外部类class OuterClass06 {// 成员方法public void method1() {System.out.println("外部类的公有非静态成员方法");}private void method2() {System.out.println("外部类的私有非静态成员方法");}public static void method3() {System.out.println("外部类的公有静态成员方法");}private static void method4() {System.out.println("外部类的私有静态成员方法");}// 成员内部类class InnerClass06 {// 成员方法public void showByInner() {// 成员内部类的成员方法可以访问其所在的外部类的所有成员方法(不论是否公有,不论是否静态)method1();method2();method3();method4();}}
静态内部类 (联想到静态成员,使用static修饰)
-
// 外部类class OuterClass08 {// 静态内部类static class InnerClass08 {}}
-
观察一下生成的字节码文件,有两个:OuterClass08.class、OuterClass08 I n n e r C l a s s 08. c l a s s ,静态内部类字节码名称为:外部类名称 InnerClass08.class,静态内部类字节码名称为:外部类名称 InnerClass08.class,静态内部类字节码名称为:外部类名称静态内部类名称.class。
创建方式:
-
格式:外部类名.内部类名 静态内部类对象名 = new 外部类名.静态内部类构造函数();
-
package cn.temptation;public class Test {public static void main(String[] args) {OuterClass08.InnerClass08 innerClass08 = new OuterClass08.InnerClass08();System.out.println(innerClass08);// cn.temptation.OuterClass08$InnerClass08@15db9742}}
特点:
-
静态内部类中不论是静态的成员方法,还是非静态的成员方法,只能访问其所在的外部类的静态成员变量
-
// 外部类class OuterClass09 {// 非静态的成员变量public int i = 2;private int j = 3;// 静态的成员变量public static int x = 4;private static int y = 5;// 静态内部类static class InnerClass09 {// 非静态的成员方法public void showByInner() {// 静态内部类中的非静态成员方法无法访问其所在的外部类的非静态的成员变量// Cannot make a static reference to the non-static field i// System.out.println(i);// Cannot make a static reference to the non-static field j// System.out.println(j);// 静态内部类中的非静态成员方法可以访问其所在的外部类的静态的成员变量System.out.println(x); // 4System.out.println(y); // 5}// 静态的成员方法public static void showByInnerStatic() {// 静态内部类中的静态成员方法无法访问其所在的外部类的非静态的成员变量// Cannot make a static reference to the non-static field i// System.out.println(i);// Cannot make a static reference to the non-static field j// System.out.println(j);System.out.println(x); // 4System.out.println(y); // 5}}
-
静态内部类中不论是静态的成员方法,还是非静态的成员方法,只能访问其所在的外部类的静态成员方法
-
// 外部类class OuterClass10 {// 成员方法public void method1() {System.out.println("外部类的公有非静态成员方法");}private void method2() {System.out.println("外部类的私有非静态成员方法");}public static void method3() {System.out.println("外部类的公有静态成员方法");}private static void method4() {System.out.println("外部类的私有静态成员方法");}// 静态内部类static class InnerClass10 {// 非静态的成员方法public void showByInner() {// 语法错误:Cannot make a static reference to the non-static method method1() from the type OuterClass10// method1();// 语法错误:Cannot make a static reference to the non-static method method2() from the type OuterClass10// method2();method3();method4();}// 静态的成员方法public static void showByInnerStatic() {// 语法错误:Cannot make a static reference to the non-static method method1() from the type OuterClass10// method1();// 语法错误:Cannot make a static reference to the non-static method method2() from the type OuterClass10// method2();method3();method4();}}
局部内部类(联想到局部变量,位于外部类的成员方法之中)
-
// 外部类class OuterClass12 {// 成员方法public Object showByOuter() {// 局部内部类class InnerClass12 {}// 在外部类的成员方法中实例化局部内部类InnerClass12 innerClass12 = new InnerClass12();return innerClass12;}}
-
观察一下生成的字节码文件,有两个:OuterClass12.class、OuterClass12$1InnerClass12.class 局部内部类字节码名称为: 外部类名称 $ 编号 局部内部类名称.class。这里的编号是局部内部类类型的相应的编号,同一种局部内部类编号从1开始。
-
注意:在同一个外部类的成员方法中,不允许创建多个同名的局部内部类;外部类中的不同成员方法中可以定义相同名称的局部内部类,这些局部内部类可以名称相同、成员不同。
-
局部内部类的成员方法访问局部内部类所在的外部类的成员方法中定义的局部变量,局部变量可以使用final修饰,也可以不使用final修饰 ,但是不论是否使用final修饰,对于局部内部类的成员方法均只能访问(读取),而不能设置(赋值)
创建方式:
- // 只能通过其所在的外部类的成员方法返回在成员方法中创建出的局部内部类对象
- OuterClass12 outerClass12 = new OuterClass12();
- System.out.println(outerClass12.showByOuter());
特点
-
局部内部类中可以直接访问其所在的外部类的非静态的成员变量 和 静态的成员变量
-
// 外部类class OuterClass13 {// 非静态的成员变量public int i = 2;private int j = 3;// 外部类和局部内部类同名的成员变量public int k = 4;// 静态的成员变量public static int x = 6;private static int y = 7;public static int z = 8;// 成员方法public void showByOuter() {// 局部内部类class InnerClass13 {// 非静态的成员变量// 外部类和局部内部类同名的成员变量public int k = 5;// 静态的成员变量// 语法错误:The field z cannot be declared static in a non-static inner type, unless initialized with a constant expression// public static int z = 8;// 成员方法public void showByInner() {// 局部内部类对于其所在的外部类的非静态成员变量均可以访问(不论使用public修饰,还是private修饰)System.out.println(i); // 2System.out.println(j); // 3// 局部内部类对于其所在的外部类的静态成员变量均可以访问(不论使用public修饰,还是private修饰)System.out.println(x); // 6System.out.println(y); // 7System.out.println(k); // 5 --- 局部内部类的非静态成员变量(就近原则)System.out.println(this.k); // 5 --- 局部内部类的非静态成员变量(this指的是局部内部类对象)System.out.println(this); // cn.temptation.OuterClass13$1InnerClass13@15db9742System.out.println(OuterClass13.this); // cn.temptation.OuterClass13@6d06d69cSystem.out.println(OuterClass13.this.k); // 4 --- 外部类的非静态成员变量System.out.println(OuterClass13.z); // 8}}// 在外部类的成员方法中实例化局部内部类对象InnerClass13 innerClass13 = new InnerClass13();innerClass13.showByInner();}
-
局部内部类中可以直接访问其所在的外部类的非静态的成员方法 和 静态的成员方法
-
// 外部类class OuterClass14 {// 成员方法public void method1() {System.out.println("外部类的公有非静态成员方法");}private void method2() {System.out.println("外部类的私有非静态成员方法");}public static void method3() {System.out.println("外部类的公有静态成员方法");}private static void method4() {System.out.println("外部类的私有静态成员方法");}public void showByOuter() {// 局部内部类class InnerClass14 {// 成员方法public void showByInner() {// 局部内部类对于其所在的外部类的非静态成员方法均可以访问(不论使用public修饰,还是private修饰)method1();method2();// 局部内部类对于其所在的外部类的静态成员方法均可以访问(不论使用public修饰,还是private修饰)method3();method4();}}// 在外部类的成员方法中实例化局部内部类对象InnerClass14 innerClass14 = new InnerClass14();innerClass14.showByInner();}
匿名内部类(联想到匿名对象和匿名数组,内部类没有名称,需要存在一个类或接口)
-
// 定义接口interface Foo {public abstract void show();}// 接口的实现类class FooImpl implements Foo {@Overridepublic void show() {System.out.println("接口的实现类的成员方法");}}// 外部类class OuterClass18 {// 成员方法public Foo showByOuter() {// 多态的写法,创建接口类型的变量,接收接口实现类类型的对象,并返回// Foo foo = new FooImpl();// return foo;// 对上述写法的变形,匿名对象的写法// return new FooImpl();// 实现了Foo接口的匿名内部类(位置位于类似局部内部类的位置)return new Foo() {@Overridepublic void show() {System.out.println("实现了接口的匿名内部类");}};}
-
观察一下生成的字节码文件,有两个:OuterClass18.class、OuterClass18 1. c l a s s , 匿名内部类字节码名称为:外部类名称 1.class,匿名内部类字节码名称为: 外部类名称 1.class,匿名内部类字节码名称为:外部类名称编号.class
创建方式
-
通过调用匿名内部类所在的外部类的成员方法
-
package cn.temptation;public class Test {public static void main(String[] args) {OuterClass18 outerClass18 = new OuterClass18();// 定义接口类型的变量来接收匿名内部类对象的返回Foo foo = outerClass18.showByOuter();System.out.println(foo);// cn.temptation.OuterClass18$1@15db9742foo.show(); // 这句语句实际调用接口实现对象(匿名内部类对象)的成员方法}}
特点
-
匿名内部类没有构造函数,且使用者无法创建构造函数,但是实际上JDK为匿名内部类生成了构造函数
-
匿名内部类的成员方法中可以访问其所在的外部类的成员变量(不论是否公有私有,不论是否静态非静态),变量相同时也是就近原则
-
// 接口interface Sample {public abstract void show();}// 外部类class OuterClass22 {// 成员变量public int i = 2;private int j = 3;int k = 6;public static int x = 4;private static int y = 5;// 成员方法public void showByOuter() {// 局部变量int k = 7;// 匿名内部类(new Sample() {@Overridepublic void show() {System.out.println(i); // 2System.out.println(j); // 3System.out.println(x); // 4System.out.println(y); // 5System.out.println(k); // 7 --- 局部变量(就近原则)// 语法错误:k cannot be resolved or is not a field// System.out.println(this.k);System.out.println(this); // cn.temptation.OuterClass22$1@15db9742System.out.println(OuterClass22.this); // cn.temptation.OuterClass22@6d06d69cSystem.out.println(OuterClass22.this.k);// 6}}).show();}