Java - 内部类(非常重要)

一、介绍

一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。

类的五大成员:属性、方法、构造器、代码块、内部类。

内部类的最大特点是可以直接访问私有属性,并且可以体现类与类之间的包含关系。

注意:
内部类是学习的难点,同时也是重点,后面看底层源码时,有大量的内部类。


二、基础语法

class Outer{ // 外部类
    class inner{ // 内部类
    }
}

class Other{ // 外部其他类
}

三、内部类的分类

定义在外部类局部位置上(比如方法内)

1. 局部内部类(有类名)

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

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

  2. 不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用 final 修饰,因为局部变量也可以使用 final。

  3. 作用域:仅仅在定义它的方法或代码块中。

  4. 局部内部类 --- 访问 ---> 外部类的成员 【访问方式:直接访问】

  5. 外部类 --- 访问 ---> 局部内部类的成员 【访问方式:创建对象,再访问(注意:必须在作用域内】

  6. 外部类其他类 --- 不能访问 ---> 局部内部类(因为 局部内部类地位是一个局部变量)

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

image.png

2. 匿名内部类(没有类名,重点!!!!)

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

  1. 匿名内部类的基本语法
new 类或接口(参数列表){
  类体  
};
  1. 匿名内部类的本质
package innerclass;

public class AnonymousInnerClass {
    public static void main(String[] args) {
        Outer04 outer04 = new Outer04();
        outer04.method();
    }
}

class Outer04{ // 外部类
    private int n1 = 10; // 属性
    public void method() { // 方法
        // 基于接口的匿名内部类
        // 1. 需求:想使用 IA 接口,并创建对象
        // 2. 传统方式,是写一个类,实现该接口,并创建对象
        // 3. 我们此次代码的需求是:Tiger/Dog 类只是使用一次,后面不再使用
        // 4. 可以使用匿名内部类来简化开发
        // 5. tiger 的编译类型 ? IA
        // 6. tiger 的运行类型 ? 就是匿名内部类 Outer04$1
        /*
            我们看底层 会分配 类名 Outer04$1
            class Outer04$1 implements IA {
                @Override
                public void cry(){
                    System.out.println("老虎叫唤...");
                }
            }
         */
        // 7. jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并把地址
        //    返回给 tiger
        // 8. 匿名内部类使用一次,就不能再使用,但是对象可以使用多次
        IA tiger = new IA() {
            @Override
            public void cry(){
                System.out.println("老虎叫唤...");
            }
        };
        System.out.println("tiger的运行类型 = " + tiger.getClass());
        tiger.cry();
    }
}

interface IA{
    public void cry();
}

//class tiger implements IA{
//    @Override
//    public void cry() {
//        System.out.println("老虎叫唤...");
//    }
//}
//
//class Dog implements IA {
//    @Override
//    public void cry() {
//        System.out.println("狗叫唤");
//    }
//}

image.png

  1. 匿名内部类的细节
  • 匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象。因此从语法上看,它既有定义类的特征,也有创建对象的特征。

image.pngimage.png

  • 可以直接访问外部类的所有成员,包括私有的。

image.png

  • 不能添加访问修饰符,因为它的地位就是一个局部变量。
  • 作用域:仅仅在定义它的方法或代码块中。
  • 匿名内部类 --- 访问 ---> 外部类成员 【访问方式:直接访问】
  • 外部其他类 --- 不能访问 ---> 匿名内部类 (因为 匿名内部类地位是一个局部变量)
  • 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用外部类名.this.成员去访问。

image.png

注意:代码中 Outer05.this 就是调用 f1() 的对象。


  1. 匿名内部类的最佳实践
  • 当做实参直接传递,简洁高效

先看下传统的写法,需要先写出一个类,然后再传参,这是种垃圾写法。

package innerclass;

public class InnerClassExercise01 {
    public static void main(String[] args) {
        //传统写法
        f1(new Picture());
    }

    //静态方法,形参是接口类型
    private static void f1(IL il) {
        il.show();
    }
}

//接口
interface IL {
    void show();
}

//类 -> 实现IL =》编程实现(硬编码)
class Picture implements IL {
    @Override
    public void show() {
        System.out.println("这是一幅名画...");
    }
}


下面的是高效的写法:值得学习

package innerclass;

public class InnerClassExercise01 {
    public static void main(String[] args) {

        //当做实参直接传递,简洁高效
        f1(new IL() {
            @Override
            public void show() {
                System.out.println("这是一幅名画...");
            }
        });
    }

    //静态方法,形参是接口类型
    private static void f1(IL il) {
        il.show();
    }
}

//接口
interface IL {
    void show();
}

定义在外部类的成员位置上

1. 成员内部类(没用 static 修饰)

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

  1. 可以直接访问 外部类的所有成员,包含私有。
class Outer08 { // 外部类
    private int n1 = 10;
    public String name = "张三";

    // 注意:成员内部类,是定义在外部类内的成员位置上的
    class Inner08 {
        public void say() {
            //可以直接访问外部类的所有成员,包含私有
            System.out.println("n1 = " + n1 + " name = " + name);
        }
    }

    //写方法
    public void t1() {
        Inner08 inner08 = new Inner08();
        inner08.say();
    }
}
  1. 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。

  2. 作用域:和外部类其他成员一样,为整个类体。

  3. 成员内部类 --- 访问 ---> 外部类 【访问方法:直接访问】

  4. 外部类 --- 访问 ---> 成员内部类 【访问方式:创建对象,再访问】

  5. 外部其他类 --- 访问 ---> 成员内部类

示例:

package innerclass;

public class MemberInnerClass01 {
    public static void main(String[] args) {
        //外部其他类,使用成员内部类的两种方式

        //第一种
        Outer08 outer08 = new Outer08();
        // outer08.new Inner08(); 相当于把 new Inner08() 当做是outer08的成员
        // 这就是和语法,不用太纠结
        Outer08.Inner08 inner08 = outer08.new Inner08();

        // 第二种:在外部类中,编写一个方法,可以返回 Inner08 的对象
        Outer08.Inner08 inner08__ = outer08.getInner08Instance();
        inner08__.say();
    }
}

class Outer08 { // 外部类
    private int n1 = 10;
    public String name = "张三";

    // 注意:成员内部类,是定义在外部类内的成员位置上的
    class Inner08 {
        public void say() {
            //可以直接访问外部类的所有成员,包含私有
            System.out.println("n1 = " + n1 + " name = " + name);
        }
    }

    //方法:返回一个Inner08的实例
    public Inner08 getInner08Instance(){
        return new Inner08();
    }

    //写方法
    public void t1() {
        Inner08 inner08 = new Inner08();
        inner08.say();
    }
}
  1. 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用外部类名.this.成员去访问。

image.png

2. 静态内部类 (使用 static 修饰)

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

  1. 可以直接访问外部类的所有静态成员,包含私有,但不能直接访问非静态成员。

  2. 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。

  3. 作用域:同其他的成员,为整个类体。

  4. 静态内部类 --- 访问 ---> 外部类 【访问方式:直接访问所有的静态成员】

  5. 外部类 --- 访问 ---> 静态内部类 【访问方式:创建对象,再访问】

  6. 外部其他类 --- 访问 ---> 静态内部类

示例:

package innerclass;

public class StaticInnerClass01 {
    public static void main(String[] args) {
        //外部其他类,使用静态内部类
        Outer10 outer10 = new Outer10();
        //方式1:
        Outer10.Inner10 inner10 = new Outer10.Inner10();
        inner10.say();

        //方式2:
        //调用的普通方法
        Outer10.Inner10 inner101 = outer10.getInner10();
        inner101.say();
        //调用的静态方法
        Outer10.Inner10 inner102 = Outer10.getInner10_();
        inner102.say();
    }
}

class Outer10{
    private static String name = "张三";
    static class Inner10 {
        public void say() {
            System.out.println(name);
        }
    }

    public void m1() { // 外部类---访问--->静态内部类,方法方式:创建对象,再访问
        Inner10 inner10 = new Inner10();
        inner10.say();
    }

    public Inner10 getInner10(){
        return new Inner10();
    }

    public static Inner10 getInner10_(){
        return new Inner10();
    }
}
  1. 如果外部类和静态内部类的成员重名时,静态内部类访问时,默认遵守就近原则,如果想访问外部类的成员,则可以使用外部类名.成员去访问。
posted @   interestinmachine  阅读(56)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示