java面试2021总结

闲聊

金三银四过去了,小亮总算脱离996的苦海进入了国企大哥的怀抱。总结一下这几个月的面试经验,那简直就是痛苦难耐,苦不堪言啊。因为各种原因(此处不吐槽上家公司),年初萌发了跳槽的想法,有了想法那就准备行动。找到一个简历模板改改就投了好多家,结果有约面试的寥寥无几。焦躁不安,内心有了离职的想法的时候感觉在公司多呆一秒都是煎熬(不知道有没有同学和我一样的感受)。最后基友小马说我的简历写得太渣,过不了简历筛选的这一关我才恍然大悟。痛定思痛,耗时一周总算把简历完善到我的最高水平范围了,于是有了我痛苦难堪的第一次面试经历。

毕竟第一次面试准备不足,很多固定答案的面试题都没有答对,当时面试官问的时候特别羞愧。回家之后,我就把所有题的答案找出来然后写在一张A4纸上面,背过记住!!!

第二天拿着题让我同事问我题,我答,那简直背得滚瓜烂熟,一字不差。果然后续的很多次面试也出现了类似的问题。

最后我总结的面试经验就是有固定答案的题就死记硬背

笔试题

1.volatile特性及原理
2.G1和CMS的区别
3.InnoDb和MyIsam的区别
4.实现快排算法

各位同学可以先自己答一下,测试一下自己掌握知识的熟练程度。

笔试题总结

1.volatile特性及原理

2.G1和CMS的区别

3.InnoDb和MyIsam的区别

4.实现快排算法

1.volatile特性及原理

特性:

  1. 可见性

  2. 禁止指令重排序

现象:

被volatile修饰的关键字,当发生修改后立即刷新到主内存,当其他cpu需要读取这个volatile变量时设置本地缓存中该变量地址无效,从主内存重新读取,实现了内存可见性

原理:

编译器在编译时发现volatile,插入lock(即内存屏障)前缀指令,而lock有以下2个特性

  1. 写变量后会强制刷新回主内存

  2. 读该变量时会直接从主内存读取

不足:无法保证原子性&安全性

volatile无法保证原子性,因为对于一个变量的读操作,可以解释为以下3部分

  1. 读取volatile变量到本地
  2. 修改变量值
  3. local值写会主内存

2.G1和CMS的区别

CMS垃圾回收器是一种以获取最短回收停顿时间为目标的垃圾回收器

CMS垃圾回收器是基于”标记–清除”(Mark-Sweep)算法实现的,整个过程分为四个步骤:

过程:

  1. 初始标记:独占PUC,stop-the-world, 仅标记GCroots能直接关联的对象

  2. 并发标记:可以和用户线程并发执行,通过GCRoots Tracing 标记所有可达对象。

  3. 重新标记:独占CPU,stop-the-world, 对并发标记阶段用户线程运行产生的垃圾对象进行标记修正,以及更新自我拯救那部分逃逸对象

  4. 并发清理:可以和用户线程并发执行,清理垃圾

优点:

并发,低停顿

缺点:

  1. CPU敏感:在并发阶段虽然不会导致用户线程停顿,但是会因为占用了一部分线程使应用程序变慢

  2. 浮动垃圾:在最后一步并发清理过程中,用户线程执行也会产生垃圾,但是这部分垃圾是在标记之后,所以只有等到下一次gc的时候清理掉,这部分垃圾叫浮动垃圾

  3. 空间碎片:CMS使用“标记-清理”法会产生大量的空间碎片,当碎片过多,将会给大对象空间的分配带来很大的麻烦,往往会出现老年代还有很大的空间但无法找到足够大的连续空间来分配当前对象,不得不提前触发一次FullGC,为了解决这个问题CMS提供了一个开关参数,用于在CMS顶不住,要进行FullGC时开启内存碎片的合并整理过程,但是内存整理的过程是无法并发的,空间碎片没有了但是停顿时间变长了

