少女祈祷中...

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等

静态嵌套类

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