Java面试问题准备(新)

2019-07-07  本文已影响0人  Moine0828

一、项目类

  1. 意义最大的项目

  2. 项目中遇到的最困难的问题

3、项目中用到的设计模式

责任链模式,工厂

4、项目的结构框图(支付系统的流程)

二、Java源码类

1、hashmap和concurrenthashmap的区别和优缺点

hashmap线程不安全,chashmap线程安全

2、加解密算法,RSA,jdk的一些加密api

3、

三、多线程类

1、自增的原子性

2、volatile (long类型的原子性) http://www.cnblogs.com/dolphin0520/p/3920373.html

cpu存在各自的高速缓存,两个线程同时操作自己cpu的高速缓存中的值,再写入内存,可能会出现和预期结果不一致的情况,这就是缓存一致性问题。解决缓存一致性问题,主要有两种方式:

1、在总线加LOCK#指令

2、缓存一致性协议

起初通过总线加锁实现,但是在加锁期间,其他CPU不能访问内存,效率有一丝低下。所以缓存一致性协议应运而生。核心思想是,当CPU在操作一个共享变量的时候,发出信号通知其他CPU将自己缓存里的变量设置为无效状态,等下次其他CPU再去操作这个变量的时候,发现缓存中变量状态是无效,就会去内存里读取,这样值就保证了一致

3、lock的实现

lock的锁实现是基于一个内部类Sync实现的,内部类Sync的实现则是基于JUC包中的并发组件的基础框架AQS,Sync有两个子类,分别的公平和不公平,对应了公平锁和不公平锁的两种实现方式。默认是不公平锁,在不公平锁的lock方法被调用时,线程会直接尝试acs的方式设置state的值为1,也就是说直接获取锁而不排队,所以叫不公平锁。如果值设置成功,也就是说当前线程已经获取了锁了,那将当前持有线程的变量值设置为当前线程,ownerThread这个变量是AQS的父类定义的一个变量。如果acs没有设值成功,则进入acquire方法尝试获取锁,首先调用的是tryAcquire方法,这个方法AQS是没有给出实现的,而是将实现留给了子类,意味着子类可以根据公平或不公平,或者其它原则来实现这个获取锁的过程。非公平锁的话首先还是ACS得方式将state值设置为1,如果成功则意味着成功获取了锁,那接下来的操作和之前一样,将持有锁的线程设置为当前线程,然后返回,如果没有成功,则说明锁被其它线程正持有,那就接着判断持有锁的线程和当前获取锁的线程是不是同一个线程,如果是的话,就将state+1,返回,这意思就是说ReetrantLock是可重入的,当前线程可以重复持有多次锁,只要使用完成后释放相应多的次数即可。AQS的state在不同的子类里代表不同的意思,在ReetrantLock中就表示了当前线程的锁的次数。两次尝试都没有成功的话,tryAcquire方法返回false,AQS调用acquireQueued方法,将当前线程加入到等待队列中,然后调用当前请求锁的线程的interrupt方法将线程阻塞。

4、AQS的实现

AQS通过模板方法模式为子类的实现定下基调。AQS中规范了acquire和release的流程,但是tryAcquire和tryRelease留给子类自己实现。AQS中维护了一个int类型的变量status,这个变量的使用很自由,子类可以任意定义变量所表示的含义,例如最常见的ReentrantLock中status就用来表示锁被线程持有的次数。AQS中维护了一个队列来放置等待获取锁的被阻塞的线程,每个线程被构造成一个Node对象。

5、线程池的实现原理,参数的用处 https://www.cnblogs.com/KingJack/p/9595621.html

execute方法:

workerCountOf(c) 获取当前线程池的线程数,如果小于指定容量,则addWorker

如果addWorker失败,或者当前线程数b不小于线程池容量

如果线程池还在工作而且向工作队列中添加任务成功,则进行一次recheck检查线程池当前的线程数是否小于线程池的容量,如果线程池没有在running,将任务从队列中移除,并且调用reject。如果线程池还在running,但是当前线程池数量为0,则新建任务

如果线程池没有在工作或者向工作队列中添加任务线程不成功,那么尝试直接创建新线程来执行任务,如果失败,reject方法调用

