java面试题_有赞一面(1)_java面经汇总_2020-09-30

 

Java基础

1、HashMap的底层如何进行实现的

1.8版本后 底层采用 (数组+链表+红黑树) 

HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。当链表长度大于8的时候,转换为红黑树.


2、ArrayList和LinkedList如何实现

ArrayList 继承自 AbstractList,实现了 List 接口 ,底层采用动态数组来是实现,默认初始容量大小为 10; 扩容时默认将扩容至原来容量的 1.5 倍。插入删除元素的时间复杂度为O(n),求表长以及增加元素,取第 i 元素的时间复杂度为O(1)

LinkedList是一个实现了List接口Deque接口双端链表。 LinkedList底层的链表结构使它支持高效的插入和删除操作,另外它实现了Deque接口,使得LinkedList类也具有队列的特性; LinkedList不是线程安全的

使用场景
LinkedList更适合从中间插入或者删除(链表的特性)。
ArrayList更适合检索和在末尾插入或删除(数组的特性)。

 


3、双亲委派模型

  双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,一步步向上请求,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。

双亲委派机制的优点:

1. 可以避免重复加载,父类已经加载了,子类就不需要再次加载.

2. 更加安全,很好的解决了各个类加载器的基础类的统一问题,如果不使用该种方式,那么用户可以随意定义类加载器来加载核心api,会带来相关隐患。


4、Tomcat中的双亲委派模型

tomcat中破坏了双亲委派模型

  • commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
  • catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
  • sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
  • WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见;


5、打破双亲委派模型的原因,双亲委派模型的意义

1. 可以避免重复加载,父类已经加载了,子类就不需要再次加载.

2. 更加安全,很好的解决了各个类加载器的基础类的统一问题,如果不使用该种方式,那么用户可以随意定义类加载器来加载核心api,会带来相关隐患。


6、GC算法

 

1:标记-清除算法

      最基础的收集算法是“标记-清除”(Mark-Sweep)算法,分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。但由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片。
 

2:标记-整理算法

上面的方法我们发现会产生内存碎片,因此在这个方法就会解决碎片问题:

第一步(标记):利用可达性遍历内存,把“存活”对象和“垃圾”对象进行标记。

第二步(整理):把所有存活对象堆到同一个地方,这样就没有内存碎片了。

3:复制算法

       将内存按照容量划分为大小相等的两块,每次只使用其中的一块。当这一块用完了,就将还活着的对象复制到另一块上,然后再把使用过的内存空间一次性清理掉.

4:分代收集算法

不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的回收算法,以便提高回收效率。

 

7、可达性分析,引用计数

 

  1. 引用计数法
    给对象添加一引用计数器,被引用一次计数器值就加 1;当引用失效时,计数器值就减 1;计数器为 0 时,认为对象死亡,即可回收,该算法简单高效,缺点是无法解决对象之间相互循环引用的问题。
  2. 可达性分析算法                                                                                                                                                                      基本思路是把所有引用的对象想象成一棵树,从树的根结点 GC Roots 出发,持续遍历找出所有连接的树枝对象,这些对象则被称为“可达”对象,或称“存活”对象。不能到达的则被可回收对象。通过一系列的称为 "GC Roots" 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的。此算法解决了上述循环引用的问题。


8、CMS和G1的区别

CMS收集器是 HotSpot 虚拟机第一款真正意义上的并发收集器,CMS收集器是老年代的收集器, 它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。基于 标记-清除”算法实现.  实现步骤: 初始标记->并发标记->重新标记->并发清除

缺点:  对 CPU 资源敏感; 无法处理浮动垃圾;   它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生。

G1:   G1收集器收集范围是老年代和新生代。是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征.  实现步骤: 初始标记->并发标记->最终标记->筛选回收


9、JMM,CMS作用范围

cms采用 标记清除  这个算法在老年代比较效率比较高


10、StopTheWorld如何解决

在这里插入图片描述

如何解决: 减少Full GC的次数也是经常用到的办法。 

 


11、标记清除会产生内存碎片,标记整理和标记清楚的区别

分为“标记”和“清除”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。它是最基础的收集算法,后续的算法都是对其不足进行改进得到。这种垃圾收集算法会带来两个明显的问题:

  1. 效率问题
  2. 空间问题(标记清除后会产生大量不连续的碎片)

标记整理 : 根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。


12、分代的优点

