java面试题刷刷01----基础概念篇

Java面试基础刷题

Oracle JDK 和 OpenJDK 的对比

可能在看这个问题之前很多人和我一样并没有接触和使用过 OpenJDK 。那么Oracle
和OpenJDK 之间是否存在重大差异?下面通过我通过我收集到一些资料对你解答这个被很
多人忽视的问题。
对于 Java 7,没什么关键的地方。OpenJDK 项目主要基于 Sun 捐赠的 HotSpot源代
码。此外OpenJDK 被选为 Java 7 的参考实现,由 Oracle 工程师维护。关于 JVM,JDK,
JRE 和 OpenJDK 之间的区别,Oracle 博客帖子在 2012 年有一个更详细的答案:
问:OpenJDK 存储库中的源代码与用于构建 Oracle JDK 的代码之间有什么区别?
答:非常接近 - 我们的 Oracle JDK 版本构建过程基于 OpenJDK 7 构建,只添加了几个部
分,例如部署代码,其中包括 Oracle 的 Java 插件和 Java WebStart的实现,以及一些封
闭的源代码派对组件,如图形光栅化器,一些开源的第三方组件,如 Rhino,以及一些零
碎的东西,如附加文档或第三方字体。展望未来,我们的目的是开源 Oracle JDK 的所有部
分,除了我们考虑商业功能的部分。

总结:

  1. Oracle JDK 版本将每三年发布一次,而 OpenJDK 版本每三个月发布一次;
  2. OpenJDK 是一个参考模型并且是完全开源的,而 Oracle JDK 是OpenJDK 的一个实
    现,并不是完全开源的;
  3. Oracle JDK 比 OpenJDK 更稳定。OpenJDK 和 Oracle JDK 的代码几乎相同,但
    Oracle JDK 有更多的类和一些错误修复。因此,如果您想开发企业/商业软件,我建议您选
    择 Oracle JDK,因为它经过了彻底的测试和稳定。某些情况下,有些人提到在使用
    OpenJDK 可能会遇到了许多应用程序崩溃的问题,但是,只需切换到 Oracle JDK 就可以解决问题;
  4. 顶级公司正在使用 Oracle JDK,例如 Android Studio,Minecraft 和IntelliJ IDEA 开发
    工具,其中 Open JDK 不太受欢迎;
  5. 在响应性和 JVM 性能方面,Oracle JDK 与 OpenJDK 相比提供了更好的性能;
  6. Oracle JDK 不会为即将发布的版本提供长期支持,用户每次都必须通过更新到最新版本
    获得支持来获取最新版本;
  7. Oracle JDK 根据二进制代码许可协议获得许可,而 OpenJDK 根据 GPLv2 许可获得许
    可。

字符型常量和字符串常量的区别?

  1. 形式上: 字符常量是单引号引起的一个字符,字符串常量是双引号引起的若干个字符
  2. 含义上: 字符常量相当于一个整形值( ASCII 值),可以参加表达式运算,字符串常量代表一个
    地址值(该字符串在内存中存放位置)。
  3. 占内存大小,字符常量只占 2 个字节 字符串常量占若干个字节(至少一个字符结束标志)
    (注意: char 在 Java 中占两个字节)

String 、StringBuffer 和 StringBuilder 的区别是什么?String 为什么是不可变的?

可变性
简单的来说:String 类中使用 final 关键字字符数组保存字符串,private final char value[],所以 String 对象是不可变的。而 StringBuilder 与StringBuffer 都继承自
AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串
char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。
StringBuilder 与 StringBuffer 的构造方法都是调用父类构造方法也就是
AbstractStringBuilder 实现的,大家可以自行查阅源码。
源码
线程安全性
String 中的对象是不可变的,也就可以理解为常量,线程安全。
AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串
的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer
对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没
有对方法进行加同步锁,所以是非线程安全的。
性能
每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向
新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成
新的对象并改变对象引用。相同情况下使用StringBuilder 相比使用 StringBuffer 仅能获得
10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
对于三者使用的总结:

  1. 操作少量的数据 = String
  2. 单线程操作字符串缓冲区下操作大量数据 = StringBuilder
  3. 多线程操作字符串缓冲区下操作大量数据 = StringBuffer

简述线程,程序、进程的基本概念。以及他们之间关系是什么?

线程: 与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中
可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,
所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正
因为如此,线程也被称为轻量级进程。

程序: 是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,也就是说程
序是静态的代码。

进程: 是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统
运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行
中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统
资源如 CPU 时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程
序在执行时,将会被操作系统载入内存中。 线程是进程划分成的更小的运行单位。线程和
进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极
有可能会相互影响。从另一角度来说,进程属于操作系统的范畴,主要是同一段时间内,可
以同时执行一个以上的程序,而线程则是在同一程序内几乎同时执行一个以上的程序段。

关于 final 关键字的一些总结

final 关键字只要用在三个地方:变量·方法·类。
1.对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能在让其指向另一个对象。
2.当用final修饰一个类时,表明这个类不能被继承,final类中所有的成员方法都会被隐藏式的指定为final方法。
3.使用final方法的原因有两个。第一个原因是把方法锁定,以防止任何继承类修改它的含义;第二个原因是效率。在早期java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看布袋内嵌调用带来的任何性能提升(现在的java版本已经不需要使用final方法进行这些优化了)。类中所有的private方法都隐式的指定为final。