addWorker方法:

6、volatile能使得非原子操作变成原子的吗

能。long和double

7、什么是线程局部变量

8、线程通信的方式

1、最容易想到的,共享变量,用synchronized,volatile等修饰的变量,可以用作线程之间的通信

2、队列,生产者消费者不同的线程之间用队列来共享内容

3、Condition

4、notify()

9、Happen Before

如果一个操作的结果需要对另一个操作可见,这两个操作就是happens-before关系

包括:

书写操作按照代码的顺序发生

unlock发生在下一个lock之前

读操作发生在写操作之前

线程的start发生在线程的其他动作之前

总的来说就是指令执行按照代码的顺序执行,如果前后代码存在关联关系

这些原则都是原生Java就满足的happens-before原则,是Java语言天生具备的有序性

10、AtomicInteger等原子类

11、ReentrantLock和synchronized的区别

1、Reen可以控制公平性,syn是天生非公平的。

2、Reen提供中断等待,超时等操作,syn不可以。

12、countdownlatch源码 https://www.jianshu.com/p/2effb6b3e6c6

13、避免重复创建线程

https://blog.csdn.net/MingHuang2017/article/details/79571529

14、多线程的同步和互斥有哪几种方法

1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。

2、互斥量:为协调共同对一个共享资源的单独访问而设计的。

3、信号量:为控制一个具有有限数量用户资源而设计。

4、事 件:用来通知线程有一些事件已发生,从而启动后继任务的开始。

15、批量执行异步任务

四、JVM类

1、Java的垃圾回收机制

Java内存分为新生代,老年代,再细分可以分为eden,两个survivor区,新创建的对象被分配在eden区,当分配不下时,触发新生代GC,将存活的对象复制到其中一个survivor区,判断对象是否应该被回收常见的有两种算法,一种是引用计数,一种是可达性分析,引用计数是为对象添加一个引用计数器,如果引用计数器是0则可以被回收,这种方法在某些情况下会导致内存泄漏,比如说循环引用,可达性分析是指定一些对象作为GC Roots,可以作为gcroots的对象一般是本地变量表中的对象,静态类属性,常量引用的对象等,从这些对象出发的调用链到达不了的对象,会被认定为是需要回收的对象。值得注意的是,虚拟机在分析对象的引用关系时,为了防止引用关系发生变化,会暂停所有线程,就是常说的stoptheworld。当对象被认定为是需要回收的对象,则虚拟机会将其回收,回收算法有很多种,新生代使用以空间换时间的复制清除,除此之外还有标记清除,标记整理等,例如在老年代这种不需要额外空间来争取效率的场景就会使用标记清除或者标记整理。

常见的收集器:

G1:全区域的垃圾收集,

2、JVM是如何实现反射的

3、jvm调优的参数

4、双亲委派机制,哪些情况违反了,双亲委派的好处

回答:哪些情况违反:

1、jdk1.2之前就已经实现的类加载器

2、基础类需要回调用户代码的时候,线程上下文类加载器,可以使父加载器去请求子类加载器,实现逆向的加载

3、OSGI模块化热部署,实现的类加载器是网状结构,而非双亲委派模型的树状结构,类加载器可以委派同级别的其它类加载器完成

双亲委派的好处:具备天生的优先级关系,使得同一个根类在不同的类加载环境中加载出来的都是一样的对象。例如Object类无论何时加载都得到一样的对象。

5、stop the world是咋回事

回答:虚拟机在GC之前需要判断对象是否存活,常见的有两种算法,引用计数和可达性分析。HotSpot的基于可达性分析算法实现的,可达性分析算法必须要求定义一系列的GCRoots节点,并且在进行分析工作时这些节点的引用关系不能动态变化,不然分析的准确性得不到保证,因此在整个分析的过程中,所有的Java线程都将被停顿,sun将这件事称为stop the world。

6、内存泄漏和内存溢出

7、Java类文件结构

Java虚拟机不与任何语言绑定,只能运行字节码的class文件。变量,关键字,运算符等都是由不同的字节码组合而成。