让不同的区域采用不同算法, 新生代因为对象较多,每次回收大量的对象,所以采用复制比较好

老年代因为对象较大,并且不容易回收,用复制会比较慢,所以采用 标记清除-整理比较好

 

线程

1、线程状态

五种状态, 创建, 就绪,运行, 阻塞,超时, 死亡


2、sleep()方法进入什么状态

sleep不释放锁,sleep执行后线程进入阻塞状态 , 时间到了会正常返回,线程处于就绪状态, 然后参与cpu调度,获取到cpu资源之后就可以运行。


3、Synchronzied的工作原理

Synchronized是java中的关键字,是一种同步锁。它修饰的对象有以下几种:(类, 方法, 代码块)

synchronized可以保证方法或代码块在运行时,同一时刻只有一个线程可以进入到临界区(互斥性)所以它也是排它锁,同时它还保证了共享变量的内存可见性。

Synchronized的四种状态

 这四种状态是在jdk1.6之后引入的,分别为:(无锁->偏向锁->轻量级锁->重量级锁 ) 这几个状态会随着竞争情况逐渐升级。


4、CAS的一个引用场景

使用CAS在线程冲突严重时,会大幅降低程序性能;CAS只适合于线程冲突较少的情况使用。


5、如何解决ABA

ABA问题: 

 CAS修改数据的时候 M初始读取的时候是A值 ,准备修改的时候M=A   符合CAS的条件了.但是这个A值有可能  是特务, 它首先变成了敌人D,然后又变成了友军A.  你说这样它还算是好人吗?  肯定不算. 所以ABA问题就是(A变成B 然后又变成A) . 为了解决这个问题.可以采用加个version版本号来解决.

 version版本号:A每次变换身份的时候,version+1. 这样M读取A的时候,把版本号也获取,如果A变成特务在变回友军,版本号就会不一致.这个时候就不能修改了. 完美解决特务.

 

 

Spring


1、Spring IOC

IoC(Inverse of Control:控制反转)是一种设计思想,就是 将原本在程序中手动创建对象的控制权,交由Spring框架来管理。 IoC 在其他语言中也有应用,并非 Spring 特有。 IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。


2、Spring AOP

AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码降低模块间的耦合度,并有利于未来的可拓展性和可维护性

Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理


3、Spring解决循环依赖

1. 什么是循环依赖?

   (1)循环依赖-->循环引用。--->即2个或以上bean 互相持有对方,最终形成闭环。

   eg:A依赖B,B依赖C,C又依赖A。【注意:这里不是函数的循环调用【是个死循环,除非有终结条件】,是对象相互依赖关系】

Spring中循环依赖的场景?where?

     ①:构造器的循环依赖。【这个Spring解决不了】

StudentA有参构造是StudentB。StudentB的有参构造是StudentC,StudentC的有参构造是StudentA ,这样就产生了一个循环依赖的情况,

   ②【setter循环依赖】field属性的循环依赖【setter方式 单例,默认方式-->通过递归方法找出当前Bean所依赖的Bean,然后提前缓存【会放入Cach中】起来。通过提前暴露 -->暴露一个exposedObject用于返回提前暴露的Bean。】

4.怎么解决的?  todo what?

 Spring的循环依赖的理论依据其实是基于Java的引用传递,当我们获取到对象的引用时,对象的field或zh属性是可以延后设置的(但是构造器必须是在获取引用之前)。

   Spring的单例对象的初始化主要分为三步: 


    ①:createBeanInstance:实例化,其实也就是 调用对象的构造方法实例化对象

    ②:populateBean:填充属性,这一步主要是多bean的依赖属性进行填充

    ③:initializeBean:调用spring xml中的init() 方法。

从上面讲述的单例bean初始化步骤我们可以知道,循环依赖主要发生在第一、第二步。也就是构造器循环依赖和field循环依赖。

那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存。

  调整配置文件,将构造函数注入方式改为 属性注入方式 即可


4、Spring 的代理方式,cglib和Java自带的一个区别

