目录
面试题一:JDK、JRE、JVM之间的区别
- JDK (Java SE Development Kit),Java 标准开发包,它提供了编译、运行 Java 程序所需的各种工
具和资源,包括Java编译器、Java 运行时环境,以及常用的Java 类库等。 - JRE ( Java Runtime Environment) ,Java 运行环境,用于运行 Java 的字节码文件。JRE 中包括了
JVM 以及 JVM 工作所需要的类库,普通用户而只需要安装 JRE 来运行 Java 程序,而程序开发者必
须安装 JDK 来编译、调试程序。 - JVM (Java Virtual Machine),Java虚拟机,是JRE的一部分,它是整个 Java 实现跨平台的最核心
的部分,负责运行字节码文件。
我们写 Java 代码,用 txt 就可以写,但是写出来的 Java 代码,想要运行,需要先编译成字节码,那就需要编译器,而 JDK 中就包含了编译器 javac ,编译之后的字节码,想要运行,就需要一个可以执行字节码的程序,这个程序就是 JVM(Java虚拟机),专门用来执行 Java 字节码的。
如果我们要开发 Java 程序,那就需要 JDK ,因为要编译 Java 源文件。
如果我们只想运行已经编译好的 Java 字节码文件,也就是 *.class 文件,那么就只需要 JRE。
JDK 中包含了 JRE ,JRE 中包含了 JVM。
另外,JVM 在执行 Java 字节码时,需要把字节码解释为机器指令,而不同操作系统的机器指令是有可能不一样的,所以就导致不同操作系统上的 JVM 是不一样的,所以我们在安装 JDK 时需要选择操作系统。
另外,JVM 是用来执行 Java 字节码的,所以凡是某个代码编译之后是 Java 字节码,那就都能在 JVM 上运行,比如Apache Groovy , Scala and Kotlin 等等。
面试题二:hashCode()与equals()之间的关系
HashCode 介绍:hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。
hashCode() 定义在 JDK 的 Object.java 中,Java 中的任何类都包含有 hashCode() 函数。
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码(可以快速找到所需要的对象) 。
追问:为什么重写 equals() 就一定要重写 hashCode() 方法?
这个问题应该是有个前提,就是你需要用到 HashMap、HashSet 等 Java 集合,用不到哈希表的话,其实仅仅重写 equals() 方法也可以。而工作中的场景是常常用到 Java 集合,所以 Java 官方建议重写 equals() 就⼀定要重写 hashCode() 方法。
在 Java 中,每个对象都可以调用自己的 hashCode() 方法得到自己的哈希值(hashCode),相当于对象的指纹信息,通常来说世界上没有完全相同的两个指纹,但是在 Java 中做不到这么绝对,但是我们仍然可以利用 hashCode 来做一些提前的判断,比如:
- 如果两个对象的 hashCode 不相同,那么这两个对象肯定不同的两个对象;
- 如果两个对象的 hashCode 相同,不代表这两个对象一定是同一个对象,也可能是两个对象;
- 如果两个对象相等,那么他们的 hashCode 就一定相同。
在 Java 的一些集合类的实现中,在比较两个对象是否相等时,会根据上面的原则,会先调用对象的 hashCode() 方法得到 hashCode 进行比较,如果 hashCode 不相同,就可以直接认为这两个对象不相同,如果 hashCode 相同,那么就会进一步调用 equals() 方法进行比较。而 equals() 方法,就是用来最终确定两个对象是不是相等的,通常 equals 方法的实现会比较重,逻辑比较多,而 hashCode() 主要就是得到一个哈希值,实际上就一个数字,相对而言比较轻,所以在比较两个对象时,通常都会先根据 hashCode 想比较一下。
所以我们就需要注意,如果我们重写了 equals() 方法,那么就要注意 hashCode() 方法,一定要保证能遵守上述规则。
面试题三:String、StringBuffer、StringBuilder的区别
- String 用于字符串操作,属于不可变类,如果尝试去修改,会新生成一个字符串对象,StringBuffer 和 StringBuilder 是可变的;
String 不是基本数据类型,是引⽤类型,底层用 char 数组实现的
- StringBuffer 是线程安全的,对⽅法加了同步锁,线程安全。
StringBuffer 中并不是所有方法都使用了 Synchronized 修饰来实现同步:
- StringBuilder 是线程不安全的,所以在单线程环境下 StringBuilder 效率会更高;
执行效率:StringBuilder > StringBuffer > String。
追问一:String 字符串修改实现的原理?
当用 String 类型来对字符串进行修改时,其实现方法是首先创建⼀个 StringBuffer,其次调用 StringBuffer 的 append() 方法,最后调用 StringBuffer 的 toString() 方法把结果返回。
追问二:String 为什么要设计为不可变类?
- 字符串常量池的需要:字符串常量池是 Java 堆内存中⼀个特殊的存储区域,当创建⼀个 String 对象时,假如此字符串值已经存在于常量池中,则不会创建⼀个新的对象,而是引用已经存在的对象;
- 允许 String 对象缓存 HashCode:Java 中 String 对象的哈希码被频繁地使用,比如在 HashMap 等容器中。字符串不变性保证了 hash 码的唯⼀性,因此可以放心地进行缓存。这也是⼀种性能优化手段,意味着不必每次都去计算新的哈希码;
- String 被许多的 Java 类(库)用来当做参数,例如:网络连接地址 URL、文件路径 path、还有反射机制所需要的 String 参数等, 假若 String 不是固定不变的,将会引起各种安全隐患。