目录
1.没有自我介绍
2.对于集合的了解,ArrayList和LinkedList的区别和适应场景,哪个查询的效率高
3.hashmap的构造方法,解决冲突的方法,扩容原理,HashMap的扩容因子
4.treemap的底部实现(红黑树),hashset的去重
5.红黑树,平衡二叉树,(2-3树也说了点)底层结构,查询和插入的复杂性
11.tcp三次握手四次挥手,网络层有什么协议,OSI七层模型
19.stringbuilder和stringbuffer的区别,初始容量是多少
21jvm的内存模型volatile的特性,可以保证原子性吗
2.对于集合的了解,ArrayList和LinkedList的区别和适应场景,哪个查询的效率高
集合分为List,Map和Set.他们都是接口,list的实现类有ArrayList,LinkedList,Vector,Map的实现类有HashMap,LinkedHashMap,HashTable,TreeMap,set有三个实现类:HashSet,TreeSet,LinkedHashSer
ArrayList和LinedList的区别:
- ArrayList的底层是数组,LinkedList的底层是双向链表
- 在ArrayList的中间插入元素不方便,而在LinkedList的中间插入或者删除元素方便
- LinkedList不支持高效的随机元素访问
适用场景:ArrayList使用与频繁查询的场景,LinkedList适用于频繁增删的场景,数组的查询,如果已知索引,查询的时间复杂度是o(1),如果不知道索引,查询一个数的时候,时间复杂度是o(n),链表的查询时间复杂度也是o(n),但是链表的存储是不连续的,而数组的存储是连续的,所以数组的查询效率高
3.hashmap的构造方法,解决冲突的方法,扩容原理,HashMap的扩容因子
构造方法:
Hashmap一共有四个构造函数
HashMap()没有指定时,使用默认的初始化大小16,使用默认的加载因子0.75
HashMap(int inititalCapacity)
HashMap(int inititalCapacity,float loadFactor)
指定初始化大小,hashmap调用tablesize,找到大于指定初始化容量大小的最小二次幂值
int n=cap-1;
n|=n>>>1;
n|=n>>>2;
n|=n>>>4;
n|=n>>>8;
n|=n>>>16;
返回n+1表示初始的大小
table初始化不是在初始化时完成的,而是在resize的时候完成的。
解决冲突的方法
参考:https://www.cnblogs.com/wuchaodzxx/p/7396599.html
四种方法:
- 开放定址法
1.1线性探测再散列:
冲突发生时,顺序查看表中下一个单元,直到找到一个空单元,或查遍全表。表后面查完,如果没有,在表头继续查看(相当于是一个循环),步长为1.
1.2二次线性再散列
冲突发生时,分别在表的左右进行跳跃式探测,较为灵活,不易产生聚集,但缺点是不能探测到整个散列的地址空间。步长是1,-1,4,-4…
1.3伪随机探测再散列
建立一个随机数发生器,并给定一个随机数作为起始点。(随机数序列是 预先产生的)步长是一个随机序列。
2.拉链法
把所有具有地址冲突的关键字链在同一个链表中。
3.再哈希法
构造多个hash函数,当发生冲突时,用其他的哈希函数,产生hash值, 直到不冲突为止。
4.建立公共溢出区
将哈希表分为基本表和溢出表两个部分,凡是和基本表发生冲突的,一 律填入溢出表。
HashMap怎么解决hash冲突:
采用拉链法(链地址法)。当好多bin被映射到同一个桶时,如果桶中bin<6,采用链表存储,如果bin>8但是Hash表的容量小于64,依然用链表存储,对hash表进行扩容。,然后进行hash表的重构。如果bin>8并且桶的容量大于64,则将链表的结构转换为红黑树的结构进行存储。
HashMap的扩容
Hashmap在容量超过负载因子所定义的容量之后,就会扩容。Hashmap的默认负载因子是0.75.阈值=0.75*容量,也就是说当前hashmap存储的容量超过负载因子的容量就会扩容。这时就会将hashmap的大小扩大为原来数组的两倍。并将原来的对象放入新的数组中。这个过程叫做再hash,因为他调用hash方法来确定。
HashMap的扩容因子默认的是0.75(也可以再跟面试官说一下扩容因子自定义的时候需要什么)
4.treemap的底部实现(红黑树),hashset的去重
TreeMap是基于红黑树的实现,因此它要求一定要有key比较的方法,要么传入Comparator实现,要么实现Comparable接口,在put操作是,基于红黑树的遍历,基于comparator来比较key应该放在树的左边还是右边,如果找到相等的key,则直接替换掉value
HashSet在存元素时会调用对象的hashcode方法进行计算出存储索引的位置,如果其存储索引已经存在元素(Hash碰撞)则和该索引位置上所有的元素进行equals比较,如果该位置没有其他元素或者比较的结果都是false就存进去,否则就不存,所以可以看见元素是按照hash值来找位置的,故而无序且可以保证无重复元素,因此我们在往HashSet集合中存储元素时,元素对象应该正确重写Object类的hashcode和equals方法,否则会出现不可预知的错误。set集合中,相同的元素可能会存储[set先用hashcode比较,然后用equals比较,如果没有写equals方法的话,有可能存储重复的元素],而且hashcode 的冲突会加大。
5.红黑树,平衡二叉树,(2-3树也说了点)底层结构,查询和插入的复杂性
这个具体查阅我的博客:https://blog.csdn.net/if_i_were_a/article/details/89380875
https://blog.csdn.net/if_i_were_a/article/details/89371048
红黑树与2-3树等价
红黑树的定义:
1.每个节点定义成红色或者黑色的
2.根节点是黑色的
3.每个叶子节点(最后的空节点)也是黑色的,定义空这样的节点是红色的,而不是左右子树都为空的节点,空树本身也是红黑树
4.如果一个节点是红色的,那么他的孩子节点都是黑色的
5.从任意一个节点到叶子节点,经过的黑色节点是一样的
红黑树在查找的时候时间复杂度不如平衡二叉树,虽然都是logn级别,但是红黑树在构造的时候,高度比平衡二叉树高,所以红黑树的适用场景是增删比较多的情况
6.hashtable和hashmap的区别
- HashMap是线程不安全的,HashTable实线程安全的 HashMap的效率比HashTable高 。HashTable是synchronize,多个线程访问时,不需要自己为他的方法实现同步,而HashMap需要自己为他的方法实现同步
- HashMap允许值为null(key和value都可以为null),HashTable不允许值为null,(key和value的值)
- HashMap的迭代器iterator是fail-fast机制的,而HashTable的迭代时enumeration机制的
- [HashTable继承自Dictionary接口且实现了接口,而HashMap继承自AbstractMap且实现了map接口] dictionary是和map结构类似,但是过时了
- Hashtable不要求底层数组的容量一定是2的整数次幂,而HashMap要求一定是2的整数次幂
- HashTable扩容时将容量扩展为原来的2倍加1,而HashMap扩容时将容量扩展为原来的2倍
7.方法的重载和重写的区别
方法的重载和重写都是实现多态的方式,前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载指的是一个类中具有多个功能相似的函数。重写是指子类继承了父类的方法并且覆盖了子类的方法。
方法重载的规则:
- 方法名一致,参数列表中的顺序,类型,个数不同
- 重载的方法与返回值无关
- 重载可以抛出不同的异常,可以有不同的修饰符
方法重写的命名规则
- 参数列表必须与被重写的方法一致,返回值类型必须相同
- 构造方法不能被重写,声明为final的方法不能被重写,声明为static的方法不能被重写,但是可以被重新声明
- 访问权限不能比父类中的方法权限更低
- 重写的方法不能抛出比父类更多的异常。因为子类可以解决父类的一些问题,而不能比父类有更多的问题。
8.面向对象的三大特性
封装继承和多态,重载和重写是实现多态的形式(这个面试官没有让详细说)
9.继承的实现两种方式(继承类和实现接口)
继承类,和接口两种方式
10.抽象类和接口的区别
- 从抽象的角度来看,类是对对象的抽象,抽象类是对类的抽象,接口是对行为的抽象
- 从适用的场景来看,如果行为跨越不同的类,可以使用接口,对于一些相似的类对象,让其继承抽象类
- 从设计角度来看,抽象类是从子类中发现了不同的东西,泛化出父类,然后子类继承父类,接口是不知道子类的实现,方法如何实现还不确定,预先定义
- 从表象上来看:
- 抽象类中可以有方法的实现,接口中不能有方法的实现
- 抽象类可以有构造方法,接口不能有构造方法
- 抽象类可以有不同的访问权限修饰符,接口的方法权限修饰符是public
- 抽象类继承用extends,接口用implements
- 一个类可以继承一个抽象类和多个抽象方法
相同点:
- 都不能实例化
- 都可以作为引用类型
- 一个类如果继承了某个抽象类,或者实现了某个接口,都需要实现其中的全部抽象方法。
11.tcp三次握手四次挥手,网络层有什么协议,OSI七层模型
Tcp的三次握手
- 第一次握手: 建立连接。客户端发送连接请求报文段,将SYN置为1,Seq为X,客户端进入SYN_SEND状态,等待服务器的确认
- 第二次握手: 服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置ACK为x+1(seq+1),同时,自己还要发哦送SYN请求信息,将SYN位置为1,seq为y,服务器端将所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态
- 第三次握手:客户端收到服务器的SYN+ACK报文段,然后将ACK置为y+1,向服务器发送ACK报文段,这个报文段发送完毕后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手
TCP四次挥手
(1)第一次挥手:Client 发送一个 FIN,用来关闭 Client 到 Server 的数据传送,Client进入 FIN_WAIT_1 状态。
(2)第二次挥手:此时服务器端可能还要给客户端传送数据,不能急着关闭。Server 收到 FIN 后,发送一个 ACK 给 Client,确认序号为收到序号 seq+1(与 SYN 相同,一个FIN 占用一个序号),Server 进入 CLOSE_WAIT 状态。
(3)第三次挥手:服务器端数据传送完成之后,Server 发送一个 FIN,用来关闭 Server到 Client 的数据传送,Server 进入 LAST_ACK 状态。
(4)第四次挥手:Client 收到 FIN 后,Client 进入 TIME_WAIT 状态,接着发送一个ACK 给 Server,确认序号为收到序号+1,Server 进入 CLOSED 状态,完成四次挥手。
网络层的协议有IP协议,(TCP协议是传输层的)
应用层 表示层 会话层 传输层 网络层 传输链路层 物理层
12.数据库隔离级别
串行化: 可避免脏读,不可重复读,幻读的发生
可重复读 可避免脏读不可重复读的发生
读已提交 可避免脏读的发生
读未提交 最低级别,可以避免数据的丢失
13.sql优化
- 应尽量避免全表扫描,在where及order by涉及的列上建立的索引。加快查询速度
- 应尽量避免在where字段中对字段进行null值判断,否则放弃使用索引进行全表扫描
Select id from t where num is null
可以在num在设置默认值0,确保表中num列没有null值
意思就是设置为0也不要设置成为nul
- 用exist代替in,exist跟索引有关
- 用varchar代替char,因为首先变长字段存储空间小,可以节省存储空间,查询效率高
- 不要使用select * from t用具体的字段列表来代替”*”,
- 尽量避免向客户端返回大数据量,用limit进行分页
- 用in来代替or
- Like双百分号无法使用索引
- 批量insert插入
- 使用连接来代替子查询,子查询会建立一个临时的表
- 在建立有索引的字段上不要使用函数,这样会使得索引失效
- Exist使用的是外表较大的查询,in使用的是内表较大的查询
- 使用union会对查询的结果自动去重,并且按照默认的顺序进行排序
使用union all查询的结果包括重复值 不排序
14.innodb的特性以及和其它存储引擎的区别
两种存储引擎的大致区别表现在:
- InnoDB支持事务,MyISAM不支持,这一点非常重要。事务是一种高级的处理方式,如在一些列增删该中只要哪个出错还可以回滚还原,而MyISAM不可以
- MyISAM适合查询以及插入为主的应用,InnoDB适合频繁修改以及涉及到安全性较高的应用
- InnoDB支持外键,MyISAM不支持
- InnoDB支持行锁
- 清空整个表时,InnoDB是一行一行的删除,效率非常慢。MyISAM会重建表
- Mysql5.5.5以后,InnoDB是默认引擎
- InnoDB不支持全文索引
- 对于自增长的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中可以和其他字段一起建立联合索引
应用场景:
myisam适合做很多count计算,插入不频繁,查询非常频繁 ,没有事务
InnoDB适合可靠性要求较高,或者要求事务 表的更新和查询都相当频繁,并且行锁的机会比较大的情况。
15.mysql的主从分离原理,怎样解决主从复制慢的问题
型网站为了减轻服务器的压力,将读操作和写操作相分离
对于数据库的不停的连接,数据库会崩溃,造成数据库的数据丢失,后果不堪设想
读操作和写操作分别导入不同的服务器集群去实现
1、数据库层面的主从配置实现
2. 代码层面的读写分离实现[无需改动现有的代码]
主从同步如何工作
1. Master将对数据库的操作记录到二进制日志文件中,写入完成后,master通知存储引擎提交事务[Binary log]
2. slave将master的二进制文件拷入到自己的文件中[slave log]
3. sql thread的线程读取relay log中的日志,重做事务,使得slave中的数据与master的数据保持一致
主从复制慢可以减少主从同步的相隔时间
16.知道什么设计模式,详细说单例模式的实现方式
设计模式有...
单例模式https://mp.csdn.net/postedit/83001963
17.回溯算法的思想,代码怎么写
用回溯算法解决问题的一般步骤:
1、 针对所给问题,定义问题的解空间,它至少包含问题的一个(最优)解。
2 、确定易于搜索的解空间结构,使得能用回溯法方便地搜索整个解空间 。
3 、以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。
代码来源:http://www.cnblogs.com/hygeia/p/5812128.html
1: int a[n],i;
2: 初始化数组a[];
3: i = 1;
4: while (i>0(有路可走) and (未达到目标)) // 还未回溯到头
5: {
6: if(i > n) // 搜索到叶结点
7: {
8: 搜索到一个解,输出;
9: }
10: else // 处理第i个元素
11: {
12: a[i]第一个可能的值;
13: while(a[i]在不满足约束条件且在搜索空间内)
14: {
15: a[i]下一个可能的值;
16: }
17: if(a[i]在搜索空间内)
18: {
19: 标识占用的资源;
20: i = i+1; // 扩展下一个结点
21: }
22: else
23: {
24: 清理所占的状态空间; // 回溯
25: i = i –1;
26: }
27: }
18.nginx的负载均衡算法的实现
- 轮询(默认)
- 加权weight
- Ip_hash
- Fair
- url_hash
19.stringbuilder和stringbuffer的区别,初始容量是多少
共同点:都继承与AbstractStringBuilder
- 可变不可变:string是常量,在修改时不会改变
- 执行速度: StringBuilder>StringBuffer>String
- 线程安全 stringBuider是线程不安全的,stringbuffer是线程安全的string类也是线程安全的
stringbuider的初始容量是:16
20string都有什么方法
indexOf,charAt,toLowerCase,toUpperCase,repalceOf,repalceAll,split,substring,trim,intern,concat,contains...
21jvm的内存模型volatile的特性,可以保证原子性吗
JMM决定一个线程对共享变量的写吐何时对另一个线程可见。线程之间的共享内存在主内存中,每个线程还有一个私有的本地内存,本地内存中存储了用于读写的共享变量的副本
A线程与B线程通信需要经过两个步骤
- 线程A把本地更新的共享变量刷新到主内存中
- 然后,线程B把从主内存中更新已被线程A更新过的共享变量读出来,更改自己的私有的内存变量
volatile保证了内存的可见性,和禁止指令重排序,不具备原子性
22.jvm的垃圾回收算法
标记清除,标记整理,复制,分代回收
标记清除法:
标记清除法包括两个阶段:”标记”和“清除”。在标记阶段,确定要回收的对象,并做标记。在清除阶段,紧随标记阶段,将标记阶段确定不可用的对象清除。标记清除算法是基础的收集算法,标记和清除阶段的效率不高,而且清除后产生大量的不连续空间,这样当程序需要分配大内存对象时,可能无法找到足够的连续空间。
复制算法
复制算法是把内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候,把存活的对象复制到另一块上,然后把这块内存整个清理掉。复制算法实现简单,但是由于每次只能使用其中的一半,造成内存的利用率不高。现在的jvm用复制方法收集新生代,由于新生代中大部分对象(98%)都是朝生夕死的,所有两块内存的比例不是1:1大概是8:1
标记整理算法
标记整理算法和标记清除算法一样,但是标记整理算法不是把存活对象复制到另外一块内存,而是把存活对象往内存的一端移动,然后直接回收便捷以外的内存。标记-整理算法提高了内存的利用率,并且它适合在收集对象存活时间较长的老年代
分代收集是根据对象的存活时间把内存分为新生代和老年代,根据各个对象的存活特点,每个代采用不同的垃圾回收算法,新生代采用复制算法,老年代采用标记整理算法,算法的实现设计大量的程序细节,而且不同的虚拟平台实现的方法也各不相同
GC的两次标记过程
在可达性分析后发现GC ROOTS没有任何引用链相连时,会被第一次标记,并且进行一次筛选,判断此对象是否有必要执行finalize()方法
如果没有必要执行该方法;[对象没有覆盖finalize方法,finalize方法已经被jvm调用过],这种情况就可以认为对象已死,可以回收
有必要执行该方法时
GC将对F- queue队列中的对象进行第二次小规模的标记
Finalize()方法是对象逃脱死亡的最后一次机会
如果对象在其finalize()方法中重新与引用链上的任何一个对象建立关联,第二次标记时会将其移除即将回收的集合
如果对象没有,也可以认为对象已死,可以回收了
一个对象的finalize()方法只会被系统自动调用一次,经过finalize()方法逃脱死亡的对象,第二次不会再调用
23.数据库三范式
1NF:字段不可分 原子性,否则就不是关系数据库
2NF: 有主见,非主键字段依赖主键,唯一性一个表只说明一个事务
3NF:非主见字段不能相互依赖,每列都与主键有直接关系,不存在传递依赖
24.数据库慢查询怎么解决explain
分析Mysql语句查询性能的方法除了使用explain输出执行几行,还可以让MYsql记录下查询超过指定时间的语句,我们将超过指定时间的sql语句查询成为慢查询
记录慢查询的方法 查看慢查询的时间定义show varaiables like “long%”
使用explain表示查询sql语句,得到的结果的解释:
Id:select查询的序列号 若没有子查询和联合查询,id都是1,Mysql会按照id从大到小的顺序执行query,在相同的情况下,则从上到下执行
Select_type: select查询的类型(simple,primary,union)主要是区别普通查询和联合查询子查询之类的复杂查询
Table:输出的行所用的表
Type联合查询所使用的类型,从最好到最差
System.const,eq_ref,ref,fulltext,ref_or_null,index_merge,unqiue_subquery,index_sunsquery,range,index,all
Possible_keys在查询过程中可能用到的索引,如果为空,则没有相关的索引
Key:访问过程中实际用到的索引。如果没有索引被选择,键是NULL
Key_len:使用索引的长度。这个值可以得出一个多重主键里MYSQL实际使用了哪一部分
Ref:显示哪个字段或常数与key一起被使用
Rows返回请求数据的行数,在innodb上是不准确的
Extra附加说明