class文件是以8字节为基础单位的一组二进制流。Class文件只有两种数据类型:无符号数和表。无符号数包括u1,u2,u4,u8四种,分别代表一个字节,2个,4个,8个字节。表是由无符号数和其它表组合而成的复杂数据结构,所有的表都习惯性的以_info结尾。整个Class文件实际上就是一张表。

每个Class文件的头四个字节都是魔数,标记Class文件的合法性。接下来4-2-2个字节分别是文件版本号次版本号和主版本号。主版本号之后紧接着是常量池的入口,常量池的常量数量不是固定的,所以常量池入口需要放置一个u2类型的数据代表常量池的计数值,且计数是从1开始而不是从0开始的。常量池当中存放字面量和符号引用

常量池结束之后,紧接着两个字节代表访问标志--access_flags。这个标志用于识别类信息,例如class是类还是接口,是public还是private等等。access_flags一共有16位,但是目前只使用了8位,b其余的位用0填充。

类索引和父类索引都是u2类型的数据,但是接口索引是u2类型的数据集。Class文件依据这三种数据来判断类的继承关系。父类索引只有一个。Object类的父类索引位为0 。这三类索引数据按顺序排在访问标志之后。

紧接着是字段表。字段表包含了类级变量和实例变量,但是不包含方法中的局部变量。

紧接着是方法表。数据含义与字段表类似,但是由于volatile等关键字不能修饰方法,因此标志位少了几个。方法里的代码,被编译成字节码存放在方法表中的一个code字段。

8、字节码指令简介:

1)加载和存储指令

五、Spring

1、Spring bean的生命周期

4.png

2、Spring的元注解

3、xml 中配置的 init、destroy 方法怎么可以做到调用具体的方法

4、applicationContext.xml是怎么被加载的

XmlBeanFactory 或ClassPathXmlApplicationContext加载xml文件

getInputStream获取文件流

doLoadBeanDefinitions方法 读取xml文件 SAX解析出Document对象

验证xml文件的格式规范,加载命名空间,下载dtd

BeanDefinitionDocumentReader .registerBeanDefinitions() 加载xml中定义的bean

registerBeanDefinitions方法中,又调用parseDefaultElement等方法来解析import标签,bean标签等等

5、spring aop的实现原理,动态代理

aop中的动态代理:

1、JDK动态代理:代理对象与被代理对象实现同一接口

Proxy.newProxyInstance JDK反射包提供的工厂,Class对象在运行时以 参数形式传入,也就是说代理关系是在运行时指定的,不是在编译时指定的,因此是动态代理,和静态的有区分,字节码级别的代理对象

2、CGLIB:被代理类不需要强制实现接口,通过实现MethodInterceptor ,重写intercept方法,dubboxieyidt tedian

所有的动态代理,都是在运行时生成被代理的子类。spring aop根据被代理的子类是否实现了接口,选择使用哪一种代理方式,【问题:如何知道是否实现了接口呢】

6、spring拦截器

7、spring事务

7大传播特性

有事务,加入,没有就新建

有事务,加入,没有就非事务

有事务,加入,没有抛出异常

有事务,嵌套(等于还是新建了一个)

有事务,挂起当前,新建一个

有事务,挂起当前,非事务执行

有事务,抛出异常,没有就非事务

5种隔离级别:

默认和数据库的一样

未提交读:没提交就能读

已提交读:提交了才能读

可重复读:

串行化

8、springMVC dispatcherServlet源码

六、MyBatis

1、mybatis接口和xml映射 https://blog.csdn.net/birthmarkqiqi/article/details/68939535

1、MapperRegistry 类注册了所有mapper接口

2、MapperProxyFactory是mapper代理类工厂

3、在factory中调用Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface },mapperProxy); 创建mapper的代理类。这是jdk的动态代理

mapperProxy就是扮演了InovationHandler的角色,在方法调用前后进行代理

mapperProxy有invoke方法,通过将method作为参数传入cachedMapperMethod方法,获取到MapperMethod ,即执行sql的封装对象,然后再execute对应的sql

sql语句是从哪里来?Method对应了接口的方法,所以method对象在cachedMapperMethod方法里,创建了MapperMethod对象,而MapperMethod中有一个SqlCommand对象,保存了sql的信息。至于信息的转换,sql的填充等等,都是在SqlCommand的构造方法里实现的,里边有一个resolveMappedStatement方法,解析出MappedStatement对象。

