当前位置: 首页 > 图灵资讯 > 技术篇> 各种内部类和枚举类的使用(附面试题)

各种内部类和枚举类的使用(附面试题)

来源:图灵教育
时间:2023-06-04 09:21:37

内部类不仅经常出现在各种面试问题中,也经常出现在各种面试问题中 Java 源代码经常出现,所以只能理解 Java 只有在内部类别中,我们才能完成面试并理解各种类型 Java 源码。

内部类

Java 内部类分为以下四类:

  • 成员内部类
  • 静态成员内部类
  • 局部内部类
  • 匿名内部类

让我们来看看这些内部类别的使用。

成员内部类的定义

如果在一个类中定义了另一个类,则将定义在类中的类称为成员内部类。成员内部类也是最常见的内部类。

使用

使用成员内部类的示例如下:

class Outer {    public Outer() {        System.out.println("Outer Class.");    }    class Inner {        public void sayHi() {            System.out.println("Hi, Inner.");        }    }}

其中 Inner 类别是成员内部类别。对于成员内部类别的创建和使用,请参考以下完整的示例代码:

class InnerTest {    public static void main(String[] args) {        Outer out = new Outer();        // 创建成员内部类别        Outer.Inner inner = out.new Inner();        inner.sayHi();    }}class Outer {    public Outer() {        System.out.println("Outer Class.");    }    class Inner {        public void sayHi() {            System.out.println("Hi, Inner.");        }    }}

创建成员内部类别

语法:

Outer.Inner inner = new Outer().new Inner();

内部类访问外部类

语法:

Outer.this.xxx

代码示例:

class Outer {    private String name = "OuterClass";    public void sayHi() {        System.out.println("Hi, Outer.");    }    class Inner {        public void sayHi() {            // 内部类访问外部类            Outer.this.sayHi();            System.out.println(Outer.this.name);            System.out.println("Hi, Inner.");        }    }}class InnerTest {    public static void main(String[] args) {        Outer.Inner inner =  new Outer().new Inner();        inner.sayHi();    }}

外部类访问内部类

语法:

new Inner().xxx

代码示例:

class Outer {    public void sayHi() {        System.out.println(new Inner().name);        System.out.println("Hi, Outer.");    }    private class Inner {        String name = "InnerClass";        public void sayHi() {            System.out.println("Hi, Inner.");        }    }}class InnerTest {    public static void main(String[] args) {        new Outer().sayHi();    }}

小结
  • 成员内部类可以直接访问外部类(使用:外部类:.this.xxx);
  • 要访问内部类,外部成员必须首先建立内部类对象;
  • 成员的内部类别可以使用任何作用域进行修改(public、protected、默认、private);
  • 成员内部类可访问任何外部类作用域修改的属性和方法;
  • 在外部类建立成员内部类对象后,可以访问任何作用域修改的内部类属性和方法。
内部类定义静态成员

在一个类中定义另一个类 static 类,将定义在类中的类 static 类称为静态成员内部类。

静态成员的内部类别是内部成员类别 static 修饰符。

使用

静态成员内部类的使用示例如下:

class OuterClass {    public OuterClass() {        System.out.println("OuterClass Init.");    }    protected static class InnerClass {        public void sayHi() {            System.out.println("Hi, InnerClass.");        }    }}class InnerClassTest {    public static void main(String[] args) {        OuterClass.InnerClass innerClass = new OuterClass.InnerClass();        innerClass.sayHi();    }}

创建与内部成员相关的方法 new Outer().new Inner() 不同的是,静态成员的内部类别可以使用 new OuterClass.InnerClass() 创建的方式。

注:非静态外部对象不能从静态成员的内部类中访问。

局部内部类定义

一类定义为另一类的局部(方法或任何作用域),称为局部内部类。

使用

局部内部类的使用示例如下:

class OutClass {    public void sayHi() {        class InnerClass {            InnerClass(String name) {                System.out.println("InnerClass:" + name);            }        }        System.out.println(new InnerClass("Three"));        System.out.println("Hi, OutClass");    }}class OutTest {    public static void main(String[] args) {        new OutClass().sayHi();    }}

局部内部类特征
  • 任何访问修饰符都不能用于局部内部类;
  • 如果局部类可以在方法中直接使用方法中的变量,则无需通过 OutClass.this.xxx 获得的方式。
匿名内部类定义

没有名字的内部类称为匿名内部类。

使用

匿名内部类的使用示例如下:

interface AnonymityOuter {    void hi();}class AnonymityTest {    public static void main(String[] args) {        AnonymityOuter anonymityOuter = new AnonymityOuter() {            @Override            public void hi() {                System.out.println("Hi, AnonymityOuter.");            }        };        anonymityOuter.hi();    }}

其中,new AnonymityOuter() 之后的 {...} 大括号包含匿名内部类别。

匿名内部类特征
  • 匿名内部类必须继承一个父亲或实现一个接口
  • 匿名内部类不能定义任何静态成员和方法
  • 匿名内部类的方法不能抽象
枚举类

枚举类是 JDK 1.5 使用关键词“引入新特性”enum声明。枚举功能虽小,但非常实用,极大地方便了程序开发者。

使用枚举类

请参考以下代码:

enum ColorEnum {    RED,    BLUE,    YELLOW,    GREEN}class EnumTest {    public static void main(String[] args) {        ColorEnum color = ColorEnum.GREEN;        switch (color) {            case RED:                System.out.println("Red");                break;            case BLUE:                System.out.println("Blue");                break;            case YELLOW:                System.out.println("Yellow");                break;            case GREEN:                System.out.println("Green");                break;            default:                break;        }    }}

枚举类命名规范

《阿里巴巴 Java 《开发手册》对枚举类命名规范的建议如下图所示:

各种内部类和枚举类的使用(附面试题)_System

扩展枚举类

我们可以定制一些枚举方法来扩展枚举类的使用,请参考以下代码:

enum ColorsEnum {    RED(“红”, 1),    BLUE(“蓝”, 2),    YELLOW(“黄色”, 3),    GREEN(绿色”, 4);    ColorsEnum(String name, int index) {        this.name = name;        this.index = index;    }    private String name;    private int index;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getIndex() {        return index;    }    public void setIndex(int index) {        this.index = index;    }}class EnumTest {    public static void main(String[] args) {        System.out.println(ColorsEnum.RED.getName());        System.out.println(ColorsEnum.RED.getIndex());    }}

执行上述代码返回的结果:

红色1

相关面试题1.Java 内部类有哪些?

答:内部类包括以下内容 4 种:

