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分配时间的单位 。

浙公网安备 33010602011771号