Java 面试题整理
文章目录
- Java 面试题整理
- 多线程相关的面试题
- java集合相关的面试题
- JVM相关面试题
- 数据结构对应的使用场景是怎样的?
- 对数据库事务的理解
- JDBC是怎样实现事务的
- hashmap原理
- 说说springMVC加载过程
- AOP的实现原理
- 怎么样定义一个注解
- Java中如何使用正则表达式
- web中session,cookie,token的关系
- JVM的主要的配置参数有那些
- 防止SQL注入的处理办法
- JS脚本注入攻击
- java从1.5.0至1.8.0中都更新了那些内容
- 什么是Java闭包
- Java的synchronized的同步代码块和同步方法的区别
- mybatis中#和$的区别
- mybatis中如何调用存储过程
- SQL优化相关
- spring是怎么实现事务的
- redis如何保持原子性
- 性能杀手是指那些CPU操作
- 类加载的4中情况
- 初始化对象的时候代码的执行顺序是怎样的
- 线程之间如何达到通信
- springboot、springcloud面试题
- 引用
- 需要代码说明会在对应的问题后给出blog链接
- 如果发现错误希望能再下面指出,方便更正
- 这里的题有时间会整理一下,暂时没有排序,都是自己在工作或者面试中遇到的
多线程相关的面试题
java集合相关的面试题
JVM相关面试题
数据结构对应的使用场景是怎样的?
如果不知道就说一下他们各自的优缺点
常用的数据结构有:单向链表,双向链表,数组,二叉树,哈希表
数据结构名称 | 优点 | 缺点 |
---|---|---|
数组 | 地址连续,查找速度快 | 大小固定,增加或者删除都需要重新分配内存 |
栈 | 提供先进后出,后进先出的存取方式 | 存取其他项目较慢 |
队列 | 提供先进先出的存取方式 | 存取其他项目较慢 |
链表 | 增删操作快 | 查找速度慢只能一个一个查找 |
二叉树 | 除删除外其他操作快 | 删除速度慢,切算法复杂 |
哈希表 | 需要有较好的hash算法,在已知关键字的前提下,存取速度快 | 对存储空间使用不充分,如果不知道关键字,则存取速度很低 |
对数据库事务的理解
事务是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行,好处就是可以简化错误恢复并使应用程序更加可靠。
要使逻辑单元称为一个事务必须满足ACID原则(原子性、一致性、隔离性和持久性,及事务的四大特性)。
- 原子性:是事务本身的特性,即要么全执行,要么全不执行
- 一致性:是事务控制的数据的特性,要么都处于执行前的状态,要么多是执行后的状态
- 隔离性:是事务和事务之间的特性,即事务之间不能有影响
- 持久性:事务的操作必须是永久的
JDBC是怎样实现事务的
- 在jdbc中关闭自动的事务提交,改为手动提交
connection.setAutoCommit(false);
- 在可能抛异常的地方增加回滚操作
connection.rollback();
注: 想要使用事务必须使用同一个connect操作数据库
hashmap原理
这个问题需要回答的点比较多
- 什么是hash算法
- hashmap的数据结构是什么样的
- hashmap是如何扩容的
- hashmap中put方法是如何实现的
- 附加:使用hashmap需要注意些什么
什么是hash算法
hash又称为散列算法,就是将不定长度的key通过一定的算法,散列到数据下标的过程,hashcode也是java中object类的方法,可以重写。
hashmap中的一些重要常亮
initialCapacity:初始容量。指的是 HashMap 集合初始化的时候自身的容量。可以在构造方法中指定;如果不指定的话,总容量默认值是 16 。需要注意的是初始容量必须是 2 的幂次方。
size:当前 HashMap 中已经存储着的键值对数量,即 HashMap.size()
loadFactor:加载因子。所谓的加载因子就是 HashMap (当前的容量/总容量) 到达一定值的时候,HashMap 会实施扩容。加载因子也可以通过构造方法中指定,默认的值是 0.75 。举个例子,假设有一个 HashMap 的初始容量为 16 ,那么扩容的阀值就是 0.75 * 16 = 12 。也就是说,在你打算存入第 13 个值的时候,HashMap 会先执行扩容。
threshold:扩容阀值。即 扩容阀值 = HashMap 总容量 * 加载因子。当前 HashMap 的容量大于或等于扩容阀值的时候就会去执行扩容。扩容的容量为当前 HashMap 总容量的两倍。比如,当前 HashMap 的总容量为 16 ,那么扩容之后为 32 。
table:Entry 数组。我们都知道 HashMap 内部存储 key/value 是通过 Entry 这个介质来实现的。而 table 就是 Entry 数组。
hashmap的存储结构
hashmap的实现主要依赖两种数据接口,数组和链表。数组是hashmap的主体,而链表是为了解决hash冲突的问题
数据的保存过程:将元素的关键字进行hash运算,保存到数组对应的位置,如果发生hash冲突(数据对应下标已经有数据),则通过链表的方式将数据保存到链表的下一个节点
添加put操作的执行过程
- 首先table不是在new的时候创建的,而是在第一次put的时候创建的,所以每次put的时候先判断是否table已经初始化,如果没有的话,先初始化table。
- 对元素的key进行hash运算,得到hash值,判断该hash对应的位置时候有值,如果有值,则比对key,如果已经存在则替换,返回原有的value。
- 如果不存在,则需要添加新的键值对,新增数据的第一步需要先判断是否需要扩容,第二步才是保存数据。
关于hashmap的扩容
关于hashmap的大小为什么要是2的n次幂
说说springMVC加载过程
springMVC初始化只要有三个类,DispatcherServlet,FrameWorkServlet,HttpServletBean
他们的继承关系是DispatcherServlet继承了FrameWorkServlet,FrameWorkServlet又继承了HttpServletBean
启动的过程是:
- 服务启动,会扫描web.xml文件,在web.xml配置的DispatcherSerlvet开始初始化。
- DispatcherServlet会调用父类的init()方法,init()方法的作用是初始化一些DispatcherServlet启动会用到的参数,然后调用FrameWorkServlet的initServletBean()方法。
- initServletBean()方法初始化SpringMVC容器,因为SpringMVC是在Spirng基础之上的,所以还需要和Spring的容器关联,是MVC的容器能够访问Spring中的Bean,之后调用DispatcherServlet中的onRefresh()方法。
- onRefresh()会对SpringMVC的组件进行初始化,比如url映射,文件解析的初始化等等
简化版
springmvc的启动有三个阶段
- 配置加载阶段
- 初始化阶段
- 运行阶段
在配置加载阶段,首先需要配置web.xml,web.xml是容器的入口,在web.xml中需要
- 配置DispatchServlet,指定初始化的入口方法和请求处理的方法
- 配置applicationContext.xml,指定spring的相关配置,比如数据库配置等等
- 配置url-pattern,指定需要拦截的路径
在初始化阶段
- 在init方法中,首先需要加载applicationContext.xml参数,初始化spirng的配置
- 根据注解配置扫描相关的类
- 初始化IOC容器,放入实例化的bean,实现依赖注入
- 初始化HandlerMapping,等待请求到来,就进入运行阶段
在运行阶段
- 进入DispatchServlet的doService方法,doService方法有两个参数request和response
- 根据request参数获取到请求的路径,用获取到的路径在handlerMapping中对应的method
- 调用这个method,执行该方法,使用response放回结果
AOP的实现原理
AOP是OOP的一个补充,通常面向对象编程可以看成是方法调用时间线的纵向展开,把方法的调用看做是连接点,程序的执行就是链接点的执行过程,AOP将每个连接点看做是编程的入口,针对每个方法的调用进行编程,相当于在原来的时间线上横向切入。链接点,切面,切入点是AOP的核心定义,切入点就是已经选择的作用入口的连接点。在spring AOP中,使用方法匹配表达式来(
execution(**)
)表示切入点(Point Cut)
怎么样定义一个注解
需要回答注解的作用、注解的实现原理、如何自定义一个注解
注解的作用:
- 生成文档。常用的有@param @return 等。
- 跟踪代码依赖性,简化配置文件中的内容。比如:spring的一些常用注解。
- 在编译时进行格式检查。如@override。
注解的实现原理:
- 注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。
自定义注解的编写规则:
- Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口。
- 参数成员只能用public或默认(default)这两个访问权修饰。
- 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组。
- 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation对象,因为你除此之外没有别的获取注解对象的方法。
- 注解也可以没有定义成员, 不过这样注解就没啥用了,使用
String value() default "";
来定义一个属性 - 定义注解是需要使用到元注解,即
- @Documented –注解是否将包含在JavaDoc中
- @Retention –什么时候使用该注解
- @Target –注解用于什么地方
- @Inherited – 是否允许子类继承该注解
自定义注解主要有三步
- 使用@interface定义一个注解,需要使用元注解
- 定义注解的解析器,需要用到java的放射
- 最后使用这个注解
Java中如何使用正则表达式
web中session,cookie,token的关系
JVM的主要的配置参数有那些
防止SQL注入的处理办法
SQL注入攻击就是在向服务端传递数据中拼写sql,让数据库执行了原本不属于程序的sql,防止注入攻击的方法如下:
- 使用PreparedStatement来想sql中传入参数,因为PreparedStatement是预编译的,传入的参数会被当做一个整体去处理
- 使用正则表达式对参数进行预处理,将特殊字符格式化
- 在jsp中对输入的参数进行处理,比如禁止输入特殊字符等
JS脚本注入攻击
JavaScript注入漏洞能发生作用主要依赖两个关键的动作,一个是用户要能从界面中注入JavaScript到系统的内存或者后台存储系统中;二是系统中存在一些UI会展示用户注入的数据。防范注入漏洞主要有两个思路:一个是在用户输入数据后Encode内容后再保存到持久存储,另一个是在展示用户输入数据的地方Encode从持久存储中取到的数据。
java从1.5.0至1.8.0中都更新了那些内容
具体内容建议参考博客 JDK1.5-JDK1.8各个新特性
这里主要列举常用的,或者主要的特性
1.5的特性:
- 自动装箱与拆箱,指基本类型与包装类之间的转换
- 枚举:枚举类型使代码更具可读性,理解清晰,易于维护
- foreach循环
1.6的特性: - Desktop类和SystemTray类:Desktop可以用来使用桌面应用打开一些文件,比如浏览器打开网页,打开pdf文件等等;SystemTray可以用来在系统托盘区创建一个托盘程序。(托盘程序就是在桌面右下角的小图标程序)
1.7的特性: - switch中可以使用字串
- 给集合定义泛型
- 新增了一些获取环境信息的方法,
System.getJavaHomeDir() // 获取jre的安装目录
- 提供一套安全的运算方法
1.8的特性: - 数值之间可以添加下划线
int million = 1_000_000; // 不会报错
- Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可。
- Lambda 表达式,简化匿名函数的定义
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
现在可以写作
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
- 待完成。。。
什么是Java闭包
Java的synchronized的同步代码块和同步方法的区别
- 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
- 然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
- 尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
mybatis中#和$的区别
- #是将传入的值当做字符串的形式,eg:
sq select id,name,age from student where id =#{id}
,当前端把id值1,传入到后台的时候,就相当于 select id,name,age from student where id =‘1’。- $是将传入的数据直接显示生成sql语句,eg:
sql select id,name,age from student where id =${id}
,当前端把id值1,传入到后台的时候,就相当于 select id,name,age from student where id = 1。- 使用#可以很大程度上防止sql注入。(语句的拼接)
- 但是如果使用在order by 中就需要使用 $。
- 在大多数情况下还是经常使用#,但在不同情况下必须使用$.
总结: #{} 传入值时,sql解析时,参数是带引号的,而${}传入值,sql解析时,参数是不带引号的。
mybatis中如何调用存储过程
- 定义储存过程
- 在mybaits的xml文件中使用call来调用存错过程
- 需要在的调用存储过程的sql标签上添加statementType参数,类型为“callable”
- 如果需要传入参数的话,需要在调用是使用
#{}
的方式引入参数,多参数或者少参数都会报错需要注意
SQL优化相关
spring是怎么实现事务的
redis如何保持原子性
因为redis是单线程的,于此同时为什么设计成单线程就变成了新的问题,redis是基于内存操作的,CPU不是redis的瓶颈,影响redis的主要可能是内存大小和带宽。
性能杀手是指那些CPU操作
- 多线程的线程切换开销
- 请求和释放线程锁
- 非必要的内存拷贝
类加载的4中情况
- 调用类的构造器
- Class.forName()加载类
- 调用类的静态属性
- 调用类的静态方法
初始化对象的时候代码的执行顺序是怎样的
1.静态代码块
static {}
(*注:只执行一次)
2.构造代码块{}
3.构造方法public Foo() {}
线程之间如何达到通信
使用wait和notify,定义公共共享对象,以共享对象为锁来达到通信效果