java面试题

1、Java 为啥不支持多继承,C++ 是怎么解决该问题的

Java虽然不支持多继承,但是可以实现多个接口,而一个接口可以继承多个接口,可以达到类似多继承。

(JDK1.8之前)因为接口只允许有方法声明而不允许有实现,所以不会出现像C++那样的实现多继承的二义性问题。

也就是当两个父类有相同的方法或者属性时,子类不知道继承的是哪一个。

(JDK1.8开始)接口允许有默认的实现,所以他也会涉及到多继承的二义性问题。

但是他语言层面有专门规定去解决该问题的方案---哪个都不选择,

而是发现会继承多个默认方法实现并且没有override时报错,逼使用户显示override可能冲突的方法。

这使得Java 8开始接口可以达到多继承的目的。

2、abstract class 和 interface 的区别

abstract class 的核心在于,我知道这类物体的部分行为和属性,但是不清楚另一部分的。

所以我并不能实例化他,因为不知道他的所有属性和行为。

interface的核心在于,我只知道这类物体能干嘛,具体时什么不需要遵从类的继承关系。

所以abstract class和 interface是不能相互替代的。

3、SpringBoot 和 Spring 的区别

Ⅰ、SpringBoot支持起步依赖starter,而SpringBoot不支持。

Ⅱ、SpringBoot默认提供嵌入式Tomcat容器的支持,Spring没有提供。

Ⅲ、SpringBoot尽可能的提供自动化配置,而Spring没有。

Ⅳ、另外SpringBoot只是Spring本身的扩展,使开发、测试和部署更方便。

4、String 与 StringBuffer或StringBuilder 在做字符串拼接时有什么区别?

Ⅰ、String使用的是final char[] 数组,StringBuffer、StringBuilder使用的是char[]数组,没有用final修饰。

Ⅱ、StringBuffer和StringBuilder都是继承的AbstractStringBuilder,几乎没有区别。

Ⅲ、StringBuffer与StringBuilder的区别是前者是线程安全的,后者是非线程安全的。

StringBuffer与StringBuilder都是通过调用append方法来拼接字符串的,而最终返回字符串是通过 toString()方法。

public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

count默认为0,每次添加一个字符串就将count的值加上字符串的长度。

而 ensureCapacityInternal 是判断 char[] 数据是否需要扩容:

private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }

在StringBuffer初始化的时候会将value.length设置为传入的字符串的长度加上16:

public StringBuffer(String str) {
        super(str.length() + 16);
        append(str);
    }

而每次拼接字符串的话都是通过getChars方法进行拼接:

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }

而最终返回的toString()方法会创建一个String对象:

public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
    }

至于String创建了几个对象可以看这篇:请别再问我new String()创建了几个对象

5、垃圾回收有哪几种方式,什么时候触发垃圾回收?

6、不用乘法 if、else、while...等语句如何实现从 1 到 n 的相加?

可以用递归替代循环,用&&将表达式和n连接起来。

7、异常体系

8、进程和线程(以下内容来自于RedSpider社区)

进程是一个独立的运行环境,而线程是在进程中执行的一个任务。

他们的本质区别:是否单独占有内存地址空间以及其他系统资源(例如I/O)。

Ⅰ、进程间存在内存隔离,据是分开的,数据共享复杂但是同步简单,各个进程之间互不干扰;

       而线程共享所属进程占有的内存地址空间和资源,数据共享简单,但是同步复杂。

Ⅱ、一个进程出现问题不会影响其他进程,不影响主程序的稳定性,可靠性高;

       而一个线程崩溃可能影响整个程序的稳定性,可靠性较低。

Ⅲ、进程的创建和销毁不仅需要保存寄存器和栈信息,还需要资源的分配回收以及页调度,开销较大;

       而线程只需要保存寄存器和栈信息,开销较小。

Ⅳ、进程是操作系统进行资源分配的基本单位,而线程是操作系统进行调度的基本单位,即CPU分配时间的单位 。

posted @ 2021-03-11 16:43  M-Anonymous  阅读(107)  评论(0)    收藏  举报