  • 静态内部类:static class StaticInnerClass{};
  • 成员内部类:private class InstanceInnerClass{};
  • 局部内部类:定义在方法或表达式内部;
  • 匿名内部:(new Thread(){}).start()。
2.以下关于匿名内部类的说法是错误的?

A:匿名内部类必须继承父亲或实现接口B:匿名内部类的方法不能是抽象的C:匿名内部类可以实现部分接口抽象方法D:匿名内部类不能定义任何静态成员和方法

答:C题目分析:匿名内部类规定必须实现界面的所有抽象方法,否则程序将报错,如下图所示。

各种内部类和枚举类的使用(附面试题)_System_02

3.以下枚举类比“==”和“==”equals结果一致吗?为什么?

class EnumTest {    public static void main(String[] args) {        ColorEnum redColor = ColorEnum.RED;        ColorEnum redColor2 = ColorEnum.RED;        System.out.println(redColor == redcolor2);        System.out.println(redColor.equals(redcolor2));    }}enum ColorEnum {    RED,    BLUE}

答:结果一致,都是 true。题目分析:因为枚举类重写 equals 方法,equals 该方法直接使用 == 相比之下,枚举类不能通过 new 创建、使用 ColorEnum.RED 事实上,获得的对象使用对象的引用地址,因此 == 比较的结果一定是 true。equals 被重写的源码如下图所示:

各种内部类和枚举类的使用(附面试题)_枚举类_03

4.使用静态内部类有什么好处?

答:使用静态内部类的好处如下:作用域不会扩散到包外;

  • 可以通过“外部类、内部类”直接访问;
  • 内部类可以访问外部类中的所有静态属性和方法。
5.执行以下代码的结果是什么?

class OuterClass {    String name = "OuterClass";    protected static class InnerClass {        String name = "InnerClass";        public void sayHi() {            System.out.println(OuterClass.this.name);        }    }}class InnerClassTest {    public static void main(String[] args) {        OuterClass.InnerClass innerClass = new OuterClass.InnerClass();        innerClass.sayHi();    }}

答:程序报错。题目分析:非静态外部类不能直接访问静态成员内部类,所以程序会报错。

6.成员内部类和局部内部类有什么区别?

答:内部成员和局部内部的区别如下。

