内部类
一、内部类
1.1 内部类的分类

1.2 什么是内部类
- 概念:在一个类的内部再定义一个完整的类。
- 特点:
- 编译之后可生成独立的字节码文件。
- 内部类可直接访问外部类的私有成员,而不破坏封装。
- 可为外部类提供必要的内部功能组件。
案例1:编译之后生成独立的字节码文件
1 2 3 4 5
| public class Outer { class Inner{
} }
|
内部类Inner
生成了字节码文件Outer$Inner.class

二、成员内部类
案例1:内部类的创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Outer { class Inner{
} } class TestOuter{ public static void main(String[] args) {
Outer outer1 = new Outer(); Outer.Inner inner1 = outer1.new Inner();
Outer.Inner inner2 = new Outer().new Inner(); } }
|
案例2:当外部类、内部类存在重名属性时,会优先访问内部类属性。
如需访问外部类的属性则需要使用外部类.this.属性名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Outer { public String name="张三"; public int age; class Inner{ public String name="李四"; void test(){ System.out.println(name); System.out.println(Outer.this.name); } } } class TestOuter{ public static void main(String[] args) { Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); inner.test(); } }
|
运行结果:

案例3:成员内部类不能定义静态成员(但可以定义静态常量)
静态内部类可以定义静态成员。
1 2 3 4 5 6 7 8 9
| public class Outer { public int age; class Inner{
public static String name; public static final String name1; public static final String name2=""; } }
|


三、静态内部类
- 不依赖外部类对象,可直接创建或通过类名访问,可声明静态成员。
- 只能直接访问外部类的静态成员(实例成员需实例化外部类对象)
- 声明方法:成员内部类前添加
static
关键字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class Outer { public String name="aaa"; public int age = 10; static class Inner{ public String address="武汉"; public String num = "123"; public static String count; public static String count1="123"; public void test(){
Outer outer = new Outer(); System.out.println(outer.name);
System.out.println("方法1:"+count); System.out.println("方法2:"+Outer.Inner.count1); } public static void test1(){ System.out.println("内部类中的静态方法"); } } } class TestOuter{ public static void main(String[] args) { Outer.Inner inner = new Outer.Inner(); inner.test(); System.out.println("***********************");
Outer.Inner.test1(); System.out.println(Outer.Inner.count); Outer.Inner.count="abcdefg"; System.out.println(Outer.Inner.count); } }
|
运行结果:

注意:只有内部类能够加static
修饰,普通类无法使用static
修饰。
四、局部内部类
- 定义在外部类的方法当中,作用范围和作用对象范围仅限于当前方法。
- 局部内部类访问外部类当前方法中的局部变量时,因无法保障变量的生命周期与自身相同,变量必须修饰为
final
- 限制类的使用范围
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public class Outer { private String name="xiaoming"; private int age=18; public void test(){
String phone = "12345";
class Inner{ private String a = "aaa"; private String b = "bbb"; public void test1(){ System.out.println(name); System.out.println(Outer.this.name); System.out.println(this.a); System.out.println(b); } } Inner inner = new Inner(); inner.test1(); } }
class TestOuter{ public static void main(String[] args) { Outer outer = new Outer(); outer.test(); } }
|
运行结果:

案例1:
局部内部类访问外部类当前方法中的局部变量时,因无法保障变量的生命周期与自身相同,变量必须修饰为final
,JDK1.7要求,JDK1.8及以后自动隐式添加final
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class Outer { public void test(){ String variable = "我是局部变量"; final String variable1 = "我是局部变量1"; class Inner{ void mytest(){ System.out.println(variable); System.out.println(variable1); } } Inner inner = new Inner(); inner.mytest(); } }
class TestOuter{ public static void main(String[] args) { Outer outer = new Outer(); outer.test(); } }
|
运行结果:
为什么要被final
修饰成常量呢?这是因为局部变量variable
与variable1
在当前的test
方法结束后就被销毁了,而创建的Inner
类不会被销毁,如果这个时候再次调用局部变量variable
或variable1
,程序将会出现异常。

案例2:局部内部类中不能声明静态成员,但是可以声明静态常量。
1 2 3 4 5 6 7
| public class Outer { public void test(){ class Inner{ private static String str; } } }
|
运行结果:

1 2 3 4 5 6 7 8
| public class Outer { public void test(){ class Inner{
private static final String str = "123"; } } }
|
五、匿名内部类
- 没有类名的局部内部类(一切特征都与局部内部类相同)。
- 必须继承一个父类或者实现一个接口。
- 定义类、实现类、创建对象的语法合并,只能创建一个该类的对象。
- 优点:减少代码量
- 缺点:可读性较差
案例1:
电脑有两个USB接口,键盘插入电脑,可以发现我们需要声明KeyBord
的类,然后再去创建对象,但是我们仅仅就使用一次,这样就显得过于麻烦了,那如果要插入鼠标,就需要再去声明鼠标类,去实现Usb
的方法。
Computer.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Computer { public Usb usb1; public Usb usb2; void run(){ System.out.println("电脑开始运行"); if(usb1 != null){ usb1.service(); } if(usb2 != null){ usb2.service(); } } }
|
Usb.java
1 2 3
| public interface Usb { void service(); }
|
KeyBoard.java
1 2 3 4 5 6 7
| public class KeyBord implements Usb{
@Override public void service() { System.out.println("键盘开始工作了"); } }
|
TestComputer.java
1 2 3 4 5 6 7
| public class TestComputer { public static void main(String[] args) { Computer computer = new Computer(); computer.usb1 = new KeyBoard(); computer.run(); } }
|
运行结果:

案例2:使用局部内部类创建键盘对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class TestComputer { public static void main(String[] args) { Computer computer = new Computer();
class KeyBoard implements Usb{ @Override public void service() { System.out.println("局部内部类 键盘开始运行了"); } } computer.usb1 = new KeyBoard(); computer.run(); } }
|
运行结果:

案例3:使用匿名内部类创建键盘对象
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class TestComputer { public static void main(String[] args) { Computer computer = new Computer();
computer.usb1 = new Usb() { @Override public void service() { System.out.println("匿名内部类 键盘开始运行了"); } }; computer.run(); } }
|
运行结果:

其实匿名内部类是用名字的,只是这个名字是由java编译器为我们取的,打开编译完成的字节码文件可以看到TestComputer$1KeyBoard.class
。