MapperMethod 持有了一些sql的信息,调用其execute方法,就可以执行sql了。在execute内部,调用的是rowCountResult方法来执行sql的。

2、mybatis一对多查询

七、Dubbo

1、dubbo实现原理 https://blog.csdn.net/u013076044/article/list/2?

是一个分布式框架,分为服务消费者,服务提供者,注册中心等几个角色。注册中心会对服务的提供者进行实时的心跳检测,发现变化之后将最近的serverList推送给消费者,消费者在本地会缓存一份。一个服务新注册的时候,会主动向注册中心发送心跳。

dubbo是基于spring的命名空间加载的,spring在装配bean 的时候,遇上dubbo的命名空间,就会调用DubboNamespaceHandler来加载对象。

Dubbo会在Spring实例化完bean之后,在刷新容器最后一步发布ContextRefreshEvent事件的时候,通知实现了ApplicationListener的ServiceBean类进行回调onApplicationEvent 事件方法,dubbo会在这个方法中调用ServiceBean父类ServiceConfig的export方法,而该方法真正实现了服务的(异步或者非异步)发布。

https://blog.csdn.net/yanpenglei/article/details/80261762

2、export的步骤简介

首先会检查各种配置信息,填充各种属性,总之就是保证我在开始暴露服务之前,所有的东西都准备好了,并且是正确的。

加载所有的注册中心,因为我们暴露服务需要注册到注册中心中去。

根据配置的所有协议和注册中心url分别进行导出。

5.png

导出的具体操作:

1、loadRegistries方法构造url的列表

2、url的对象存储的是注册中心的消息,包括注册中心的host地址,协议,application,类型(常用的是zk)等信息

3、循环列表,调用doExportUrlsFor1Protocol方法将同一种协议发布到每一个注册中心上

4、doExportUrlsFor1Protocol方法:

1)、InetAddress.getLocalHost().getHostAddress()

2)、获取port等信息,没有配置则获取随机可用端口

3)、将application,side,method等参数解析到一个map里,application通过ApplicationConfig对象解析获得,method通过反射获得

4)、将host,port,map等信息构造成一个url对象

5)、如果配置的不是remote,则做本地导出。如果配置的不是local,则暴露为远程服务。

6)、proxyFactory构造Invoker

7)、获取完Invoker之后,转换成对外的Exporter,缓存起来。

所有的provider都被解析成ServiceBean。ServiceBean实现了InitializingBean接口,继承了ServiceConfig类。在afterProperties方法中对注册中心等进行配置,配置完成后调用了export方法,export方法中设置了延迟发布等配置,最后最核心的逻辑是调用doExport方法,将ip,端口,方法,类名等拼成url暴露给注册中心解析。针对每一个注册中心,分别组装url,进入RegistoryProtocol类的export方法,打开一个netty Server侦听服务,并接受客户端的各种请求,向注册中心暴露服务。

doExport方法的核心逻辑,校验application,校验注册中心等,最后调用doExportUrls。doExportUrls方法的逻辑是将同一种协议发布到不同的注册中心上。发布的方法是doExportUrlsFor1Protocol。

doExportUrlsFor1Protocol步骤:获取provider的host,

服务的提供者和消费者之间的调用是不经过注册中心的,而是直接的调用,因此消费者也可以直接在配置中指定提供者的ip地址。

消费者调用过程:RegistryProtocol对象的refer()方法,根据url和服务接口构造出invoker对象,

八、数据库

1、数据库的范式

1、第一范式:每一个字段保证原子性

2、第二范式:表字段与主键有依赖关系

3、第三范式:表字段与主键的依赖要求直接依赖

2、数据库索引为什么用树

3、索引使用B+树的好处,索引的原理

主键是唯一的聚集索引,覆盖了真实的数据,对其它字段加索引都是非聚集索引,非聚集索引是先找到主键,再通过主键查询数据。如果两个字段组成一个联合索引,那么以一个字段为条件查询另一个字段就不需要经过表,查询效率也会很高

