面经 | 记 21 年 3 月一次 java 面试经历

本贴最后更新于 995 天前,其中的信息可能已经斗转星移

1.笔试题

Integer intger = new Integer(100)和 String str = new String(”ABC”)分别会创建多少个对象。

解析

// 一个
Integer intger = new Intger(100) 
// 是2个,"abc"创建在常量池,new String()创建在堆
String str = new String("abc");
// 1个,"abc"创建在常量池
String str = "abc";
// 1个,"abc"
String str1 ="abc"; String str2 = "abc";
// 3个,"ab" , "c" ,"abc" 
String str = "ab" + "c";

数据表 t 有三个字段 title,name,age 建立复合索引 tab_index(“name”,”age”),下面哪些语句能用上索引?()

A. select * from t where age=18
B. insert into t values('title','name',18)
C. update t set title = 'a' where name = 'A'
D. select * from t where name like "%t%" and age=18

解析
m1.jpg

(wps 简述题)为什么 TCP4 次挥手时客户端需要等待 2MSL?

解析


MSL(Maximum Segment Lifetime),TCP 允许不同的实现可以设置不同的 MSL 值。

第一,保证客户端发送的最后一个 ACK 报文能够到达服务器,因为这个 ACK 报文可能丢失,站在服务器的角度看来,我已经发送了 FIN+ACK 报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个 2MSL 时间段内收到这个重传的报文,接着给出回应报文,并且会重启 2MSL 计时器。

第二,防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个 2MSL 时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。

[来源]CSDN


(wps 简述题)线程与进程有多少种状态以及他们之间的的区别?

解析


进程:

创建状态(new)、就绪状态(ready) 、运行状态(running)、阻塞状态(waiting) 、结束状态(terminated)

线程:
初始(new)、运行(runnable)、阻塞(blocked)、等待(waiting)、超时等待( timed_waiting)、终止(terminated)

两者区别:

做个简单的比喻,进程=火车,线程=车厢。线程在进程下行进(单纯的车厢无法运行),一个进程可以包含多个线程(一辆火车可以有多个车厢),不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘),同一进程下不同线程间数据很易共享(A 车厢换到 B 车厢很容易)。

进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源),进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢),进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)。

进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-"互斥锁",进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”。

[来源]知乎


逻辑题:有 100 个人,从 1 排到 100 分别在背后随机贴红黑两种颜色的卡片,然后 100 号能看到前面的 99 个人的颜色卡,以此类推,2 号能看到 1 号的颜色,每个人都能看见自己前面人的颜色。从后往前开始回到颜色,猜出自己的卡片颜色正确的一分,出一个策略,写出你的策略能拿到的保底分数以及写出思路

解析


3 人一组,100 号看 99 和 98 号的颜色,如果 98 号跟 99 号一样就红色,不一样就黑色,99 号根据看到的颜色与 100 的答案说出自己的颜色,98 号根据 100 号和 99 号的答案推断出自己的颜色以此类推,多出来的一个盲猜颜色,总共 33 组。每组能确保 2 个人颜色一定正确,33*2=保底 66 分。


算法题:珠宝大盗偷珠宝,有一排珠宝展柜,每个格子的珠宝的价值不同,连续两个格子变空就报警,请问珠宝大盗怎么偷到最大价值的珠宝。

解析

// 动态规划
  public static int getMaxValue(int[] arr){
        int[] data = new int[arr.length];
        data[0] = arr[0];
        data[1] = Math.max(arr[0], arr[1]);
        for (int i = 2; i < arr.length; i++) {
            int val = arr[i];
            data[i] = Math.max(data[i - 1], data[i - 2] + val);
        }
        return data[data.length - 1];
    }
  // 非动态规划
  public static int getMaxValue2(int[] arr) {
        int sum1 = 0, sum2 = 0;
        for(int i = 0; i < arr.length; i++){
            if(i % 2==0){
                sum1 = Math.max(sum2, sum1 + arr[i]);
            }else{
                sum2 = Math.max(sum1, sum2 + arr[i]);
            }
        }
        return Math.max(sum1,sum2);
    }

