Java 内部类(局部内部类、匿名内部类、成员内部类、静态内部类)

文章目录

将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。
内部类最大的特点就是可以直接访问私有属性,并且可以体现类的包含关系。

内部类的本质还是一个类。

基本语法:

class 外部类类名 { 
    class 内部类类名{
    }
}

class 外部其他类类名{
}

局部位置(比如方法内)

一、局部内部类

定义在外部类的局部位置,比如方法中,并且有类名。

语法格式:

class 外部类名 {

    public 返回值 方法名(参数列表) {
        // 局部内部类是定义在外部类的局部位置,通常是方法
        class 内部类名 {
        }
        
    }
}

① 可以直接访问外部类中的所有成员,包含私有的的成员。

class Outher {
    private int n1 = 100;

    public void m1() {
    	// 内部类
        class Inner {	
        	// 内部类的方法
            public void f1() {
            	// 内部类可以直接访问外部类的私有成员
                System.out.println("n1 = " + n1);
            }
        }
    }

}

② 不能添加访问修饰符,本质是一个局部变量,局部变量是不能使用访问修饰符的,可以使用final

因为final修饰符局部变量也可以使用

class Outher {
    public void m1() {
        // public class Inner {}  编译不能通过
        // final class Inner {}  编译可以通过,但是此内部类不能被继承
        class Inner{}    // 编译可以通过,此内部类可以被继承
        class InnerZi extends Inner {}  // 内部类的继承内部类
    }

}

③ 作用域:只有在定义该内部类的方法中或代码块中可以访问。

class Outher {

    {
        class Inner{}   // 代码块中也可以定义内部类 
    }

    public void m1() {
        class Inner{}
        Inner inner = new Inner();  // 使用内部类,创建内部类对象,注意代码顺序

    }

    public void m2() {
        // Inner inner = new Inner(); 另一个方法就不能访问其他方法中的内部类
    }
}

④ 局部内部类可以直接访问外部类的成员

class Outher {
    private int n1 = 100;
    
    public void m1() {
        class Inner{
            public void f1() {
                System.out.println("n1 = " + n1);   // 访问外部类的成员变量
                m2();   // 访问外部类的成员方法
            }
        }
    }

    public void m2() {}
}

⑤ 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,可以使用:外部类名.this.成员 进行访问

如果不加this关键字的话调用的是静态的成员,外部类名.this的本质是外部类对象,谁调用就是谁

class Outher {
	// 外部类的n1成员变量
    private int n1 = 100;

    public void m1() {
        class Inner{
        	// 内部类的n1成员变量
            private int n1 = 200;
            
            public void f1() {
            	// 方法同理
                System.out.println("n1 = " + n1);   // 访问内部类的成员变量
                System.out.println("n1 = " + Outher.this.n1);   // 访问外部类的成员变量
            }
        }
    }
}

二、匿名内部类【超重要】

匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名

匿名内部类没有名字,但是系统会分配一个名字,匿名内部类同时还是一个对象。

语法格式:

new 类名或者接口名(参数列表){ 
	// 类体
};

举个例子:
正常的接口实现方式:

interface A {   // 一个接口
    public void eat();
}

class B implements A {  // 接口的实现类
    @Override
    public void eat() {
        System.out.println("我就喜欢吃吃吃");
    }
}

class Outher02 {
    public void method() {
        // 创建接口实现类对象
        A a = new B();
        // 调用接口中的方法
        a.eat();
    }
}

匿名内部类实现方式:
如果实现类只使用一次的话,这样会方便很多。

interface A {   // 一个接口
    public void eat();
}

class Fa {  // 一个普通的类
    public Fa(String name) {}
    public void test() {}
}

class Outher02 {    // 外部类
    public void method() {
        // 创建接口实现类对象
        A a = new A(){	// 匿名内部类
            @Override
            public void eat() {
                System.out.println("我就喜欢吃吃吃");
            }
        };
        // 调用接口中的方法
        a.eat();
    }
}

① 匿名内部类的底层是系统创建一个类实现了接口或继承了父类,系统会分配类名

在上面的例子中,编译时会生成一个新的类,实现了A接口,这个类的类名是外部类类名 + $ + 编号,比如这里就是Outher02$1

