3.6内部类和枚举

#王者杯·14天创作挑战营·第1期#

 内部类

成员内部类

在 Java 中,内部类是定义在另一个类内部的类。内部类提供了更好的封装性和代码组织方式,同时也可以访问外部类的成员。

实例化依赖
成员内部类的实例化依赖于外部类的实例。不能直接创建成员内部类的对象,必须先创建外部类的对象,再通过外部类对象来创建成员内部类对象。

成员内部类可以访问外部类的所有成员,包含私有成员。不过外部类访问成员内部类的成员时,需要先创建内部类的对象。

命名冲突
若成员内部类的成员和外部类的成员重名,在内部类中使用 this 关键字指向内部类的成员,使用 外部类名.this 指向外部类的成员。

class Outer {
    int num = 10;
    class Inner {
        int num = 20;
        public void printNums() {
            System.out.println(this.num); 
            System.out.println(Outer.this.num); 
        }
    }
}
定义和特点

成员内部类是定义在另一个类的内部,作为外部类的一个成员存在。它可以访问外部类的所有成员(包括私有成员),同时外部类也可以通过创建内部类的对象来访问内部类的成员。

示例代码
class Outer {
    private int outerValue = 10;

    // 成员内部类
    class Inner {
        public void printOuterValue() {
            System.out.println("Outer value: " + outerValue);
        }
    }

    public void useInner() {
        Inner inner = new Inner();
        inner.printOuterValue();
    }
}

public class MemberInnerClassExample {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.useInner();

        // 也可以直接创建内部类对象
        Outer.Inner inner = outer.new Inner();
        inner.printOuterValue();
    }
}
代码解释
  • Inner 类是 Outer 类的成员内部类,在 Inner 类的 printOuterValue 方法中可以直接访问外部类的私有成员 outerValue
  • 在 Outer 类的 useInner 方法中,创建了 Inner 类的对象并调用其方法。
  • 在 main 方法中,可以通过外部类对象创建内部类对象,语法为 外部类对象.new 内部类名()

静态内部类

定义和特点

静态内部类是使用 static 关键字修饰的内部类。它不依赖于外部类的实例,可以直接创建对象。静态内部类只能访问外部类的静态成员,不能访问外部类的非静态成员。

示例代码
class Outer {
    private static int outerStaticValue = 20;

    // 静态内部类
    static class StaticInner {
        public void printOuterStaticValue() {
            System.out.println("Outer static value: " + outerStaticValue);
        }
    }
}

public class StaticInnerClassExample {
    public static void main(String[] args) {
        // 直接创建静态内部类对象
        Outer.StaticInner staticInner = new Outer.StaticInner();
        staticInner.printOuterStaticValue();
    }
}
代码解释
  • StaticInner 类是 Outer 类的静态内部类,在 StaticInner 类的 printOuterStaticValue 方法中可以访问外部类的静态成员 outerStaticValue
  • 在 main 方法中,可以直接通过 外部类名.静态内部类名() 创建静态内部类的对象。

局部内部类

局部内部类是定义在方法或代码块内部的类。它的作用域仅限于定义它的方法或代码块内部。局部内部类可以访问外部类的成员,同时也可以访问所在方法的 final 局部变量(Java 8 及以上版本,局部变量隐式为 final)在 Java 8 之前,如果要在局部内部类或匿名内部类中访问所在方法的局部变量,该局部变量必须显式地声明为 final。而从 Java 8 开始,即使没有显式地使用 final 关键字修饰,只要该局部变量在初始化后其值不再改变,它就被视为隐式的 final 变量,局部内部类或匿名内部类就可以访问它。

原理

当局部内部类或匿名内部类访问局部变量时,实际上是通过复制该局部变量的值来实现的。这是因为局部变量的生命周期和内部类对象的生命周期可能不同,局部变量在方法执行结束后就会被销毁,而内部类对象可能还存在。为了保证内部类能够正确访问局部变量的值,Java 采用了复制的方式。如果局部变量的值可以改变,那么内部类中复制的值和原始变量的值可能会不一致,从而导致错误。因此,要求局部变量要么显式声明为 final,要么隐式为 final(即初始化后值不再改变)。

示例代码
class Outer {
    private int outerValue = 30;

    public void method() {
        final int localValue = 40;
    //  int localValue = 40; (jdk8 可以在不对localValue 从新赋值情况下 隐式加上final)

        // 局部内部类
        class LocalInner {
            public void printValues() {
                System.out.println("Outer value: " + outerValue);
                System.out.println("Local value: " + localValue);
            }
        }

        LocalInner localInner = new LocalInner();
        localInner.printValues();
    }
}

public class LocalInnerClassExample {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.method();
    }
}
代码解释
  • LocalInner 类是定义在 Outer 类的 method 方法内部的局部内部类。
  • 在 LocalInner 类的 printValues 方法中,可以访问外部类的成员 outerValue 和所在方法的 final 局部变量 localValue

匿名内部类

定义和特点

匿名内部类是一种没有显式名称的内部类,通常用于创建只需要使用一次的类的实例。它可以继承一个类或实现一个接口,并且在创建对象的同时进行定义。

示例代码
interface MyInterface {
    void doSomething();
}