2.面试题

Redis 的数据类型有哪几种

解析


9 种,5 种基础类型分别是 String,Hash,List,Set,Sorted Set(ZSet)。后面新增的 3 种高级数据类型分别是 BitMap(位图只有 0 和 1 两种情况),HyperLogLog(海量数据的计算,适用于统计数据),GEO(记录经纬度及计算距离)。Redis5.0 以后新增了 Stream(消息队列)。


你说一下你用 Zset 做排行榜是为什么

我的回答


利用了有序集合的有序性,因为有序集合以 score 大小的不同为集合中的成员进行从小到大的排序。只需要将评论数作为 score,获取集合的倒序指定的个数。完成排行。


Java 的集合有什么实现类

解析


Collection 接口:
List:
ArrayList(动态数组实现)、LinkedList(双向链表实现)、Vector(线程安全但是古老没人用了)、Stack(线程安全继承的 Vector)、CopyOnWriteArrayList(线程安全,只能保证最终结果的一致性,吃内存)

Queue:
PriorityQueue(通过数组来维护一个堆结构)、ArrayDeque(双端队列,循环数组实现)

Set:
HashSet(HashMap 实现,无序,HashMap 的封装)、LinkedHashSet(HashMap 实现,有序,保证进出顺序)、TreeSet(红黑树,有序,自然排序)

非 Collection 接口:
Map:
HashMap(无序)、LinkedHashMap(有序,按进入的顺序排序)、TreeMap(红黑树,有序,自然排序)、Hashtable(线程安全,直接锁整张表性能差)、ConcurrentHashMap(线程安全,锁节点。性能高)

(小编吐槽面试官搁这背 API 呢)


你说一下 HashMap 的扩容机制

解析


空参数的构造函数:实例化的 HashMap 默认内部数组是 null,即没有实例化。第一次调用 put 方法时,则会开始第一次初始化扩容,长度为 16。

有参构造函数:用于指定容量。会根据指定的正整数找到不小于指定容量的 2 的幂数,将这个数设置赋值给阈值(threshold)。第一次调用 put 方法时,会将阈值赋值给容量,然后让 。(因此并不是我们手动指定了容量就一定不会触发扩容,超过阈值后一样会扩容!!)

如果不是第一次扩容,则容量变为原来的 2 倍,阈值也变为原来的 2 倍。(容量和阈值都变为原来的 2 倍时,负载因子还是不变)

此外还有几个细节需要注意:首次 put 时,先会触发扩容(算是初始化),然后存入数据,然后判断是否需要扩容;不是首次 put,则不再初始化,直接存入数据,然后判断是否需要扩容;

[来源]知乎


说一下你对 CopyOnWriteArrayList 的线程安全性的理解

解析


CopyOnWriteArrayList 类的所有可变操作(add,set 等等)都是通过创建底层数组的新副本来实现的。当 List 要被修改时,将对原有数据进行一次拷贝并把新数据写入到副本中。写完之后,再将修改完的副本替换成原来的数据,所以 CopyOnWriteArrayList 不保证即时数据一致性,只能保证最终结果的数据一致性。且因为每次改变都会复制一份新副本因此需要大量内存且性能较差,合适读多写少且不追求强一致性的场景。


ArrayList 的扩容机制,后面追问扩容的时候数据怎么复制到新的数组

解析


扩容机制参考这篇:https://snailclimb.gitee.io/javaguide/#/docs/java/collection/ArrayList%E6%BA%90%E7%A0%81+%E6%89%A9%E5%AE%B9%E6%9C%BA%E5%88%B6%E5%88%86%E6%9E%90
复制新数组的方法是调用的是:System.arraycopy(elementData,index, elementData, index + 1,size - index);
System.arraycopy 调用的是 C 本地库(具体自己去了解,不深入了,超纲了)


请说一下数据库的 1NF、2NF、3NF 和 BNCF

解析


1NF:列不可再分

2NF:非主键列和主键列之间,是完全依赖于主键,还是依赖于主键的一部分(只依赖某个主键)

