Java中的内部类

定义

内部类,即定义在类内部的类。这里指的类的内部,可以是作为类的成员存在,也可以存在于类的其他成员中。

分类

在上面的定义中,可能说得很模糊,但是那就是分类的依据。首先,内部内根据是否有static关键字修饰而可以区分为静态内部类非静态内部类。而非静态内部内又可以根据其出现的位置细分为三种,作为外部类的成员存在的称作为 成员内部类; 定义在方法中的叫做方法内部类, 又称作局部内部类(下面成为方法内部类); 另外还有一种没有名称的内部类叫做匿名内部类
上述关系可以下述列表表示

  • 静态内部类
  • 非静态内部类
    • 成员内部类
    • 方法内部类
    • 匿名内部类

上面各种内部类都可以继续在内部嵌套定义其他内部类,但是有一些限制,具体细节在下面详解中会阐明。

各种内部类详解

静态内部类

静态内部类的声明如下代码所示:

static class StaticInnerClass {
// 一些属性

// 构造方法
// 一些其他方法

// 甚至一些内部类
}

创建静态内部类的实例:

StaticInnerClass sic = new TestInnerClass.StaticInnerClass();

在能够访问static成员的地方,还可以简化一下:

StaticInnerClass sic = new StaticInnerClass();

有几点需要注意:

  • 静态内部内作为外部类的成员存在,故可以有四种权限状态。
  • 关于静态内部类内部成员的权限和普通外部类一样,同样有四种可能:private, public, protected和默认。
  • 在静态内部类内部,访问外部成员的规则也和普通静态方法一样,即只能访问外部的静态成员,而不能访问实例成员。
  • 在静态内部类内部,可以继续创建上述四种内部类。

成员内部类

成员内部类的声明如下代码所示:

class FieldInnerClass{
// 一些属性

// 构造方法
// 一些其他方法

// 甚至一些其他内部类
}

特别注意如何创建成员内部类的实例:

FieldInnerClass fic = new TestInnerClass().new FieldInnerClass();

成员内部类的一般性质和静态内部类类似,除了下面的区别:

  • 相比静态内部类,成员内部类类似于实例方法,可以访问类中所有的成员,即没有静态限制。
  • 在成员内部类里面声明的静态成员必须是final的。
  • 在成员内部类可以继续定义内部类,但是由于上面第二条,不能创建静态内部类。即在整个内部类中都不能出现static关键字。

请注意静态内部类和成员内部类访问外部类的成员时对其final性质没有要求,这与下面两种内部类不同。

方法内部类

顾名思义,即定义在方法中的内部类。其作用域也仅限于该方法。具体形式如下代码所示:

public void MethodName() {
	// 一些变量定义
	// 一些其他操作
	class ClassName {
		// 一些变量定义
		// 一些方法定义
	}
	// 一些关于内部内的操作
} 

关于方法内部类有以下几点需要注意:

  • 方法内部类自身不能有任何权限修饰符,类似方法中的变量一样,不能用权限修饰符修饰,否则报错。
  • 方法内部类自身不能使用static修饰,即使其存在于静态方法中。和第一条一样,目前在方法中还没出现过static变量。
  • 方法内部类中的字段可以是静态的(即由static修饰),但是必须是final的(必须由final关键字修饰)。
  • 方法内部类的成员的权限修饰符无效,意思是不管方法内部类的成员权限如何,都能够也只能够在其所属的方法内部访问。即使方法内部类的属性是private的,在其所属方法内部也可以访问。
  • 与外部类不同的是,方法内部类必须声明后,才能在方法中使用。
  • 方法内部类可以访问方法中的变量,在JDK8以前必须要求是明确使用final修饰的常量,但是在JDK8及以后,可以省去final修饰符,但其本质还是常量,所以不能对其进行修改操作。
  • 方法内部内访问外部类的成员时,是根据其是否是静态方法有区别,具体规则差异相同于静态内部类和成员内部类的差异。

匿名内部类

匿名内部类没有类名,常作为方法的实参。举一个简单的例子:

// 接口定义
private interface Action {
    void doAction();
}

// 其他方法
public static void doAction(Action action) {
    action.doAction();
}

public static void testAnonymousInnerClass() {
    final int e = 100;
    // 匿名内部类的定义
    doAction(new Action() {
        // 一些变量
        @Override
        public void doAction() {
            // 具体实现
        }
    });
}

关于匿名内部类有以下几点需要注意:

  • 匿名内部类实例只能使用一次,类加载后立马创建实例即被使用,方法调用完毕立马销毁回收。
  • 匿名内部类中可以访问方法中的成员,与方法内部类一样,JDK8以前必须是显示使用final修饰的常量,JDK8及其以后可以省略掉关键字。
  • 访问外部类的成员时,其受到方法是否是静态的影响。
  • 匿名内部类中不能出现static关键字。
  • 与前面三种内部类不同的是,其不能拥有构造方法,因为其连类名都没有。
  • 和方法内部类一样,内部属性或方法的修饰符都无效,或者说无意义,因为加不加修饰符不影响调用。

为什么方法内部类和匿名内部类访问外部数据(不是外部类的成员)时要求是常量呢?
方法中的局部变量在栈上分配的内存,方法内部类或匿名内部类的实例存放在堆区。在类加载后内部类中存放的是备份,为了备份与原变量保持一致,所以干脆规定方法中的局部变量为常量。

使用目的和场景

经过上面的学习,了解了内部类的使用,但是为什么要使用内部类,也就是说内部类有何存在意义。
我们经常在某个类中需要临时使用一种结构化的数据,但是在其他类中有不会使用到,此时不妨定义为内部类,在其内部使用即可,可以根据该结构化数据作用域的范围,选择合适的内部类定义,比如只在某个方法中使用,即可定义为方法内部类,如果在多个方法中都会使用到,即可定义为静态内部类或成员内部类。
内部类与外部类是独立的关系,可以使用内部类解决不能多继承的问题,可以将多个类定义为其中某个类的内部类,然后单继承实则上实现了多继承。

参考链接:

鸣谢:
https://juejin.im/post/5a903ef96fb9a063435ef0c8#heading-12
https://www.cnblogs.com/chenssy/p/3388487.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值