1.嵌套类基础
- 一个类定义在别的类的内部
嵌套类
- 静态嵌套类:Static nested classes,即类前面有static修饰符
1 | public class Outer { |
- 非静态嵌套类:Non-static nested classes, 又名内部类,Inner classes
- 普通内部类(亦翻译为:成员内部类)
1
2
3
4
5
6
7public class Outer {
String name;
public class Inner {
Stirng name;
}
}- 局部内部类(Local classes)
1
2
3
4
5
6
7
8
9public class Outer {
String name;
public void f1() {
class Inner {
String name;
}
}
}- 匿名内部类(Anonymous classes)
1
2
3
4
5
6
7
8
9
10
11public class Outer {
String name;
public void f1() {
new Thread(new Runnable(){
public void run() {
System.out.println("hello");
}
}).start;
}
}
2.匿名内部类和局部内部类
匿名内部类
- 没有类名的内部类,必须继承一个父类/实现一个父接口
- 在实例化以后,迅速转型为父类/父接口
- 这种类型的对象,只能new一个对象,之后以对象名字操作
- 可在普通语句和成员变量赋值时使用内部类
- 没有正式类名的内部类
- 编译器产生内部名字:类名+$+数字编号
- 没有类名,没有构造函数,能用父类/父接口的构造函数(可带参数)
- 可以继承、改写、补充父类/父接口的方法
- 内部不可以新定义静态成员(变量+方法),常量除外
- 可以访问外部包围类的成员变量和方法(包括private)
- 如果定义在静态方法中,也只能访问外部包围类的静态成员
- 没有类名,外部包围类和其他类也无法访问到匿名内部类
局部内部类
- 定义在代码块中的非静态的类,如方法,for循环,if语句等
- 定义后,即可创建对象使用
- 只能活在这个代码块中,代码块结束后,外界无法使用该类
- 编译后名称:外部类名+$+序号+内部类名
- 可以继承其他类,或者实现其他接口
- 非静态的类,不能包含静态成员(变量和方法),除了常量
- 可以访问外部包围类的成员
- 如果定义在静态方法中,只能访问包围类的静态成员
- 局部内部类不能是一个接口,即接口不能定义在代码块中
3.普通内部类和静态嵌套类
普通内部类
- 非static的类,定义在某个类的成员变量位置
- 定义后,在类里面均可以使用
- 编译后名称:外部类名+$+内部类名
- 可以继承其他类,或者实现其他接口
- 可以用private/package private(不写)/protected/public控制外界访问
- 非静态的类,不能包含静态变量/方法,除了常量
- 和外部包围类的实例相关,一个普通内部类实例肯定是在一个外部包围类的实例中,且可以访问外部包围类的所有成员
- 在第三方类中,需要先创建外部包围类实例,才能创建普通内部类的实例,不允许单独的普通内部类对象存在!!!
静态嵌套类
- 层级和包围类(enclosing class)的成员变量/方法一样
- 第三方需要通过外部包围类才可以访问到静态嵌套类
- Outer1.Inner1 obj = new Outer1.Inner1();
- 需要加修饰符static
- 可以定义静态成员和非静态成员
- 不能直接访问包围类的非静态成员,可直接访问包围类的静态成员
- 可通过包围类的对象进行访问非静态成员
- 外界可以通过静态嵌套类名访问其静态成员,通过对象访问其非静态成员
- 外界需要通过包围类才可以访问到静态嵌套类,并创建其对象,不需要外部包围类的实例
4.嵌套类对比
位置 | 名字 | 作用范围 | 基本信息 | |
---|---|---|---|---|
匿名内部类 | 成员变量或者成员方法内 | 外部类名+$+数字编号 | 跟随被赋值变量的作用范围,外界无法访问 | 没有类名,没有构造函数,没有static没有private/ default/ protected/ public 修饰 |
局部内部类 | 成员方法内 | 外部类名+$+序号+内部类名 | 所在的方法内,外界无法访问 | 有类名,有构造函数,没有static,没有private/ default/ protected/ public 修饰 |
普通内部类 | 成员变量 | 外部类名+$+内部类名 | 包围类内可以访问,外界可以访问 | 有类名,有构造函数,没有static有private/ default/ protected/ public 修饰 |
静态嵌套类 | 成员变量 | 外部类名+$+内部类名 | 包围类内可以访问,外界可以访问 | 有类名,有构造函数,有static有private/ default/ protected/ public 修饰 |
嵌套类内部的内容 | 可访问的外部包围类内容 | 和外部类关系 | |
---|---|---|---|
匿名内部类 | 不能带静态成员,除了常量 | 访问外部的所有成员 | 在外部类对象内部 |
局部内部类 | 不能带静态成员,除了常量 | 访问外部的所有成员 | 在外部类对象内部 |
普通内部类 | 不能带静态成员,除了常量 | 访问外部的所有成员 | 外界可以new,但是对象必须依附于一个外部包围类对象 |
静态嵌套类 | 可以定义静态成员变量和方法 | 访问外部的所有静态成员 | 外界可以new,可独立进行工作 |
外部访问规则
- 普通内部类和静态嵌套类可以被外部访问
- 外部访问普通内部类和静态嵌套类,和普通类之间访问规则一样
同一个类 | 同一个包 | 不同包的子类 | 不同包的非子类 | |
---|---|---|---|---|
private | √ | |||
default | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
变量遮蔽:Shadowing
- 嵌套类变量和外部包围类的变量重名
- 以离得近作为优先原则
- 优先级高的变量会遮蔽优先级低的变量
- 外部包围类.this.变量名,可以访问到外部包围类的成员变量
- 静态嵌套类不能访问非静态变量
- Java 7及以前,匿名内部类和局部内部类只能访问外部包围类的final成员变量
- Java 8及以后,匿名内部类和局部内部类可访问外部包围类的final成员变量和事实意义上的final变量(effectively final, 一个变量定值后,再也没有改过值)
5.嵌套类应用
匿名内部类
- 无需类名,用过即焚,使用广泛
- 该类的对象只要一个,且方法只有一个,代码短
- Android中常用匿名内部类
局部内部类
- 定义在方法体内,只能在当前方法内使用,代码短
- 使用较少
- 介于匿名内部类和普通内部类之间
- 只用一次,就用匿名内部类,简便
- 使用多次,那就上升到普通内部类,整个类都可以使用
- 继承某一个类或接口,重新定义方法,并当作返回值在外部使用
- 如java.util.regex.Pattern的splitAsStream方法的局部内部类
普通内部类
- 广泛使用在具有母子结构的类,内部类对象和外围类保持联系
- 如Map和Map.Entry,ZipFile和ZipFile.ZipEntryIterator等
静态嵌套类
- 和普通类一致,只是“碰巧”声明在一个外围类的内部
- 和外围类没有太多的联系,可以脱离外围类对象存在,也可以访问外围类的静态成员
- 如果不需要访问外围类的非静态成员,尽量将普通内部类变更为静态嵌套类
- 节省普通内部类和外围类的联系开销
- 使得外围类对象更容易被垃圾回收器回收