(一)、内部类的定义:定义在另一个类中的类。
(二)、内部类的特点:
1.内部类的方法可以访问
该内部类所在的
作用域中(类域或方法域)的数据,包括私有数据;
2.内部类的对象之所以可以访问外部类对象中的数据域,是因为内部类对象中有一个
隐式的引用(但static内部类没有这种附加指针
),它引用了实例化该内部对象的外部类对象。这个引用在内部类的定义中是不可见的;内部类对象对
外部类对象的引用是在构造函数中设置的,
编译器修改了所有内部类的构造函数,在构造函数中添加了一个外部类对象引用的参数;
3.内部类可以对
同一个包中的其他类隐藏起来(可以防止命名冲突等);
4.某些地方可以使用匿名内部类简化代码编写,如:回调函数中;
5.只有内部类可以是私有的,只有内部类可以是static的;常规类,最多只能是public或默认包可见性的;
6.内部类是一种
编译器现象,与虚拟机无关。编译器将会把内部类翻译成$(美元符号)分隔外部类名与内部类名的常规文件,而虚拟机对此一无所知。
(三)、内部类的特殊语法规则:
代码1:
- package com.flan.learn;
- /**
- * 带有内部类的外部类
- * @author lynn
- */
- public class OuterClass {
- //静态域
- private static int age = 24;
- //实例域
- private String name ="out flan";
- //外部类的方法中创建使用内部类
- public void useInnerClass(){
- //此处可以加this,也可以省略
- InnerClass in = this.new InnerClass();
- in.ImInner();
- //此处不能加this,因为static的属于类,而不属于某个对象
- StaticInnerClass sin = new StaticInnerClass();
- sin.ImStaticInner();
- }
- //普通方法
- public void ImOuter(){
- System.out.println("OuterClass中的普通方法-- > "+name);
- }
- /**
- * 普通内部类,编译器会修改其构造函数,
- * 将外部类的引用传递进来
- * 访问控制符同普通成员方法一样:
- * public 则所有包的任何类中都可以访问该类并创建对象
- * private 则除了外部类,其他任何类中都无法访问(只有内部类可以是私有的)
- * protected 则只有子类和外部类中可以访问并创建对象
- * 默认(不写) 则只有同包中的类,子类,外部类可以访问并创建对象
- * @author lynn
- */
- public class InnerClass{
- private String inName = "in flan";
- public void ImInner(){
- System.out.println("InnerClass 外部类数据域-- > "+name);
- System.out.println("InnerClass 内部 内数据域-- > "+inName);
- }
- }
- /**
- * 静态内部类,编译器不会将外部类的引用传递进来(除此以外其他特性跟普通内部类一样)
- * 只有内部类可以是static的
- * 访问标号规则同普通内部类
- * @author lynn
- */
- public static class StaticInnerClass{
- private String stName = "static in flan";
- public void ImStaticInner(){
- //这里只能使用静态域中的数据
- System.out.println("StaticInnerClass 外部类静态域 -- > "+age);
- System.out.println("StaticInnerClass 内部类普通域-- > "+stName);
- }
- }
- }
代码2:
- package com.flan.learn;
- /**
- * 测试类
- * 同包下 其他类中访问内部类
- * 内部类的访问控制符不能是private的
- * @author lynn
- */
- public class OuterClassTest {
- public static void main(String[] args) {
- OuterClass out = new OuterClass();
- out.ImOuter();
- System.out.println("-------------------------------------");
- out.useInnerClass();
- System.out.println("-------------------------------------");
- //创建普通 内部类(注意new前的out对象)
- OuterClass.InnerClass in = out.new InnerClass();
- in.ImInner();
- System.out.println("-------------------------------------");
- //创建静态内部类(这里不需要out对象)
- OuterClass.StaticInnerClass sin = new OuterClass.StaticInnerClass();
- sin.ImStaticInner();
- System.out.println("-------------------------------------");
- }
- }
(四)、局部内部类:
1.局部类一般定义在方法中。
2.
局部类不能用public和private访问符进行声明。它的作用域被限定在声明这个局部类的块中。
3.局部类有两个优势:①对外部世界可以完全地隐藏起来(外部类中也不可以访问局部类);②局部内部类不仅能够访问包含它的外部类,还可以访问局部变量(不过这些
局部变量必须声明为final);
4.局部内部类中访问局部变量必须为final的,但有时候确实需要修改局部变量,因此会带来不便。补救办法是:
使用一个长度为1的final数组来保存对应的局部变量;
局部内部类访问的局部变量必须为final的原因(见代码3):
代码3:
- package com.flan.learn;
- /**
- * 测试类
- * @author lynn
- */
- public class GeneralClassTest{
- public static void main(String[] args) {
- GeneralClass glc = new GeneralClass();
- //①注意,当该方法执行结束后,参数true(isRun)就会被释放,而在循环打印线程中的②处,还要使用isRun,因此会导致错误;
- //所以,语法要求将isRun参数设置为fianl的,这样编译器会将isRun作为参数,加入到局部内部类构成函数中,
- //且在局部内部类中增加一个对应的实例域,这样在局部内部类中就保存有对应参数的副本;
- glc.startPrint(true);
- }
- }
- /**
- * 含有局部内部类的 普通类
- * @author lynn
- */
- class GeneralClass {
- //普通类中的一个含有局部内部类的方法
- public void startPrint(final boolean isRun){
- //局部内部类
- class LocalInnerClass{
- //局部内部类中的一个普通方法,用来循环打印字符串
- public void cyclePrint(){
- //这是一个匿名内部类,用来循环打印线程(暂时不用关注,后面会解释)
- new Thread(){
- @Override
- public void run() {
- //②注意这里的 isRun
- //编译器会在本局部内部类中增加一个实例域,用于保存该变量
- while (isRun) {
- //③当方法产生中的局部变量不能设置为final时,可以用一个长度的数组来解决
- //boolean[] run = new boolean[1];
- //run[0] = isRun;
- //while (run[0])
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("我在循环 "+System.currentTimeMillis());
- }
- }
- }.start();
- }
- }
- //普通方法中的 逻辑,即调用内部类中的方法,来打印字符串
- LocalInnerClass lic = new LocalInnerClass();
- lic.cyclePrint();
- }
- }
(五)、匿名内部类:
1.将局部内部类的使用再深入一步,即匿名内部类(匿名内部类一定是局部内部类);
2.由于构造函数的名称必须与类名相同,而匿名内部类内没有类名,所以,
匿名内部类不能有构造函数;
3.匿名内部类一般有两种用法:①实现接口;②扩展超类中的方法(即重写超类中的方法);
4.语法规则:
- <span style="white-space:pre"> </span>//①扩展超类
- //其中的构造函数中的参数必须是父类中有的
- new SuperType(construction param)
- {
- //内部类中的函数及实例域
- }
- //②实现接口
- //小括号内不能有参数
- new InterfaceType()
- {
- //接口中需要实现的方法
- //可以使用方法的局部参数
- }
代码4:
- package com.flan.learn;
- /**
- * 匿名内部类两种基本用法
- * @author lynn
- */
- public class AnonymityInnerClass {
- public static void main(String[] args) {
- AnonymityInnerClass aic = new AnonymityInnerClass();
- aic.uperClass();
- aic.superInterfaceFirst();
- }
- //扩展超类中的函数
- public void uperClass(){
- //①扩展超类
- //可以选用带参数的Thread构造函数
- Thread t = new Thread()
- //花括号中的代码是匿名内部类的逻辑
- {
- @Override
- public void run() {
- //在这里对超类Thread中的run方法添加逻辑
- System.out.println("我扩展了超类中的方法 --> Thread");
- }
- };
- t.start();
- }
- //实现接口中的方法
- public void superInterfaceFirst(){
- //②实现接口
- //小括号内不能有参数
- FirstInterface ff = new FirstInterface()
- //花括号中的代码是匿名内部类的逻辑
- {
- //接口中需要实现的方法
- @Override
- public void ImFirst(int first) {
- System.out.println("我实现了接口中的方法 --> "+first);
- }
- };
- ff.ImFirst(111111);
- }
- }
- /**
- * 普通接口
- * @author lynn
- */
- interface FirstInterface {
- public void ImFirst(final int first);
- }
(六)、静态内部类
1.如果使用内部类
只是为了把一个类隐藏在另一个类中,并不需要在内部类中引用外部类对象,就可以把内部类声明为static的。
2.static的内部类不会有对外部类对象的引用。
3.声明在
接口中的内部类,自动成为public和static。