文章目录
- 1、Iterator 怎么使用
- 2、Iterator 和 ListIterator 有什么区别
- 3、怎么确保一个集合不能被修改
- 4、 队列和栈是什么
- 5、什么是 Java 的内存模型
- 6、Volatile 关键字的作用
- 7、Volatile 与 Synchronized 比较
- 8、ThreadLocal 介绍
- 9、ThreadLocal 与 Synchronized 的区别
- 10、哪些集合类是线程安全的
- 11、Java 中的线程池是如何实现的
- 12、get 和 post 请求有哪些区别
- 13、在 Queue 中 poll()和 remove()有什么区别
- 14、如何让 Java 的线程彼此同步
- 15、synchronized 与 Lock 的对比
- 16、如果客户端禁止 cookie 能实现 session 还能用吗
- 17、在 Java 中,为什么不允许从静态方法中访问非静态变量
- 18、Java Bean的命名规范
- 19、如何实现数组和 List 之间的转换
- 20、什么是锁消除和锁粗化
- 21、session 和 cookie 有什么区别
- 22、tcp 为什么要三次握手
- 23、mybatis 有哪些执行器(Executor)
- 24、char 和 varchar 的区别是什么
- 25、float 和 double 的区别是什么
- 26、什么是缓存穿透
- 27、说一下 jvm 有哪些垃圾回收器
- 28、如何将字符串反转
- 29、ArrayList 和 LinkedList 的区别是什么
- 30、简述 tcp 和 udp的区别
- 31、说一下 ACID 是什么
- 32、Redis为什么是单线程的
- 33、jvm 有哪些垃圾回收算法
- 34、java 中操作字符串都有哪些类
- 35、HashMap 的实现原理
- 36、set有哪些实现类
- 37、说一下 HashSet 的实现原理
- 38、为什么说 Synchronized 是一个悲观锁,乐观锁的实现原理又是什么
- 39、forward 和 redirect 的区别
- 40、Java 中什么叫单例设计模式
- 41、说一下 mybatis 的一级缓存和二级缓存
- 42、怎么判断对象是否可以被回收
- 43、java 中都有哪些引用类型
- 44、HashMap 和 Hashtable 有什么区别
- 45、JVM 对 Java 的原生锁做了哪些优化
- 46、jsp 有哪些内置对象
- 47、什么是控制反转(IOC),什么是依赖注入(DI)
- 48、什么是 ORM 框架
- 49、自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,插入了一条数据,此时 id 是几
- 50、Redis有哪些功能
- 51、说一下类加载的执行过程
- 52、什么是双亲委派模型
- 53、hashcode是什么,有什么作用
- 54、Java 容器都有哪些
- 55、Collection 和 Collections 有什么区别
- 56、list与Set区别
- 57、Synchronized 用过吗,其原理是什么
- 58、使用 Spring 框架能带来哪些好处
- 59、Spring Boot 有哪些优点
- 60、mybatis 中 #{ }和 ${ }的区别是什么
- 61、数据库的三范式是什么
- 62、说一下 jvm 运行时数据区
- 63、你是怎样理解面向对象的
- 64、int 和 Integer 有什么区别
- 65、== 和 equals 的区别是什么
- 66、谈谈你对反射的理解
- 67、ArrayList 和 LinkedList 区别
- 68、TreeSet 和 HashSet 的区别
- 69、StringBuffer 和 StringBuilder 的区别
- 70、Final、Finally、Finalize区别
- 71、什么是Java序列化,如何实现Java序列化
- 72、Object 中有哪些方法
- 73、线程由几种状态,产生的条件是什么
- 74、什么是死锁,怎么避免死锁
- 75、ArrayList 和 Vector 的区别是什么
- 76、Array 和 ArrayList 有何区别
- 77、JUC 中常用的并发工具类
- 78、说一下 session 的工作原理
- 79、说一下 tcp 粘包是怎么产生的
- 80、什么是 Spring Profiles
- 81、mysql 的内连接、左连接、右连接有什么区别
- 82、Redis 支持的数据类型有哪些
- 83、新生代垃圾回收器和老生代垃圾回收器都有哪些,有什么区别
- 84、简述分代垃圾回收器是怎么工作的
- 85、byte类型127+1等于多少
- 86、数据库一般会采取什么样的优化方法
- 87、java 的事务传播
- 88、说一下事务的隔离级别
- 89、SpringMVC和SpringBoot的区别
- 90、Java 创建线程的三种方式有啥区别
- 91、mybatis 是如何防止sql注入的
- 92、concurrentHashMap和HashTable有什么区别
- 93、hashmap存储的数据结构
- 94、HasmMap 和 HashSet 的区别
- 95、CAS
- 96、Java常见的线程池
- 97、RPC是什么
- 98、实例化对象有哪几种方式
- 99、单点登录
- 100、Spring 是什么
1、Iterator 怎么使用
/**
* 测试Collection迭代对象的方式 迭代器的方式 Iterator接口,定义了迭代Collection 容器中对象的统一操作方式 集合对象中的迭代器是采用内部类的方式实现 这些内部类都实现了Iterator接口
* 使用迭代器迭代集合中数据期间,不能使用集合对象 删除集合中的数据
*/
@Test
public void test02() {
Collection<String> c1 = new HashSet<String>();
c1.add("java");
c1.add("css");
c1.add("html");
c1.add("javaScript");
Iterator<String> it = c1.iterator();
while (it.hasNext()) {
String str = it.next();
System.out.println(str);// css java javaScript html
if (str.equals("css")) {
// c1.remove(str);//会抛出异常
it.remove();
}
}
System.out.println(c1);// [java, javaScript, html]
}
2、Iterator 和 ListIterator 有什么区别
- ListIterator 继承 Iterator
- 使用范围不同,Iterator可以迭代所有集合,ListIterator 只能用于List及其子类
- ListIterator 比 Iterator 多方法
3、怎么确保一个集合不能被修改
final关键字可以修饰类,方法,成员变量,final修饰的类不能被继承,final修饰的方法不能被重写,final修饰的成员变量必须初始化值,如果这个成员变量是基本数据类型,表示这个变量的值是不可改变的,如果说这个成员变量是引用类型,则表示这个引用的地址值是不能改变的,但是这个引用所指向的对象里面的内容还是可以改变的。
集合(map,set,list…)都是引用类型,所以我们如果用final修饰的话,集合里面的内容还是可以修改的。我们可以采用Collections包下来让集合不能修改:
- Collections.unmodifiableList(List)
- Collections.unmodifiableSet(Set)
- Collections.unmodifiableSet(map)
4、 队列和栈是什么
- 队列特殊的线性表,队列中限制了对线性表的访问只能从线性表的一端添加元素,从另一端取出,遵循先进先出(FIFO)原则。
- 栈是队的子接口,栈继承队,栈定义类"双端列"从队列的两端可以入队(offer)和出队(poll),LinkedList实现了该接口,如果限制Deque双端入队和出队,将双端队列改为单端队列即为栈,栈遵循先进后出(FILO)的原则。
5、什么是 Java 的内存模型
Java 内存模型是 JVM 的一种规范
- 定义了共享内存在多线程程序中读写操作行为的规范
- 屏蔽了各种硬件和操作系统的访问差异,保证了 Java 程序在各种平台下对内存的访问效果一致
- 解决并发问题采用的方式:限制处理器优化和使用内存屏障
- 增强了三个同步原语(synchronized、volatile、final)的内存语义
- 定义了 happens-before 规则
6、Volatile 关键字的作用
- 线程可见性
public class VolatileTest {
boolean flag = true;
public void updateFlag() {
this.flag = false;
System.out.println("修改flag值为:" + this.flag);
}
public static void main(String[] args) {
VolatileTest test = new VolatileTest();
new Thread(() -> {
while (test.flag) {
}
System.out.println(Thread.currentThread().getName() + "结束");
}, "Thread1").start();
new Thread(() -> {
try {
Thread.sleep(2000);
test.updateFlag();
} catch (InterruptedException e) {
}
}, "Thread2").start();
}
}
打印结果如下,我们可以看到虽然线程Thread2已经把flag 修改为false了,但是线程Thread1没有读取到flag修改后的值,线程一直在运行
修改flag值为:false
我们把flag 变量加上volatile: volatile boolean flag = true;
重新运行程序,打印结果如下。Thread1结束,说明Thread1读取到了flage修改后的值
修改flag值为:false
Thread1结束
说到可见性,我们需要先了解一下Java内存模型,Java内存模型如下所示:
线程之间的共享变量存储在主内存中(Main Memory)中,每个线程都一个都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本。所以当一个线程把主内存中的共享变量读取到自己的本地内存中,然后做了更新。在还没有把共享变量刷新的主内存的时候,另外一个线程是看不到的。
7、Volatile 与 Synchronized 比较
- Volatile是轻量级的synchronized,因为它不会引起上下文的切换和调度,所以Volatile性能更好。
- Volatile只能修饰变量,synchronized可以修饰方法,静态方法,代码块。
- Volatile对任意单个变量的读/写具有原子性,但是类似于i++这种复合操作不具有原子性。而锁的互斥执行的特性可以确保对整个临界区代码执行具有原子性。
- 多线程访问volatile不会发生阻塞,而synchronized会发生阻塞。
- volatile是变量在多线程之间的可见性,synchronize是多线程之间访问资源的同步性。
8、ThreadLocal 介绍
ThreadLocal的set()方法先获取当前线程,取线程中的属性 threadLocalMap ,如果 threadLocalMap 不为空,则直接更新要保存的变量值,否则创建 threadLocalMap并赋值,也就是不同的线程间同一个 ThreadLocal 对应 ThreadLocalMap 是不一样的,ThreadLocalMap 上来储存值,从而到达了线程间变量隔离的目的,但是在同一个线程中这个value变量地址是一样的。
ThreadLocal的get方法获取值
ThreadLocal的remove方法移除
9、ThreadLocal 与 Synchronized 的区别
- Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
- Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
10、哪些集合类是线程安全的
- Vector:就比Arraylist多了个同步化机制(线程安全)
- Stack:栈,也是线程安全的,继承于Vector
- Hashtable:就比Hashmap多了个线程安全
- ConcurrentHashMap:是一种高效但是线程安全的集合
11、Java 中的线程池是如何实现的
12、get 和 post 请求有哪些区别
- get请求参数是连接在url后面的,而post请求参数是存放在requestbody内的
- get请求因为浏览器对url长度有限制,所以参数个数有限制,而post请求参数个数没有限制
- 因为get请求参数暴露在url上,所以安全方面post比get更加安全
- 在浏览器进行回退操作时,get请求是无害的,而post请求则会重新请求一次
- 浏览器在发送get请求时会将header和data一起发送给服务器,服务器返回200状态码,而在发送post请求时,会先将header发送给服务器,服务器返回100,之后再将data发送给服务器,服务器返回200
13、在 Queue 中 poll()和 remove()有什么区别
-
offer()和add()区别:添加队尾
offer()和add()都是增加新项,如果队列满了,add会抛出异常,offer返回false。 -
poll()和remove()区别:移除队首
poll()和remove()都是从队列中删除第一个元素,为空时remove抛出异常,poll返回null。 -
peek()和element()区别:获取队首
peek()和element()用于查询队列头部元素,为空时element抛出异常,peek返回null。
14、如何让 Java 的线程彼此同步
当使用多个线程来访问同一个数据时,将会导致数据不准确,相互之间产生冲突,非常容易出现线程安全问题,如下图所示:
比如多个线程都在操作同一数据,都打算修改商品库存,这样就会导致数据不一致的问题。
线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。
所以我们用同步机制来解决这些问题,加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。
15、synchronized 与 Lock 的对比
- ReentrantLock是显示锁,手动开启和关闭锁,别忘记关闭
- synchronized 是隐式锁,出了作用域自动释放
- ReentrantLock只有代码块锁,synchronized 有代码块锁和方法锁
16、如果客户端禁止 cookie 能实现 session 还能用吗
一般默认情况下,在会话中,服务器存储 session 的 sessionid 是通过 cookie 存到浏览器里。如果浏览器禁用了 cookie,浏览器请求服务器无法携带 sessionid,服务器无法识别请求中的用户身份,session失效。不支持 cookie 就只能用 url 参数或 header 参数传 sessionid 了
17、在 Java 中,为什么不允许从静态方法中访问非静态变量
静态变量属于类本身,在类加载的时候就会分配内存,可以通过类名直接访问,非静态变量属于类的对象,只有在类的对象产生时,才会分配内存,通过类的实例去访问,静态方法也属于类本身,但是此时没有类的实例,内存中没有非静态变量,所以无法调用。
18、Java Bean的命名规范
- JavaBean 类必须是一个公共类,并将其访问属性设置为 public
- JavaBean 类必须有一个空的构造函数:类中必须有一个不带参数的公用构造器,此构造器也应该通过调用各个特性的设置方法来设置特性的缺省值。
- 一个javaBean类不应有公共实例变量,类变量都为private
- 持有值应该通过一组存取方法(getXxx 和 setXxx)来访问:对于每个特性,应该有一个带匹配公用 getter 和 setter 方法的专用实例变量。
- 属性为布尔类型,可以使用 isXxx() 方法代替 getXxx() 方法。
- 通常属性名是要和 包名、类名、方法名、字段名、常量名作出区别的,必须用英文,不要用汉语拼音,驼峰式命名规则。
19、如何实现数组和 List 之间的转换
String[] arr = {"zs","ls","ww"};
List<String> list = Arrays.asList(arr);
System.out.println(list);
ArrayList<String> list1 = new ArrayList<String>();
list1.add("张三");
list1.add("李四");
list1.add("王五");
String[] arr1 = list1.toArray(new String[list1.size()]);
System.out.println(arr1);
for(int i = 0; i < arr1.length; i++){
System.out.println(arr1[i]);
}
20、什么是锁消除和锁粗化
- 锁消除
所消除就是虚拟机根据一个对象是否真正存在同步情况,若不存在同步情况,则对该对象的访问无需经过加锁解锁的操作。
比如StringBuffer的append方法,因为append方法需要判断对象是否被占用,而如果代码不存在锁竞争,那么这部分的性能消耗是无意义的。于是虚拟机在即时编译的时候就会将上面的代码进行优化,也就是锁消除。
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
- 锁粗化
锁的请求、同步、释放都会消耗一定的系统资源,如果高频的锁请求反而不利于系统性能的优化,锁粗化就是把多次的锁请求合并成一个请求,扩大锁的范围,降低锁请求、同步、释放带来的性能损耗。
21、session 和 cookie 有什么区别
- 存储位置不同:cookie在客户端浏览器,session在服务器
- 存储容量不同:cookie<=4K,一个站点最多保留20个cookie,session没有上限,出于对服务器的保护session内不可存过多东西,并且要设置session删除机制
- 隐私策略不同:cookie对客户端是可见的,不安全,session存储在服务器上,安全
22、tcp 为什么要三次握手
因为客户端和服务端都要确认连接,①客户端请求连接服务端;②针对客户端的请求确认应答,并请求建立连接;③针对服务端的请求确认应答,建立连接。
两次无法确保A能收到B的数据。
TCP 三次握手流程如下:
- 客户端发送 SYN 给服务器端,表示希望建立连接
- 服务器端接收到消息之后,回应一个 SYN 和 ACK(确认应答)给客户端
- 客户端收到服务器端的 SYN 报文之后,回应一个 ACK 报文
23、mybatis 有哪些执行器(Executor)
位于继承体系最顶层的是 Executor 执行器,它有两个实现类,分别是BaseExecutor和 CachingExecutor。
BaseExecutor是一个抽象类,这种通过抽象的实现接口的方式是适配器设计模式之接口适配的体现,是 Executor 的默认实现,实现了大部分 Executor 接口定义的功能,降低了接口实现的难度。
BaseExecutor 的子类有三个,分别是SimpleExecutor、ReuseExecutor和BatchExecutor。
-
SimpleExecutor:简单执行器。是MyBatis的默认执行器,每执行一次update或select,就开启一个Statement对象,用完就直接关闭Statement对象(可以是Statement或者PreparedStatment 对象)
-
ReuseExecutor:可重用执行器,这里的重用是指重复使用Statement。它会在内部使用一个Map把创建的Statement都缓存起来,每次执行SQL命令时候都会去判断是否存在基于该SQL的Statement对象,如果存在Statement对象并且对应的Connection还没有关闭情况下就继续使用之前的Statement对象,并将其缓存起来。因为每次Sqlsession都有一个新的Executor对象,所以缓存在ReuseExecutor的Statement对象作用域是同一个Sqlsession.
-
BatchExecutor: 批处理执行器,用于将多个SQL一次性输出到数据库
-
CachingExecutor: 缓存执行器,先从缓存中查询结果,如果存在,就返回;如果不存在,再委托给 Executor delegate 去数据库中取,delegate 可以是上面任何一个执行器。
24、char 和 varchar 的区别是什么
- char的长度是固定的,varchar的长度的可变的
- char的效率比varchar的效率高
- char占用空间比varchar大,char在查询时需要使用trim
25、float 和 double 的区别是什么
- 内存中占有的字节数不同:单精度浮点数在内存中占有4个字节,双精度浮点数在内存中占有8个字节
- 数值取值范围不同
- 在程序中处理速度不同:一般来说,CPU处理单精度浮点数的速度比双精度浮点数的速度快,如果不声明,默认小数是double类型,如果想用float,要进行强转
26、什么是缓存穿透
一般的缓存系统,都是按照key去缓存查询,如果不存在对用的value,就应该去后端系统查找(比如DB数据库)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
27、说一下 jvm 有哪些垃圾回收器
垃圾收集器 | 分类 | 作用位置 | 使用算法 | 特点 | 适用场景 |
---|---|---|---|---|---|
Serial | 串行 | 新生代 | 复制算法 | 响应速度优先 | 适用于单CPU环境下的client模式 |
ParNew | 并行 | 新生代 | 复制算法 | 响应速度优先 | 多CPU环境Server模式下与CMS配合使用 |
Parallel | 并行 | 新生代 | 复制算法 | 吞吐量优先 | 适用于后台运算而不需要太多交互的场景 |
Serial Old | 串行 | 老年代 | 标记-整理(压缩)算法 | 响应速度优先 | 适用于单CPU环境下的Client模式 |
Paraller Old | 并行 | 老年代 | 标记-整理(压缩)算法 | 吞吐量优先 | 适用于后台运算而不需要太多交互的场景 |
CMS | 并发 | 老年代 | 标记-清除算法 | 响应速度优先 | 适用于互联网或B/S业务 |
G1 | 并发、并行 | 新生代、老年代 | 标记-整理(压缩)算法 | 响应速度优先 | 响应速度优先 |
28、如何将字符串反转
将字符串加入到可变字符串容器,用StringBuilder reverse()
29、ArrayList 和 LinkedList 的区别是什么
- ArrayList是动态数组的数据结构实现,查找和遍历的效率较高
- LinkedList 是双向链表的数据结构,增加和删除的效率较高
30、简述 tcp 和 udp的区别
- TCP是传输控制协议,UDP是用户数据表协议
- TCP长连接,UDP无连接
- UDP程序结构较简单,只需发送,无须接收
- TCP可靠,保证数据正确性、顺序性;UDP不可靠,可能丢数据
- TCP适用于少量数据,UDP适用于大量数据传输
- TCP速度慢,UDP速度快
31、说一下 ACID 是什么
ACID是数据库事务执行的四大基本要素,包括原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
32、Redis为什么是单线程的
因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。
33、jvm 有哪些垃圾回收算法
① 标记 - 清除算法(Tracing Collector)
② 标记 - 整理算法(Compacting Collector)4
③ 复制算法(Copying Collector)
④ 分代收集算法
34、java 中操作字符串都有哪些类
- String:String是不可变对象,每次对String类型的改变时都会生成一个新的对象。
- StringBuilder:线程不安全,效率高,多用于单线程。
- StringBuffer:线程安全,由于加锁的原因,效率不如StringBuilder,多用于多线程。
35、HashMap 的实现原理
HashMap的存储结构:
JDK1.7中采用数组+链表的存储形式。
JDK1.8中采用数据+链表+红黑树的存储形式。当链表长度超过阈值(8)时,将链表转换为红黑树。在性能上进一步得到提升。
HashMap基于map接口,元素以键值对方式存储,允许有null值,HashMap是线程不安全的。我们使用 put(key, value)存储对象到 HashMap 中,使用 get(key)从 HashMap 中获取对象
以下是具体的 put 过程(JDK1.8 版):
- 对 Key 求 Hash 值,然后再计算下标
- 如果没有碰撞,直接放入桶中(碰撞的意思是计算得到的 Hash 值相同,需要放到同一个 bucket 中)
- 如果碰撞了,以链表的方式链接到后面
- 如果链表长度超过阀值( TREEIFY THRESHOLD==8),就把链表转成红黑树,链表长度低于 6,就把红黑树转回链表
- 如果节点已经存在就替换旧值
- 如果桶满了(容量 16*加载因子 0.75),就需要 resize(扩容 2 倍后重排)
- initialCapacity:初始容量。指的是 HashMap 集合初始化的时候自身的容量。可以在构造方法中指定;如果不指定的话,总容量默认值是 16 。需要注意的是初始容量必须是 2 的幂次方。
- size:当前 HashMap 中已经存储着的键值对数量,即 HashMap.size()
- loadFactor:加载因子。所谓的加载因子就是 HashMap (当前的容量/总容量) 到达一定值的时候,HashMap 会实施扩容。加载因子也可以通过构造方法中指定,默认的值是 0.75 。
- threshold:扩容阀值。即 扩容阀值 = HashMap 总容量 * 加载因子。当前 HashMap 的容量
大于或等于扩容阀值的时候就会去执行扩容。扩容的容量为当前 HashMap 总容量的两倍。
举个例子,假设有一个 HashMap 的初始容量为 16 ,那么扩容的阀值就是 0.75 * 16 = 12 。
也就是说,在你打算存入第 13 个值的时候,HashMap 会先执行扩容,那么扩容之后为 32 。
36、set有哪些实现类
- HashSet
- HashSet是set接口的实现类,set下面最主要的实现类就是HashSet(也就是用的最多的),此外还有
LinkedHashSet和TreeSet。 - HashSet是无序的、不可重复的。通过对象的hashCode和equals方法保证对象的唯一性。
- HashSet内部的存储结构是哈希表,是线程不安全的。
- TreeSet
TreeSet对元素进行排序的方式:
- 元素自身具备比较功能,需要实现Comparable接口,并覆盖compareTo方法。
- 元素自身不具备比较功能,需要实现Comparator接口,并覆盖compare方法。
- LinkedHashSet
- LinkedHashSet是一种有序的Set集合,即其元素的存入和输出的顺序是相同的。
37、说一下 HashSet 的实现原理
HashSet实际上是一个HashMap实例,数据存储结构都是数组+链表。
HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value都是一个统一的对象PRESENT。
private static final Object PRESENT = new Object();
HashSet中add方法调用的是底层HashMap中的put方法,put方法要判断插入值是否存在,而HashSet的add方法,首先判断元素是否存在,如果存在则插入,如果不存在则不插入,这样就保证了HashSet中不存在重复值。
通过对象的hashCode和equals方法保证对象的唯一性。
38、为什么说 Synchronized 是一个悲观锁,乐观锁的实现原理又是什么
Synchronized显然是一个悲观锁 , 因为它的并发策略是悲观的 :不 管是否会产生竞争 , 任何的数据操作都必 须要加锁
乐观锁的核心是CAS( Compareand Swap, 比 较 并 交 换 ),CAS包括内存值、预期值、新值,只有当内存值等于预期值时,才会将内存值修改为新值
39、forward 和 redirect 的区别
- forward是直接请求转发,redirect是间接请求转发,又叫重定向。
- forward,客户端和浏览器执行一次请求,redirect,客户端和浏览器执行两次请求。
- forward,地址不变,redirect,地址改变。
40、Java 中什么叫单例设计模式
保证程序只有一个对象的实例,叫做单例模,内部类的方式实现单例模式,是线程安全的
41、说一下 mybatis 的一级缓存和二级缓存
一级缓存是session级别的缓存,默认开启,当查询一次数据库时,对查询结果进行缓存,如果之后的查询在一级缓存中存在,则无需再访问数据库;
二级缓存是sessionFactory级别的缓存,需要配置才会开启。当进行sql语句查询时,先查看一级缓存,如果不存在,访问二级缓存,降低数据库访问压力。
42、怎么判断对象是否可以被回收
- 引用计数算法
- 可达性分析算法
43、java 中都有哪些引用类型
1、强引用
Java中默认声明的就是强引用,比如:
Object obj = new Object();
obj = null;
只要强引用存在,垃圾回收器将永远不会回收被引用的对象。如果想被回收,可以将对象置为null
2、软引用
在内存足够的时候,软引用不会被回收,只有在内存不足时,系统才会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会跑出内存溢出异常。
byte[] buff = new byte[1024 * 1024];
SoftReference<byte[]> sr = new SoftReference<>(buff);
3、弱引用
进行垃圾回收时,弱引用就会被回收。
4、虚引用(PhantomReference)
5、引用队列(ReferenceQueue)
引用队列可以与软引用、弱引用、虚引用一起配合使用。当垃圾回收器准备回收一个对象时,如果发现它还有引用,就会在回收对象之前,把这个引用加入到引用队列中。程序可以通过判断引用队列中是否加入了引用,来判断被引用的对象是否将要被垃圾回收,这样可以在对象被回收之前采取一些必要的措施。
44、HashMap 和 Hashtable 有什么区别
- 首先是它们的继承父类不同,HashMap 继承的是 AbstractMap,Hashtable 继承的是 Dictionary
- HashMap 是线程是线程不安全的,Hashtable 是线程安全的,所以 HashMap 比 Hashtable 效率高
- HashMap 中,null 可以作为键和值,但是 Hashtable 的键和值是 null,编译可以通过,但是会抛出 NullPointerException 异常
- HashMap 没有 contains 这个方法,它有的是 containsKey 和 containsValue 方法,Hashtable 有 contains 这个方法与 containsKey 功能相同
- HashMap 的默认的容量是 16,Hashtable 的默认容量是 11,它们的扩容方式也不同,HashMap 扩容二倍,Hashtable 扩容二倍加一
- 它们都使用了 iterator 遍历元素,但是 Hashtable 还使用了 Enumeration 方式 hasMoreElements()查询是否有数据 nextElement 取出数据
- Hashtable 计算 hash 值,直接用 key 的 hashCode(),而 HashMap 重新计算了 key 的 hash 值
45、JVM 对 Java 的原生锁做了哪些优化
-
自旋锁
在线程进行阻塞的时候,先让线程自旋等待一段时间,可能这段时间其它线程已经解锁,这时就无需让线程再进行阻塞操作了。自旋默认次数是10次。 -
自适应自旋锁
自旋锁的升级,自旋的次数不再固定,由前一次自旋次数和锁的拥有者的状态决定。 -
锁消除
在动态编译同步代码块的时候,JIT编译器借助逃逸分析技术来判断锁对象是否只被一个线程访问,而没有其他线程,这时就可以取消锁了。 -
锁粗化
当JIT编译器发现一系列的操作都对同一个对象反复加锁解锁,甚至加锁操作出现在循环中,此时会将加锁同步的范围粗化到整个操作系列的外部。锁粒度:不要锁住一些无关的代码。锁粗化:可以一次性执行完的不要多次加锁执行。
46、jsp 有哪些内置对象
pageContext,request,response,session,application,page,exception,out,config
47、什么是控制反转(IOC),什么是依赖注入(DI)
IOC就是把 new 对象不放在具体类中去 new,把 new 对象的控制权反转给第三方 Spring 容器去 new 对象。
DI就是从spring容器中取出容器中的对象,然后把对象注入到需要的地方
48、什么是 ORM 框架
对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。ORM框架是连接数据库的桥梁,只要提供了持久化类与表的映射关系,ORM框架在运行时就能参照映射文件的信息,把对象持久化到数据库中。
ORM框架:为了解决面型对象与关系数据库存在的互不匹配的现象的框架。
49、自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,插入了一条数据,此时 id 是几
一般情况下,我们创建的表类型是InnoDB。不重启MySQL,如果新增一条记录,id是8,重启,ID是6;因为InnoDB表只把自增主键的最大ID记录在内存中,如果重启,已删除的最大ID会丢失。
如果表类型是MyISAM,重启之后,最大ID也不会丢失,ID是8,InnoDB必须有主键(建议使用自增主键,不用UUID,自增主键索引查询效率高)、支持外键、支持事务、支持行级锁。系统崩溃后,MyISAM很难恢复;
综合考虑,优先选择InnoDB,MySQL默认也是InnoDB。
50、Redis有哪些功能
-
服务端的数据缓存
专门部署一个Redis的服务器 -
持久化
Redis持久化指的是Redis会把内存中的数据写到磁盘中,在Redis重启时先加载这些数据,就觉Redis服务器重启导致的内存丢失问题。 -
哨兵(Sentinel)和复制
Sentinel可以管理多个Redis服务器,它提供了监控、提醒以及自动的故障转移功能复制则是让Redis服务器可以配备备份的服务器;Redis也是通过这两个功能保证Redis的高可用; -
集群(Cluster)
单台服务器资源总是有上限的,CPU和IO资源可以通过主从复制,进行读写分离,把一部分CPU和IO的压力转移到从服务器上,但是内存资源怎么办,主从模式只是数据的备份,并不能扩充内存;
现在我们可以横向扩展,让每台服务器只负责一部分任务,然后将这些服务器构成一个整体,对外界来说,这一组服务器就像是集群一样。
51、说一下类加载的执行过程
Java类加载过程主要可以分为三个步骤:加载、连接、初始化。
-
加载:是类加载的第一个阶段,就是将需要用到的类对应的.class字节码文件加载到虚拟机内存中,并在方法区生产一个java.lang.Class对象,作为程序访问这个类的各种数据的访问入口。
-
连接:是把原始的类定义信息平滑地转入JVM运行的过程中。这一阶段可以细分为验证、准备、解析三步。
验证
从字面上就可以看出这是来效验加载进来的.class文件,里面的内容是否符合规范。毕竟.class文件还是可以人为修改,如果不符合规范,虚拟机就无法执行。
(1) 文件格式验证
检查字节流是否符合class文件格式规范,是否能被当前版本的虚拟机处理。
(2) 元数据验证
对字节码信息进行语义分析,是否符合Java语言规范。
例如:是否有父类(除java.lang.Object)、父类是否继承了不允许被继承的类(final修饰类)
(3) 字节码验证
通过数据流和控制流分析,确定程序语义是否合法。
准备
准备阶段是,正式为类变量分配内存并且、设置类变量初始值的阶段。会为这个类中的类变量分配内存空间,并给一个初始值,不过这里要注意:这个仅包括类变量,不包括实例变量和局部变量等。并且只给一个初始值,int型的初始值是0。真正赋值为是在初始化阶段。
这个一阶段是这个类分配内存空间,先给类分配内存,在给里面的类变量分配内存。
解析
是将常量池的符号引用替换为直接引用的过程,解析主要针对类或接口,字段,类方法,接口方法,方法类型,方法句柄,调用点限定符等7类符号引用。
- 初始化:是执行类初始化的代码逻辑,包括静态字段赋值的动作,以及执行类定义中的静态初始化块内的逻辑。
是类加载中核心的一步,准备阶段我们已经将为变量分配了内存空间并给了初始值。现在就是真正赋值的时候,如果类中还有静态代码块的话,也在这一阶段执行。类初始化的时候,如果父类还没有加载和初始化,也会触发父类的加载和初始化。
52、什么是双亲委派模型
java中存在3种类型的类加载器:引导类加载器,扩展类加载器和系统类加载器。三者是的关系是:引导类加载器是扩展类加载器的父类,扩展类加载器是系统类加载器的父类。
- Bootstrap classLoader:主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。
- ExtClassLoader:主要负责加载jre/lib/ext目录下的一些扩展的jar。
- AppClassLoader:主要负责加载应用程序的主函数类。
从上图中我们就更容易理解了,当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。
为什么使用双亲委派
- 安全:就算自己定义了一个Java.lang.String,加载器也会通过AppClassLoader->ExtClassLoader->BootstrapLoader路径加载到核心jar包。这样便可以防止核心
API库被随意篡改。 - 避免类重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一 次,保证被加载类的唯一。
- 90%以上的类都是应用加载器进行加载,虽然第一次加载类的时候需要经历一次AppClassLoader->ExtClassLoader->BootstrapLoader。但是第二次用的时候就不需要了。如果直接从BootstrapLoader找有没有加载的话,第一次很快。但是已加载的类,特别是应用类加载器加载的,每次都需要经历引导类加载器和扩展类加载器,这样就太慢了。
53、hashcode是什么,有什么作用
Java中Object有一个方法:
public native int hashcode();
-
hashcode()方法的作用
hashcode()方法主要配合基于散列的集合一起使用,比如HashSet、HashMap、HashTable。当集合需要添加新的对象时,先调用这个对象的hashcode()方法,得到对应的hashcode值,实际上hashmap中会有一个table保存已经存进去的对象的hashcode值,如果table中没有改hashcode值,则直接存入,如果有,就调用equals方法与新元素进行比较,相同就不存了,不同就存入。 -
equals和hashcode的关系
如果equals为true,hashcode一定相等
如果equals为false,hashcode不一定不相等
如果hashcode值相等,equals不一定相等
如果hashcode值不等,equals一定不等 -
重写equals方法时,一定要重写hashcode方法
54、Java 容器都有哪些
1、Collection
(1)set
HashSet、TreeSet
(2)list
ArrayList、LinkedList、Vector
2、Map
HashMap、HashTable、TreeMap
55、Collection 和 Collections 有什么区别
首先说下 collection,collection 它是一个接口,collection 接口的意义是为各种具体的集合提供
统一的操作方式,它继承 Iterable 接口,Iterable 接口中有一个最关键的方法, Iterator iterator()方法
迭代器,可以迭代集合中的元素。
collections 他是一个工具类,其中提供一系列的静态方法,其中我了解到的关键的一个就是 sort 方法
他是一个排序的方法sort 方法中的参数有一个的时候,参数必须实现了 Comparable 接口中 compareTo 方法,根据 compareTo 方法中的规则进行排序sort 排序的另一种写法是 sort 方法的参数有两个。
56、list与Set区别
- List,Set都是继承自Collection接口
- List特点:元素有放入顺序,元素可重复 ,Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉
- Set和List对比:
- Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
- List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
57、Synchronized 用过吗,其原理是什么
- 同步代码块:编译后,将monitorenter指令插入到代码块的开始处,然后将monitorexit指令插入到代码块结束处或者异常处。每个对象都有一个关联的monitor,当遇到monitorenter时,就需要去获取monitor,获取不到则进入阻塞队列,等待这个monitor被持有者释放后的唤醒通知。
- 同步方法:在method_info的结构里,有ACC_Sychronized标记,线程执行时如果识别到这个标记,如果设置了,执行线程将先获取 monitor,获取成功之后才能执行方法体,方法执行完后再释放 monitor。在方法执行期间,其他任何线程都无法再获得同一个 monitor 对象。
这两者的细节不一样,但是本质上都是尝试获取对象的monitor,如果获取不到,则线程会被阻塞(状态为BLOCKED),进入同步队列。当持有monitor的线程释放了锁后,就会唤醒阻塞在同步队列的线程,然后大家重新尝试获取monitor
58、使用 Spring 框架能带来哪些好处
- 轻量级框架、容器
- 控制反转IOC:Spring通过控制反转实现松耦合
- 支持AOP:Spring提供对AOP的支持,它允许将一些通用任务,如安全、事务、日志等进行集中式处理,从而提高了程序的复用性
- 方便集成各种优秀框架:如Struts、hibernate、mybstis
- 支持声明式事务处理:只需通过配置就可以完成对事务的管理,而无须手动编程
59、Spring Boot 有哪些优点
- 快速构建项目,可以选一些必要的组件
- 对主流框架的无配置集成
- 内嵌Tomcat容器,项目可独立运行
- 删除了繁琐的xml配置文件
- 极大地提高了开发和部署效率
- 提供starter,简化maven配置
60、mybatis 中 #{ }和 ${ }的区别是什么
- #{}是预编译处理,$ { }是字符串替换
- 使用 #{ } 可以有效的防止SQL注入,提高系统安全性
- MyBatis在处理#{}时,会将SQL中的#{ }替换为?号,使用PreparedStatement的set方法来赋值;MyBatis在处理 $ { } 时,就是把 ${ } 替换成变量的值
61、数据库的三范式是什么
1、列不可再分
2、属性完全依赖于主键
3、任何非主属性不依赖于其它非主属性
62、说一下 jvm 运行时数据区
https://blog.csdn.net/yy139926/article/details/126641686
63、你是怎样理解面向对象的
封装、继承、多态
64、int 和 Integer 有什么区别
- int是基本数据类型,Integer是int的封装类型
- int可以没有初始值,Integer必须设置初始值
- int默认为0,Integer默认为null
65、== 和 equals 的区别是什么
基本数据类型,== 比较的是值,引用数据类型 == 比较的是地址,equals不能用于基本数据类型的比较,引用数据类型用equals比较,如过没有重写equals方法比较是地址和 == 效果是一样的,如果重写了这个方法,比较的就是内容。
66、谈谈你对反射的理解
运行期间动态获取类中的信息(属性,方法,包的信息,注解等)以及动态调用对象的方法和属性的功能,称之为java语言的反射机制,通俗的理解,就是在运行期间对类的内容进行操作。
https://blog.csdn.net/yy139926/article/details/124831677
67、ArrayList 和 LinkedList 区别
- LinkedList 和 ArrayList 的差别主要来自于 Array 和 LinkedList 数据结构的不同。ArrayList 是基于数组实现的,LinkedList 是基于双链表实现的。另外 LinkedList 类不仅是 List 接口的实现类,LinkedList 还实现了 Deque 接口,Deque 接口是 Queue 接口的子接口,Queue 它代表一个双向队列,因此 LinkedList 可以作为双向队列,List 集合使用,功能强大。
- 因为 Array 是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的,可以直接返回数组中 index 位置的元素,因此在随机访问集合元素上有较好的性能。对于随机访问,ArrayList 优于 LinkedList。ArrayList 时间复杂度 O(1)LinkedList 时间复杂度 O(n)。
- LinkedList 的随机访问集合元素时性能较差,但在插入,删除操作是更快的。因为 LinkedList 不像 ArrayList 一样,不需要改变数组的大小,不需要在数组装满的时候要将所有的数据重新装入一个新的数组,对于插入和删除操作,LinkedList 优于 ArrayList.ArrayList 时间复杂度 O(n)LinkedList 时间复杂度 O(1)。
- LinkedList 需要更多的内存,因为 ArrayList 的每个索引的位置是实际的数据,而 LinkedList 中的每个节点中存储的是实际的数据和前后节点的位置。
68、TreeSet 和 HashSet 的区别
HashSet 是采用 hash 表来实现的。其中的元素没有按顺序排列,add()、remove()以及 contains()等方法都是复杂度为 O(1)的方法。
TreeSet 是采用树结构实现(红黑树算法)。元素是按顺序进行排列,但是 add()、remove()以及 contains()等方法都是复杂度为 O(log (n))的方法。它还提供 了一些方法来处理排序的 set,如 first(), last(), headSet(), tailSet()等等。
69、StringBuffer 和 StringBuilder 的区别
- StringBuffer 与 StringBuilder 中的方法和功能完全是等价的
- 只是 StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不 安全的
- 在单线程程序下,StringBuilder 效率更快,因为它不需要加锁,不具备多线程安全而 StringBuffer 则每次都需要判断锁,效率相对更低
70、Final、Finally、Finalize区别
final可以修饰类,变量,方法,修饰的类不能被继承,修饰的变量不能重新赋值,修饰的方法不能被重写。
finally用于抛异常,finally代码块内语句无论是否发生异常,都会在执行finally,常用于一些流的关闭。
finalize方法用于垃圾回收。finalize() Object 类中定义的方法,Java 中允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集 器在销毁对象时调用的,通过重写 finalize() 方法可以整理系统资源或者执行其他清理工作。
71、什么是Java序列化,如何实现Java序列化
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化(将对象转换成二进制)。
可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。
类通过实现 Java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
序列化:把 Java 对象转换为字节序列的过程。
反序列化:把字节序列恢复为 Java 对象的过程。
72、Object 中有哪些方法
getcode()、getClass()、toString()、equals()、finalize()、wait()、notify()、notifyAll()、clone()
73、线程由几种状态,产生的条件是什么
- 新建状态(new):创建一个线程对象。
- 就绪状态(Runnable):线程对象创建之后,调用start()方法,就绪状态的线程只处于等待CPU的使用权,变为可运行。
- 运行状态(Running): 就绪状态的线程,获取到了CPU资源,执行程序代码。
- 阻塞状态(Blocked): 等待阻塞(线程执行了一个对象的wait()方法,进入阻塞状态,只有等到其他线程执行了该对象的notify()或notifyAll()方法,才可能将其唤醒)、线程阻塞(线程获取synchronized同步锁失败(因为锁被其它线程锁占用),它会进入同步阻塞状态)、其它阻塞(通过调用线程的sleep()或join()或发出了IO请求时,线程就会进入阻塞状态。当sleep()超时、join()等待线程终止或超时、或者IO处理完毕时,线程重新转入就绪状态)。
- 死亡状态(Dead):线程任务执行结束,即run()方法结束,该线程对象就会被垃圾回收,线程对象即为死亡状态。
74、什么是死锁,怎么避免死锁
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程
- 加锁顺序(线程按照一定的顺序加锁)
- 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
- 死锁检测
75、ArrayList 和 Vector 的区别是什么
- Vector 的方法都是同步的,线程安全;ArrayList 非线程安全,但性能比 Vector 好
- 默认初始化容量都是 10,Vector 扩容默认会翻倍,可指定扩容的大小;ArrayList 只增加 50%
76、Array 和 ArrayList 有何区别
- Array 可以容纳基本数据类型和对象 ArrayList 只能容纳对象
- Array 是制定大小的,ArrayList 大小是固定的
- ArrayList 比 Array 功能多 例如:addAll iterator 等
77、JUC 中常用的并发工具类
CountDownLatch:CountDownLatch是我目前使用比较多的类,CountDownLatch初始化时会给定一个计数,然后每次调用countDown() 计数减1,当计数未到达0之前调用await() 方法会阻塞直到计数减到0
78、说一下 session 的工作原理
当客户端登录完成后,会在服务端产生一个session,此时服务端会将sessionid返回给客户端浏览器。客户端将sessionid储存在浏览器的cookie中,当用户再次登录时,会获得对应的sessionid,然后将sessionid发送到服务端请求登录,服务端在内存中找到对应的sessionid,完成登录,如果找不到,返回登录页面。
79、说一下 tcp 粘包是怎么产生的
发送方发送的多个数据包,到接收方缓冲区首尾相连,粘成一包,被接收
80、什么是 Spring Profiles
Spring Profiles允许用户根据配置文件(dev、test、prod)来判定加载哪些配置文件
81、mysql 的内连接、左连接、右连接有什么区别
- 内连接,显示两个表中有联系的所有数据;
- 左链接,以左表为参照,显示所有数据,右表中没有则以null显示
- 右链接,以右表为参照显示数据,,左表中没有则以null显示
82、Redis 支持的数据类型有哪些
String、hash、list、set、zset(sorted set:有序集合)
83、新生代垃圾回收器和老生代垃圾回收器都有哪些,有什么区别
新生代回收器:Serial、ParNew、Parallel Scavenge
老年代回收器:Serial Old、Parallel Old、CMS
新生代回收器一般采用的是复制算法,复制算法效率较高,但是浪费内存
老生代回收器一般采用标记清楚算法,比如最常用的CMS
84、简述分代垃圾回收器是怎么工作的
分代回收器分为新生代和老年代,新生代大概占1/3,老年代大概占2/3;
新生代包括Eden、From Survivor、To Survivor;Eden区和两个survivor区的 的空间比例 为8:1:1 ;
垃圾回收器的执行流程:
- 把 Eden + From Survivor 存活的对象放入 To Survivor 区;
- 清空 Eden + From Survivor 分区,From Survivor 和 To Survivor 分区交换;
- 每次交换后存活的对象年龄+1,到达15,升级为老年代,大对象会直接进入老年代;
- 老年代中当空间到达一定占比,会触发全局回收,老年代一般采取标记-清除算法;
85、byte类型127+1等于多少
byte的范围是-128~127。
字节长度为8位,最左边的是符号位,而127的二进制为01111111,所以执行+1操作时,01111111变为10000000。
大家知道,计算机中存储负数,存的是补码的兴衰。左边第一位为符号位。
那么负数的补码转换成十进制如下:
一个数如果为正,则它的原码、反码、补码相同;一个正数的补码,将其转化为十进制,可以直接转换。
已知一个负数的补码,将其转换为十进制数,步骤如下:
先对各位取反;将其转换为十进制数;加上负号,再减去1;
例如10000000,最高位是1,是负数,①对各位取反得01111111,转换为十进制就是127,加上负号得-127,再减去1得-128;
86、数据库一般会采取什么样的优化方法
- 选取适合的字段属性
- 使用join连接代替子查询
- 使用联合union来代替手动创建的临时表
- 使用索引
- 优化的查询语句
87、java 的事务传播
https://blog.csdn.net/yy139926/article/details/125381822
88、说一下事务的隔离级别
数据库事务的隔离级别有4种,由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable 。
89、SpringMVC和SpringBoot的区别
- SpringMVC和SpringBoot都是Spring的衍生产品;
- Spring MVC是基于servlet的一个MVC框架,主要应用于web开发。
- Spring Boot解决的是Spring配置繁琐的问题,为简化开发而生;
90、Java 创建线程的三种方式有啥区别
实现Runnable和实现Callable接口的方式基本相同,不过是后者执行call()方法有返回值,后者线程执行体run()方法无返回值,因此可以把这两种方式归为一种这种方式与继承Thread类的方法之间的差别如下:
- 线程只是实现Runnable或实现Callable接口,还可以继承其他类。
- 这种方式下,多个线程可以共享一个target对象,非常适合多线程处理同一份资源的情形。
- 但是编程稍微复杂,如果需要访问当前线程,必须调用Thread.currentThread()方法。
- 继承Thread类的线程类不能再继承其他父类(Java单继承决定)。
91、mybatis 是如何防止sql注入的
不管输入什么参数,打印出的SQL都是这样的。这是因为MyBatis启用了预编译功能,在SQL执行前,会先将上面的SQL发送给数据库进行编译;执行时,直接使用编译好的SQL,替换占位符“?”就可以了。因为SQL注入只能对编译过程起作用,所以这样的方式就很好地避免了SQL注入的问题。
MyBatis是如何做到SQL预编译的呢?其实在框架底层,是JDBC中的PreparedStatement类在起作用,PreparedStatement是我们很熟悉的Statement的子类,它的对象包含了编译好的SQL语句。这种“准备好”的方式不仅能提高安全性,而且在多次执行同一个SQL时,能够提高效率。原因是SQL已编译好,再次执行时无需再编译。
92、concurrentHashMap和HashTable有什么区别
concurrentHashMap融合了hashmap和hashtable的优势,hashmap是不同步的,但是单线程情况下效率高,hashtable是同步的同步情况下保证程序执行的正确性。
但hashtable每次同步执行的时候都要锁住整个结构,如下图:
concurrentHashMap锁的方式是细粒度的。concurrentHashMap将hash分为16个桶(默认值),诸如get、put、remove等常用操作只锁住当前需要用到的桶。
concurrentHashMap的读取并发,因为读取的大多数时候都没有锁定,所以读取操作几乎是完全的并发操作,只是在求size时才需要锁定整个hash。
而且在迭代时,concurrentHashMap使用了不同于传统集合的快速失败迭代器的另一种迭代方式,弱一致迭代器。在这种方式中,当iterator被创建后集合再发生改变就不会抛出ConcurrentModificationException,取而代之的是在改变时new新的数据而不是影响原来的数据,iterator完成后再讲头指针替代为新的数据,这样iterator时使用的是原来的数据。
93、hashmap存储的数据结构
数组结构中有数组和链表,实现对数据的存储。
1、数组
数组存储区间是连续的,占用内存严重,故空间复杂度大,但数组的二分查找时间复杂度小,为O(1);
数组的特点是:查找容易,插入和删除困难。
2、链表
链表存储区间离散,暂用内存比较宽松,故空间复杂度小,但时间复杂度大,达到O(N)。
链表的特点:查找困难,插入和删除容易。
3、哈希表
哈希表有多种不同的实现方法,现在介绍一种最常用的方法—拉链法,可以简单的理解为“链表的数组”,如图:
4、HashMap其实就是一个线性的数组
首先HashMap中有一个静态内部类Entry,其重要的属性有key、value、next,key、value构成Entry[],next的指针指向下一个元素的引用,也就构成了链表。
94、HasmMap 和 HashSet 的区别
Java中的集合有两类,一类是List,一类是Set。
List:元素有序,可以重复;
Set:元素无序,不可重复;
要想保证元素的不重复,拿什么来判断呢?这就是Object.equals方法了。如果元素有很多,增加一个元素,就要判断n次吗?
显然不现实,于是,Java采用了哈希表的原理。哈希算法也称为散列算法,是将数据依特定算法直接指定到一根地址上,初学者可以简单的理解为,HashCode方法返回的就是对象存储的物理位置(实际上并不是)。
这样一来,当集合添加新的元素时,先调用这个元素的hashcode()方法,就一下子能定位到他应该放置的物理位置上。如果这个位置上没有元素,他就可以直接存储在这个位置上,不用再进行任何比较了。如果这个位置上有元素,就调用它的equals方法与新元素进行比较,想同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际上调用equals方法的次数就大大降低了,几乎只需要一两次。
简而言之,在集合查找时,hashcode能大大降低对象比较次数,提高查找效率。
Java对象的equals方法和hashCode方法时这样规定的:
- 相等的对象就必须具有相等的hashcode。
- 如果两个对象的hashcode相同,他们并不一定相同。
如果两个Java对象A和B,A和B不相等,但是A和B的哈希码相等,将A和B都存入HashMap时会发生哈希冲突,也就是A和B存放在HashMap内部数组的位置索引相同,这时HashMap会在该位置建立一个链接表,将A和B串起来放在该位置,显然,该情况不违反HashMap的使用规则,是允许的。当然,哈希冲突越少越好,尽量采用好的哈希算法避免哈希冲突。
equals()相等的两个对象,hashcode()一定相等;equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。
HashMap | HashSet |
---|---|
实现了Map接口 | 实现了Set接口 |
存储键值对 | 存储对象 |
调用put()添加元素 | 调用add()添加元素 |
HashMap使用key计算HashCode | HashSet使用成员对象计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false |
HashMap比HashSet快 | HashSet比HashMap慢 |
95、CAS
1、CAS实现原理
CAS是Compare And Swap的缩写,意思就是比较并交换。
它是无锁化的实现,是经典的乐观锁。synchronized是一种悲观锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。乐观锁就是不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁的机制就是CAS。
CAS操作很简单,它包含三个操作数:内存地址V、预期原值A、新值B。先比较内存地址V处的值和预期原值A是否相等,如果相等就将内存地址V处更新为新值B。在配合循环使用时,若CAS操作失败,会循环执行或到达某个终止处。此操作配合循环使用时,又称为自旋锁的实现方式。
2、CAS存在的问题
① ABA问题:因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A
② 循环时间开销大:自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销(时刻a的内存值和时刻b的内存值比较,如果相等则更新,否则一直循环)
96、Java常见的线程池
Java线程池类型有四种,分别为:newCachedThreadPool、newFixedThreadPool、newSingleThreadExecutor、newScheduleThreadPool。
1、newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理所需,可灵活回收空闲线程,若线程数不够,则新建线程。
2、newFixedThreadPool:创建一个固定大小的线程池。可控制并发的线程数量,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
3、newSingleThreadExecutor:创建一个单线程的线程池,即只创建唯一的工作者线程来执行任务,,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
4、newScheduleThreadPool:创建一个定长的线程池,支持定时及周期性任务执行。
97、RPC是什么
https://blog.csdn.net/yy139926/article/details/129075710
98、实例化对象有哪几种方式
- new
- 克隆的 clone() 方法
- 通过反射机制创建 newinstance()方法
- 序列化反序列化,将一个对象实例化后,进行序列化,再反序列化,也可以获得一个对象
99、单点登录
单点登录SSO,说的是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任。
单点登录的要点
①存储信任
②验证信任
100、Spring 是什么
Spring是一个轻量级的IOC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。常见的配置方式有三种:基于XML的配置、基于注解的配置、基于java的配置。
主要由以下几个模块组成:
Spring Core:核心类库,提供IOC服务;
Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等);
Spring AOP:AOP服务;
Spring DAO:对JDBC的抽象,简化了数据访问异常的处理;
Spring ORM:对现有的ORM框架的支持;
Spring Web:提供了基本的面向web的总和特性,例如多方文件上传;
Spring MVC:提供面向Web应用的Model-View-Controller实现。