class Outher02 {    // 外部类
    public void method() {
        // 创建接口实现类对象
        A a = new A(){	// 匿名内部类
            @Override
            public void eat() {
                System.out.println("我就喜欢吃吃吃");
            }
        };
        // 调用接口中的方法
        a.eat();
       // 查看变量a的运行类型
        System.out.println(a.getClass());
    }
}

通过以上代码,可以看到以下结果
在这里插入图片描述

所以在底层,会分配一个匿名类,这个类的名字是Outher02$1,类的方法体就是匿名内部类的方法体,应该是下面这样:

class Outher02$1 implements A {  // 系统底层创建的接口的实现类
    @Override
    public void eat() {
        System.out.println("我就喜欢吃吃吃");
    }
}

然后创建对象的时候将该对象的引用返回给给变量a,这是变量a的运行类型是A接口,编译类型实际上是Outher02$1实现类。

② 匿名内部类使用一次就不能再使用了,但是创建的这个对象是还可以继续使用的。

class Outher02 {    // 外部类
    public void method() {
        A a = new A(){ // 匿名内部类
            @Override
            public void eat() { 
                System.out.println("我就喜欢吃吃吃");
            }
        };
        a.eat();
        a.eat();
        a.eat();
    }
}

对象可以多次使用,但是匿名内部类就不能再创建对象了,就算new Outher02$1()也不行。

③ 匿名内部类可以实现接口,也可以继承类,原理是一样的。

这里创建的fu对象的运行类型和编译类型都是Fu这个类

class Fu {  // 一个普通的类
    public Fu(String name) {}
    public void test() {}
}

class Outher02 {    // 外部类
    private int n = 100;

    public void method() {
        Fu fu = new Fu("zhangsan");
    }
    
}

但是如果将创建的对象变成匿名内部类对象的话,这里的运行类型是Fu,而编译类型就是匿名内部类对象了,jdk底层也会为其创建一个一次性的类,类名可能就是Outher02$2

class Outher02 {    // 外部类
    private int n = 100;

    public void method() {
        Fu fu = new Fu("zhangsan") {
            
        };
    }

}

加上大括号之后就是匿名内部类,相当于继承了new的这个类,所以底层生成的这个类应该是:

class Outher02$2 extends Fu{
     public Outher02$2(String name) {
        super(name);
    }
}

如果在大括号中重写了父类的方法,则也是按照动态绑定机制使用编译类型的方法

④ 基于抽象类或接口的匿名内部类必须实现抽象方法

abstract class Fu {  // 一个抽象类
    public abstract void test();
}

class Outher02 {    // 外部类
    private int n = 100;

    public void method() {
        // 匿名内部类
        Fu fu = new Fu() {
        	// 实现抽象方法
            @Override
            public void test() {
            }
        };
    }
}

⑤ 可以直接访问外部类的所有成员,包含私有的成员

同上局部内部类的第一条

⑥ 不能添加访问修饰符,他的本质是一个局部变量,作用域也是定义的方法体或代码块中

同上局部内部类的第二条和第三条

⑦ 如果外部类和匿名内部类的成员重名时,匿名内部类需要访问,则遵循就近原则,如果想访问外部类的成员,可以使用:外部类名.this.成员 进行访问

同上局部内部类第五条

成员位置(类成员位置)

三、成员内部类

成员内部类是定义在外部类的成员位置,并且没有static修饰

语法格式:

class 外部类 { 
    访问修饰符 class 内部类{
    }
}

① 可以直接访问外部类的所有成员,包含私有的

成员内部类可以理解为类的一个成员,类中的成员是可以访问该类的其他成员的。

class Outher03 {
    
    private int n1 = 300;
    public String name = "zhangsan";
    
    // 成员内部类
    class Inner {
        public void say() {
            // 可以访问外部类的所有成员,包含私有的
            System.out.println(n1);
            System.out.println(name);
        }
    }
}

② 可以添加任意访问修饰符,成员内部类的本质是一个成员

class Outher03 {

    public class Inner01 {
    }

    protected class Inner02 {
    }

    class Inner03 {
    }

    private class Inner04 {
    }
}

③ 成员内部类的作用域和其他成员一样,可以在外部类中创建对象,如果有权限则也可以在其他外部类中可以使用。

// 定义类:
public class Person {
    private boolean live = true;
    // 如果成员内部类是私有的,则其他外部类无法创建该内部类的对象
    public class Heart {
        public void jump() {
            // 直接访问外部类成员
            if (live) {
                System.out.println("心脏在跳动");
            } else {
                System.out.println("心脏不跳了");
            }
    	}
	}
	