CMS 出现FullGC的原因:

  1. 年轻代晋升到老年代没有足够的连续空间,很有可能是内存碎片导致的

  2. 在并发过程中JVM觉得在并发过程结束之前堆就会满,需要提前触发FullGC

G1:是一款面向服务端应用的垃圾回收器

整个过程分为四个步骤:

初始标记(stop the world事件 CPU停顿只处理垃圾);
并发标记(与用户线程并发执行);
最终标记(stop the world事件 ,CPU停顿处理垃圾);
筛选回收(stop the world事件 根据用户期望的GC停顿时间回收)
G1只有并发标记阶段能做到用户线程和回收线程并发执行

特点:

1、并行与并发:G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短stop-The-World停顿时间。部分其他收集器原本需要停顿Java线程执行的GC动作,G1回收器仍然可以通过并发的方式让java程序继续执行。

2、分代收集:分代概念在G1中依然得以保留。虽然G1可以不需要其它回收器配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。也就是说G1可以自己管理新生代和老年代了。

3、空间整合,没有内存碎片产生:由于G1使用了独立区域(Region)概念,G1从整体来看是基于“标记-整理”算法实现收集,从局部(两个Region)上来看是基于“复制”算法实现的,但无论如何,这两种算法都意味着G1运作期间不会产生内存空间碎片

在最后筛选回收阶段,对每个region里的回收对象价值(回收该区域的时间消耗和能得到的内存比值)最后进行排序,用户可以自定义停顿时间,那么G1就可以对部分的region进行回收!这使得停顿时间是用户自己可以控制的

在java语言里,可作为GC Roots的对象包括下面几种:

  1. 虚拟机栈(栈帧中的局部变量表)中的引用的对象;
  2. 方法区中类静态属性引用的对象;
  3. 方法区中常量引用的对象;
  4. 本地方法栈中JNI(一般说的Native方法)的引用的对象。

3.InnoDb和MyIsam的区别

  1. InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;

  2. InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败;

  3. InnoDB是聚集索引,使用B+Tree作为索引结构,数据文件是和(主键)索引绑在一起的(表数据文件本身就是按B+Tree组织的一个索引结构),必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。

MyISAM是非聚集索引,也是使用B+Tree作为索引结构,索引和数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。

  1. InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快(注意不能加有任何WHERE条件)

  2. MyISAM表格可以被压缩后进行查询操作

  3. InnoDB支持表、行(默认)级锁,而MyISAM支持表级锁

7、InnoDB表必须有唯一索引(如主键)(用户没有指定的话会自己找/生产一个隐藏列Row_id来充当默认主键),而Myisam可以没有

8、Innodb存储文件有frm、ibd,而Myisam是frm、MYD、MYI

Innodb:frm是表定义文件,ibd是数据文件

Myisam:frm是表定义文件,myd是数据文件,myi是索引文件

4.实现快排算法

快排算法:

从要排序的数据中取一个数为基准元素。
通过一趟排序将要排序的数据分割成独立的两部分,其中左边的数据都比基准元素小,右边的数据都比基准元素大。
然后再按步骤2对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

public class QuickSort {
    private static void quickSort(int[] array, int low, int high) {
        if (low >= high) {
            return;
        }
        int i = low, j = high, index = array[i]; // 取最左边的数作为基准数
        while (i < j) {
            while (i < j && array[j] >= index) { // 向左寻找第一个小于index的数
                j--;
            }
            if (i < j) {
                array[i++] = array[j]; // 将array[j]填入array[i],并将i向右移动
            }
            while (i < j && array[i] < index) {// 向右寻找第一个大于index的数
                i++;
            }
            if (i < j) {
                array[j--] = array[i]; // 将array[i]填入array[j],并将j向左移动
            }
        }
        array[i] = index;
        quickSort(array, low, i - 1); // 递归调用
        quickSort(array, i + 1, high); // 递归调用
    }
 
    public static void quickSort(int[] array) {
        if (array == null || array.length == 0) {
            return;
        }
        quickSort(array, 0, array.length - 1);
    }
}

想知道的更多,请关注我的公众号小亮学java
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值