内部类知识

一、内部类概述

1. 什么是内部类

把一个类定义在另一个类的里面 ,外面的类叫外部类 ,里面的类叫内部类。 Java 内部类一共 4 种:

  1. 成员内部类
  2. 局部内部类
  3. 静态内部类(静态成员内部类)
  4. 匿名内部类

核心特点:内部类可以直接访问外部类所有成员(包括私有成员);外部类访问内部类成员,需要先创建内部类对象。 Lambda 表达式:专门简化函数式接口的匿名内部类,属于匿名内部类场景的语法糖。


二、成员内部类

1. 定义位置

写在外部类的成员位置(和成员变量、成员方法同级,不在方法里面)。

复制代码
class Outer{ //外部类
    // 成员变量
    private String name = "外部类私有属性";
    // 成员内部类
    class Inner{
        public void show(){
            // ✅内部类可以直接访问外部类的私有成员
            System.out.println(name);
        }
    }
}

2. 创建成员内部类对象语法

复制代码
外部类.内部类 对象名 = new 外部类().new 内部类();

完整演示代码

复制代码
class Outer{
    private String name = "外部类私有属性";
    //成员内部类
    class Inner{
        public void show(){
            System.out.println(name);
        }
    }
}
public class Test{
    public static void main(String[] args) {
        // 创建成员内部类对象
        Outer.Inner inner = new Outer().new Inner();
        inner.show(); //输出:外部类私有属性
    }
}

3. 重点:重名变量访问(外部类.this)

当内部类成员变量、外部类成员变量重名时:

  • this.变量:访问内部类自己的成员

  • 外部类名.this.变量:访问外部类成员

    class Outer{
    String num = "外部类";
    class Inner{
    String num = "内部类";
    public void test(){
    String num = "局部变量";
    System.out.println(num); //局部变量
    System.out.println(this.num); //内部类成员
    System.out.println(Outer.this.num); //外部类成员
    }
    }
    }

4. 成员内部类注意事项

  1. 不能定义static 静态成员(static 属于类,成员内部类依赖外部对象);

  2. 成员内部类属于外部类实例,必须先有外部类对象,才能创建内部类对象;

  3. 可以使用private修饰:私有化之后,外部无法直接创建内部类对象,只能在外部类内部提供方法获取。

    class Outer{
    private class Inner{ //私有成员内部类
    public void show(){
    System.out.println("私有内部类");
    }
    }
    //外部类提供方法,返回内部类对象
    public Inner getInner(){
    return new Inner();
    }
    }


三、局部内部类

1. 定义位置

定义在外部类的方法里面,属于方法局部。

作用域:只能在当前方法内使用,方法外面不可见。

复制代码
class Outer{
    public void method(){
        //局部内部类,写在方法内部
        class Inner{
            public void show(){
                System.out.println("局部内部类方法");
            }
        }
        // 只能在当前方法内创建对象使用
        Inner inner = new Inner();
        inner.show();
    }
}
public class Test{
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.method();
    }
}

2. 核心特性

  1. 作用域仅限于当前方法,外部完全无法访问;
  2. JDK8 及以后:访问方法的局部变量,变量要求有效 final(变量一旦赋值,不能二次修改);

原因:局部变量存放在栈内存,方法执行完毕就销毁;局部内部类对象存在堆内存,生命周期更长,为防止数据不一致,要求变量不可修改。

复制代码
class Outer{
    public void method(){
        int num = 10; //有效final,没有二次赋值
        class Inner{
            public void show(){
                System.out.println(num); //可以访问
            }
        }
        Inner inner = new Inner();
        inner.show();
        // num = 20;  如果写这句,上面代码直接报错!
    }
}
  1. 不能加访问修饰符(public/private/protected),和局部变量一样。

四、静态内部类(静态成员内部类)

1. 定义

成员内部类前面加static,就是静态内部类。

static 属于外部类本身,不依赖外部类对象。

复制代码
class Outer{
    private static String name = "外部类静态属性";
    //静态内部类
    static class Inner{
        public void show(){
            //静态内部类,只能直接访问外部类static成员
            System.out.println(name);
        }
    }
}