	public void m1() {
		// 成员内部类可以类外部类中直接创建对象调用并使用
		Heart heart = new Heart();
		heart.jump();
	}
}

// 定义测试类:
class InnerDemo {
    public static void main(String[] args) { 
    	// 成员内部类在其他外部类中,需要先创建外部类的对象,通过外部类的对象再创建内部类的对象,才可调用并使用
        // 创建外部类对象
        Person p = new Person();
        // 创建内部类对象
        Person.Heart heart = p.new Heart();
        // 调用内部类方法
        heart.jump();
    }
}
// 输出结果:
// 心脏在跳动

④ 成员内部类在其他外部类创建对象的三种方式

第一种: 外部类名.内部类名 变量名 = new 外部类().new 成员内部类();

class Outher03 {
    // 成员内部类
    public class Inner01 {
    }
}

class Test {
    // 创建成员内部类对象的测试方法
    public void test() {
        Outher03.Inner01 inner01 = new Outher03().new Inner01();
    }
}

第二种: 外部类名 外部类变量名 = new 外部类();
外部类名.内部类名 内部类变量名 = 外部类变量名.new 内部类();

class Outher03 {
    // 成员内部类
    public class Inner01 {
    }
}

class Test {
    // 创建成员内部类对象的测试方法
    public void test() {
        Outher03 outher03 = new Outher03();
        Outher03.Inner01 inner01 = outher03.new Inner01();
    }
}

第三种: 使用get方法

class Outher03 {
    // 成员内部类
    public class Inner01 {
    }
    
    // get方法
    public Inner01 getInner() {
        return new Inner01();
    }
    
}

class Test {
    // 创建成员内部类对象的测试方法
    public void test() {
        Outher03 outher03 = new Outher03();
        Outher03.Inner01 inner = outher03.getInner();
    }
}

⑤ 如果外部类和内部类成员重名时,内部类访问则按照就近原则,如果想访问外部类成员,可以使用:外部类名.this.成员

同上局部内部类第五条

四、静态内部类

静态内部类是定义在外部类的成员位置,并且有static修饰

class 外部类 {
    访问修饰符 static class 内部类 {
    }
}

① 可以访问外部类的所有静态成员,但是不能直接访问非静态成员

class Outher04 {
    
    private static String name = "lisi";
    private int age = 10;
    
    static class Inner04 {
        // 静态内部类访问外部类的静态成员,非静态成员无法直接访问
        public void m1() {
            System.out.println(name);
        }
        
    }
}

② 可以添加任意访问修饰符,静态内部类本质是一个静态成员。

同上成员内部类的第二条

③ 作用域:同其他成员,为类的整体

class Outher04 {
    private static String name = "lisi";

    static class Inner04 {
        public void m1() {
            System.out.println(name);
        }
    }
    
    public void say() {
        // 外部类访问静态内部类的非静态成员方法
        Inner04 inner04 = new Inner04();
        inner04.m1();
    }
}
class Outher04 {
    private static String name = "lisi";

    static class Inner04 {
        public static void m2() {
            System.out.println(name);
        }
    }

    public void say02() {
        // 外部类访问静态内部类的静态成员方法
        Inner04.m2();
    }
}

④ 其他外部类访问静态内部类的方式:

第一种: 外部类名.静态内部类名 变量名 = new 外部类.内部类();
跟成员内部类是不一样

class Outher04 {
    static class Inner04 {
        public void m1() {
        }
    }
}

class Test02 {
    public void test() {
        Outher04.Inner04 inner04 = new Outher04.Inner04();
    }
}

第二种: 使用get方法
get方法也可以设计成静态的,或许会更方便一些。

class Outher04 {
    static class Inner04 {
        public void m1() {
        }
    }
    
    // get方法
    public Inner04 getInner04(){
        return new Inner04();
    }
}

class Test02 {
    public void test() {
        Outher04 outher04 = new Outher04();
        Outher04.Inner04 inner04 = outher04.getInner04();
    }
}

⑤ 如果外部类和静态内部类成员重名时,静态内部类访问则按照就近原则,如果想访问外部类成员,可以使用:外部类名.成员

同上局部内部类第五条,只不过这里是静态的,无需使用this

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VincentHu_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值