B+树:每一个节点的叶子上限是2d。只有叶节点存储data。

https://www.cnblogs.com/wuchanming/p/6886020.html

mysql联合索引的最左前缀机制

https://www.jianshu.com/p/e1dce41a6b2b

4、数据库乐观锁悲观锁

https://blog.csdn.net/xiaokang123456kao/article/details/75268240

5、聚集索引和非聚集索引的区别

聚集索引,叶子为数据。非聚集索引,叶子为聚集索引

聚集索引物理顺序对应表数据的逻辑顺序。非聚集不是

6、索引失效

1、索引存储null值。或使用is null,会触发全表扫描

2、重复数据较多的列

3、模糊查询'%xxx%'

4、sql语句中有or或者not

7、 脏读:事务A读到了事务B未提交的数据

不可重复读:事务A两次查询某一列的间隙,事务B对列进行了修改

幻读:事务A先搜到了N条,事务B又加了M条,事务A第二次查询时条数增加

九、设计模式

1、熟悉的设计模式

策略,责任链,builder,工厂,适配器,观察者,模板方法,装饰器,代理,

创建型:工厂,抽象工厂,单例,建造者,原型

结构型:适配器,装饰器,代理,外观,桥接,组合,享元

行为型:策略,模板方法,责任链,观察者,迭代子模式,命令,状态,备忘录,访问者,

十、分布式,高并发架构类

1、项目中如何解决高并发的问题

1、在高频率读的数据加入缓存

2、注意并发的安全问题,在需要的地方使用同步容器或加锁

3、加机器

4、避免循环查库等消耗性能的操作

2、分布式环境下如何保证订单号唯一

服务唯一编码+时间戳+oracle自增序列

3、微服务和SOA

4、分布式缓存的一致性hash

https://www.cnblogs.com/akaneblog/p/6736386.html

5、分布式系统间的事务一致性

CAP,一致性,可用性,分区容忍性,不可兼得。分区容忍性是必须的,因此各类分布式中间件和框架,要么选择弱化C,要么选择弱化A。

分布式事务算法:

1、2PC/3PC 两阶段提交或三阶段提交。涉及到跨库事务,每一个节点只能保证,也只能知道自己节点的成功与失败,无法知道其他节点。因此引入一个协调者,或者叫参与者来掌控所有节点的操作结果并决定是否将事务提交。

第一阶段:协调者问所有节点是否可以提交,各个节点自我检查后回应可以提交或者不可以

第二阶段:所有的节点都回应了可提交,那么协调者向所有参与者发送正式提交的命令。如果有一个节点回应不可提交,那么协调者向所有参与者发送回滚请求。

两段提交的最大问题,就是参与者如果没有收到决策,那么就会一直处于阻塞状态。因此三段提交将提交修改为:询问的时候不锁资源,等到正式提交命令下达时再锁资源

2、TCC

十一、Redis

1、redis线程模型

https://www.cnblogs.com/barrywxx/p/8570821.html

2、redis的持久化机制

RDB,AOF

3、mysql主从复制和redis主从复制的区别

4、redis数据持久化 https://www.cnblogs.com/chenliangcl/p/7240350.html

5、redis缓存失效策略:

1、定时器 set值的时候就设置一个定时器

2、惰性 get的时候再判断

3、定期回收

6、redis数据淘汰策略:

数据集到一定大小,就需要实行数据淘汰策略

redis 提供 6种数据淘汰策略:

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-enviction(驱逐):禁止驱逐数据

7、redis选举机制:

哨兵机制:启动多个哨兵对集群进行监控。发现异常就报告给管理员,或自动进行故障迁移。将失效的Master的其中一个Salve选举为新的Master。

从主服务器的从服务器中选出一个从服务器作为新的主服务器。选点的依据依次是:网络连接正常->5秒内回复过INFO命令->10*down-after-milliseconds内与主连接过的->从服务器优先级->复制偏移量->运行id较小的。选出之后通过slaveif no ont将该从服务器升为新主服务器

当主服务器宕机之后,从服务器切换成主服务器的时间内,服务是不可用的

十二、MQ

1、MQ重复消费

业务上幂等

2、mq重发机制,nameServer挂了之后还能不能接着发消息