1、Jdk动态代理:利用拦截器(必须实现InvocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理

2、 Cglib动态代理:利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来处理

JDK 动态代理只能只能代理实现了接口的类,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。

就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。


5、静态代理和动态代理的区别,各自的优点和缺点。

静态代理中,我们对目标对象的每个方法的增强都是手动完成的,非常不灵活(_比如接口一旦新增加方法,目标对象和代理对象都要进行修改_)且麻烦(_需要对每个目标类都单独写一个代理类)。 实际应用场景非常非常少,日常开发几乎看不到使用静态代理的场景。

上面我们是从实现和应用角度来说的静态代理,从 JVM 层面来说, 静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。

相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类( CGLIB 动态代理机制)。

从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。

 

  1. 灵活性 :动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的!
  2. JVM 层面 :静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。

 

计算机网络


1、OSI七层模型,挑选几层解释一下


2、能不能详细解释一下TCP三次握手四次挥手的过程以及原因,为什么一定要三次握手,四次挥手

TCPä¸æ¬¡æ¡æ

TCPå次æ¥æ

三次握手的最主要目的是保证连接是双工的。


3、为什么要四次挥手三次握手,为什么多了一次

这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,我们也未必全部数据都发送给对方了,所以我们不可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,我们的ACK和FIN一般都会分开发送。


4、GET,POST,UPDATE,delete哪一个是幂等的,能不能讲解一下

  所谓幂等是指不管进行多少次操作,结果都一样。比如,调用GET方法只是去请求资源,自然每次调用结果都是相同的,就是幂等的。对于POST请求,两次调用POST请求,可能就会在服务器创建两份资源,所以当然不是幂等的。

  • GET,安全且幂等

  • POST,不安全且不幂等

  • PUT,不安全但幂等

  • DELETE,不安全但幂等

 

5、你经常使用的Linux命令

ps ls tail grep cd vim cat 


6、你是如何理解Linux的启动过程的,如果我们使用SSH链接服务器,服务器断开之后能否会话能否继续执行,不能,如何解决,tmux

不能

 

操作系统

1、简单介绍一下进程和线程

1.进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。还存在资源开销、包含关系、内存分配、影响关系、执行过程等区别...

2.同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源相互独立。

协程是比线程更小的一种执行单元,你可以认为是轻量级的线程

2、进程和线程的区别

同上

 

数据库
1、MySql的事务隔离级别

SQL 标准定义了四个隔离级别:

  • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
  • READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
  • SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读
隔离级别脏读不可重复读幻影读
READ-UNCOMMITTED
READ-COMMITTED×
REPEATABLE-READ××
SERIALIZABLE×××


2、可重复读和已提交读的区别

  • READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生


3、Mysql中的不同join链接的一个问题

    left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 
  right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
  inner join(等值连接) 只返回两个表中联结字段相等的行


4、不同的事务隔离级别他们分别如何实现

通过mysql锁技术以及MVCC基础

1. mysql锁技术
读写锁
解决上述问题很简单,只需用两种锁的组合来对读写请求进行控制即可,这两种锁被称为:

共享锁(shared lock),又叫做"读锁"
读锁是可以共享的,或者说多个读请求可以共享一把锁读数据,不会造成阻塞。

排他锁(exclusive lock),又叫做"写锁"
写锁会排斥其他所有获取锁的请求,一直阻塞,直到写入完成释放锁。

总结:
通过读写锁,可以做到读读可以并行,但是不能做到写读,写写并行
事务的隔离性就是根据读写锁来实现的!!!

2. MVCC基础

MVCC (MultiVersion Concurrency Control) 叫做多版本并发控制。

InnoDB的 MVCC ,是通过在每行记录的后面保存两个隐藏的列来实现的。这两个列,
一个保存了行的创建时间,一个保存了行的过期时间,
当然存储的并不是实际的时间值,而是系统版本号。

以上片段摘自《高性能Mysql》这本书对MVCC的定义。他的主要实现思想是通过数据多版本来做到读写分离。从而实现不加锁读进而做到读写并行。


5、最左前缀

多列索引,  查找的时候需要进行最左匹配才能使用索引,否则索引将会失效


6、有没有遇到过慢SQL如何进行解决,怎么进行优化

 

1:  利用explain关键字可以模拟优化器执行SQL查询语句,来分析sql慢查询语句

 2:查看索引没起作用的情况,   索引失效有很多种情况,这个建议具体的了解下

3分解关联查询    将一个大的查询分解为多个小查询是很有必要的。

4优化LIMIT分页


7、索引的数据结构

hash  b+树  涉及的内容很多,建议专门学习该知识点

 

 

题目来自牛客,  题的答案都是我个人理解的或网上的参考,仅仅只作为复习用.因个人水平有限,不合理的地方请多多指正.

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值