JackLove1234 发表于 2021-2-20 19:20

java面试题,坚持复习第四天

本帖最后由 JackLove1234 于 2021-2-20 19:23 编辑

#### 类与 Object 的应用
#### 1\. 类的组成部分有哪些?

答:在 Java 语言中,类主要是由方法和变量两部分组成。

#### 2\. 类与对象有哪些区别?

答:类是一个抽象的概念,是对某一事物的描述;而对象是类的实例,是实实在在存在的个体。比如,“人”就是一个类(一个概念),而老王(王磊)就是实实在在的一个“对象”。

#### 3\. Java 中可以多继承吗?

答:Java 中只能单继承,但可以实现多接口。

#### 4\. Java 中为什么不能实现多继承?

答:从技术的实现角度来说,是为了降低编程的复杂性。假设 A 类中有一个 m() 方法,B 类中也有一个 m() 方法,如果 C 类同时继承 A 类和 B
类,那调用 C 类的 m() 方法时就会产生歧义,这无疑增加了程序开发的复杂性,为了避免这种问题的产生,Java 语言规定不能多继承类,但可以实现多接口。

#### 5\. 覆盖和重载有哪些区别?

答:覆盖和重载的区别如下:

* 覆盖(Override)是指子类对父类方法的一种重写,只能比父类抛出更少的异常,访问权限不能比父类的小,被覆盖的方法不能是 private,否则只是在子类中重新定义了一个方法;
* 重载(Overload)表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同。

#### 6\. 以下不属于重载特性的是?

A:方法的参数类型不同
B:方法的返回值不同
C:方法的参数个数不同
D:方法的参数顺序不同

答:B

#### 7\. 为什么方法不能根据返回类型来区分重载?

答:因为在方法调用时,如果不指定类型信息,编译器就不知道你要调用哪个方法了。比如,以下代码:


​   
​    float max(int x,int y);
​    int max(int x,int y);
​    // 方法调用
​    max(1,2);


因为 `max(1,2)` 没有指定返回值,编译器就不知道要调用哪个方法了。

#### 8\. 构造方法有哪些特征?

答:构造方法的特征如下:

* 构造方法必须与类名相同;
* 构造方法没有返回类型(void 也不能有);
* 构造方法不能被继承、覆盖、直接调用;
* 类定义时提供了默认的无参构造方法;
* 构造方法可以私有,外部无法使用私有构造方法创建对象。

#### 9\. 构造函数能不能被覆盖?能不能被重载?

答:构造函数可以重载,但不能覆盖。

#### 10\. 以下说法正确的是?

A:类中的构造方法不能忽略
B:构造方法可以作为普通方法被调用
C:构造方法在对象被 new 时被调用
D:一个类只能有一个构造方法

答:C

#### 11\. 以下程序执行的结果是?


​   
​    class ExecTest {
​      public static void main(String[] args) {
​            Son son = new Son();
​      }
​    }
​    class Parent{
​      {
​            System.out.print("1");
​      }
​      static{
​            System.out.print("2");
​      }
​      public Parent(){
​            System.out.print("3");
​      }
​    }
​    class Son extends Parent{
​      {
​            System.out.print("4");
​      }
​      static{
​            System.out.print("5");
​      }
​      public Son(){
​            System.out.print("6");
​      }
​    }


答:打印的结果是:`251346`

加载顺序如下:

* 执行父类的静态成员;
* 执行子类的静态成员;
* 父类的实例成员和实例初始化;
* 执行父类构造方法;
* 子类的实例成员和实例初始化;
* 子类构造方法。

#### 12\. 以下程序执行的结果是?


​   
​    class A {
​      public int x = 0;
​      public static int y = 0;
​      public void m() {
​            System.out.print("A");
​      }
​    }
​    class B extends A {
​      public int x = 1;
​      public static int y = 2;
​      public void m() {
​            System.out.print("B");
​      }
​      public static void main(String[] args) {
​            A myClass = new B();
​            System.out.print(myClass.x);
​            System.out.print(myClass.y);
​            myClass.m();
​      }
​    }


答:打印的结果是:`00B`

题目解析:在 Java 语言中,变量不能被重写。

#### 13\. 以下程序执行的结果是?


​   
​    class A {
​      public void m(A a) {
​            System.out.println("AA");
​      }
​      public void m(D d) {
​            System.out.println("AD");
​      }
​    }
​    class B extends A {
​      @Override
​      public void m(A a) {
​            System.out.println("BA");
​      }
​      public void m(B b) {
​            System.out.println("BD");
​      }
​      public static void main(String[] args) {
​            A a = new B();
​            B b = new B();
​            C c = new C();
​            D d = new D();
​            a.m(a);
​            a.m(b);
​            a.m(c);
​            a.m(d);
​      }
​    }
​    class C extends B{}
​    class D extends B{}


答:打印结果如下。


​   
​    BA
​    BA
​    BA
​    AD


题目解析:

* 第一个 BA:因为 A 的 m() 方法,被子类 B 重写了,所以输出是:BA;
* 第二个 BA:因为 B 是 A 的子类,当调用父类 m() 方法时,发现 m() 方法被 B 类重写了,所以会调用 B 中的 m() 方法,输出就是:BA;
* 第三个 BA:因为 C 是 B 的子类,会直接调用 B 的 m() 方法,所以输出就是:BA;
* 第四个 AD:因为 D 是 A 的子类,所以会调用 A 的 m() 方法,所以输出就是:AD。

#### 14\. Java 中的 this 和 super 有哪些区别?

答:this 和 super 都是 Java 中的关键字,起指代作用,在构造方法中必须出现在第一行,它们的区别如下。