3、

十三、网络类

1、TCP和UDP的区别

2、HTTP协议

十四、算法,数据结构

1、链表反转

1.就地反转法 public ListNode reverseList1(ListNode head) { if (head == null) return head; ListNode dummy = new ListNode(-1); dummy.next = head; ListNode prev = dummy.next; ListNode pCur = prev.next; while (pCur != null) { prev.next = pCur.next; pCur.next = dummy.next; dummy.next = pCur; pCur = prev.next; } return dummy.next; }

2、N个数字找K个最大值

1、小顶堆 时间复杂度O(NlogK)

2、分成很多份,每份再做小顶堆或者直接快排,可以发挥多核优势

3、bitmap去重

3、动态规划 https://blog.csdn.net/rock_joker/article/details/68928150

4、快排,红黑树

十五、Linux

1、linux常用命令 https://www.cnblogs.com/irisrain/p/4346086.html

◆ 安装和登录命令:login、shutdown关闭计算机、halt 等于就是shutdown -h关机后关电源、reboot重启、install、mount、umount、chsh更改shell设定、exit退出系统、last显示近期用户的登录状况;

◆ 文件处理命令:file判断文件类型、mkdir、grep、dd复制文件、find目录中搜索文件、mv移动文件,也可以用来重命名、ls、diff两个文件之间的比较、cat、ln文件之间创建连接;

◆ 系统管理相关命令:df检查文件系统的磁盘占用、top显示执行中的文件进程、free显示内存占用比例、quota、at、lp打印文件、adduser、groupadd将新组加入系统、kill、crontab;

◆ 网络操作命令:ifconfig查看和更改ip地址广播地址等、ip -s输出状态 -f接协议种类 、ping -c设置完成要求回应的次数 -R记录路由过程 、netstat 查看自身网络情况 、telnet 连接远程端口、ftp远程文件传输、route -add 增加路由、rlogin、rcp远程文件拷贝 -r递归拷贝目录 、finger查询登陆账号的信息 -s -l、mail、 nslookup;scp scp file_path_1 root@192.168.1.1:file_path_2 远程file2拷贝到本地file1

◆ 系统安全相关命令:passwd、su、umask、chgrp、chmod、chown、chattr、sudo ps、who;

◆ 其它命令:tar、unzip、gunzip、unarj、mtools、man、unendcode、uudecode

十六、NIO

https://blog.csdn.net/chen8238065/article/details/48315085#%E7%9B%B8%E5%85%B3%E8%B5%84%E6%96%99

https://segmentfault.com/a/1190000003063859

https://blog.csdn.net/u014507083/article/details/73784898

十七、操作系统

1、死锁发生的条件和解决方案

死锁产生的条件:

1、互斥条件:资源不能被共享,只能由一个进程使用

2、请求与保持条件:进程已获得了一些资源,但因请求其它资源被阻塞时,对已获得的资源保持不放。

3、不可抢占条件(No pre-emption) :有些系统资源是不可抢占的,当某个进程已获得这种资源后,系统不能强行收回,只能由进程使用完时自己释放。

4、循环等待条件(Circular wait) :若干个进程形成环形链,每个都占用对方申请的下一个资源。

处理死锁的方法:

1、鸵鸟算法:

2、银行家算法:

对待死锁的策略主要有:

(1) 死锁预防:破坏导致死锁必要条件中的任意一个就可以预防死锁。例如,要求用户申请资源时一次性申请所需要的全部资源,这就破坏了保持和等待条件;将资源分层,得到上一层资源后,才能够申请下一层资源,它破坏了环路等待条件。预防通常会降低系统的效率。

(2) 死锁避免:避免是指进程在每次申请资源时判断这些操作是否安全,例如,使用银行家算法。死锁避免算法的执行会增加系统的开销。

(3) 死锁检测:死锁预防和避免都是事前措施,而死锁的检测则是判断系统是否处于死锁状态,如果是,则执行死锁解除策略。

(4) 死锁解除:这是与死锁检测结合使用的,它使用的方式就是剥夺。即将某进程所拥有的资源强行收回,分配给其他的进程。

上一篇下一篇

猜你喜欢

热点阅读