  • 内部成员可以使用任何访问修饰符,局部内部类不能使用任何访问修饰符;
  • 局部内部类是在外部类的方法或其他功能范围内声明的,内部类是在外部类中直接声明的,与方法和属性相等。
7.为什么要使用内部类?内部类的使用场景是什么?

答:使用内部类有以下两个好处。

  • 它可以作为实现多继承的一种方式。内部类最早的实现是平衡 Java 语言中没有多种继承方式;
  • 方便将存在具有一定逻辑关系的类组织可以隐藏在外部世界。内部类可以作为实现多继承的一种方式,因为每个内部类都可以独立继承一个类或接口,因此整个类可以实现多继承。
8.执行以下代码的结果是什么?

class Outer {    public int num = 1;    class Inner {        public int num = 2;        public void show() {            int num = 3;            System.out.println(num);            System.out.println(this.num);            System.out.println(Outer.this.num);        }    }}class InnerTest {    public static void main(String[] args) {        new Outer().new Inner().show();    }}

答:输出内容如下。

321

9.枚举有哪些应用场景?

答:枚举类的主要应用场景如下:① 枚举类可作为高级常量类示例代码如下:

public enum Color {    RED(#FF0000”, "255,0,0"    GREEN(#00FFF”, "0,255,255"    YELLOW(#FFFF00”, "255,255,0");    String hex, rgb;    Color(String hex, String rgb) { this.hex = hex; this.rgb = rgb;    }}

② 枚举类可方便使用 switch 判断示例代码如下:

switch(color){case RED:    System.out.println(“红灯停”;    break;case GREEN:    System.out.println(绿灯行);    break;case YELLOW:    System.out.println(“看情况”;    break;default:    System.out.println(“灯坏了”);}

10.枚举类在 JVM 它是如何实现的?

答:枚举类在 JVM(Java 虚拟机) 其实是通过普通的 static final 形式实现。主题分析:我们使用它 javap 命令分析枚举类的最终编译结果,查看编译结果,找到枚举类 JVM 具体实现。首先定义一个枚举类,代码如下:

enum DBEnum {    ORACLE,    DB2,    MYSQL,    SQLSERVER}

再使用命令 javac DBEnum.java 编译 .class 文件,然后使用命令 javap DBEnum.class,我们看到最终执行的结果如下:

Compiled from "EnumTest.java"final class DBEnum extends java.lang.Enum<DBEnum> {public static final DBEnum ORACLE;public static final DBEnum DB2;public static final DBEnum MYSQL;public static final DBEnum SQLSERVER;public static DBEnum[] values();public static DBEnum valueOf(java.lang.String);static {};}

可以得出结论,枚举类是 JVM 实现也是通过普通的方式实现的 static final 实现的。

11.枚举类能继承吗?

答:不能继承,因为枚举类编译后的实际代码是 final class 形式,类被 final 修饰自然不能继承。

12.线程安全吗?

答:枚举类是线程安全的,因为枚举类是编译后的 final class 因此,枚举类是线程安全的。

13.枚举能序列化吗?

答:枚举可以序列化,Oracle 官方解释如下:

Enum constants are serialized differently than ordinary serializable or externalizable objects. The serialized form of an enum constant consists solely of its name; field values of the constant are not transmitted. To serialize an enum constant, ObjectOutputStream writes the string returned by the constant's name method. Like other serializable or externalizable objects, enum constants can function as the targets of back references appearing subsequently in the serialization stream. The process by which enum constants are serialized cannot be customized; any class-specific writeObject and writeReplace methods defined by enum types are ignored during serialization. Similarly, any serialPersistentFields or serialVersionUID field declarations are also ignored--all enum types have a fixed serialVersionUID of 0L

原文地址:<https://docs.oracle.com/javase/8/docs/api/java/io/ObjectOutputStream.html>一般来说,枚举的序列化不同于其他普通类别的序列化。枚举序列化时,只对象进行枚举 name 将属性输出到结果中,反序列化是通过 java.lang.Enum 的 valueOf 该方法根据名称找到枚举对象。

总结

通过这篇文章,我们系统地学习 Java 各种内部类别:静态内部类别、成员内部类别、局部内部类别、匿名内部类别,了解其特点和差异,学习使用枚举类别,知道编译后的枚举类别,实际上是普通的最终类别(final class)。