有些问题真的挺无语的,面高级怎么还在问这些
==和equals的区别, 0.1*5 == 0.5的结果
==对于基本类型,比较的是值,但对于引用类型,比较的是内存地址
equals对于引用类型,首先,equals会用==比较两个类型在内存中的地址,如果一样,则直接返回true,如果不一样,则直接往下走,再判断是否为string类型,如果不是,则强转为string类型,再将字符串数组拆分为单个字符,一一比较,有一个不相同,则返回false,否则返回true
equals的源码如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
从上面可以看出 0.1*5 == 0.5 的结果应该为true,因为他们是基本数据类型,直接比较值
还遇到了另外一个问题,如下图。
因为String a = "abc"在编译的时候,jvm将abc放入常量池,并在常量池中创建该常量的地址,然后将a去指向这个常量,当我们执行String a = "abc"的时候,会去常量池中寻找有没有abc这个常量,如果有,则将b指向它,没有就创建,所以a和b都指向常量池中的abc,故内存地址相同。
而在 String str1 = new String("abc")中,首先会在堆内存中去开辟一个空间去存储abc,当我们再new对象的时候,还会再去开辟,所以二则的内存地址不相同,故返回false
聚簇索引和非聚簇索引的区别
- 聚集索引
聚集索引即索引结构和数据一起存放的索引。主键索引属于聚集索引。
在 MySQL 中,InnoDB 引擎的表的 .ibd文件就包含了该表的索引和数据,对于 InnoDB 引擎表来说,该表的索引(B+树)的每个非叶子节点存储索引,叶子节点存储索引和索引对应的数据。
聚集索引的优点:聚集索引的查询速度非常的快,因为整个 B+树本身就是一颗多叉平衡树,叶子节点也都是有序的,定位到索引的节点,就相当于定位到了数据。
聚集索引的缺点:- 依赖于有序的数据 :因为 B+树是多路平衡树,如果索引的数据不是有序的,那么就需要在插入时排序,如果数据是整型还好,否则类似于字符串或 UUID 这种又长又难比较的数据,插入或查找的速度肯定比较慢。
- 更新代价大:如果对索引列的数据被修改时,那么对应的索引也将会被修改,而且聚集索引的叶子节点还存放着数据,修改代价肯定是较大的,所以对于主键索引来说,主键一般都是不可被修改的。
- 非聚集索引
非聚集索引即索引结构和数据分开存放的索引。二级索引属于非聚集索引。
非聚集索引的叶子节点并不一定存放数据的指针,因为二级索引的叶子节点就存放的是主键,根据主键再回表查数据。
非聚集索引的优点:
- 更新代价比聚集索引要小 。非聚集索引的更新代价就没有聚集索引那么大了,非聚集索引的叶子节点是不存放数据的
非聚集索引的缺点: - 同样以来有序的数据
- 可能会二次查询(回表):这应该是非聚集索引最大的缺点了。 当查到索引对应的指针或主键后,可能还需要根据指针或主键再到数据文件或表中查询。
这是mysql的文件截图
聚集索引和非聚集索引:
因为是实战面试,索引其他的问题就不扩展了
JVM内存模型,垃圾清理算法
-
jdk1.8之前
-
jdk1.8
堆
堆存放的是对象实例,几乎所有的对象实例以及数组都在这里分配内存。
比如现在我们有个User类,
现在 User user = new User();
在user对象初始化的时候默认code和name为null。(根据不同的数据类型来,比如int就默认为0)
user.setName = "小明";
这里就把name从null重新赋值了小明。
从这里可以看出对象创建过程是 new对象->设默认值->调用构造方式设初始值
@Data
@Accessors(chain = true)
public class User {
private String code;
private String name;
}
方法区(元空间)
方法区属于是 JVM 运行时数据区域的一块逻辑区域,是各个线程共享的内存区域。方法区存放类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
栈 (本地方法栈,虚拟机栈,程序计数器)
垃圾回收算法
- 标记-清除算法
该算法分为“标记”和“清除”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。它是最基础的收集算法,后续的算法都是对其不足进行改进得到。这种垃圾收集算法会带来两个明显的问题:
效率问题
空间问题(标记清除后会产生大量不连续的碎片)
- 标记-复制算法
为了解决效率问题,“标记-复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。 - 标记-整理算法
根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。 - 分代收集算法
当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将 java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。
参考: