文章目录
1. 内部类
- 内部类:一个类的内部又完整的嵌套了另一个类。被嵌套的类叫内部类。
- 特点:
- 可以直接访问外部类的私有属性。
- 很好的体现类与类的包含关系。
- 区分:外部类、内部类、外部其他类
2. 内部类分类
内部类有4种:
- 定义在外部类局部位置上(比如方法内,构造器内,代码块内):
- 局部内部类:有类名
- 匿名内部类!!!:没有类名
- 定义在外部类的成员位置上:
- 成员内部类:没有用static修饰
- 静态内部类:用static修饰
2.1 局部内部类
- 作用域:定义该类的代码块中。(比如方法内,构造器内,代码块内)
- 在定义局部内部类不可以用访问修饰符,因为该类相当于局部变量,而局部变量不可以用访问修饰符;但是可以用final修饰。
- 在定义局部内部类可以使用
final
关键字,这样同一作用域内再定义一个局部内部类就不可以继承该局部内部类。
- 可以直接访问外部类的所有成员,包括私有的。
- 在局部内部类的作用域内,可以new局部内部类对象。
- 局部内部类与外部类的成员重名时,遵循就近原则。
- 可以使用
外部类名.this.成员
访问外部成员。解释:
外部类名.this
本质上是外部类的一个对象,即哪个对象调用了该部分,外部类名.this
就是该对象。
2.2 匿名内部类(重点)
2.2.1 基于接口的匿名内部类
- 应用场景:写一个类实现某接口,并创建一次对象,以后再也不使用该类创建对象。那么就可以使用匿名内部类,这样就不用写一个类文件,使得项目更加清晰,简化开发。
- 语法:直接new接口,然后后面加上大括号。
- 例子:
- 以上面的例子说几个细节
- 匿名内部类的类名只是程序员看不见,其底层有默认类名:
外部类名$1
、外部类名$2
…【与代码的先后顺序有关,第一个是$1,第二个是$2】
tiger
的编译类型是IAnimal
;tiger
的运行类型是Outer$1
2.2.2 基于类的匿名内部类
- 应用场景:和基于接口的匿名内部类类似。
- 语法:
- 例子:
- 以上面的例子说几个细节
- 匿名内部类在底层有类名,类名和基于接口的匿名内部类规则一样。
tiger
的编译类型是Animal
;tiger
的运行类型是Outer$1
- 基于类的匿名内部类,可以基于抽象类,也可以基于一般类。但更多的用于抽象类,取重写其抽象方法。
2.2.3 细节
- 关于访问外部类的成员和局部内部类一样:
- 可以直接访问外部类的所有成员,包括私有的。
- 局部内部类与外部类的成员重名时,遵循就近原则。
可以使用外部类名.this.成员
访问外部成员。- 使用匿名内部类时,必须继承一个类或者实现一个接口。但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
- 匿名内部类中是不能定义构造函数的。
- 匿名内部类中不能存在任何的静态成员变量和静态方法。
- 当形参是接口类型或者类类型,要传的实参需要重写方法并只实例化一次时,实参可以使用匿名内部类直接传递,简洁高效。
- 对继承相同类或实现相同接口的匿名内部类,是两个不同的类
2.3 成员内部类
- 成员内部类:定义在外部类的成员位置上,且没有static修饰。
- 作用域:在整个外部类中
- 细节:
- 可以添加任意修饰符,因为它就是一个成员。
- 内部类访问外部类:可以直接访问外部类的所有成员,包括私有的。
- 外部类访问内部类:该内部类可以在成员变量赋值或方法中直接实例化并使用。
- 外部其他类访问内部类:两种方式:【其本质是外部类的成员】
外部类名.内部类名 变量名 = 外部类对象.new 内部类名();
- 在外部类中编写一个方法,在方法中实例化内部类并返回。外部其他类使用该方法,即可获得内部类对象。
- 局部内部类与外部类的成员重名时,遵循就近原则。可以使用
外部类名.this.成员
访问外部成员。
2.4 静态内部类
- 静态内部类:定义在外部类的成员位置上,且有static修饰。
- 作用域:和其他静态成员一样,为整个类
- 细节:
- 可以添加任意修饰符,因为它就是一个成员。。
- 静态内部类访问外部类:可以访问外部类的所有静态成员,包括私有的。但是不能访问非静态的成员
- 外部类访问静态内部类:该内部类可以在成员变量赋值或方法中直接实例化并使用。
- 外部其他类访问静态内部类:两种方式
外部类名.内部类名 变量名 = new 外部类名.内部类名();
- 在外部类中编写一个方法,在方法中实例化内部类并返回。外部其他类使用该方法,即可获得内部类对象。
- 静态内部类与外部类的成员重名时,遵循就近原则。可以使用
外部类名.成员
访问外部成员。【此时不是外部类名.this.成员
,因为内部类是静态的,在类加载时,先于创建对象】- 应用场景:对于明确的父子关系,可以用内部类简化
3. 内部类的加载时机
- 外部类被加载时,并不会加载内部类,只有执行到调用内部类的代码时,才加载内部类。
- 直接调用内部类 (指①静态内部类;②静态方法内有局部内部类或匿名内部类;因为非静态的内部类或方法都需要先创建对象,而创建对象肯定导致外部内加载),外部类不会加载,只会加载静态内部类
比如,拿静态内部类的举例:
public class MyTest {
public static void main(String[] args) throws InterruptedException {
System.out.println("打印时间:" + System.currentTimeMillis() + ".....静态内部类加载时间:" + Student.InnerStaticClass.INNER_STATIC_DATE);
System.out.println("打印时间:" + System.currentTimeMillis() + ".....外部类静态变量加载时间:" + Student.OUTER_DATE);
System.out.println("打印时间:" + System.currentTimeMillis() + ".....调用外部静态方法:" ) ;Student.outerStaticMethod();
Student student1 = new Student();
System.out.println("打印时间:" + System.currentTimeMillis() + ".....非静态内部类加载时间:" + student1.new InnerClass().INNER_DATE);
}
}
public class Student {
public static long OUTER_DATE = System.currentTimeMillis();
static{
System.out.println("外部类静态块加载时间:"+System.currentTimeMillis());
}
public Student(){
System.out.println("外部类构造时间:"+System.currentTimeMillis());
}
static class InnerStaticClass {
public static long INNER_STATIC_DATE = System.currentTimeMillis();
static{
System.out.println("静态内部类静态代码块加载时间:"+System.currentTimeMillis());
}
}
class InnerClass {
public long INNER_DATE = 0;
public InnerClass() {
INNER_DATE = System.currentTimeMillis();
}
}
public static void outerStaticMethod() {
System.out.println("外部类静态方法执行了");
}
}