少女祈祷中...

1.嵌套类基础

  • 一个类定义在别的类的内部

嵌套类

  • 静态嵌套类:Static nested classes,即类前面有static修饰符
1
2
3
4
5
6
7
public class Outer {
String name;

static class Inner {
String name;
}
}
  • 非静态嵌套类:Non-static nested classes, 又名内部类,Inner classes
    • 普通内部类(亦翻译为:成员内部类)
    1
    2
    3
    4
    5
    6
    7
    public class Outer {
    String name;

    public class Inner {
    Stirng name;
    }
    }
    • 局部内部类(Local classes)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Outer {
    String name;

    public void f1() {
    class Inner {
    String name;
    }
    }
    }
    • 匿名内部类(Anonymous classes)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public 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等

静态嵌套类

  • 和普通类一致,只是“碰巧”声明在一个外围类的内部
  • 和外围类没有太多的联系,可以脱离外围类对象存在,也可以访问外围类的静态成员
  • 如果不需要访问外围类的非静态成员,尽量将普通内部类变更为静态嵌套类
    • 节省普通内部类和外围类的联系开销
    • 使得外围类对象更容易被垃圾回收器回收