public class AnonymousInnerClassExample {
    public static void main(String[] args) {
        // 匿名内部类
        MyInterface myInterface = new MyInterface() {
            @Override
            public void doSomething() {
                System.out.println("Doing something...");
            }
        };

        myInterface.doSomething();
    }
}
代码解释
  • 在 main 方法中,创建了一个实现 MyInterface 接口的匿名内部类的对象。
  • 匿名内部类没有显式的类名,直接在创建对象的同时实现了接口的方法。
匿名内部类的优点
  • 封装性:内部类可以将相关的类组织在一起,提高代码的封装性。
  • 访问权限:内部类可以访问外部类的私有成员,提供了更灵活的访问控制。
  • 代码组织:内部类可以使代码更加清晰和易于维护,特别是在处理复杂的逻辑时。
匿名内部类的注意事项
  • 内部类会增加代码的复杂度,使用时需要谨慎。
  • 性能影响
    每次创建匿名内部类的实例时,都会生成一个新的类文件,这可能会对性能和磁盘空间造成一定影响。会影响代码的性能和可维护性。

通用注意事项

  1. 编译生成的类文件
    内部类编译后会生成独立的类文件,文件名格式为 外部类名$内部类名.class。例如,成员内部类 Outer.Inner 编译后会生成 Outer$Inner.class 文件。
  2. 序列化问题
    如果内部类需要实现序列化接口 Serializable,要确保外部类也实现该接口,否则可能会出现序列化异常。

枚举

枚举的定义

枚举是一种特殊的数据类型,它用于定义一组命名的常量。使用enum关键字来定义枚举类型。

enum Weekday {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

上述代码定义了Weekday枚举类型,它包含了一周七天的常量。

枚举的特点

  • 常量集合:枚举类型定义了一组固定的常量值,这些值在程序运行期间不会改变。
  • 类型安全:使用枚举可以避免使用字符串或整数来表示常量时可能出现的错误,编译器会进行类型检查。
  • 单例模式实现:枚举类型天然支持单例模式,因为枚举的每个实例都是唯一的。

枚举的使用

可以像使用普通类一样使用枚举类型。以下是一些常见的用法:

public class EnumExample {
    public static void main(String[] args) {
        Weekday today = Weekday.MONDAY;
        System.out.println("Today is " + today);

        // 使用 switch 语句处理枚举值
        switch (today) {
            case MONDAY:
                System.out.println("It's a busy day.");
                break;
            case SATURDAY:
            case SUNDAY:
                System.out.println("It's weekend.");
                break;
            default:
                System.out.println("It's a normal weekday.");
        }
    }
}

上述代码展示了如何声明枚举类型的变量,以及如何使用switch语句处理枚举值。

枚举的构造函数和方法

枚举类型可以有构造函数和方法。构造函数用于初始化枚举常量的属性,方法可以为枚举常量提供额外的行为。示例如下:

enum TrafficLight {
    RED(30), YELLOW(5), GREEN(40);

    private int duration;

    TrafficLight(int duration) {
        this.duration = duration;
    }

    public int getDuration() {
        return duration;
    }
}

在上述代码中,TrafficLight枚举类型有一个构造函数,用于初始化每个信号灯的持续时间。同时,提供了getDuration方法来获取信号灯的持续时间。

枚举既可以作为独立的类存在,也可以作为内部类存在。以下是作为内部类的示例:

package com.example.pkg3;

public class Test1 {
    enum InnerEnum {
        VALUE1, VALUE2, VALUE3
    }

    static class InnerClass {
        public static final int VALUE1 = 1;
        public static final int VALUE2 = 2;
        public static final int VALUE3 = 3;
    }
    public static void main(String[] args) {
        Test1.InnerEnum value = InnerEnum.VALUE1;
        System.out.println(value);
        System.out.println(InnerClass.VALUE1);
    }
}

在上述代码中,InnerEnumOuterClass的内部枚举类。枚举类也有静态的特性。Java 枚举是一种特殊的类,它可以是独立的类,也可以作为内部类存在。枚举类型为定义一组固定的常量提供了一种安全、方便的方式。

所有通过enum定义的枚举类,在编译后会由编译器自动生成继承java.lang.Enum 的代码

由于Java的单继承限制,枚举类不能再显式声明继承其他类

非抽象的枚举类默认被final修饰,因此无法被其他类继承(即不能有子类)

继承java.lang.Enum 的影响

  1. 获得通用方法
    通过继承Enum类,枚举类自动获得以下方法:
    • name():返回枚举常量名称(如SPRING.name() 返回"SPRING")。
    • ordinal():返回枚举常量的声明顺序索引(从0开始)。
    • values()valueOf():获取所有枚举实例或通过名称查找实例。
  2. 构造器限制
    枚举类的构造器必须为private,确保实例只能在枚举内部定义,无法通过new创建

抽象枚举类的特殊性
若枚举类包含抽象方法,则被视为抽象类(隐式abstract修饰),但仍不能被继承。枚举内部的值,必须实现该抽象方法。

public enum Direction {
    UP { @Override void move() { /* 向上逻辑 */ } },
    DOWN { @Override void move() { /* 向下逻辑 */ } };
    abstract void move();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

chxii

小小打赏,大大鼓励!

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

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

打赏作者

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

抵扣说明:

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

余额充值