2. 创建对象语法

复制代码
外部类.内部类 对象名 = new 外部类.内部类();

完整示例

复制代码
class Outer{
    private static String staticStr = "外部静态变量";
    private String instanceStr = "外部实例变量";
    static class Inner{
        public void show(){
            System.out.println(staticStr);
            // System.out.println(instanceStr); 报错!静态内部类不能直接访问外部非static成员
            // 如果要访问外部实例成员,必须手动创建外部类对象
            Outer outer = new Outer();
            System.out.println(outer.instanceStr);
        }
    }
}
public class Test{
    public static void main(String[] args) {
        //静态内部类创建,不需要外部类对象
        Outer.Inner inner = new Outer.Inner();
        inner.show();
    }
}

3. 重点总结

  1. 静态内部类只能直接访问外部类静态成员;
  2. 访问外部非静态成员,需要手动 new 外部类对象;
  3. 创建静态内部类对象,不需要外部类实例;
  4. 静态内部类里面可以定义静态成员。

开发常用场景:工具类、实体类的嵌套(比如 HashMap 里面的 Node 就是静态内部类)


五、匿名内部类 + Lambda 表达式

5.1 匿名内部类

1. 定义

没有名字的内部类,用来简化代码,一次性使用,只创建一次对象。 前提:必须继承一个类 / 实现一个接口。 语法格式:

复制代码
new 接口/父类(){
    //重写抽象方法
    @Override
    public void 方法名(){

    }
};
代码演示
复制代码
//定义接口
interface Animal{
    void eat();
}
public class Test{
    public static void main(String[] args) {
        //匿名内部类:相当于创建了一个没有名字的类,实现Animal接口,并直接new对象
        Animal cat = new Animal(){
            @Override
            public void eat() {
                System.out.println("小猫吃鱼");
            }
        };
        cat.eat();
    }
}
匿名内部类特点
  1. 没有类名,不能写构造方法;
  2. 适合接口只有少量抽象方法,临时使用;
  3. 可以访问外部变量,遵守有效 final规则;
  4. 可以用于有多个抽象方法的接口。
匿名内部类传参写法
复制代码
interface Run{
    void run();
}
class Test{
    public static void test(Run r){
        r.run();
    }
    public static void main(String[] args) {
        //直接把匿名内部类传入方法
        test(new Run(){
            @Override
            public void run() {
                System.out.println("奔跑");
            }
        });
    }
}

5.2 Lambda 表达式(简化匿名内部类)

Lambda 是语法糖 ,只能简化【函数式接口】的匿名内部类。 函数式接口:接口里面有且仅有一个抽象方法,可以用@FunctionalInterface注解标记,编译校验

语法格式
复制代码
(参数列表) -> {方法体};
  • ():对应抽象方法的参数列表,无参数就空括号;
  • ->:Lambda 操作符,读作箭头;
  • {}:方法体,写实现逻辑。
匿名内部类 VS Lambda 代码对比
复制代码
//标记函数式接口
@FunctionalInterface
interface Animal{
    void eat();
}

public class Test{
    public static void main(String[] args) {
        // 匿名内部类写法
        Animal cat1 = new Animal(){
            @Override
            public void eat() {
                System.out.println("小猫吃鱼");
            }
        };
        cat1.eat();

        // Lambda简化写法
        Animal cat2 = () -> {
            System.out.println("小猫吃鱼");
        };
        cat2.eat();
    }
}
Lambda 省略规则(重点)
  1. 参数类型可以省略,所有参数统一省略,不能只省一部分;

  2. 只有一个参数 ,小括号()可以省略;

  3. 方法体只有一行代码 ,大括号{}可以省略,同时去掉末尾分号;

  4. 方法体只有一行 return 语句:省略{}的同时,必须省略 return 关键字。

    @FunctionalInterface
    interface Calc{
    int add(int a, int b);
    }

    public class Test{
    public static void main(String[] args) {
    //完整版
    Calc c1 = (int a, int b) -> {return a + b;};
    //省略参数类型
    Calc c2 = (a, b) -> {return a + b;};
    //一行return,省略{}和return
    Calc c3 = (a, b) -> a + b;
    System.out.println(c3.add(10,20));
    }
    }