java 中的异常处理

java异常类层次结构图

异常类层次结构图
在java中,所有的异常类都有一个共同的父类java.long包中的Throwable类。
Throwable:有两个重要的子类:Exception(异常)和Error(错误),二者都是java异常处理重要子类,各自都包含大量子类。
Error(错误): 是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时JVM(Java虚拟机)出现的问题。例如,java虚拟机运行错误(Virtual MachineError),当JVM不在有继续执行操作所需的内存资源时,将出现OutOfMemoryError。这些错误发生时,java虚拟机一般会选择线程终止。这些错误表示故障发生于虚拟机本身,或者发生在虚拟机试图执行应用时,如java虚拟机运行错误(Virtual MachineError),类的定义错误(NoClassDefFoundError)等。这些错误是不可查的,因为它们在应用程序的控制和处理能力之外。而且绝大多数是程序运行时不允许出现的情况,对于设计合理的应用程序来说,即使确实发生了错误,本质上也绝不应该试图去处理它所引起的异常状况(理解为问题引发的异常现象,你应该解决问题本身,而不是修正异常现象来掩盖问题,试图该变本来就正常的异常现象)。在java中,错误通过Error的子类描述。
Exception(异常): 是程序本身可以处理的异常。Exception类有一个重要的子类:RuntimeException。RuntimeException(运行时异常)是由java虚拟机抛出。
NullPointerException(空指针异常–要访问的变量没有引用任何对象时,抛出该异常)。
ArithmeticException(算术运算异常,一个整数除以0时,抛出该异常)。
ArrayIndexOutOfBoundsException(下标越界异常)。
注意:异常和错误的区别:异常能被程序本身处理,错误无法处理。
Throwable类的常用方法:
public string getMessage():返回异常发生时的详细信息
public string toString():返回异常发生时的简要描述
public string getLocalizedMessage(): 返回异常对象的本地化信息。使用Throwable的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同。
public void printStackTrace():在控制台上打印Throwable对象封装的异常信息
异常处理总结:
try块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个final块。
catch块:用于处理try捕获到的异常。
finally块:无论是否捕获或处理异常,finally块里的语句都会被执行。
当try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。
在以下4中特殊情况下,finally块不会被执行:
1.在finally语句中发生了异常。
2.在前面的代码中使用了System.exit()退出程序。
3.程序所在的线程死亡。
4.关闭cpu。

java序列化中如果有些字段不想进行序列化如何处理?

对于不想进行序列化的变量,使用transient关键字修饰。transient关键字的作用是:阻止实例中那些使用此关键字修饰的变量序列化,当对象被序列化时,被transient修饰的变量值不会被持久化和恢复。transient只能修饰变量,不能修饰类和方法。

List和Set的区别

List和Set都继承自Collection接口,
List特点:元素放入有序,元素可重复,
Set特点:元素放入无序,元素不可重复,重复元素会覆盖掉。
元素虽然无放入顺序,但是元素在set中的位置是由该元素的HashCode决定的,其位置其实是固定的,加入Set的Object必须定义equals()方法,另外List支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为它无序,无法使用下标来获取想要的值。
set和List对比: 检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。list和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。

HashSet是如何保证不重复的?

想HashSet中add()添加元素时,判断元素是否存在的依据,不仅要比较hash值,同时还要结合equals方法比较。
HashSet中的add()方法会使用HashMap的add()方法。以下是HashSet部分源码:

private static final Object PRESENT = new Object();
private transient HashMap<E,Object> map;
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}

HashMap 的 key 是唯一的,由上面的代码可以看出 HashSet 添加进去的值就是作为 HashMap
的key。所以不会重复( HashMap 比较key是否相等是先比较 hashcode 在比较 equals )。

对象的四种引用

强引用只要引用存在,垃圾回收器就永远不会回收。
可直接通过obj取得对应对的对象,如obj.equels(new Object());而这样obj对象后面new Object的一个强引用,只有当obj这个引用被释放之后,对象才会被释放掉,这也是我们经常用到的编码形式。
软引用,非必须引用,内存溢出之前进行回收,可以通过以下代码实现:

Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;
sf.get();//有时候会返回null

这时候sf是对obj的一个软引用,通过sf.get()方法可以取到这个对象,当然,当这个对象被标
记为需要回收的对象时,则返回null; 软引用主要用户实现类似缓存的功能,在内存足够的情况下
直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部
分缓存数据,从真正的来源查询这些数据。

弱引用 第二次垃圾回收时回收,可以通过如下代码实现:

Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
wf.get();//有时候会返回null
wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾

弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行
过第二次垃圾回收时,将返回null。弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回
收的垃圾,可以通过弱引用的isEnQueued() 方法返回对象是否被垃圾回收器标记。

ThreadLocal 中有使用到弱引用

public class ThreadLocal<T> {
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//....
}
//.....
}

虚引用 垃圾回收时回收,无法通过引用取到对象值,可以通过如下代码实现:

Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj=null;
pf.get();//永远返回null
pf.isEnQueued();//返回是否从内存中已经删除

虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,
因此也被成为幽灵引用。虚引用主要用于检测对象是否已经从内存中删除。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值