Java各种称呼类详解
Java有各种各样类,内部类、嵌套类、成员类、局部类(本地类)、静态类、匿名类、文件类以及这些组合起来的称呼类,成员内部类,成员匿名类,成员嵌套类,本地匿名类等,真是多的不行,但有些其实是一个意思,在这里好好理一理。
声明
1.注意,这些称呼都是翻译过来的,但是同一个英文单词或一个词组翻译过来可能有不同叫法,比如local nested class,local有局部、本地意思,其实是一个意思,local nested class翻译过来就是局部嵌套类(本地嵌套类),又因为非静态嵌套类都称为内部类,所以local nested class又叫为内部局部类(本地内部类),最终又简称为:局部类(本地类)。我个人觉得局部类更加形象点所以下面都优先采用局部类一说。
2.我通过搜索找到两种不太相同的内部类定义,一种认为:定义在类里面的称为嵌套类(nested class),而非静态嵌套类才是内部类(inner class),也就是说嵌套类包含内部类;另外一种则是认为:定义在类里面的称为内部类,这样静态嵌套类也是内部类,也就是说嵌套类=内部类。但是,第一种才是Java语言规范规定的,我一开始不确定,找到Java7的语言规范验证,结果确实如此,即第一种才是正确的,那么问题来了:依照第一种划分,静态内部类其实是一种错误说法。。。只有静态嵌套类,内部类是没有静态一说的!
但是对于大多数中国程序员,貌似将定义在类里面的类称为内部类不是更符合文意么?也许这就是为什么会有这么多人一直认为静态内部类的存在。嘛,反正不管怎么称呼,只要知道代表什么意思就好,细节什么的不要在意(光哥:所以不注意细节的你写的代码才这么一大堆bug么?还不赶紧去改(怒吼))。
我这里采用Java语言规范上说的。
0.文件类
文件类放在最前面,是因为文件类是与主类(一个文件中public类)关系最不密切的一类。什么是文件类?看代码就知道:
public class Main{}
class Test{}//Test就是文件类
//是的,一个.java文件里面定义在主类外面的就是文件类
//主类、文件类称为顶级类(top level class),Java语言规范中定义:非嵌套类即为顶级类。
【注意】:主类这一定义是我自己按语义称呼的,有的地方称为基本类,但我觉得很不符合语义,Java语言规范我也没找到相关定义。
因为一个.java文件只能有一个主类(public 类),所以文件类默认只能是包访问权限,即:不是同一个包的是无法引入和使用的。
1.嵌套类
由上面文件类可有类似定义:一个.java文件里面定义在类里面的就是嵌套类,定义在类内部,包括块、静态块、构造器、方法内。这个时候该类也相对来被称为包装类(enclosing class)或外部类。
嵌套类是可以有层次的,也就是说嵌套类里面还可以定义类,成为嵌套类中的嵌套类。
在Java语言规范里面,嵌套类定义是:
A nested class is any class whose declaration occurs within the body of
another class or interface.
说的简单一点,就是定义在类(这里还包括接口,下同)里面的类。所以说,以下所有的类都可以称为嵌套类。嵌套类分为两种:静态嵌套类和非静态嵌套类,非静态嵌套类就是内部类(inner class)。
静态嵌套类
简称静态类,和主类关系也不大,只是在其他类中引用使用的时候需要加上外部类限定,但在技术上来看,完全是两个独立无关系的类。
public class Main{
public static class NestClass{}
}
//在外面使用时候形式如下,在Main中使用则不需要加上外部类限定
Main.NestClass nestClass = new Main.NestClass();
从形式上来看,静态类可以说是类的一个静态成员(所以也可以说是成员类一种),但从技术上来看,其实二者没什么关系,可以看做第三类顶级类。但也因此,静态类不怎么常用,因为它同一般类没什么优势可言。
静态类不能访问外部类的非静态成员和非静态方法(不管是public还是private的);
静态类的实例不需要先实例化外部类成员,可直接实例化。
2.内部类
Java语言规范里的定义:
An inner class is a nested class that is not explicitly or implicitly
declared static.
//即非静态嵌套类即为内部类
内部类包括:成员类、局部类、匿名类。
内部类中不能有静态修饰的成员(比如块、字段、方法、接口等),总之不能有static关键字,除了一种情况,那就是静态常量,又因为常量成员字段必须在声明的时候初始化,所以形式只能如:public static final int a = 6;
内部类可以访问外部类任何成员,不管是公有的还是私有的,静态的还是非静态的(并且内部类的成员的名字也可以同外部相同,只不过这样会覆盖掉去外部类的),这是因为每一个内部类都保存了一个对外部类的一个引用。这很好理解,因为你要实例化这个内部类,肯定是通过外部类的一个实例,而内部类保留的这个引用就是这个外部类实例。
内部类命名格式:外部类名称+$+[该种类同名类中该类顺序]+[内部类名称],例如成员类,成员类不能同名,所以也就没有同名类顺序:com.fcc.test.OuterClass$MemberClass;局部类:com.fcc.test.OuterClass$1LocalClass;匿名类:匿名类没有名称,所以格式如:com.fcc.OuterClass$1。
成员类
这里说的是非静态成员内部类(如果静态嵌套类也算是成员类一种的话),non-static member (inner) class。而一般说的也是这种,但从技术上来看,成员类应该还包括静态嵌套类。
A member class is a class whose declaration is directly enclosed in
another class or interface declaration.
成员类算是最常见最常用的一种内部类,我们一般说的内部类说的就是成员类:在类里面,但不在块、构造器、方法里面。
//成员类,从技术上来说,可以分为两种:成员内部类和成员嵌套类。
//1.成员内部类即这里说的成员类,全称是非静态成员内部类
//2.成员嵌套类即上面的静态嵌套类
public class Main{
public class MemberClass{}//成员内部类,常简称为成员类
}
成员类,可以使用public,private,protected访问控制符,也可以用static,final关键字修饰,并且有enclose class属性。
这里题外说明一下:
成员(member),只要是在类里面的(但不在块、构造器、方法内),都是成员:可以是变量,就是成员变量(一般又称为成员字段,Field);可以是方法,好吧,方法都是成员(因为Java中方法不可能位于类外)的;当然,同样的,也可以是接口、枚举、注释类以及类。
关于static理解,有static修饰的是类本身属性(共性),所以访问可以不通过类的实例对象,而非static修饰的,是对象属性(个性),必须通过类的实例对象访问,因为个性是个体的属性啊,当然要创建出个体,然后这个个性才有意义。
关于enclose class,enclose method,enclose constructor属性,可以理解为这个类是被类、还是方法、构造器包装起来的。关于这些属性,可以参考Class类:Java源码解析(2) —— Class(1)。
局部类(本地类)
local nested class,局部嵌套类,简称局部类,局部类所属范围:在块、构造器以及方法内,这里的块包括普通块和静态块。局部类只在本块范围内有效。
定义:
A local class is a nested class (§8) that is not a member of any class
and that has a name.
翻译过来就是:局部类是嵌套类,但不是成员类,而且有名称(不是匿名类)。
public class Test {
{
class AA{}//块内局部类
}
public Test(){
class AA{}//构造器内局部类
}
public static void main(String[] args){
}
public void test(){
class AA{}//方法内局部类
}
}
//注意到了吧,可以同名,编译后,形成诸如:外部类名称+$+同名顺序+局部类名称
//Test$1AA.class/Test$2AA.class/Test$3AA.class
局部类最多只能有final修饰,但不同的是,块内局部类有enclose class属性,而构造器局部类有enclose constructor属性,方法局部类有enclose method属性,嘛,其实很好理解的吧,一看就知道。
局部类只能访问(使用)这个块中(局部类外)final属性。这里的块包括了上面说的块、构造器、方法。
匿名类
定义:
An anonymous class declaration is automatically derived from a class
instance creation expression by the Java compiler
匿名类,就是没有名称的类,其名称由Java编译器给出,一般是形如:外部类名称+$+匿名类顺序,没有名称也就是其他地方就不能引用,不能实例化,只用一次,当然也就不能有构造器。
匿名类根据位于地方不同分为:成员匿名类和局部匿名类。
public class Test {
InterfaceA a = new InterfaceA() {};//成员匿名类
public static void main(String[] args){
InterfaceA a = new InterfaceA() {};//局部匿名类
//以上两种是通过实现接口实现匿名类,称为接口式匿名类,也可以通过继承类
Test test = new Test(){};//继承式匿名类
//还可以是位于参数上
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();//属于局部匿名类一种
}
private interface InterfaceA{}
}
匿名类不能使用任何关键字和访问控制符,匿名类和局部类访问规则一样,只不过内部类显式的定义了一个类,然后通过new的方式创建这个局部类实例,而匿名类直接new一个类实例,没有定义这个类。匿名类最常见的方式就是回调模式的使用,通过默认实现一个接口创建一个匿名类然后,然后new这个匿名类的实例。
总结
本文讨论的是:嵌套类、内部类、成员类、局部类、匿名类相关,这些类的划分主要是根据类声明(或位于)的地方划分的:
1.嵌套类,位于类内部,又分为:静态嵌套类和非静态嵌套类。
2.静态嵌套类即为静态类。静态类只有这一种,技术来说,也可以看成静态成员类。
3.非静态嵌套类即为内部类,又分为:成员类、局部类(本地类)、匿名类。
4.成员类:位于类内部但不包括位于块、构造器、方法内,且有名称的类。
5.局部类:位于块、构造器、方法内的有名称类。
6.匿名类:类内无名称类,又可细分为:成员匿名类和局部匿名类。