Lambda 访问局部变量

Lambda 访问方法局部变量,变量同样要求有效 final,一旦赋值不能二次修改,否则编译报错。

复制代码
public class Test{
    public static void main(String[] args) {
        int num = 10; //有效final
        Runnable r = () -> {
            System.out.println(num);
        };
        // num = 20;  //打开这行直接报错
    }
}
Lambda 与匿名内部类区别
对比项 匿名内部类 Lambda 表达式
使用范围 普通类、抽象类、接口(不限抽象方法数量) 仅函数式接口(只有 1 个抽象方法)
this 含义 指向匿名内部类对象 指向外部类对象
能否定义构造器 可以 不可以
JDK 自带常用函数式接口(开发高频)

java.util.function包,无需自己定义接口:

  1. Runnable:无参数,无返回值 void run()
  2. Consumer<T>:有参数,无返回值 void accept(T t)
  3. Supplier<T>:无参数,有返回值 T get()
  4. Function<T,R>:有参数,有返回值 R apply(T t)
  5. Predicate<T>:有参数,返回布尔值 boolean test(T t)

示例:Predicate

复制代码
import java.util.function.Predicate;
public class Test{
    public static void main(String[] args) {
        //判断数字是否大于10
        Predicate<Integer> p = num -> num > 10;
        System.out.println(p.test(15)); //true
        System.out.println(p.test(5));  //false
    }
}

六、总对比表

类型 定义位置 创建对象 适用场景
成员内部类 外部类成员位置 new Outer().new Inner() 需要访问外部实例成员
局部内部类 外部类方法内部 方法内 new 方法内临时使用,逻辑复杂
静态内部类 外部类成员,加 static new Outer.Inner() 不需要外部对象,工具嵌套类
匿名内部类 new 的时候定义 直接 new 临时实现接口 / 继承类,可多抽象方法
Lambda 表达式 方法内,赋值给函数式接口变量 直接赋值 仅函数式接口,简化匿名内部类

七、高频易错点汇总

  1. 成员内部类不能写 static 成员;静态内部类可以定义 static;
  2. 静态内部类不等于成员内部类,创建对象不需要外部实例;
  3. 局部内部类不能加 public/private 修饰符;
  4. Lambda不能用于拥有多个抽象方法的接口;
  5. @FunctionalInterface只是校验注解,不加注解,只要只有一个抽象方法,依旧是函数式接口;
  6. Lambda 中this代表外部类对象,和匿名内部类的 this 不一样。
相关推荐
Knight_AL1 小时前
从 EasyExcel 到 Apache Fesod:Java Excel 处理生态的一次重构
java·apache·excel
yuniko-n1 小时前
【JUC】集合线程安全
java
FPGA小徐2 小时前
【一生一芯 / PA】异常响应机制:RISC-V 中 ecall → mtvec → mret 的完整代
开发语言·数据库·c#
北京盛世宏博2 小时前
工业环境监测选型:Modbus TCP以太网温湿度传感器部署、调试、避坑全指南
开发语言·网络·php
沫璃染墨2 小时前
《Linux工程实践篇(二):线程池实战——从线程安全单例到完整源码实现》
linux·开发语言·c++·单例模式·设计模式·系统架构
starzy19902 小时前
Flink SlotManager启动流程源码深度剖析:从ResourceManager到Slot分配的完整链路
java·大数据·flink
极光通讯2 小时前
服务器内存来料检验(IQC)实操:批次追溯、外观判定与上机验证
java·服务器·算法
4SAPI2 小时前
AI API Gateway平台哪个好?从架构视角看企业多模型网关选型与实践
java·大数据·人工智能·gateway·php
EatFan2 小时前
2026 后端 AI 工程化:Spring AI 2.0、MCP 协议与 Agent 内嵌如何收进 Java 生产系统
java·人工智能·spring·agent·spring ai·spring boot 3·mcp