内部类
🐓内部类
可以将一个类定义在另一个类或方法中,这样的类称为内部类
成员内部类
将类定义在另一个类中成员的位置
public class Inner {
// 定义在类内部
class Demo{
public void say(){
System.out.println("hello!");
}
}
}
成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)
成员内部类依赖于外部类, 要创建内部类的对象, 先要创建外部类的对象
public class Inner {
private static final String name ="张三";
public static void say(){
System.out.println("我是外部类方法!");
}
class Demo{
public void go(){
// 调用外部类方法及变量
System.out.println(name);
Inner.say();
}
}
}
/*
测试
*/
@Test
public void test1(){
// 先创建外部类实例
Inner.Demo demo = new Inner().new Demo();
demo.go();
}
外部类访问内部类时,必须要先实例化内部类的对象
public class Inner {
public void go(){
// 实例化内部类
new Demo().say();
}
class Demo{
private final String name = "李四";
public void say(){
System.out.println("我是内部类方法!");
}
}
}
成员内部类可以拥有如成员方法般的多种修饰符
public class Inner {
// public
public class Demo{}
// private
private class Demo2{}
// protected
protected class Demo3{}
// default
class Demo4{}
}
当外部类和内部类拥有同名的成员变量和方法时,会发生隐藏现象,默认调用内部类的成员
使用外部类.this.变量 和 外部类.this.方法,指定使用外部类变量及方法
public class Inner {
private String name = "张三";
public void say(){
System.out.println("外部类方法!");
}
class Demo{
private String name = "李四";
public void say(){
System.out.println("内部类方法!");
}
public void go(){
System.out.println(Inner.this.name);
Inner.this.say();
}
}
}
局部内部类
将内定义在另一个类的方法中
public class Inner {
public void method() {
class Demo {
public void say() {
System.out.println("hello");
}
}
// 在方法内部调用内部类
new Demo().say();
}
}
/*
测试
*/
@Test
public void test1(){
Inner inner = new Inner();
inner.method();
}
局部内部类的访问权限仅限于方法内或者该作用域内
局部内部类和局部变量一样,不能有public, private, protected以及static修饰符
匿名内部类
使用内部类能够在实现父类或者接口方法的同时, 产生一个对象
// 接口
public interface Demo{
void say();
}
/*
测试
*/
// 产生对象直接调用
@Test
public void test1(){
Demo demo = new Demo() {
@Override
public void say() {
System.out.println("匿名内部类!");
}
};
demo.say();
}
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
public class Inner {
// 需要接口类型的参数
public void go(Demo demo){
demo.say();
}
}
interface Demo{
void say();
}
/*
测试
*/
@Test
public void test1(){
Inner inner = new Inner();
// 匿名内部类
inner.go(new Demo() {
@Override
public void say() {
System.out.println("匿名内部类!");
}
});
}
静态内部类
和成员内部类的区别是, 静态内部类不依赖于外部类, 但是静态内部类不能调用外部类的非static成员变量或方法
public class Inner{
private static final String name = "张三";
// 无法调用非static方法
private Integer age = 22;
public static void say(){
System.out.println("外部类方法!");
}
static class Demo{
public void say(){
System.out.println(name);
Inner.say();
}
}
}
/*
测试
*/
@Test
public void test1(){
// 无依赖于外部类
Inner.Demo demo = new Inner.Demo();
demo.say();
}
细节
-
为什么成员内部类可以无条件访问外部类?
编译器会默认为成员内部类添加一个指向外部类对象的引用 虽然成员内部类的构造函数是空参的,但编译器仍会默认添加一个参数,类型是内部类指向外部类对象的引用类型,从而达到将引用传入的效果
-
JDK8之前, 使用局部内部类和匿名内部类访问的变量只能是final类型
为什么使用final修饰
会出现外部方法已经结束,而局部内部类还没有结束的情况,如果局部内部类需要外部方法的传参,而方法已经结束了,就会出现错误 所以编译器采用复制的方法,在编译期间变量字面值确定的情况下,在匿名内部类或局部方法的常量池中添加一个内容相同的字面量或直接键相应的字节码嵌入到执行字节码中,这样就可以和方法中局部变量分隔开 但是,如果修改方法中的局部变量,又该怎么办呢,所有要求局部内部类访问的变量只能是final类型,使字面量无法修改
-
为什么使用内部类
1. 每个类都能独立继承一个父类,不管外部类是否继承了父类,使用多继承的解决方案变得完整 2. 方便将存在一定逻辑的类组织在一起,又实现对外的隐藏 3. 方便编写事件驱动 4. 方便编写线程代码 5. 简化代码,利于维护