Java 面试总结之二:
一、java初级相关问题:
-
简单聊下 java集合
Java 集合分为value(单列集合)和key-value(双列集合)两种,即Conllection和map
1.1Collection
分为List和set:
i.List: 有序的,可以重复的
a. ArrayList:底层是数据结构,要求分配的内存是连续的,通过角标可以获得元素的值,所以查询的速度特点快,支持快速随机访问(get(int index)),空间浪费在list结尾要预留一些空间
线程不安全的,如果要想线程安全可以用Vector
b. LinkedList:底层是双向链表结构,空间浪费在每个元素都要比ArrayList多消耗空间(保存每个节点的直接前驱结点和直接后续结点,以及数据)
c.Vector:底层是Object数据
ii.set: 无序的,不可以重复的,根据equals和hashcode 来判断。即如果一个对象存储在set中必须重写equals和hashcode
。
a. HashSet:无序,唯一,基于HashMap实现,底层采用hashMap来保存元素。实现set接口,只存储对象
,比hashMap慢,使用对象成员计算hashcode值
b. LinkedHashSet:继承HashSet,底层通过LinkedHashMap来实现的
c.TreeSet:有序,唯一,底层采用红黑树(自平衡二叉树)
1.2map
分为HashMap和hashTable:
a. HashTable:键值对不允许为null,线程同步(里面的方法使用了synchronized修饰),效率低
b. HashMap:键值对允许为null(只有一个键为null,多个值为null),线程不同步,效率高;解决哈希冲突时,当链表长度大于8,就改为红黑树;使用键计算hashcode值
concurrentHashMap:
底层结构采用数组+链表/红黑二叉树,采用CAS和synchronized来保证并发安全,synchronized只锁定当前链表或红黑二叉树的首节点,只要不哈希冲突,就不会产生并发,效率提高了
c. LinkedHashMap:继承了HashMap,在HashMap的基础上增加了一条双向链表
d. TreeMap:红黑树(自平衡的排序二叉树) -
SpringMVC流程:
-
Servlet :
3.1 什么是servlet?
是用Java编写的服务器端程序
。主要用能在于交互式地浏览和修改数据,生成动态Web内容。
Servlet可以处理来自客户端的HTTP请求,并生成响应返回给客户端。
3.2Servlet的生命周期?
加载——》实例化——》初始化——》响应请求——》销毁
Servlet的生命周期可以概括为以下几个阶段:
1)当客户端第一次请求Servlet时,Servlet被加载到内存中,容器会创建Servlet实例,并调用其init()方法进行初始化工作。
2)容器创建请求对象和响应对象,然后调用Servlet的service()方法为客户端提供服务。
3)当Servlet不再被需要时,容器调用Servlet的destory()方法将Servlet实例销毁。
当客户端请求的Servlet已经存在于服务器内存时,容器会创建信的线程调用service()方法响应客户端请求。在Servlet的整个生命周期中,init()方法和destory()方法只会被调用一次。
Servlet处理请求的流程:
i.客户端在浏览器的地址栏中输入一个请求的URL,按回车后就向服务器端发起一个http request(由浏览器生成)。
ii.服务器端的Web Server首先接受到请求,并将请求转交给容器,容器会根据请求的URL去调用客户端要访问的Servlet。
iii.容器会根据web.xml中对Servlet的描述去查找要访问的Servlet,若找到,将此Servlet
装载进虚拟机并实例化(第一次访问),然后调用Servlet实例中的service方法处理请求,并分配一个线程去执行。
注: 当第二次去访问同一个Servlet时,若容器判断到该Servlet已经被装载并实例化,
那么容器就不会再去创建一个新的Servlet实例,直接调用原来那个Servlet实例中的service方法来处理请求。
iv.若没有查找到,直接返回一个404的错误代码到客户端,表示访问的资源不存在。
3.3Servlet中的forword与redirect的区别:
i.forward是服务端的转向,redirect是客户端的转向
ii.使用forwoard浏览器的地址不会发生改变,而redirect会发生改变
iii.forward是一次请求完成,而redirect是重新发起请求
iv.forward是在服务端完成的,而不用客户端重新发起请求,效率高
3.4 jsp和Servlet
jsp是一个特殊的Servlet
:所有的jsp文件都会被翻译成一个继承httpServlet类的Servlet
jsp可以html和java代码共存,主要侧重于视图
Servlet侧重逻辑
3.5 jsp的内置对象(9个):
request:用户请求
reponse:响应
session:
application:
3.6 jsp 的四大作用域:
pageContext:
request:
session:
application: -
JDBC连接数据库的步骤:
加载驱动:三种方式,class.ForName,Person.class,
获取连接:
创建preparedStatement/statement/callableStatement语句:preparedStatement是预编译的,可以防止sql注入,可读性和可维护性高
执行语句:
遍历结果:
关闭连接:由小到大关闭 -
线程
5.1 创建线程的3种方式:
继承Thread类;
实现Runnable接口,通过Runnable接口实例作为Thread的target来创建Thread对象;
实现Callable接口,创建Callable实现类的实例,使用FutureTask类包装Callable对象,使用FutureTask对象作为Thread对象的target创建并启动新线程。
5.2 sleep()、join()、yield()区别方法 释放锁 特点 sleep 不释放锁 让低优先级的线程得到执行的机会 join - 使同等优先级或高优先级线程获得执行机会 yield 不释放锁 Tread的非静态join()方法,让一个线程B加入到另一个线程A的尾部 5.3 CountDownLatch、 CyclicBarrier、Semaphore 5.4 ThreadLocal
实现变量线程间的隔离(空间换时间)且不会降低并发性
使用ThreadLocal可以实现双数据源(gbs和smts库)
5.5线程的生命周期
新建、就绪、运行、阻塞、死亡 -
线程池
①创建线程池的4种方式
创建一个固定线程池
创建一个可缓存的线程池
创建一个单线程的线程池,如果·出现异常会创建一个新的
创建一个固定长度线程池且以延迟或定时方式来执行任务,类似Timer。 -
锁机制
①线程安全问题
②volatile原理
③悲观锁、乐观锁
④
二、java中级层次:
-
写一个二极管,与门或门
-
java的异常体系
问题:什么是运行时异常和检查时异常?
-
java流系列知识:
Java IO流按数据走向分为输出流和输入流:
Java IO流按处理方式分为字符流和字节流:
字节流能处理所有的数据类型,以字节为单位,超类是InputStream,OutputStream;字符流只能处理以字符类型数据,超类是Reader,Writer
OutputStreamWriter 是OutputStream 到Writer 转换的桥梁
PS:优先选择字节流。因为硬盘上的所有文件都是以字节的形式传输或者保存的,而字符只是在内存中才会形成。
二、java高级层次: -
谈谈 Redis 分布式锁:
-
JDK1.8的新特性:
① 线程安全API
② 函数表达式
③ 函数编程API -
多线程
①CountDownLatch:做减法
例子:用户注销好福利app时,展示用户理赔、年金、保单记录时,使用了CountDownLatch获取理赔、年金、保单记录,只有三个都获取到了才返回前端数据
②CyclicBarrier:做加法
例子:人到齐了才开会
③Semaphore:信号灯
主要用于多个共享资源的互斥使用或者用于并发线程数的控制
例子:6车抢3个停车位
④BlockingQueue:阻塞队列
阻塞队列:
a.当队列是空的时候,从队列中取数据时会阻塞
b.当队列是满的时候,往队列放数据时会阻塞
阻塞:在多线程的
BlokingQueue的好处:我们不需要关心什么时候阻塞线程,什么时候唤醒线程
⑤
⑥
15.
16.
17.
18.
19.
20.
- 红黑树
- HashMap源码,ConcurrentHashMap
- synizelized
锁的优化:
无锁、轻量级锁、偏向锁、重量级锁
锁的优化—》在性能和安全性这块做个平衡
-----------------------》性能开销越大
无锁-》轻量级锁-》偏向锁-》重量级锁(挂起,队列):(不是由开发人员决定的)
线程的竞争程度
CAS-》共享标识
自旋的目的?->是取获取锁(霸占CPU),减少线程的切换(从内核态到用户态,代价很大)
-
跳跃表
-
双亲委派:
子类加载器AppClassLoder 接到请求,先交给父加载器ExtClassLoder,父类加载器ExtClassLoder接到请求,先交给根类加载器Bootstrap加载器,能加载,就加载,不能就交给子类ExtClassLoder,如果子类ExtClassLoder也不能加载请求类,就AppClassLoder自己来。如果父类能加载请求类,子类不会再去重复加载,由加载器的单一性保证着的
为什么使用双亲委派模式?
可以避免重复加载,当父类已经加载了该类,就没必要子ClassLoader再加载一次
更加安全,如果不使用这种委托模式,那我们可以随时使用自定义的String来动态替代java核心api中定义类型,会存在很大的隐患,而双亲委派可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的ClassLoader.
java.*开头的类,jvm的实现中已经保证了必须由bootstrp来加载。
定义自己的ClassLoader:
java中提供的默认classLoader,只加载指定目录下的jar和class
,如果想要加载其他位置上的类和jar=,比如:加载网络上的一个class文件,通过动态加载进内存,然后调用这个类中的方法实现业务逻辑。这个时候就需要我们定义自己的类加载器
分两步:继承java.lang.ClassLoader;重写父类的findClass方法 -
负载均衡
互联网发展初期,业务流量不是很大且业务逻辑不是很复杂,单台服务器能满足一般的需求,但是随着互联网的发展,业务流量越来越多,业务逻辑越来越复杂,单台服务器已不能满足需求了,且暴露出单点问题问题,因此用多台服务器使性能水平扩展和能有效的避免单点问题(某一台服务器挂掉)。接来下的重点就是如何将不同用户的流量分散到不同的服务器上。
最开始使用DNS进行负载,通过给客户端解析不同的IP地址,让客户端的流量直接到达各个服务器。但是会出现延迟性问题,在做出调度策略改变以后,因为DNS各级节点的缓存不难过不会及时在客户端生效,而且调度策略比较简单,无法满足需求,因此出现了负载均衡。
客户端的流量首先会先到达负载均衡服务器,然后由负载均衡服务器通过一定的调度算法将流量分散到不同的应用服务器上,同时负载均衡服务器会对应用服务器做周期性健康检查,发现故障节点时会动态的将节点从应用服务器集群中踢出,以保证应用的高可用。
负载均衡分为两种:四层负载均衡和七层负载均衡。四层负载均衡在OSI模型的传输层,主要工作是转发,在接受到客户端的流量以后通过修改数据包的地址信息将流量转发到应用服务器;七层负载均衡在OSI模型的应用层上,需要解析应用层流量,还需要一个完整的Tcp/ip协议栈,它起一个代理作用,会与客户端建立一个独立TCP连接,也会与服务端建立一个独立tcp连接。 -
注册中心
-
熔断器
-
接口幂等性
-
魔术
-
缓存污染:
-
GC
三、数据结构层次:
- 数组:
- List
- Set
四·、简单算法:
-
冒泡排序
-
选择排序
五、java 源码层架:
- HashMap源码
- HashSet源码
六、用过哪些中间件?:
- Redis
- 消息队列(RabbitMQ)
① 实现步骤:异步、解耦、削峰(核心)
② 发送消息都是发送到交换机上的
③交换机类型:
直连交换机(direct exchange):使用唯一关键字绑定
主题交换机(topic exchange):使用通配符(#、*)绑定消息队列
广播交换机(Fanout Exchange):不需要关键字
④ Spring boot 整合RabbitMQ:
配置类:创建交换机、创建队列、创建绑定关系
消费者:监听类(监听队列)
生产者:调用template发送消息(exchange),即RabbitTemplate,所有的消息都会转换成JSON发送
服务器信息配置:约定大于配置
⑤ 消息可靠性投递分析:
a.确保消息成功发送到RabbitMQ服务器
a1.服务端确认-事务模式:发送消息的效率低
将channel设为事务,成功事务提交,失败事务回滚
a2.服务端确认-确定模式
将channel设为confirm模式,if(waitForConfirms){
发送成功
}
普通确认方式(发一条确认一条,效率低)、批量确认方式()、异步确认方式(编码复杂,让程序变得复杂)
b.确保消息达到正确的路由(路由保证)
mandatory = true +
⑥ 确保消息在 队列的正确地存储(消息存储)
队列持久化(声明队列的时候指定的属性)
交换机持久化
消息持久化(内存-磁盘)
⑦确保消息从队列正确的投递到消费者(消费者确认)
默认的自动的应答
channel.basicAck():手工应答
channel.basicReject():单条拒绝
channel.basicNack():批量拒绝
⑧ 其他:
a. 消费者回调:
落库 api 、定时扫描(超过时间,又没得到应答,会去重新投递)
b. 补偿机制:
消息重发
c.消息幂等性:幂等性指的是无论操作多少次结果都一样。
对于每条消息,MQ内部生成一个全局唯一、与业务无关的消息ID:inner-msg-id。当MQ-server接收到消息时,先根据inner-msg-id判断消息是否重复发送,再决定是否将消息落地到DB中。这样,有了这个inner-msg-id作为去重的依据就能保证一条消息只能一次落地到DB。
⑨ 思考:幂等性与并发(表单重复提交和并发增删改及校验唯一操作场景)
先说乐观锁version 表 字段v=1
a.并发更新操作
先select 当前版本号1,同时更新操作update version set v=v+1 where v=1;
第一个执行的将v更新为2,其他并发重复操作因数据库v更新为2,where 2=1更新0条记录,判断为过期无效操作
b.删除操作可以不考虑并发,多删几次,结果都一样
c.并发插入操作(用户重复提交):
c1.首先在新增数据库时,没有旧值进行乐观锁的控制,需要借助redis,来自同一表单数据通过校验tokenid 防止重复提交
c2.新增表单是,在action的add()方法中生成uuid,并保存在redis中
七、用过哪些框架?
八、新技术层次:
- Springboot
- Spring Cloud
2.1 Spring Cloud 的组成:基于分布式系统的,因此有很多组件:Consul、Cluster、Security、Netflix、Bus(消息总线)、Sleuth(日志)、Dataflow、Starters(批量)
2.2 核心思想:引导应用程序上下文。这个上下文是主应用程序的父上下文。负责从外部源加载配置属性,还解密本地外部配置文件中的属性。
2.3 Spring Cloud Eureka是服务治理模块:由Eureka 服务端和Eureka客户端组成。
Eureka 服务端用作服务注册中心。支持集群部署
Eureka客户端是一个java客户端,用来处理服务注册和发现。
2.4 服务消费基本过程:
a.需要一个服务注册中心 Eureka Server
b.
c.