JavaSE之面向对象

2023-08-31 八股文
1.面向对象的五大基本原则?
  • 单一职责原则:一个类最好只做一件事

  • 开放封闭原则:对扩展开放、对修改封闭

  • 里氏替换原则:子类替换父类时要确保原程序的逻辑以及正确性,应用时尽量不直接使用父类,而是使用子类实现

  • 依赖倒置原则:程序要依赖于抽象接口,而不是具体的实现

  • 接口隔离原则:使用多个小的专门的接口,而不要使用一个大的总接口,接口中的方法尽量少

2.为什么Java不支持多继承?

因为多继承存在菱形继承的问题,而经过分析人们发现我们其实真正想要使用多继承的情况并不多。菱形继承如图所示: 菱形继承

因为D同时继承了B和C,并且B和C又同时继承了A,那么D中就会因为多重继承,继承到两份来自A中的属性和方法。这时候在使用D的时候,如果想要调用一个定义在A中的方法时,就会出现歧义。

java中如果需要实现多继承效果可以使用接口,java支持同一个类可以同时实现多个接口

3.java8的接口支持默认函数,一个类继承多个接口引发菱型问题如何处理?

代码如下所示,定义了两个接口都由名称相同的eat()默认函数,Cat类继承两个接口,就有两个eat该使用谁的呢?

public interface Pet {
    public default void eat(){
        System.out.println("Pet Is Eating");
    }
}

public interface Mammal {
    public default void eat(){
        System.out.println("Mammal Is Eating");
    }
}

public class Cat implements Pet,Mammal {}

编译期会报错:error: class Cat inherits unrelated defaults for eat() from types Mammal and Pet

要求Cat类中,必须重写eat()方法。所以Java并没有帮我们解决多继承的歧义问题,而是把这个问题留给开发人员,通过重写方法的方式自己解决。

4.接口和抽象类的区别,如何选择?

接口主要用于制定规范,而抽象类主要目的是为了复用,通常在工具类或完成某个功能使用抽象类。

比如我想通过MQ发送广播消息,在发送前和发送后在定义时并没有规定完成什么功能,可以交给子类自己实现。

抽象类中的抽象方法可以有public、protected和default这些修饰符,而接口中默认修饰符是public。不可以使用其它修饰符。

5.方法中的重载与重写有什么区别?
  • 重载:函数或方法有同样的名称,但是参数列表不相同
  • 重写:Java的子类与父类中有两个名称、参数列表都相同的方法的情况,子类中的新方法将覆盖父类中原有的方法。
6.为什么需要包装类?
  • 首先Java面向对象语言,很多地方都需要使用对象而不是基本数据类型,比如在集合类中无法将int 、double等类型放进去;
  • 为了让基本类型也具有对象的特征就出现了包装类型,java为包装类添加了属性和方法,丰富了基本类型的操作。
  • 基本类型和包装类型的默认值不同,基本类型的默认值为0, false或\u0000等,包装类默认均为null
7.在对象内使用静态变量初始化对象,和使用static代码块初始化对象有什么区别?

如下所示是使用static静态代码块初始化对象,静态代码块在类加载时执行,这意味着defaultProcess对象只会被创建一次,而不是每次调用defaultProcess时都创建一个新的实例。

public class ProcessFactory {
    // 使用静态代码块初始化 defaultProcess 对象
    private static final BaseProcess defaultProcess;
    static {
        defaultProcess = new BaseProcess() {
            @Override
            public void processBefore() {
                // ...
            }

            @Override
            public void processAfter() {
                // ...
            }
        };
    }
}

直接在声明时初始化defaultProcess对象,这意味着每次调用defaultProcess时都会创建一个新的实例,这种方法可能会导致性能下降,特别是在创建成本较高的对象时。

public class ProcessFactory {
    // 直接在声明时初始化 defaultProcess 对象
    private static final BaseProcess defaultProcess = new BaseProcess() {
        @Override
        public void processBefore() {
            // ...
        }

        @Override
        public void processAfter() {
            // ...
        }
    };
}
8.new一个String创建了几个对象?

1个或两个,一次new的过程会在堆创建对象这是必然的;如果是第一次执行还会在堆的字符串常量池创建字符数组对象。

9.什么是类型擦除,开发中有什么提现?泛型中KTVN分别是什么含义?

类型擦除是指在编译后的字节码(.class)文件中是不包含泛型中的类型信息的,比如在类方法的重载不支持两个方法名称相同参数为List<String>List<Integer>的方法,因为jdk只有List.class,没有List<String>.clasee

  • T – Type(Java 类)
  • K – Key(键)
  • V – Value(值)
  • N – Number(数值类型)
  • ?– 表示不确定的java类型(无限制通配符类型)
10.泛型中上下界限定符extends和super有什么区别?
  • <? extends T>只能是T或其子类
  • <? super T>只能使用T或其父类直至Object
11.什么是反射机制?为什么反射慢?

反射可以用来在运行时获取到任意一个对象所属的类,类所具有的变量或方法,调用任意一个对象的方法,构造任意对象。

反射不能执行一些JVM优化,反射调用方法时会从方法数组中遍历查找,需要做很多额外的检查,比如说参数等。

12.Java中创建对象有哪些种方式?
  1. new关键字
  2. 反射
  3. 使用clone方法
  4. 使用反序列化
13.Java的动态代理如何实现?
  1. JDK动态代理
  2. Cglib
14.Serializable有什么用?

用于声明一个类可序列化;当试图对一个对象进行序列化的时候,如果遇到不支持 Serializable 接口的对象。在此情况下,将抛出 NotSerializableException。

15.serialVersionUID 有何用途? 如果没定义会有什么问题?
  • 进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化。
  • 如果UID没有显式的定义虚拟机会随机分配一个UID,如果Class文件没有发生变化就算再编译多次,serialVersionUID也不会变化的。但是,如果发生了变化,那么这个文件对应的serialVersionUID也就会发生变化,如果此时反序列化之前的数据则会报错。
上次更新: 5 个月前