* 基础概念:this 是访问本类实例属性或方法;super 是子类访问父类中的属性或方法。
* 查找范围:this 先查本类,没有的话再查父类;super 直接访问父类。
* 使用:this 单独使用时,表示当前对象;super 在子类覆盖父类方法时,访问父类同名方法。

#### 15\. 在静态方法中可以使用 this 或 super 吗?为什么?

答:在静态方法中不能使用 this 或 super,因为 this 和 super
指代的都是需要被创建出来的对象,而静态方法在类加载的时候就已经创建了,所以没办法在静态方法中使用 this 或 super。

#### 16\. 静态方法的使用需要注意哪些问题?

答:静态方法的使用需要注意以下两个问题:

* 静态方法中不能使用实例成员变量和实例方法;
* 静态方法中不能使用 this 和 super。

#### 17\. final 修饰符的作用有哪些?

答:final 修饰符作用如下:

* 被 final 修饰的类不能被继承;
* 被 final 修饰的方法不能被重写;
* 被 final 修饰的变量不能被修改。

#### 18\. 覆盖 equals() 方法的时候需要遵守哪些规则?

答:Oracle 官方的文档对于 equals() 重写制定的规则如下。

* 自反性:对于任意非空的引用值 x,x.equals(x) 返回值为真。
* 对称性:对于任意非空的引用值 x 和 y,x.equals(y) 必须和 y.equals(x) 返回相同的结果。
* 传递性:对于任意的非空引用值 x、y 和 z,如果 x.equals(y) 返回值为真,y.equals(z) 返回值也为真,那么 x.equals(z) 也必须返回值为真。
* 一致性:对于任意非空的引用值 x 和 y,无论调用 x.equals(y) 多少次,都要返回相同的结果。在比较的过程中,对象中的数据不能被修改。
* 对于任意的非空引用值 x,x.equals(null) 必须返回假。

此题目不要求记忆,能知道大概即可,属于加分项题目。

#### 19\. 在 Object 中 notify() 和 notifyAll() 方法有什么区别?

答:notify() 方法随机唤醒一个等待的线程,而 notifyAll() 方法将唤醒所有在等待的线程。

#### 20\. 如何使用 clone() 方法?

答:如果是同一个类中使用的话,只需要实现 Cloneable 接口,定义或者处理 CloneNotSupportedException
异常即可,请参考以下代码:


​   
​    class CloneTest implements Cloneable {
​      int num;
​      public static void main(String[] args) throws CloneNotSupportedException {
​            CloneTest ct = new CloneTest();
​            ct.num = 666;
​            System.out.println(ct.num);
​            CloneTest ct2 = (CloneTest) ct.clone();
​            System.out.println(ct2.num);
​      }
​    }


如果非内部类调用 clone() 的话,需要重写 clone() 方法,请参考以下代码:


​   
​    class CloneTest implements Cloneable {
​      int num;
​      public static void main(String[] args) throws CloneNotSupportedException {
​            CloneTest ct = new CloneTest();
​            ct.num = 666;
​            System.out.println(ct.num);
​            CloneTest ct2 = (CloneTest) ct.clone();
​            System.out.println(ct2.num);
​      }
​      @Override
​      protected Object clone() throws CloneNotSupportedException {
​            return super.clone();
​      }
​    }
​    public class CloneTest2 {
​      public static void main(String[] args) throws CloneNotSupportedException {
​            CloneTest ct = new CloneTest();
​            ct.num = 666;
​            System.out.println(ct.num);
​            CloneTest ct2 = (CloneTest) ct.clone();
​            System.out.println(ct2.num);
​      }
​    }


### 总结

本文我们学习了类的基础用法,类引用:import 和 import static,访问修饰符的作用,构造函数和继承的特点以及使用技巧等,通过这些内容让我们对整个 Java 程序的组成,有了更加清晰直观的印象。

#### 各种内部类和枚举类的使用
#### 1.Java 中的内部类有哪些?

答:内部类包含以下 4 种:

* 静态内部类:static class StaticInnerClass{};
* 成员内部类:private class InstanceInnerClass{};
* 局部内部类:定义在方法或者表达式内部;
* 匿名内部类:(new Thread(){}).start()。

#### 2.以下关于匿名内部类说法错误的是?

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

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

![enter image description
here](https://images.gitbook.cn/7d00d230-c7b3-11e9-9e56-8d3dec542204)

#### 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 被重写的源码如下图:

![enter image description
here](https://images.gitbook.cn/8feae480-c7b3-11e9-9e56-8d3dec542204)

#### 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();
​      }
​    }


答:输出内容如下。


​   
​    3
​    2
​    1


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

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


​   
​    public enum Color {
​      RED("#FF0000", "255,0,0"),
​      GREEN("#00FFFF", "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
的各种内部类:静态内部类、成员内部类、局部内部类、匿名内部类,知道了它们特点和区别,并学习了枚举类了使用,知道了枚举类在编译之后,其实还是普通的最终类(finalclass)。

K.T 发表于 2021-2-20 20:33

多谢版主哦

masongxin9 发表于 2021-2-20 20:37

程序员的初级测试吧

爱破解音乐 发表于 2021-2-20 21:04

语言中,类主要是由方法和变量两部分组成。

2. 类与对象有哪些区别?

林伊轩 发表于 2021-2-20 21:29

这些问题好细啊,二本计算机渣渣表示 都不会

柒萧萧 发表于 2021-3-1 09:05

十三题 代码最后一行 应该是这样的把 class D extendsA{}
D 子类 继承 A父类

Hxd! 发表于 2021-3-1 11:51

页: [1]
查看完整版本: java面试题,坚持复习第四天