3NF:非主键列之间,不存在依赖,只直接依赖主键。非主键属性之间无依赖关系

BCNF:主键列之间,不存在依赖。主键属性之间无依赖关系


MySQL 的四种事务隔离级别

解析


1.原子性(Atomicity) :事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;

2.一致性(Consistency):执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;

3.隔离性(Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;

4.持久性(Durabilily):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。


主键索引跟主键数据是存放在一起的吗?

解析


InnoDB 引擎中,其数据文件本身就是索引文件。树的叶节点 data 域保存了完整的数据记录。

MyISAM 引擎中,索引文件和数据文件是分离的,B+Tree 叶节点的 data 域存放的是数据记录的地址。


数据和索引分别存在 B+ 树的什么节点?

解析


叶子节点的 key 域是数据表的主键而 data 域保存了完整的数据记录。

非叶子节点只存储索引并不存储行数据,只为了能存储更多索引键,从而降低 B+ 树的高度,进而减少 IO 次数。


如果没有设置主键索引,mysql 用什么做主键

解析


如果定义了主键,那么 InnoDB 会使用主键作为聚簇索引

如果没有定义主键,那么会使用第一非空的唯一索引(NOT NULL and UNIQUE INDEX)作为聚簇索引。

如果既没有主键也找不到合适的非空索引,那么 InnoDB 会自动生成一个不可见的名为 ROW_ID 的列名为 GEN_CLUST_INDEX 的聚簇索引,该列是一个 6 字节的自增数值,随着插入而自增。

缺少主键的表,InnoDB 会内置一列用于聚簇索引来组织数据。而没有建立主键的话就没法通过主键来进行索引,查询的时候都是全表扫描,小数据量没问题,大数据量就会出现性能问题。


TCP 与 UDP 有什么不同?

解析


UDP 在传送数据之前不需要先建立连接,远地主机在收到 UDP 报文后,不需要给出任何确认。虽然 UDP 不提供可靠交付,但在某些情况下 UDP 确是一种最有效的工作方式(一般用于即时通信)

TCP 提供面向连接的服务。在传送数据之前必须先建立连接,数据传送结束后要释放连接。TCP 不提供广播或多播服务。由于 TCP 要提供可靠的,面向连接的传输服务(TCP 的可靠体现在 TCP 在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源),这一难以避免增加了许多开销,如确认,流量控制,计时器以及连接管理等。这不仅使协议数据单元的首部增大很多,还要占用许多处理机资源。


TCP 的握手与挥手的示意图

解析


自己搜吧懒得贴了


说一下你对 Spring 的 IOC/DI 的理解

解析


IOC(Inverse of Control:控制反转)是一种设计思想,就是 将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。

而 DI(依赖注入)组件之间依赖关系由容器在运行期决定,即由容器动态的将某个依赖关系注入到组件之中。

DI 的概念诞生比 IOC 晚,是 2004 年大神 Martin Fowler 提出的。IOC 与 DI 它们是同一个概念的不同角度描述。


为什么要用 IOC

解析


将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。

IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。

在实际项目中一个 Service 类可能有几百甚至上千个类作为它的底层,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。

如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。


Elasticsearch 搜索为什么那么快

解析


ES 是基于 Lucene 的全文检索引擎,它会对数据进行分词后保存索引,擅长管理大量的索引数据,相对于 MySQL 来说不擅长经常更新数据及关联查询。

正常来说我们一般使用 id 查询到具体对象的方式称为使用正排索引,其实也能理解为一种散列表。本质是通过 key 来查找 value。比如通过 id=4 便能很快查询到 name=张红尘,age=20 这条数据。

ES 中采用的是一种名叫倒排索引的数据结构。通过分词器将所有非关键词标识的内容进行分词,并将分词作为 key,而 li(类似于 id)作为 value 反向存储,每当搜索关键词时只需要取得改关键词的 li 即可获得所有数据。


  • 面试

    面试造航母,上班拧螺丝。多面试,少加班。

    324 引用 • 1395 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • z875479694h

    原创评分低的原因是作者先在其他平台发了。并非抄袭文章。