每日一句
你为什么要那么努力?
“想去的地方很远,想要的东西很贵,喜欢的人很优秀,父亲的白发,周围人的嘲笑,以及天生傲骨。”
每日一刷
考试时间 100min
一面(45min)
1. 具体讲讲你的项目,服务如何拆分。某某功能如何做的。
略
2. 项目中数据库表的设计,索引的优化。
索引虽好,但也不是无限制的使用,最好符合一下几个原则
1) 最左前缀匹配原则,组合索引非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
2)较频繁作为查询条件的字段才去创建索引
3)更新频繁字段不适合创建索引
4)若是不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低)
5)尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
6)定义有外键的数据列一定要建立索引。
7)对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。
8)对于定义为text、image和bit的数据类型的列不要建立索引。
3. Spring IoC和AOP的概念?AOP的设计模式?IoC和哪个设计模式比较像?
- IOC:Inversion of Control —— 控制反转
IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。 - AOP —— Asepct-Orentid-Programming,面向切面编程
面向切面编程,往往被定义为促使软件系统实现关注点的分离的技术。系统是由许多不同的组件所组成的,每一个组件各负责一块特定功能。除了实现自身核心功能之外,这些组件还经常承担着额外的职责。例如日志、事务管理和安全这样的核心服务经常融入到自身具有核心业务逻辑的组件中去。这些系统服务经常被称为横切关注点,因为它们会跨越系统的多个组件。
AOP就是把与核心业务逻辑无关的代码全部抽取出来,放置到某个地方集中管理,然后在具体运行时,再由容器动态织入这些共有代码。
3. 事务的特性?
事务的 ACID
事务具有四个特征:原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )和持久性( Durability )。这四个特性简称为 ACID 特性。
(1)原子性
事务是数据库的逻辑工作单位,不可分割,事务中包含的各操作要么都做,要么都不做
(2)一致性
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。
(3)隔离性
一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。 事务的隔离级别有4级,可以查看这篇博客,关于MySQL的事务处理及隔离级别
(4)持续性
也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的,不能回滚。接下来的其它操作或故障不应该对其执行结果有任何影响。
4. MySQL的事务隔离级别?
SQL 标准定义了四种隔离级别,MySQL 全都支持。这四种隔离级别分别是:
读未提交(READ UNCOMMITTED)
读提交 (READ COMMITTED)
可重复读 (REPEATABLE READ)
串行化 (SERIALIZABLE)
从上往下,隔离强度逐渐增强,性能逐渐变差。采用哪种隔离级别要根据系统需求权衡决定,其中,可重复读是 MySQL 的默认级别。
事务隔离其实就是为了解决上面提到的脏读、不可重复读、幻读这几个问题,下面展示了 4 种隔离级别对这三个问题的解决程度。
只有串行化的隔离级别解决了全部这 3 个问题,其他的 3 个隔离级别都有缺陷。
5. 讲讲MVCC?
MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。
MVCC的理解大家可以看看这篇文章:https://blog.csdn.net/flying_hengfei/article/details/106965517
6. 数据库最左匹配原则的原理?
在MySQL建立联合索引时会遵守最左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。 要想理解联合索引的最左匹配原则,先来理解下索引的底层原理。 索引的底层是一颗B+树,那么联合索引的底层也就是一颗B+树,只不过联合索引的B+树节点中存储的是键值。
7. 用什么方法测试我们的sql是否用到索引?
explain显示了mysql如何使用索引来处理select语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。
使用方法,在select语句前加上explain就可以了:
如:
explain select surname,first_name form a,b where a.id=b.id
8. 乐观锁和悲观锁。都举个例子
- 悲观锁
比较适合写入操作比较频繁的场景,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量 - 乐观锁
比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发现冲突的可能性会加大,为了保证数据的一致性,应用层需要不断的重现获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。
9. 讲一下synchrinzed关键字怎么使用?
第十章 进程间的通信 之 Java/Android多线程开发(二)
算法:【LC110 验证平衡二叉树】
题目描述
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
class Solution {
public boolean isBalanced(TreeNode root) {
if (root == null) {
return true;
}
int[] res = getDepth(root);
return res[0] == 1;
}
// 返回值结构[0或者1,depth]
int[] getDepth(TreeNode node) {
int[] res = new int[2];
if (node == null) {
res[0] = 1;
return res;
}
int[] left = getDepth(node.left);
int[] right = getDepth(node.right);
// 如果左右子树非平衡二叉树 或 左右子树高度之差超过1
if (left[0] == 0 || right[0] == 0 || Math.abs(left[1] - right[1]) > 1) {
return res;
}
res[0] = 1;
res[1] = Math.max(left[1], right[1]) + 1;
return res;
}
}
二面(50min)
1. 从发起请求到浏览器展示页面的过程
1、输入地址
2、浏览器查找域名的 IP 地址
3、浏览器向 web 服务器发送一个 HTTP 请求
4、服务器的永久重定向响应
6、服务器处理请求
7、服务器返回一个 HTTP 响应
8、浏览器显示 HTML
9、浏览器发送请求获取嵌入在 HTML 中的资源(如图片、音频、视频、CSS、JS等等)
2. 从DNS解析出来ip之后,怎么到服务器的?
请求从客户端到服务器的过程简单分析
3. Nginx的作用?Nginx可以做什么?
Nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器;同时也是一个IMAP、POP3、SMTP代理服务器;Nginx可以作为一个HTTP服务器进行网站的发布处理,另外Nginx可以作为反向代理进行负载均衡的实现。
4. Spring Bean的生命周期
实例化 Instantiation
属性赋值 Populate
初始化 Initialization
销毁 Destruction
实例化 -> 属性赋值 -> 初始化 -> 销毁
5. Spring怎么处理Bean的循环依赖
创建B 对象,并将创建出来的B 对象放到Spring 的三级缓存中; B 对象的a 属性为空,需要填充a 属性,随后在缓存中找到A 对象,完成填充注入; 最后再对A 对象的b 属性进行填充,从缓存中顺利拿到B 对象,完成属性注入,循环依赖到此解决。
6. 简述Java的发射机制
什么是反射机制?
1、在运行状态中,对于任意一个类,都能够知道这个类的属性和方法。
2、对于任意一个对象,都能够调用它的任何方法和属性。这种动态获取信息以及动态调用对象的方法的功能称为JAVA的反射。
反射的作用
1、在运行时判断任意一个对象所属的类;
2、在运行时构造任意一个类的对象;
3、在运行时判断任意一个类所具有的成员变量和方法;
4、在运行时调用任意一个对象的方法;生成动态代理。
7. 怎样替换原始的String类?
replace() 方法
replace() 方法用于将目标字符串中的指定字符(串)替换成新的字符(串),其语法格式如下:
字符串.replace(String oldChar, String newChar)
其中,oldChar 表示被替换的字符串;newChar 表示用于替换的字符串。replace() 方法会将字符串中所有 oldChar 替换成 newChar。
8. HTTP1.1和HTTP2.0都有哪些改进?
- 新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
- 多路复用(MultiPlexing),即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。
- header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
- 服务端推送(server push),同SPDY一样,HTTP2.0也具有server push功能。
9. git rebase和git merge的区别?
git merge 与 git rebase的区别
10. git提交记录可以合并吗?
git合并多个远程commit并提交
11. git的过程?如何写入到index,如何推到远端
有时候我们开发需要开一个分支,这样可以有效的并行开发.
开分支有两种方式:
一种是在远程开好分支,本地直接拉下来;
一种是本地开好分支,推送到远程.
远程先开好分支然后拉到本地
git checkout -b feature-branch origin/feature-branch //检出远程的feature-branch分支到本地
本地先开好分支然后推送到远程
$ git checkout -b feature-branch //创建并切换到分支feature-branch
$ git push origin feature-branch:feature-branch //推送本地的feature-branch(冒号前面的)分支到远程origin的feature-branch(冒号后面的)分支(没有会自动创建)
12. 简单介绍一下项目,架构,技术,数据怎么流传的?
略
13. xx功能怎么实现,缓存热点数据可以怎么做?
略
算法1【剑指 Offer 25. 合并两个排序的链表】
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
代码实现
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1 == null) return list2;
if(list2 == null) return list1;
ListNode head = null;
if(list1.val <= list2.val){
head = list1;
list1.next = Merge(list1.next,list2);
}
else{
head = list2;
list2.next = Merge(list1,list2.next);
}
return head;
}
}
算法2【LC106. 从中序与后序遍历序列构造二叉树】
题目描述
根据一棵树的中序遍历与后序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
代码实现
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
return helper(inorder, postorder, postorder.length - 1, 0, inorder.length - 1);
}
public TreeNode helper(int[] inorder, int[] postorder, int postEnd, int inStart, int inEnd) {
if (inStart > inEnd) {
return null;
}
int currentVal = postorder[postEnd];
TreeNode current = new TreeNode(currentVal);
int inIndex = 0;
for (int i = inStart; i <= inEnd; i++) {
if (inorder[i] == currentVal) {
inIndex = i;
}
}
TreeNode left = helper(inorder, postorder, postEnd - (inEnd- inIndex) - 1, inStart, inIndex - 1);
TreeNode right = helper(inorder, postorder, postEnd - 1, inIndex + 1, inEnd);
current.left = left;
current.right = right;
return current;
}
}