java面试总结

文章目录

1.JDBC面试题

1.JDBC操作数据库步骤
  1. 注册驱动
  2. 创建数据库连接
  3. 编写SQL
  4. 执行SQL语句
  5. 处理结果集
  6. 关闭数据库连接
2.JDBC中的PreparedStatement区别
  1. PreparedStatement的是预编译的SQL语句,效率高于声明。
  2. PreparedStatement的支持?操作符,相对于声明更加灵活
  3. PreparedStatement的可以防止SQL注入,安全性高于声明。
3.说说数据库连接池工作原理和实现方案?(面试提问过)
1.JDBC连接访问数据库步骤 创建数据库连接->运行sql->关闭连接
2.每次创建链接都消耗大量的资源每次操作都要重新获取连接对象,执行操作后就把连接关闭,这样数据库连接对象使用率低

连接池的概念:连接池就是一个容器,连接池保存了一些数据库连接,可以重复利用

连接池的好处:保存一些连接,这些连接可以重复使用降低数据资源的消耗

连接池原理:

​ 程序启动,连接池就会初始化一些连接

​ 当使用数据库连接,直接从连接池中取出

​ 连接使用完,会将链接重新放回连接池

工作原理:
 	java服务器启动时就会创建一定数量的连接并一直维持不少于此数目的池连接。客户端程序需要连接时,池驱动程序会返回一个未使用的池连接并将其表记为忙。如果当前没有空闲连接,池驱动程序就新建一定数量的连接,新建连接的数量有配置参数决定。当使用的池连接调用完成后,池驱动程序将此连接表记为空闲,其他调用就可以使用这个连接。
 	
实现方案:
	返回的Connection是原始Connection的代理,代理Connection的close方法,当调用close方法时,不是真正关连接,而是把它代理的Connection对象放回到连接池中,等待下一次重复利用。
4.事务的四大属性
  1. 原子性
  2. 一致性
  3. 隔离性
  4. 持久性

一个逻辑单元必须有四个属性(ACID)只有这样才能成为一个事务

原子性:事务的最小执行单元,不润许分割,要么全部成功要么全部失败相当于转账

一致性:事务执行前后数据不变 就比如转账发生错误后对方余额没有增加我方数量也不会减少

隔离性:并发访问数据库,一个事务不会被其他事务干扰

持久性:一个事务被提交之后,数据永久的保存在数据库中

5.JDBC是如何实现java程序和JDBC驱动松耦合的
通过制定接口,数据库厂商来实现。我们只要通过接口调用即可。随便看一个简单的JDBC示例,你会发现所有操作都是通过JDBC接口完成的,而驱动只有在通过的Class.forName反射机制来加载的时候才会出现。
6.执行,的executeQuery,executeUpdate的的区别是什么?

executeQuery

Statement的executeQuery(String query)接口用来执行select查询,并且返回ResultSet。即使查询不到记录返回的ResultSet也不会为null。我们通常使用executeQuery 来执行查询语句,这样的话如果传进来的是插入者更新语句的话,它会抛出错误信息为“executeQuery方法不能用于更新”的java.util.SQLException

executeUpdate

Statement的executeUpdate(String query)方法用来执行insert或者update / delete(DML)语句,或者什么也不返回DDL语句。返回值是int类型,如果是DML语句的话,它就是更新的条数,如果是DDL的话,就返回0。

只有当你不确定用什么语句的时候才应该使用 execute()方法 否则应该使用的executeQuery或者executeUpdate的方法。

7.JDBC赃读是什么 (面试问过)

赃读:一个事务读取另一个事务未提交的数据

例子:一个向乙转账,A执行了转账语句,但一个还没有提交事务,B读取数据,发现自己账户钱变多了乙跟一个说,我已经收到钱了。一个回滚事务【!回滚】,等乙再查看账户的钱时,发现钱并没有多
8.什么是幻读面试问过)

幻读: 是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。

JDBC的DriverManager的是用来做什么的?

9.JDBC数据源的是什么,有什么好处

数据源即数据源,它是定义在的javax.sql中的一个接口,跟DriverManager的相比,它的功能要更强大。我们可以用它来创建数据库连接,当然驱动的实现类会实际去完成这个工作。除了能创建连接外,它还提供了如下的特性:

10.常见的JDBC异常有哪些?
  1. java.sql.SQLException–这是JDBC异常的基类。
  2. java.sql.BatchUpdateException–当批处理操作执行失败的时候可能会抛出这个异常。这取决于具体的JDBC驱动的实现,它也可能直接抛出基类异常java.sql.SQLException中
  3. java.sql.SQLWarning的 - SQL操作出现的警告信息。
11.JDBC存在哪些不同类型的锁

两种锁机制来繁防止多个用户同时操作引起的数据破坏

  1. 乐观锁: 只有当更新数据的时候才会锁定记录。
  2. 悲观锁 - 从查询到更新和提交整个过程都会对数据记录进行加锁
12.java.util.Date和java.sql.Date有什么区别?
java.util.Date包含日期和时间,而java.sql.Date只包含日期信息,而没有具体的时间信息。如果你想把时间信息存储在数据库里,可以考虑使用时间戳或者日期时间字段
13.JDBC的批处理是什么,有什么好处?
有时候类似的查询我们需要执行很多遍,比如从CSV文件中加载数据到关系型数据库的表里。我们也知道,执行查询可以用Statement或者PreparedStatement。除此之外,JDBC还提供了批处理的特性,有了它,我们可以在一次数据库调用中执行多条查询语句。

JDBC通过Statement和PreparedStatement中的addBatch和executeBatch方法来支持批处理
14.JDBC插入大量数据如何操作

步骤:

  1. 注册驱动
  2. 获取数据库连接
  3. 编写SQL语句
  4. 开始事务
  5. 调用 prepareStatement (sql) 语句
  6. 判断每一万条插入一次

因为每一条insert语句,都会产生一条log写入磁盘 一定要开启事务 在设置批量插入

代码如下

String sql = "insert into table *****";
con.setAutoCommit(false);
ps = con.prepareStatement(sql);
for(int i=1; i<65536; i++){
    ps.addBatch();
    // 1w条记录插入一次
    if (i % 10000 == 0){
         ps.executeBatch();
         con.commit();
     }
}
// 最后插入不足1w条的数据
ps.executeBatch();
con.commit();

15.Java中如何进行事务的处理?
Connection类中提供了4个事务处理方法:

setAutoCommit(Boolean autoCommit):设置是否自动提交事务,默认为自动提交,即为true,通过设置false禁止自动提交事务;
commit():提交事务;
rollback():回滚事务.
savepoint:保存点

注意:savepoint不会结束当前事务,普通提交和回滚都会结束当前事务的


2.Nginx面试题?

1.什么Nginx

Nginx是一个 轻量级/高性能的反向代理Web服务器,他实现非常高效的反向代理、负载平衡,他可以处理2-3万并发连接数,官方监测能支持5万并发,现在中国使用nginx网站用户有很多,例如:新浪、网易、 腾讯等。

2.为什么要用Nginx?
  • 跨平台、配置简单、方向代理、高并发连接:处理2-3万并发连接数,官方监测能支持5万并发,内存消耗小:开启10个nginx才占150M内存 ,nginx处理静态文件好,耗费内存少,
  • 而且Nginx内置的健康检查功能:如果有一个服务器宕机,会做一个健康检查,再发送的请求就不会发送到宕机的服务器了。重新将请求提交到其他的节点上。
  • 使用Nginx的话还能:
    1. 节省宽带:支持GZIP压缩,可以添加浏览器本地缓存
    2. 稳定性高:宕机的概率非常小
    3. 收用户请求是异步的
3.为什么Nginx性能这么高?
  • 因为他的事件处理机制:异步非阻塞事件处理机制:运用了epoll模型,提供了一个队列,排队解决
4.Nginx怎么处理请求的?
  • nginx接收一个请求后,首先由listen和server_name指令匹配server模块,再匹配server模块里的location,location就是实际地址

  • server {            		    	        # 第一个Server区块开始,表示一个独立的虚拟主机站点
         listen       80;      		        # 提供服务的端口,默认80
         server_name  localhost;    		    # 提供服务的域名主机名
         location / {            	         # 第一个location区块开始
             root   html;       		        # 站点的根目录,相当于Nginx的安装目录
             index  index.html index.htm;     # 默认的首页文件,多个用空格分开
         }          				             # 第一个location区块结果
     } 
       
    
    
5.什么是正向代理和反向代理?
  1. 正向代理就是一个人发送一个请求直接就到达了目标的服务器

  2. 反方代理就是请求统一被Nginx接收,nginx反向代理服务器接收到之后,按照一定的规 则分发给了后端的业务处理服务器进行处理了

6.使用反向代理服务器的优点是什么
  • 反向代理服务气哦可以隐藏真正服务器的存在和特征,它充当互联网和web服务器之间的中间层对于安全方面来说很安全很好,特别是当你使用web托管服务时

7.Nginx的优缺点

优点:

  1. 占内存小,可实现高并发连接,处理响应快
  2. 可实现http服务器、虚拟主机、方向代理、负载均衡
  3. Nginx配置简单
  4. 可以不暴露正式的服务器IP地址

缺点:动态处理差 nginx处理静态文件好,耗费内存少,但是处理动态页面则很鸡肋,现在一般前端用nginx作为反向代理抗住压力,

7.Nginx应用场景
1.http服务器。Nginx是一个http服务可以独立提供http服务。可以做网页静态服务器。
2.虚拟主机。可以实现在一台服务器虚拟出多个网站,例如个人网站使用的虚拟机。
3.反向代理,负载均衡。当网站的访问量达到一定程度后,单台服务器不能满足用户的请求时,需要用多台服务器集群可以使4.用nginx做反向代理。并且多台服务器可以平均分担负载,不会应为某台服务器负载高宕机而某台服务器闲置的情况。
5.nginz 中也可以配置安全管理、比如可以使用Nginx搭建API接口网关,对每个接口服务进行拦截。


3.集合面试题

1.什么是集合
  1. 集合就是一个放数据的容器,准确的说是放数据对象引用的容器
  2. 集合类存放的都是对象的引用,而不是对象的本身
  3. 集合类型主要有3种 :set list map
2.集合的特点
  1. 集合用于存储对象的容器,对象是用来封装数据,对象多了也需要存储集中式管理。
  2. 和数组对比对象的大小不确定。因为集合是可变长度的。数组需要提前定义大小
3.集合和数组的区别
  • 数组是固定长度的;集合可变长度的。
  • 数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。
  • 数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型
4.使用集合框架的好处
  1. 容量自增长;
  2. 提供了高性能的数据结构和算法,使编码更轻松,提高了程序速度和质量;
  3. 可以方便地扩展或改写集合,提高代码复用性和可操作性。
  4. 通过使用JDK自带的集合类,可以降低代码维护和学习新API成本。
5.常用的集合类有哪些
  • Map接口和Collection接口是所有集合框架的父接口:
  1. Collection接口的子接口包括:Set接口和List接口
  2. Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
  3. Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等
  4. List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等
6.List,Set,Map三者的区别?

Collection

  1. list : 有序可重复
           1.Vector : 数组结构,线程安全
           2.ArrayLlist: 数组结构  非线程安全
           3.LinkedList 链表结构 非线程安全
           
    set  : 无序 唯一
           1.HashSet  LinkdeHashSet  哈希表结构  哈希表和链表结构
           2.TreeSet  红黑树结构
           
    map双列集合
           1.HashTable   哈希表结构,线程安全‘
           2.HashMap   哈希表结构 非线程安全  
           3.TreeMap 红黑树结构
    
    
  • Java 容器分为 Collection 和 Map 两大类,Collection集合的子接口有Set、List、Queue三种子接口。我们比较常用的是Set、List,Map接口不是collection的子接口。
  • Set:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet、LinkedHashSet 以及 TreeSet。
  • Map是一个键值对集合,存储键、值和之间的映射。 Key无序,唯一;value 不要求有序,允许重复。Map没有继承于Collection接口,从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。
7.集合框架底层数据结构

Collection

  1. List

    • Arraylist: Object数组
    • Vector: Object数组
    • LinkedList: 双向循环链表
  2. Set

    • HashSet(无序,唯一):基于 HashMap 实现的,底层采用 HashMap 来保存元素
    • LinkedHashSet: LinkedHashSet 继承与 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 Hashmap 实现一样,不过还是有一点点区别的。
    • TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树。)

Map

  • HashMap: JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突).JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间
  • LinkedHashMap:LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。
  • HashTable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的
  • TreeMap: 红黑树(自平衡的排序二叉树)
8.Map
  • HashMap: JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突).JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间
  • LinkedHashMap:LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。
  • HashTable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的
  • TreeMap: 红黑树(自平衡的排序二叉树)
9.遍历一个 List 有哪些不同的方式?
  • 遍历有一下几种方式

    1. for 循环遍历,基于计数器。在集合外部维护一个计数器,然后依次读取每一个位置的元素,当读取到最后一个元素后停止。

    2. 迭代器遍历,Iterator。Iterator 是面向对象的一个设计模式,目的是屏蔽不同数据集合的特点,统一遍历集合的接口。Java 在 Collections 中支持了 Iterator 模式。

    3. foreach 循环遍历。foreach 内部也是采用了 Iterator 的方式实现,使用时不需要显式声明 Iterator 或计数器。优点是代码简洁,不易出错;缺点是只能做简单的遍历,不能在遍历过程中操作数据集合,例如删除、替换。

10.说一下 ArrayList 的优缺点

优点:

  • ArrayList底层以数组实现,所以查询特别快
  • ArrayList 在顺序添加一个元素的时候非常方便

缺点:

  • 删除元素的时候,需要做一次元素复制操作。如果要复制的元素很多,那么就会比较耗费性能。
  • 插入元素的时候,也需要做一次元素复制操作,缺点同上。
11.如何实现数组和 List 之间的转换?
  • 数组转 List:使用 Arrays. asList(array) 进行转换。

  • List 转数组:使用 List 自带的 toArray() 方法。

    // list to array
    List<String> list = new ArrayList<String>();
    list.add("123");
    list.add("456");
    list.toArray();
    
    // array to list
    String[] array = new String[]{"123","456"};
    Arrays.asList(array);
    
    
    
12.ArrayList 和 LinkedList 的区别是什么?

数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现

线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全

  • 综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。
13.ArrayList 和 Vector 的区别是什么?

这两个类都实现了 List 接口(List 接口继承了 Collection 接口),他们都是有序集合

线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。
性能:ArrayList 在性能方面要优于 Vector。
扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%

  • Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。
  • Arraylist不是同步的,所以在不需要保证线程安全时时建议使用Arraylist
14.插入数据时,ArrayList、LinkedList、Vector谁速度较快?
  • ArrayList和Vector 底层的实现都是使用数组方式存储数据。数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢。
  • Vector 中的方法由于加了 synchronized 修饰,因此 Vector 是线程安全容器,但性能上较ArrayList差
  • LinkedList 使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但插入数据时只需要记录当前项的前后项即可,所以 LinkedList 插入速度较快
15.多线程场景下如何使用 ArrayList?

ArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections 的 synchronizedList 方法将其转换成线程安全的容器后再使用。例如像下面这样:

List<String> synchronizedList = Collections.synchronizedList(list);
synchronizedList.add("aaa");
synchronizedList.add("bbb");

for (int i = 0; i < synchronizedList.size(); i++) {
    System.out.println(synchronizedList.get(i));
}

16.List 和 Set 的区别
  • LIst,Set都继承Collection接口
  • LIst特点:有序(元素存入集合的顺序) 容器,元素可以重复,可以插入多个null元素,元素都有索引常用的实现类有ArrayLIst,LinkedList和Vector
  • Set特点:一个无序(存入和取出顺序有可能不一致)容器不可以存储重复元素,只允许存入一个null元素,必须保持唯一性, Set 接口常用实现类是 HashSet、LinkedHashSet 以及 TreeSet。
  • 另外 List 支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。
  • Set和List对比
    • Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
    • List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变
17.说一下 HashSet 的实现原理?

HashSet 是基于 HashMap 实现的,HashSet的值存放于HashMap的key上,HashMap的value统一为present,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值。

18. ==与equals的区别
  1. ==是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同
  2. ==是指对内存地址进行比较 equals()是对字符串的内容进行比较
19.什么是哈希?

Hash就是指使用哈希算法是指把任意长度的二进制映射为固定长度的较小的二进制值,这个较小的二进制值叫做哈希值

20.Array 和 ArrayList 有何区别?

Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。

Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。

Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。

21.如何实现 Array 和 List 之间的转换?
  • Array 转 List: Arrays. asList(array) ;
  • List 转 Array:List 的 toArray() 方法
22.comparable 和 comparator的区别?
  • comparable接口实际上是出自java.lang包,它有一个 compareTo(Object obj)方法用来排序

  • comparator接口实际上是出自 java.util 包,它有一个compare(Object obj1, Object obj2)方法用来排序

  • 一般我们需要对一个集合使用自定义排序时,我们就要重写compareTo方法或compare方法,当我们需要对某一个集合实现两种排序方式,比如一个song对象中的歌名和歌手名分别采用一种排序方法的话,我们可以重写compareTo方法和使用自制的Comparator方法或者以两个Comparator来实现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的Collections.sort().

23.Collection 和 Collections 有什么区别?

  • java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
  • Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作
  1. 简述HashMap的工作原理?

1.HashMap是面向查询优化的数据结构,查询性能优异。
2.在其内部利用数组存储数据。
3.插入数据时,先根据Key的HashCode计算出数组的下标位置,再利用Key的equals()方法检查是否以存在重复的Key,如果不重复直接存储到数组中,如果重复就作为链表存储到散列桶中。
4.插入的数据和数组容量的比值大于加载因子则进行数组扩容,并重新散列,默认的加载因子为“0.75”。
5.查询时,先根据Key的HashCode计算出数组的下标位置,再利用Key的equals()方法检查到Key的位置,如果找到返回Key对应的Value,否则返回Null。
6.由于利用Key的HashCode直接计算出数据的存储位置,由于直接确定数据的存储位置,相对于其他查找方式,查询效率非常高

25.HashMap 的实现原理?

1.HashMap 基于 Hash 算法实现的,我们通过 put(key,value)存储,get(key)来获取。
2.当传入 key 时,HashMap 会根据 key. hashCode() 计算出 hash 值,根据 hash 值将 value 保存在 bucket 里。
3.当计算出的 hash 值相同时,我们称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。
4.当 hash 冲突的个数比较少时,使用链表否则使用红黑树。
5.HashMap 对应的线程安全类是 ConcurrentHashMap

26.Map
1.你看过hashMap源码吗

hashMap 采用 Entry 数组来存储key-vlaue键值对,每个键值对组成了一个Entry实体,Entry类实际上是一个单项链表结构,它具有Next指针,可以连接Entry实体 ,JDK8中链表长度大于8的时候,链表转成红黑树

2.为什么用数组+链表?

数组是用来确定桶的位置,利用元素的key的hash值对数组长度取模得到.

链表是用来解决hash冲突问题,当出现hash值一样的情形,就在数组上的对应位置形成一条链表。ps:这里的hash值并不是指hashcode,而是将hashcode高低十六位异或过的。至于为什么要这么做,继续往下看。

3.那么HashMap什么时候进行扩容呢?

数组的默认值为十六 当hashMap中这个元素个数超过数据默 * 认值加载因子(16*0.75 的结果也叫临界值),这个时候就把数组的大小扩展两倍(16 * 2=32) ,然后重新计算每个元素在数组中的位置

4.负载因子的值大小,对hashMap有什么影响
  • 负载因子的大小决定了HashMap的数据密度

  • 负载因子越大密度越大,发生碰撞的几率越高,数组中的链表越容易长,

    造成查询或插入时的比较次数增多,性能会下降

  • 负载因子越小,就越容易触发扩容,数据密度也越小,意味着发生碰撞的

    几率越小,数组中的链表也就越短,查询和插入时比较的次数也越小,性

    能会更高。但是会浪费一定的内容空间。而且经常扩容也会影响性能,建

    议初始化预设大一点的空间。

  • 按照其他语言的参考及研究经验,会考虑将负载因子设置为0.7~0.75,此

    时平均检索长度接近于常数。

总结:

1.HashMap map = new HashMap();//默认情况下,先不创建长度为16的数组

2.当首次调用map.put()时,再创建长度为16的数组

3.数组为Node类型,在jdk7中称为Entry类型

4.形成链表结构时,新添加的key-value对在链表的尾部(七上八下)

5.当数组指定索引位置的链表长度>8时,且map中的数组的长度> 64时,此索引位置

上的所有key-value对使用红黑树进行存储。

27.ArrsayList的底层实现

1.看看ArrayList的属性 初始容量为0,指定该ArrayLIst容量为0时返回空数组,可以看出ArrayList使用的是懒加载,当第一次添加元素进ArrayList数组进行扩容

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Was9mSp-1655137869468)(H:\学习笔记\java笔记\复习图片文件\面试总结\1.png)]

根据图中可以清晰的看出ArrayList底层其实就是一个数组,ArrayLIst有扩容这个概念,正因为它的扩容,所以能够实现动态的实现增长

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xtLud0J3-1655137869472)(H:\学习笔记\java笔记\复习图片文件\面试总结\2.png)]

由图可以看出,如果不指定容量使用空参构造器创建一个空的数组

只有调用Add方法添加数据的时候才开始进行扩容、

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1PNECDR3-1655137869474)(H:\学习笔记\java笔记\复习图片文件\面试总结\3.png)]

点击Add方法

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    //可以看到Add方法很简单都没什么,点击ensureCapacityInternal() 方法
    
  
       private void ensureCapacityInternal(int minCapacity) {    //参数为1
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    
    //点击calculateCapacity()方法可以看出
        private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {   //给数组赋值为一个空数组
            return Math.max(DEFAULT_CAPACITY, minCapacity);  参数110, 参数二为1  
        }
        return minCapacity;  //数组第一次扩容
    }
    
    
    //点击max方法
        public static int max(int a, int b) {  //参数1为10, 参数二为1
        return (a >= b) ? a : b;    三元运算符  返回10
    }
    
    
    //返回点击ensureExplicitCapacity()方法
        private void ensureExplicitCapacity(int minCapacity) {  //参数为10
        modCount++;          //记录集合被修改次数

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)  // 10 - 0 true  
            grow(minCapacity);             //参数为10  elementData大小不够用就调用grow()去扩容
    }
    
    //进入grow(minCapacity) 方法
    
        private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;   //给oldCapacity赋值=0
        int newCapacity = oldCapacity + (oldCapacity >> 1);  // 0+ (0*0.5)
        if (newCapacity - minCapacity < 0)   //条件满足
            newCapacity = minCapacity;    
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);   //开始扩容 长度为10
          // 为什么使用  Arrays.copyOf  因为它可以保留数据增加空间
    }
    
    
    
    
    

第二次添加元素的时候,也会去判断是否需要扩容

    //返回点击ensureExplicitCapacity()方法
        private void ensureExplicitCapacity(int minCapacity) {  //参数为10
        modCount++;          //记录集合被修改次数

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)  // 10 - 0 true  
            grow(minCapacity);             //参数为10  elementData大小不够用就调用grow()去扩容
    }

28.HashMap与HashTable有那些区别

HashMap

  1. 线程不安全,
  2. 效率高
  3. 可以允许key为null,存放在数组第0个位置

HashTable

  1. 线程安全,
  2. 效率低
  3. 不允许key为空,存放为null直接报错

4.Mybatis面试题

1.Mybatis是什么?
  • Mybatis 是一个半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement 等繁杂的过程。程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高。
  • MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
2.Mybatis优缺点

优点:

1.基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用

2.与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)

3.提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护

4.能够与Spring很好的集成

缺点

  • SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求
  • SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库
3.Hibernate 和 MyBatis 的区别

相同点:都是对JDBC的封装,都是持久层框架,都用于持久层开发

不同点:

映射关系:

  • Mybatis 是一个半自动映射框架,配置java对象与Sql语句执行结果的对应关系,多表关联配置简单
  • Hibernate 是一个全表映射的框架,配置Java对象与数据库表的对应关系,多表关联关系配置复杂
4. SQL优化和移植性

​ Hibernate 对SQL语句封装,提供了日志、缓存、级联(级联比 MyBatis 强大)等特性,此外还提供 HQL(Hibernate Query Language)操作数据库,数据库无关性支持好,但会多消耗性能。如果项目需要支持多种数据库,代码开发量少,但SQL语句优化困难。

MyBatis 需要手动编写 SQL,支持动态 SQL、处理列表、动态生成表名、支持存储过程。开发工作量相对大些。直接使用SQL语句操作数据库,不支持数据库无关性,但sql语句优化容易。

5.ORM是什么
  • ORM(Object Relational Mapping),对象关系映射,是一种为了解决关系型数据库数据与简单Java对象(POJO)的映射关系的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。
6.为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?

Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。

而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。

7.传统的JDBC开发纯在什么问题?
  • 频繁创建数据库连接对象、释放,容易造成系统资源浪费,影响系统性能。可以使用连接池解决这个问题。但是使用jdbc需要自己实现连接池。
  • sql语句定义、参数设置、结果集处理存在硬编码。实际项目中sql语句变化的可能性较大,一旦发生变化,需要修改java代码,系统需要重新编译,重新发布。不好维护。
  • 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护
  • 结果集处理存在重复代码,处理麻烦。如果可以映射成Java对象会比较方便。
8.JDBC编程有哪些不足之处
  • 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库连接池可解决此问题。 解决方案 : 在mybatis-config.xml中配置数据链接池,使用连接池管理数据库连接。
  • Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码 :解决方案: 将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
  • 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。 解决方案: Mybatis自动将java对象映射至sql语句。
  • 对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。 解决方案: Mybatis自动将sql执行结果映射至java对象
9.MyBatis和Hibernate的适用场景?
  • MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。
  • 对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。

开发难易程度和学习成本

  • Hibernate 是重量级框架,学习使用门槛高,适合于需求相对稳定,中小型的项目,比如:办公自动化系统
  • MyBatis 是轻量级框架,学习使用门槛低,适合于需求变化频繁,大型的项目,比如:互联网电子商务系统

总结:

  • MyBatis 是一个小巧、方便、高效、简单、直接、半自动化的持久层框架,
  • Hibernate 是一个强大、方便、高效、复杂、间接、全自动化的持久层框架。

10.MyBatis编程步骤是什么样的?
  • 创建SqlSessionFactory
  • 通过SqlSessionFactory创建SqlSession
  • 通过sqlsession执行数据库操作
  • 调用session.commit()提交事务
  • 调用session.close()关闭会话
11.说说mybatis的工作原理
  • 读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。
  • 加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
  • 构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
  • 创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
  • 执行查询器: 它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
  • 输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。
  • 输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。
12.Mybatis都有哪些Executor执行器?它们之间的区别是什么?

Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。

SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。

ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。

BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。

13.Mybatis中如何指定使用哪一种Executor执行器?

在Mybatis配置文件中,在设置(settings)可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数,如SqlSession openSession(ExecutorType execType)。

配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。

14.Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false

它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。

15.#{}和${}的区别
  • #{}是占位符,预编译处理;${}是拼接符,字符串替换,没有预编译处理。
  • Mybatis在处理#{}时,#{}传入参数是以字符串传入,会将SQL中的#{}替换为?号,调用PreparedStatement的set方法来赋值。
  • #{} 可以有效的防止SQL注入,提高系统安全性;${} 不能防止SQL 注入
16.模糊查询like语句该怎么写
  1. ’%${question}%’ 可能引起SQL注入,不推荐
  2. “%”#{question}“%” 注意:因为#{…}解析成sql语句时候,会在变量外侧自动加单引号’ ',所以这里 % 需要使用双引号" ",不能使用单引号 ’ ',不然会查不到任何结果。
17.在mapper中如何传递多个参数

方法1:顺序传参代码如下:

public User selectUser(String name, int deptId);

<select id="selectUser" resultMap="UserResultMap">
    select * from user
    where user_name = #{0} and dept_id = #{1}
</select>

  • #{}里面的数字代表传入参数的顺序。
  • 这种方法不建议使用sql层表现不直观,一旦调用顺序调整就很容易出错

方法2: @Param注解传参法

public User selectUser(@Param("userName") String name, int @Param("deptId") deptId);

<select id="selectUser" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>

  • #{}里面的名称对应的是注解@Param括号里面修饰的名称。
  • 这种方法参数不多的情况下比较直观 推荐使用

方法3.Map传参法

public User selectUser(Map<String, Object> params);

<select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>
  • #{}里面的名称对应的是Map里面的key名称
  • 这种方法合适传递多个参数,且参数异变能灵活的传递情况(推荐使用)

方法4:java Bean传参法

public User selectUser(User user);

<select id="selectUser" parameterType="com.jourwon.pojo.User" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>

  • #{} 里面的名称对应的是User类里面的成员属性
  • 这种方法很直观,一个实体类,扩展不容易,需要添加属性,但代码可读性强,业务逻辑处理方便推(荐使用)
18.Mybatis如何执行批量操作

使用foreach标签

foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach标签的属性主要有item,index,collection,open,separator,close。

  • item   表示集合中每一个元素进行迭代时的别名,随便起的变量名;
  • index   指定一个名字,用于表示在迭代过程中,每次迭代到的位置,不常用;
  • open   表示该语句以什么开始,常用“(”;
  • separator 表示在每次进行迭代之间以什么符号作为分隔符,常用“,”;
  • close   表示以什么结束,常用“)”。

在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:

  1. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
  2. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
  3. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Map的,
    map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key

具体用法如下

!-- 批量保存(foreach插入多条数据两种方法)
       int addEmpsBatch(@Param("emps") List<Employee> emps); -->
<!-- MySQL下批量保存,可以foreach遍历 mysql支持values(),(),()语法 --> //推荐使用

<insert id="addEmpsBatch">
    INSERT INTO emp(ename,gender,email,did)
    VALUES
    <foreach collection="emps" item="emp" separator=",">
        (#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})
    </foreach>
</insert>
复制代码<!-- 这种方式需要数据库连接属性allowMutiQueries=true的支持
 如jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true -->  
 
<insert id="addEmpsBatch">
    <foreach collection="emps" item="emp" separator=";">                                 
        INSERT INTO emp(ename,gender,email,did)
        VALUES(#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})
    </foreach>
</insert>


19.如何获取生成的主键
  • 新增标签中添加:keyProperty=" ID " 即可 mapper接口在返回一个int类型就能获取的到
<insert id="insert" useGeneratedKeys="true" keyProperty="userId" >
    insert into user( 
    user_name, user_password, create_time) 
    values(#{userName}, #{userPassword} , #{createTime, jdbcType= TIMESTAMP})
</insert>

20当实体类中的属性名和表中的字段名不一样 ,怎么办

第一种: 通过在查询的SQL语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。

<select id="getOrder" parameterType="int" resultType="com.jourwon.pojo.Order">
       select order_id id, order_no orderno ,order_price price form orders where order_id=#{id};
</select>

第2种: 通过``来映射字段名和实体类属性名的一一对应的关系。

select id="getOrder" parameterType="int" resultMap="orderResultMap">
	select * from orders where order_id=#{id}
</select>
    
<resultMap type="com.jourwon.pojo.Order" id="orderResultMap">
    <!–用id属性来映射主键字段–>
    <id property="id" column="order_id">
    
    <!–用result属性来映射非主键字段,property为实体类属性名,column为数据库表中的属性–>
	<result property ="orderno" column ="order_no"/>
	<result property="price" column="order_price" />
</reslutMap>
21.什么是MyBatis的接口绑定?有哪些实现方式?
  • 接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置
    1. 通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;
    2. 通过xml里面写SQL来绑定, 在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名。当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多。
22.使用MyBatis的mapper接口调用时有哪些要求?

Mapper接口方法名和mapper.xml中定义的每个sql的id相同。00000000000000000000000000000000000000

Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同。

Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。

Mapper.xml文件中的namespace即是mapper接口的类路径

23.这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗
  1. Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行结果返回。
  2. Dao接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。
24.Mybatis是否可以映射Enum枚举类?
  • Mybatis可以映射枚举类,不单可以映射枚举类,Mybatis可以映射任何对象到表的一列上。映射方式为自定义一个TypeHandler,实现TypeHandler的setParameter()和getResult()接口方法
  • TypeHandler有两个作用,一是完成从javaType至jdbcType的转换,二是完成jdbcType至javaType的转换,体现为setParameter()和getResult()两个方法,分别代表设置sql问号占位符参数和获取列查询结果
25.Mybatis的一级、二级缓存

1一级缓存原理

Mybatis的一级缓存是指同一个SqlSession中的操作。一级缓存的作用域是一个SqlSession。
在同一个SqlSession中,执行相同的查询SQL,第一次会去查询数据库,并写到缓存中;第二次直接从缓存中取。当执行SQL时两次查询中间发生了增删改操作,则SqlSession的缓存清空

二级缓存原理

Mybatis的二级缓存是指mapper映射文件。二级缓存是多个sqlSession共享的,其作用域是mapper下的同一个namespace。
在不同的sqlSession中,相同的namespace下,相同的查询sql语句并且参数也相同的情况下,会命中二级缓存。如果调用相同namespace下的mapper映射文件中的增删改SQL,并执行了commit操作。此时会清空该namespace下的二级缓存

2二级缓存

二级缓存机制与一级缓存相同,作用域不同,一级缓存作用域为SqlSession,二级缓存作用域Mapper 默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置`` 标签

3.缓存的更新机制

对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 清空

26.序列化以反序列化

1.序列化思想

序列化与反序列化是开发过程中不可缺少的一步,简单的来说,序列化是将对象转换成为字节流的过程,反序列化,是将字节流恢复成对象的过程。

2.使用场景

1.对象的持久化(将对象内容保存到数据库或者文件中)

2.远程数据传输(将对象 发送给其他计算机系统)

3.为什么要序列化和反序列化

序列化与反序列化主要解决的是数据的一致性,简单的说就是输入是什么样输出就是什么样

4.Redis面试题?

1.什么是Redis?

Redis 是一个使用 C 语言写成的,开源的高性能key-value非关系缓存数据库。它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。Redis的数据都基于缓存的,所以很快,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。Redis也可以实现数据写入磁盘中,保证了数据的安全不丢失,而且Redis的操作是原子性的

2.Redis有哪些优缺点?

优点:

  • 读写性能优异, Redis能读的速度是110000次/s,写的速度是81000次/s。

  • 支持数据持久化,支持AOF和RDB两种持久化方式。

  • 支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。

  • 数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构。

  • 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。

缺点

  • 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上
  • Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复
  • 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
  • Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
3.使用redis有哪些好处?
  1. 读写速度快, 因为数据存在内存中,
  2. 支持丰富的数据类型, 支持string,list,set,sorted set,hash
  3. 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
  4. 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
4.为什么要用redis/为什么要用缓存

从两点出发去理解,高性能和高并发

  • 高性能

    • 假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在数缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可
  • 高并发

    • 直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。
5.Redis为什么这么快
  1. 完全基于内存, 绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中
  2. 数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的;
  3. 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
  4. 使用多路 I/O 复用模型,非阻塞 IO;
  5. 使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
6.Redis有哪些数据类型
  1. list
  2. string
  3. set 无序集合
  4. hash 键值对散列表
  5. zset 有序集合
7.Redis的应用场景
  1. 计数器

    可以对 String 进行自增自减运算,从而实现计数器功能。Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。

  2. 缓存

    将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率。

  3. 会话缓存

  4. 分布式锁实现

  5. Set 可以实现交集、并集等操作,从而实现共同好友等功能。ZSet 可以实现有序性操作,从而实现排行榜等功能

8.持久化
  1. 什么是Redis持久化? 持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
9.Redis 的持久化机制是什么?各自的优缺点?
  1. Redis 提供两种持久化机制 RDB(默认) 和 AOF 机制:

RDB:是Redis DataBase缩写快照

  • RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。
  • 优点:
    1. 只有一个rdb文件方便持久化
    2. 容灾好,一个文件可以安全保存保存磁盘种
    3. 性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能
    4. 相对于数据集大时,比 AOF 的启动效率更高
  • 缺点:
    1. 数据安全性低,RDB是间隔一段时间进行持久化, 如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候)
    2. AOF(Append-only file)持久化方式: 是指所有的命令行记录以 redis 命令请 求协议的格式完全持久化存储)保存为 aof 文件。

AOF:持久化:

  1. AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。
  2. 当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复

优点

1.  数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次 命令操作就记录到 aof 文件中一次。 
2.  通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。
3.  AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令 进行合并重写),可以删除其中的某些命令(比如误操作的 flushall)) 

缺点:

1、AOF 文件比 RDB 文件大,且恢复速度慢。
2、数据集大的时候,比 rdb 启动效率低。

各自优缺点

AOF文件比RDB更新频率高,优先使用AOF还原数据。
AOF比RDB更安全也更大
RDB性能比AOF好
如果两个都配了优先加载AO

10.如何选着持久化方式
  • 一般来说, 如果想达到足以媲美PostgreSQL的数据安全性,你应该同时使用两种持久化功能。在这种情况下,当 Redis 重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
  • 如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化
  • 有很多用户都只使用AOF持久化,但并不推荐这种方式,因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用RDB还可以避免AOF程序的bug。
  • 如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。
11.Redis的过期键的删除策略

我们都知道,Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理

  • 定时过期 : 每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。

  • 惰性过期 : 只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。

  • 定期过期 :每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。 (expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。

    `Redis中同时使用了惰性过期和定期过期两种过期策略。

Redis key的过期时间和永久有效分别怎么设置?

expire和persist命令

12.Redis主要消耗什么物理资源?
  1. 内存
13.Redis的内存用完了会发生什么?
  • 如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。
14.事务

1.什么是事务?

  • 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
  • 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

2.Redis事务的概念

  • Redis 事务的本质是通过MULTI、EXEC、WATCH等一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
  • 总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。

3.Redis事务的三个阶段

  1. 事务开始 MULTI
  2. 命令入队
  3. 事务执行 EXEC

4.事务管理

  1. 原子性
  2. 一致性
  3. 隔离性
  4. 持久性
15.Redis事务支持隔离性吗
  • Redis 是单进程程序,并且它保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事务队列中的命令为止。因此,Redis 的事务是总是带有隔离性的
16.什么是redis穿透?

缓存穿透的产生: 就是用户请求透过redis去请求mysql服务器,导致mysql压力过载,单一个web服务里,极容易 极容易出现瓶颈的就是mysql,所以才让redis去分担mysql 的压力,所以这种问题是万万要避免的

解决方法:

  1. 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
  2. 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
  3. 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力
17.什么是redis雪崩?
  1. 就是redis服务由于负载过大而宕机,导致mysql的负载过大也宕机,最终整个系统瘫痪
  2. 解决方法:
    • redis集群,将原来一个人干的工作,分发给多个人干
    • 缓存预热(关闭外网访问,先开启mysql,通过预热脚本将热点数据写入缓存中,启动缓存。开启外网服务)
    • 数据不要设置相同的生存时间,不然过期时,redis压力会大
18.数据一致性

无论时先更新数据库在删除缓存,还是删除缓存在更新数据库,在并发环境的条件下都有可能纯在问题

假如A,B两个并发请求:

  • 先更新数据库在删除缓存,当请求A更新数据库之后还没来得的急删除缓存,此时请求B查询到的并使用缓存的旧数据
  • 先删除缓存在更新数据,当请求A执行清楚缓存后,还未进进行数据库更新,此时请求B进行查询,查询到就的数据更新到缓存中,

通过分析,尽量的操作数据库在操作缓存,但是即使这样也存在问题,解决问题的办法就是延迟双删

延迟双删: 先执行缓存清除操作,在执行数据库更新操作,延迟N秒后在执行一次缓存清除操作,这样就不担心缓存中的数据和数据库中的数据不一致了。

5.Spring面试题

1.什么是spring?
  • Spring是一个轻量级Java开发框架
  • 解决企业应用开发复杂性,简化开发
  • 两大核心功能 : **依赖注入(dependency injection,DI)*和*面向切面编程(aspect-oriented programming,AOP)

为了降低Java开发的复杂性,Spring采取了以下4种关键策略

  • 基于POJO的轻量级和最小侵入性编程;
  • 通过依赖注入和面向接口实现松耦合;
  • 基于切面和惯例进行声明式编程;
  • 通过切面和模板减少样板式代码。
2.Spring的俩大核心概念
  • IOC(控制反转)
  • AOP(面向切面编程)
3.Spring框架的设计目标,设计理念,和核心是什么
  • Spring设计目标:Spring为开发者提供一个一站式轻量级应用开发平台;
  • Spring设计理念:在JavaEE开发中,支持POJO和JavaBean开发方式,使应用面向接口开发,充分支持OOP(面向对象)设计方法;Spring通过IOC容器实现对象耦合关系的管理,并实现依赖反转,将对象之间的依赖关系交给IOC容器,实现解耦;
  • Spring框架的核心:IOC容器和AOP模块。通过IOC容器管理POJO对象以及他们之间的耦合关系;通过AOP以动态非侵入的方式增强服务。
  • IOC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。
4.Spring的优缺点是什么?

优点:

  • 方便解耦,简易开发: Spring就是一个打工厂,可以将所有对象的创建和依赖关系的维护,交给Spring管理
  • AOP编程的支持 : Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。
  • 声明式事务的支持: 只需要通过配置就可以完成对事务的管理,而无需手动编程。
  • 方便程序的测试 : Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架的直接支持(如:Struts、Hibernate、MyBatis等)。
  • 降低JavaEE API的使用难度 : Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低。

缺点:

  • Spring明明一个很轻量级的框架,却给人感觉大而全
  • Spring依赖反射,反射影响性能
  • 使用门槛升高,入门Spring需要较长时间
5.Spring 框架中都用到了哪些设计模式?

工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;

单例模式:Bean默认为单例模式。

代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;

模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。

观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。

6.什么是Spring IOC 容器?
  • 控制反转即IOC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。
  • Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。
7.控制反转(IOC)有什么作用
  • 管理对象的创建和依赖关系的维护。对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的
  • 解耦,由容器去维护具体的对象
  • 托管了类的产生过程,比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的
8.bean的注入方式有哪几种
  1. Set方法注入: 任意修改不会创建一个新的实例 可以设置少量属性
  2. 构造器注入: 任意修改会创建一个新的实例 可以设置大量属性
9.解释Spring支持的几种bean的作用域(面试问到过)

Spring框架支持以下五种bean的作用域:

  • singleton : bean在每个Spring ioc 容器中只有一个实例。
  • prototype:一个bean的定义可以有多个实例。每一次调用都会创建
  • request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
  • session:每一次 HTTP 请求都会产生一个新的实例,并且该 bean 仅在当前 HTTP 请求内有效。Session - 每一次 HTTP 请求都会产 生一个新的 bean,同时该 bean 仅在当前 HTTP session 内有效
  • global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
10.Spring框架中的单例bean是线程安全的吗?(面试问道过)
  • 不是,Spring框架中的单例bean不是线程安全的。

  • spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理

  • 实际上大部分时候 spring bean 无状态的(比如 dao 类),所有某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。

    • 有状态就是有数据存储功能。
    • 无状态就是不会保存数据。
11.bean的生命周期
  1. 实例化bean对象 (反射创建)
  2. 设置对象属性
  3. 调用bean的初始化方法 (1.检查Awnre相关接口并设计相关依赖,)
  4. 使用bean
  5. 容器关闭前调用bean的销毁方法
12.什么是bean装配?
  • 装配,或bean 装配是指在Spring 容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。
13.什么是bean的自动装配?

在Spring框架中,在配置文件中设定bean的依赖关系是一个很好的机制,Spring 容器能够自动装配相互合作的bean,这意味着容器不需要和配置,能通过Bean工厂自动处理bean之间的协作。这意味着 Spring可以通过向Bean Factory中注入的方式自动搞定bean之间的依赖关系。自动装配可以设置在每个bean上,也可以设定在特定的bean上

14.你可以在Spring中注入一个null 和一个空字符串吗?

可以

<bean id="myConverter"
	class="com.mkyong.convert.MoneyConverter">
	<property name="typeMapper">
	<null/>
	</property>
  </bean>

15.怎样开启注解装配?
  • 注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在Spring配置文件中配置 ``元素。

  • <context:annotation-config/>
    
    
16.@Component, @Controller, @Repository, @Service 有何区别?
  • @Component:这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。
  • @Controller:这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 IOC 容器中。
  • @Service:此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component,因为它以更好的方式指定了意图。
  • @Repository:这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IOC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。
17.@Autowired 注解有什么作用

@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。@Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。它的用法和@Required一样,修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。

18.@Qualifier 注解有什么作用
  • 当您创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,您可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。
19.@RequestMapping 注解有什么用?

@RequestMapping 注解用于将特定 HTTP 请求方法映射到将处理相应请求的控制器中的特定类/方法。此注释可应用于两个级别:

  • 类级别:映射请求的 URL
  • 方法级别:映射 URL 以及 HTTP 请求方法
20.Spring支持的事务管理类型, spring 事务实现方式有哪些?
  • Spring支持两种类型的事务管理:
    • 编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。
    • 声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。
21.说一下 spring 的事务隔离?

spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:

  1. ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;

    2.ISOLATION_READ_UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);

    3.ISOLATION_READ_COMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;

    4.ISOLATION_REPEATABLE_READ:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;

    5.ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。

22.什么是AOP

般称为面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。

23.JDK动态代理和CGLIB动态代理的区别
  • JDK动态代理只提供接口的代理,不都要提供代理类,他会自动盛成代理类
  • 当没有接口,就会选择 CGLIB来动态代理 通过继承的方式来做动态代理 ,
24.解释一下Spring AOP里面的几个名词
  • 切面( Aspect ) : 切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。
  • 连接点(Join point) :指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。 应用可能有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
  • 通知(Advice): 在AOP术语中,切面的工作被称为通知。
  • 切入点(Pointcut):
25.Spring通知有哪些类型?

Spring切面可以应用5种类型的通知:

  1. 前置通知(Before):在目标方法被调用之前调用通知功能;
  2. 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
  3. 返回通知(After-returning ):在目标方法成功执行之后调用通知;
  4. 异常通知(After-throwing):在目标方法抛出异常后调用通知;
  5. 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
26.BeanFactory 和 ApplicationContext有什么区别?(面试遇到过)
  • BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。

    从依赖关系看:

    • BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。

      ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:

      • 继承MessageSource,因此支持国际化。
      • 统一的资源文件访问方式。
      • 提供在监听器中注册bean的事件。
      • 同时加载多个配置文件。
      • 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

      加载方式来看:

    • ​ BeanFactroy采用的是延迟加载形式来注入Bean的 ,只有调用到某个bean时才将这个bean实例化 如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。

    • ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。 这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。

    注册方式来看:

    BeanFactory需要手动注册,而ApplicationContext则是自动注册。

27.Spring框架中的单例bean是线程安全的吗?

不是,Spring框架中的单例bean不是线程安全的。

spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。

实际上大部分时候 spring bean 无状态的(比如 dao 类),所有某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。

  • 有状态就是有数据存储功能。
  • 无状态就是不会保存数据。
28.扫描是依赖注入

在依赖注入中你不必创建对象,但是必须如何描述创建他们,不是直接在不代码中将组件和服务连接在一起,而是描述配置文件中那些组件需要那些服务,由IOC容器将他们装配在一起

29.@Transactional失效场景

1.失效一

同一个类中方法调用@Transactional就会失效,比如一个方法A,A调用本类的方法B(不论方法B是用public还是prlivate修饰),但是方法A没有声明注解事务,而B方法有。则外部调用方法A之后。方法B事务是不会起作用的,这就是常犯错误

2失效二

如果Transactional注解应用在public修饰的方法上,Transactional失效

3失效三

数据库引擎不支持事务,这种情况出现并不高,事务能否生效数据支持事务时关键,常用的Mysql数据库默认使用支持事务的innodb引擎。一旦数据引擎换成不支持myisam,那事务从根本失效了

6.SringMVC面试题

1.什么是Spring MVC?简单介绍下你对Spring MVC的理解?
  • Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把模型-视图-控制器分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。
2.Spring MVC的优点
  1. 可以支持各种视图技术, 而不仅仅局限于JSP;
  2. 与Spring框架集成(如IoC容器、AOP等);
  3. 晰的角色分配:前端控制器(dispatcherServlet) , 请求到处理器映射(handlerMapping), 处理器适配器(HandlerAdapter), 视图解析器(ViewResolver)。
  4. 支持各种请求资源的映射策略。
3.Spring MVC的主要组件?
  1. 前端控制器 DispatcherServlet (不需要程序员开发)
    • 作用: 接收请求、响应结果,相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。
  2. 处理器映射器HandlerMapping(不需要程序员开发)
    • 作用:根据请求的URL来查找Handler
  3. 处理器适配器HandlerAdapter
    • 注意:在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。
  4. 处理器Handler(需要程序员开发)
  5. 视图解析器 ViewResolver(不需要程序员开发)
    • 作用:进行视图的解析,根据视图逻辑名解析成真正的视图(view)
  6. 视图View(需要程序员开发jsp)
    • View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)
4.什么是DispatcherServlet
  • Spring的MVC框架是围绕DispatcherServlet来设计的,它用来处理所有的HTTP请求和响应。
5.什么是Spring MVC框架的控制器?
  • 控制器提供一个访问应用程序的行为,此行为通常通过服务接口实现。控制器解析用户输入并将其转换为一个由视图呈现给用户的模型。Spring用一个非常抽象的方式实现了一个控制层,允许用户创建多种用途的控制器。
6.Spring MVC的控制器是不是单例模式,如果是,有什么问题,怎么解决?
  • 答:是单例模式,所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能的,解决方案是在控制器里面不能写字段
7.请描述Spring MVC的工作流程?描述一下 DispatcherServlet 的工作流程?
  1. 用户发送请求至前端控制器DispatcherServlet;
  2. DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
  3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
  4. DispatcherServlet 调用 HandlerAdapter处理器适配器;
  5. HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器);
  6. Handler执行完成返回ModelAndView;
  7. HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
  8. DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
  9. ViewResolver解析后返回具体View;
  10. DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
  11. DispatcherServlet响应用户。

简单的说:

1.用户发送器请求 到DispatcherServlet 接受请求

2.DispatcherServlet 给处理器映射器发送请求 根据请求的URL来查询 然后返回Handler

3.处理器适配器请求执行Handler 调用方法处理业务逻辑

4.返回视图ModelAndView

5.通过视图名称查找视图 返回视图对象

6.进行视图的渲染,返回渲染的视图,返回给前端

8.MVC是什么?MVC设计模式的好处有哪些
  • mvc是一种设计模式(设计模式就是日常开发中编写代码的一种好的方法和经验的总结)。模型(model)-视图(view)-控制器(controller),三层架构的设计模式。用于实现前端页面的展现与后端业务数据处理的分离

  • mvc设计模式的好处

    • 分层设计,实现了业务系统各个组件之间的解耦,有利于业务系统的可扩展性,可维护性。
    • 有利于系统的并行开发,提升开发效率。
9.Spring MVC常用的注解有哪些?
  1. @RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。
  2. @RequestBody:注解实现接收http请求的json数据,将json转换为java对象。
  3. @ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。
  4. @Conntroller:控制器的注解,表示是表现层,不能用用别的注解代替
10.SpingMvc中的控制器的注解一般用哪个,有没有别的注解可以替代?

一般用@Controller注解,也可以使用@RestController,@RestController注解相当于@ResponseBody + @Controller,表示是表现层,除此之外,一般不用别的注解代替。

11.如何解决POST请求中文乱码问题,GET的又如何处理呢?

在web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf-8;

<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
    <param-name>encoding</param-name>
    <param-value>utf-8</param-value>
</init-param>
</filter>

<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

12.如果在拦截请求中,我想拦截get方式提交的方法,怎么配置
  • 答:可以在@RequestMapping注解里面加上method=RequestMethod.GET。
13.怎样在方法里面得到Request,或者Session?

直接在方法的形参中声明request,Spring MVC就自动把request对象传入。

14.如果想在拦截的方法里面得到从前台传入的参数,怎么得到?

直接在形参里面声明这个参数就可以,但必须名字和传过来的参数一样。

15.Spring MVC中函数的返回值是什么?
  • 答:返回值可以有很多类型,有String, ModelAndView。ModelAndView类把视图和数据都合并的一起的,但一般用String比较好
16.怎么样把ModelMap里面的数据放入Session里面?
  • 答:可以在类上面加上@SessionAttributes注解,里面包含的字符串就是要放入session里面的key。
17.Spring MVC里面拦截器是怎么写的

有两种写法,一种是实现HandlerInterceptor接口,另外一种是继承适配器类,接着在接口方法当中,实现处理逻辑;然后在Spring MVC的配置文件中配置拦截器即可:

<!-- 配置Spring MVC的拦截器 -->
<mvc:interceptors>
    <!-- 配置一个拦截器的Bean就可以了 默认是对所有请求都拦截 -->
    <bean id="myInterceptor" class="com.zwp.action.MyHandlerInterceptor"></bean>
    <!-- 只针对部分请求拦截 -->
    <mvc:interceptor>
       <mvc:mapping path="/modelMap.do" />
       <bean class="com.zwp.action.MyHandlerInterceptorAdapter" />
    </mvc:interceptor>
</mvc:interceptors>

18.SpringBoot自动配置原理是什么

1.点开SpringBootApplication 里面有三个注解

  1. @SpringBootConfiguration
  2. @EnableAutoConfiguration
  3. @ComponentScan

2.点开EnableAutoConfiguration 里面有个selectImports方法 就去从配置文件 META_INF/Spring.factories加载可能用到的自动配置类 里面的都是类的全名。加载进入ioc容器种

19.拦截器的创建 (面试出过)

1.自定义拦截器 实现 HandlerInterceptor 接口 交给Spring管理

2.创建配置类 实现WebMvc Configurer 把拦截器进行注册

7.Mysql面试题

1.什么是SQL?
  • 结构化查询语言(Structured Query Language)简称SQL,是一种数据库查询语言。

作用:用于存储数据,查询更新和管理关系数据库系统

2.什么是MySQL?

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。在Java企业级开发中非常常用,因为 MySQL 是开源免费的,并且方便扩展。

3.数据库三大范式是什么
  • 第一范式:每个列都不可以再拆分。
  • 第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。
  • 第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键

在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由。比如性能。事实上我们经常会为了性能而妥协数据库的设计。

4.数据库经常使用的函数

count(*/column):返回行数

sum(column): 返回指定列中唯一值的和

max(column):返回指定列或表达式中的数值最大值

min(column):返回指定列或表达式中的数值最小值

avg(column):返回指定列或表达式中的数值平均值

date(Expression): 返回指定表达式代表的日期值

5.引擎
MySQL存储引擎MyISAM与InnoDB区别

存储引擎Storage engine:MySQL中的数据、索引以及其他对象是如何存储的,是一套文件系统的实现

常用的存储引擎有以下:

  • Innodb引擎:Innodb引擎提供了对数据库ACID事务的支持。并且还提供了行级锁和外键的约束。它的设计的目标就是处理大数据容量的数据库系统。
  • MyIASM引擎(原本Mysql的默认引擎):不提供事务的支持,也不支持行级锁和外键。
MyISAM索引与InnoDB索引的区别?

InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引。

InnoDB的主键索引的叶子节点存储着行数据,因此主键索引非常高效。

MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据。

InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效。

InnoDB引擎的4大特性
  • 插入缓冲(insert buffer)
  • 二次写(double write)
  • 自适应哈希索引(ahi)
  • 预读(read ahead)
存储引擎选择
  • 如果没有特别的需求,使用默认的Innodb即可。
  • MyISAM:以读写插入为主的应用程序,比如博客系统、新闻门户网站。
  • Innodb:更新(删除)操作频率也高,或者要保证数据的完整性;并发量高,支持事务和外键。比如OA自动化办公系统。
6.什么是索引?
  • 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。
  • 索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。
  • 更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。

索引的优点

  • 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
  • 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能

索引的缺点

  • 时间方面:创建索引和维护索引要耗费时间,具体地,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,会降低增/改/删的执行效率;
  • 空间方面:索引需要占物理空间
7.怎么创建索引的,有什么好处,有哪些分类
  1. 创建索引的语法:create index depe_unique_ide on depe(dept_no) tablespace idx_

索引分类

  1. 单值索引
  2. 主键索引
  3. 唯一索引
  4. 符合索引
8.索引使用场景

当数据多且字段值有相同的值得时候用普通索引。

当字段多且字段值没有重复的时候用唯一索引。

当有多个字段名都经常被查询的话用复合索引。

普通索引不支持空值,唯一索引支持空值。

但是,若是这张表增删改多而查询较少的话,就不要创建索引了,因为如果你给一列创建了索引,那么对该列进行增删改的时候,都会先访问这一列的索引,

若是增,则在这一列的索引内以新填入的这个字段名的值为名创建索引的子集,

若是改,则会把原来的删掉,再添入一个以这个字段名的新值为名创建索引的子集,

若是删,则会把索引中以这个字段为名的索引的子集删掉。

所以,会对增删改的执行减缓速度,

所以,若是这张表增删改多而查询较少的话,就不要创建索引了。

更新太频繁地字段不适合创建索引。

不会出现在where条件中的字段不该建立索引。

9.事务
什么是数据库事务?
  • 事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。
事务的四大特性
  • 原子性:事务最小的执行单元,不允许分割,事务的原子性确保动作要么全部完成要么全部完全不起作用
  • 一致性:执行事务前后,数据保持一致性,多个事务 同一个数据读取的结果是相同的;
  • 隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
  • 持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响
什么是脏读?(面试遇到)

某个事务更新一份数据,另一个事务此时读取了同一份数据,由于某些原因前一个 RollBack 了操作,则后一个事务所读取的数据就会不正确 (简单的说一个事务读取了另一个事务未提交的数据)

什么幻读?(面试遇到)

:在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

简单的说:(一个事务中多次根据同一条件查询出来的记录行数不一致)

什么不可重复读?(面试遇到)

不可重复读是指一个事务内多次根据同一条件查询出来的同一行记录的值不一致

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

可重复读

Oracle默认隔离级别

读已提交

8.java基础面试

1.接口和抽象类的区别

不同点:

1.抽象类:有构造方法 成员变量 成员方法

2.接口:没有构造方法 ,只有成员方法

3.类与接口的区别 :类只能单继承,接口能多继承

相同点:

  • 接口和抽象类都不能实例化
  • 都位于继承的顶端,用于被其他实现或继承
  • 都包含抽象方法,其子类都必须覆写这些抽象方法
2.Java有哪些数据类型

分两类:

  1. 基本数据类型
    • byte
    • short
    • int
    • long
    • float
    • double
    • char
    • boolean
  2. 引用类型
    • 接口
    • 数组
3.final 有什么用?

用于修饰类,属性和方法

  • 被final修饰的类不可以被继承
  • 被final修饰的方法不可以被重写
  • 被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的
4.final finally finalize区别

final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表 示该变量是一个常量不能被重新赋值。

finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块 中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。

finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调 用,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的 最后判断。

5.this关键字的用法

his是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针。

this的用法在java中大体可以分为3种:

  • 1.普通的直接引用,this相当于是指向当前对象本身。
  • 2.形参与成员名字重名,用this来区分:
6.super关键字的用法
  • super可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类。

  • super也有三种用法:

    • 1.普通的直接引用

      与this类似,super相当于是指向当前对象的父类的引用,这样就可以用super.xxx来引用父类的成员。

    • 2.子类中的成员变量或方法与父类中的成员变量或方法同名时,用super进行区分 句

7.this与super的区别
  • super: 它引用当前对象的直接父类中的成员
  • this:它代表当前对象名
  • super()和this()类似,区别是,super()在子类中调用父类的构造方法,this()在本类内调用本类的其它构造方法。
  • super()和this()均需放在构造方法内第一行。
8.static关键字的理解
  • static 关键字是静态的意思,是java中的一个修饰符,可以修饰成员方法,成员变量。
  • static关键字的特点 : staic所修饰的成员被类的所有对象共享
  • 随着类的加载而加载,优先于对象存在
  • 随着类的加载而加载,优先于对象存在
9.使用static的时候需要注意什么?
  • 静态方法只能访问静态的成员
  • 非静态方法可以访问静态的成员,也可以访问非静态的成员
  • 静态方法中是没有this关键字
10.break ,continue ,return 的区别及作用
  • break 跳出总上一层循环,不再执行循环(结束当前的循环体)
  • continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件)
  • return 程序返回,不再执行下面的代码(结束当前的方法 直接返回)
11.说说你对面向对象的理解?
  • 首先要说明一点,对象不是java的,事实上他只是一种概念,一种用于解决问题的程序设计的思想方法
  • 在面向对象中把某一类事物所共有的属性和行为抽象出来之后就形成了一个类。 这个例子就叫对象
  • 所以从上可以看出类和对象之间存在着这样一种关系:类是具有共同的属性名称和行为的一组对象的抽象,而对象则是一个类的真实的例子。
12.面向对象的三大特征

1.封装

把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了

2.继承

是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。

  • 关于继承如下 3 点请记住:
    • 子类拥有父类非 private 的属性和方法。
    • 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
    • 子类可以用自己的方式实现父类的方法。(以后介绍)。

3.多态

父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。提高了程序的拓展性。在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。

  • Java实现多态有三个必要条件:继承、重写、向上转型。
    • 继承:在多态中必须存在有继承关系的子类和父类。
    • 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
    • 向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。
只有满足了上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。
对于Java而言,它多态的实现机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。

13.什么是方法返回值
  • 方法的返回值是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能产生结果)。返回值的作用:接收出结果,使得它可以用于其他的操作!
14.内部类
什么是内部类

在Java中,可以将一个类的定义放在另外一个类的定义内部,这就是内部类。内部类本身就是类的一个属性,与其他属性定义方式一致

内部类的分类有哪些

内部类可以分为四种:成员内部类、局部内部类、匿名内部类和静态内部类

public class Outer {

    private static int radius = 1;

    static class StaticInner {
        public void visit() {
            System.out.println("visit outer static  variable:" + radius);
        }
    }
}

静态内部类可以访问外部类所有的静态变量,而不可访问外部类的非静态变量;静态内部类的创建方式,new 外部类.静态内部类(),如下:

Outer.StaticInner inner = new Outer.StaticInner();
inner.visit();

成员内部类

定义在类内部,成员位置上的非静态类,就是成员内部类。

public class Outer {

    private static  int radius = 1;
    private int count =2;
    
     class Inner {
        public void visit() {
            System.out.println("visit outer static  variable:" + radius);
            System.out.println("visit outer   variable:" + count);
        }
    }
}
复制代码

成员内部类可以访问外部类所有的变量和方法,包括静态和非静态,私有和公有。成员内部类依赖于外部类的实例,它的创建方式外部类实例.new 内部类(),如下:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.visit();

局部内部类

定义在方法中的内部类,就是局部内部类。

public class Outer {

    private  int out_a = 1;
    private static int STATIC_b = 2;

    public void testFunctionClass(){
        int inner_c =3;
        class Inner {
            private void fun(){
                System.out.println(out_a);
                System.out.println(STATIC_b);
                System.out.println(inner_c);
            }
        }
        Inner  inner = new Inner();
        inner.fun();
    }
    public static void testStaticFunctionClass(){
        int d =3;
        class Inner {
            private void fun(){
                // System.out.println(out_a); 编译错误,定义在静态方法中的局部类不可以访问外部类的实例变量
                System.out.println(STATIC_b);
                System.out.println(d);
            }
        }
        Inner  inner = new Inner();
        inner.fun();
    }
}
复制代码

定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义在静态方法中的局部类只能访问外部类的静态变量和方法。局部内部类的创建方式,在对应方法内,new 内部类(),如下:

public static void testStaticFunctionClass(){
    class Inner {
    }
    Inner  inner = new Inner();
 }

匿名内部类

匿名内部类就是没有名字的内部类,日常开发中使用的比较多。

public class Outer {

    private void test(final int i) {
        new Service() {
            public void method() {
                for (int j = 0; j < i; j++) {
                    System.out.println("匿名内部类" );
                }
            }
        }.method();
    }
 }
 //匿名内部类必须继承或实现一个已有的接口 
 interface Service{
    void method();
}
复制代码

除了没有名字,匿名内部类还有以下特点:

  • 匿名内部类必须继承一个抽象类或者实现一个接口。
  • 匿名内部类不能定义任何静态成员和静态方法。
  • 当所在的方法的形参需要被匿名内部类使用时,必须声明为 final。
  • 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

匿名内部类创建方式:

new 类/接口{ 
  //匿名内部类实现部分
}

15.重写与重载
构造器是否可被重写

构造器不能被继承,因此不能被重写,可以重载

重载和重写的区别

重载:发生在同一 个类中,方法名相同参数列表不同(参数类型不同、个数不同、顺序不同),与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分

重写: 发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类(里氏代换原则);如果父类方法访问修饰符为private则子类中就不是重写。

16.== 和 equals 的区别是什么

1、equals()方法用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的却是== 的判断;
2、“==” 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象

public class test1 {
    public static void main(String[] args) {
        String a = new String("ab"); // a 为一个引用
        String b = new String("ab"); // b为另一个引用,对象的内容一样
        String aa = "ab"; // 放在常量池中
        String bb = "ab"; // 从常量池中查找
        if (aa == bb) // true
            System.out.println("aa==bb");
        if (a == b) // false,非同一对象
            System.out.println("a==b");
        if (a.equals(b)) // true
            System.out.println("aEQb");
        if (42 == 42.0) { // true
            System.out.println("true");
        }
    }
}


结果看出,

String中的equals方法是被重写过的,因为object的equals方法是比较的对象的内存地址,而String的equals方法是比较对象的值

17.hashCode与equals之间的区别

1.HashCode如果相等的情况下,对象的值不一定相等

2.但是equals比较对象的内容相同,hashCode一定相等

package com.example.demotest.demo;

public class Demo01 {
    public static void main(String[] args) {
        Integer aint = 97;
        String a = "a";
        String b = "a";
        System.out.println("b = " + b.hashCode());
        System.out.println("aint = " + aint.hashCode());
        System.out.println("a = " + a.hashCode());
        /*  结果显示
        * b = 97
          aint = 97
          a = 97
        * */
    }
}


小结,

  • 可以看出值equals相同,hashCode一定相等
  • hashCode相等 ,equals不一定相等
18.为什么重写equals方法之后,一定要重写HashCode为什么?

1.创建对象

package com.example.demotest.demo;
public class UserDemo {
    private String name;
    private Integer age;
    public UserDemo() {
    }
    public UserDemo(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

2.比较两个对象是否相等

package com.example.demotest.demo;

public class Demo02 {
    public static void main(String[] args) {
        //创建对象
        UserDemo userDemo1 = new UserDemo("来了老弟", 123);
        UserDemo userDemo2 = new UserDemo("来了老弟", 123);
        System.out.println(userDemo1.equals(userDemo2));
        
        /*
         * 结果为 false
         * 没有重写两个对象比较的是内存地址,没new一次内存地址都不一样
         * */
    }
}


3.实体类重写equals方法

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof UserDemo)) return false;
        UserDemo userDemo = (UserDemo) o;
        return Objects.equals(name, userDemo.name) &&
                Objects.equals(age, userDemo.age);
    }


4.重写equals方法后比较相等,但是HashCode值不相等代码如下

public class Demo02 {
    public static void main(String[] args) {
        //创建对象
        UserDemo userDemo1 = new UserDemo("来了老弟", 123);
        UserDemo userDemo2 = new UserDemo("来了老弟", 123);
        System.out.println(userDemo1.equals(userDemo2));
        System.out.println("userDemo1.hashCode() = " + userDemo1.hashCode());
        System.out.println("userDemo2.hashCode() = " + userDemo2.hashCode());

        /*
         * 结果为 true
         * userDemo1.hashCode() = 11483240
         * userDemo2.hashCode() = 21338231
         * */
    }
}


5.可以看出不满足.hashCode与equals公式

必须重写hashCode

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof UserDemo)) return false;
        UserDemo userDemo = (UserDemo) o;
        return Objects.equals(name, userDemo.name) &&
                Objects.equals(age, userDemo.age);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }


public class Demo02 {
    public static void main(String[] args) {
        //创建对象
        UserDemo userDemo1 = new UserDemo("来了老弟", 123);
        UserDemo userDemo2 = new UserDemo("来了老弟", 123);
        System.out.println(userDemo1.equals(userDemo2));
        System.out.println("userDemo1.hashCode() = " + userDemo1.hashCode());
        System.out.println("userDemo2.hashCode() = " + userDemo2.hashCode());

        /*
         * 结果为 true
         * userDemo1.hashCode() = -694020771
         * userDemo2.hashCode() = -694020771
         * */
    }
}


总结:

HashCode相等,对象值不一定相等,如果equals比较对象内容相同,HashCode一定相等

为什么重写equals后要重写HashCode呢?因为重写equals,对象的hashcode不一定相等,必须得重写HashCode保证原则不变

19.IO流
java 中 IO 流分为几种?
  • 按照流的流向分,可以分为输入流和输出流;
  • 按照操作单元划分,可以划分为字节流和字符流;
  • 按照流的角色划分为节点流和处理流
19.反射
什么是反射
  • JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
反射机制优缺点
  • 优点: 运行期类型的判断,动态加载类,提高代码灵活度。
  • 缺点: 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多
Java获取反射的三种方法
  • 通过类对象获取class对象
  • 通过路径获取class对象
  • 通过类名获取class对象
public class Student {
    private int id;
    String name;
    protected boolean sex;
    public float score;
}


public class Get {
    //获取反射机制三种方式
    public static void main(String[] args) throws ClassNotFoundException {
        //方式一(通过建立对象)
        Student stu = new Student();
        Class classobj1 = stu.getClass();
        System.out.println(classobj1.getName());
        //方式二(所在通过路径-相对路径)
        Class classobj2 = Class.forName("fanshe.Student");
        System.out.println(classobj2.getName());
        //方式三(通过类名)
        Class classobj3 = Student.class;
        System.out.println(classobj3.getName());
    }
}


20.常用api
String 是最基本的数据类型吗

不是。Java 中的基本数据类型只有 8 个 :byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(referencetype),Java 5 以后引入的枚举类型也算是一种比较特殊的引用类型

String真的是不可变的吗?

我觉得如果别人问这个问题的话,回答不可变就可以

是否可以继承 String 类
  • String 类是 final 类,不可以被继承。
String str="i"与 String str=new String(“i”)一样吗?
  • 不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。
21.泛型
说说泛型擦除

泛型的擦除:就是假设泛型是Integer 的数据,可以通过反射调用方法add方法就可以添加字符串

代码如下:

public class Test {

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {


        ArrayList<Integer> list = new ArrayList<>();

        list.add(123);
        list.add(456);
        list.add(789);

        Class aClass = list.getClass();

        Method add = aClass.getDeclaredMethod("add", Object.class);
         add.invoke(list, "add");
        System.out.println("add = " + list);  //add = [123, 456, 789, add]


    }
}


小结: ***泛型擦除*就是在擦除前,如果指定了类型,那么使用时会进行类型检查,然后在编译后进行泛型擦除,字节码中已经不存在泛型,泛型相应使用处都使用Object替代。

泛型了解吗?介绍一下泛型?

是JDK5中引入的一种新特性,泛型提供了编译时类型安全检测 该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

泛型实现的原理,为什么要实现泛型

因为对于一些工具类来说,编写时是不知道使用者到时用在哪些类上,所以为了能够自适应类型,就得使用泛型这种机制

工作原理: **泛型只在编译阶段有作用,编译器检验泛型信息之后,**如果没有限定类型,**那么泛型的相应使用处会替换为Object类型。*如果限定了类型,*那么在使用时会先进行类型检查,然后会进行*类型擦除*

22.自动装拆箱
  • 装箱:将基本类型用他们对应的引用类型包装起来
  • 拆箱:将包装类型转换为基本类型:
23.深拷贝和浅拷贝
  1. 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递拷贝,称为浅拷贝
  2. 深拷贝:对基本类型进行值传递,对引用数据类型,创建一个新的对象,并复复制其内容,称为深拷贝
24.java序列化中如果有些字段不想进行序列化怎么办?

使用关键字transient关键字修饰

9.Servlet面试题

1.Servlet的生命周期可以分为5个步骤
  1. 加载Servlet。当Tomcat第一次访问Servlet的时候,Tomcat会负责创建Servlet的实例
  2. 初始化。当Servlet被实例化后,Tomcat会调用init()方法初始化这个对象
  3. 处理服务。当浏览器访问Servlet的时候,Servlet 会调用service()方法处理请求
  4. 销毁。当Tomcat关闭时或者检测到Servlet要从Tomcat删除的时候会自动调用destroy()方法,让该实例释放掉所占的资源。一个Servlet如果长时间不被使用的话,也会被Tomcat自动销毁

简单总结:第一次访问servlet init() 和service() 被调用 第二次访问就调用service() 关掉 Tomcat 就会调用 destroy()

2.get方式和post方式有何区别

区别分析:

数据携带上:

  • GET方式:在URL地址后附带的参数是有限制的,其数据容量通常不能超过1K。
  • POST方式:可以在请求的实体内容中向服务器发送数据,传送的数据量无限制。

请求参数上:

  • GET方式:请求参数放在URL地址后面,以?的方式来进行拼接
  • POST方式:请求参数放在HTTP请求包中
3.Servlet相关 API
1. doGet与doPost方法的两个参数是什么
  1. HttpServletRequest:封装了与请求相关的信息
  2. HttpServletResponse:封装了与响应相关的信息
2. 获取页面的元素的值有几种方式,分别说一下
  1. request.getParameter() 返回客户端的请求参数的值
  2. request.getParameterNames() 返回所有可用属性名的枚举
  3. request.getParameterValues() 返回包含参数的所有值的数组
3. request.getAttribute()和request.getParameter()区别

用途上:

  • request.getAttribute(), 一般用于获取request域对象的数据(在跳转之前把数据使用setAttribute来放到request对象上)
  • request.getParameter(), 一般用于获取客户端提交的参数

存储数据上:

  • request.getAttribute()可以获取Objcet对象
  • request.getParameter()只能获取字符串(这也是为什么它一般用于获取客户端提交的参数)
4.forward和redirect的区别

1.实际发生位置不同,地址栏不同

  • 转发是由服务器进跳转的,浏览器地址栏没有发生变化
  • 重定向是由浏览器进行的跳转的,浏览器地址会发生变化

2.能够去往的URL的范围不一样:

  • 转发是服务器跳转只能去往当前web应用的资源
  • 重定向是服务器跳转,可以去往任何的资源

3.传递数据的类型不同

  • 转发的request对象可以传递各种类型的数据,包括对象
  • 重定向只能传递字符串

那么转发(forward)和重定向(redirect)使用哪一个?

根据上面说明了转发和重定向的区别也可以很容易概括出来。转发是带着转发前的请求的参数的。重定向是新的请求

也就是说携带参数用转发,没有参数随便用

4.tomcat容器是如何创建servlet类实例?用到了什么原理?
  1. 当容器启动时,会读取在webapps目录下所有的web应用中的web.xml文件,然后对 xml文件进行解析,并读取servlet注册信息。然后,将每个应用中注册的servlet类都进行加载,并通过 反射的方式实例化。(有时候也是在第一次请求时实例化)
5.什么是cookie?Session和cookie有什么区别 ?
什么是cookie

Cookie 是个存储在浏览器目录的文本文件,当浏览器运行时,存储在 RAM 中。一旦你从该网站或网络服务器退出,Cookie 也可存储在计算机的硬驱上。当访客结束其浏览器对话时,即终止的所有 Cookie

Session和cookie有什么区别?
  • 从存储方式上比较
    • Cookie只能存储字符串,如果要存储非ASCII字符串还要对其编码。
    • Session可以存储任何类型的数据,可以把Session看成是一个容器

从隐私安全上比较

  • Cookie存储在浏览器中,对客户端是可见的。信息容易泄露出去。如果使用Cookie,最好将Cookie加密
  • Session存储在服务器上,对客户端是透明的。不存在敏感信息泄露问题
从浏览器的支持上比较**
  • 如果浏览器禁用了Cookie,那么Cookie是无用的了!
  • 如果浏览器禁用了Cookie,Session可以通过URL地址重写来进行会话跟踪。
5.Servlet安全性问题

由于Servlet是单例的,当多个用户访问Servlet的时候,服务器会为每个用户创建一个线程当多个用户并发访问Servlet共享资源的时候就会出现线程安全问题

原则:

  1. 如果一个变量需要多个用户共享,则应当在访问该变量的时候,加同步机制synchronized (对象){}
  2. 如果一个变量不需要共享,则直接在 doGet() 或者 doPost()定义.这样不会存在线程安全问题
6.ServletContext对象

web容器在启动的时候,会为每一个web应用创建一个唯一的ServletContext对象,它代表了当前的web应用

1.共享数据

@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 使用ServletContext共享数据
        ServletContext servletContext = this.getServletContext();
        servletContext.setAttribute("username","Vecal");
    }

2.获取初始化参数

<!-- 自定义初始化参数 -->
<context-param>
    <param-name>url</param-name>
    <param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
复制代码
@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String url = context.getInitParameter("url");
        resp.getWriter().print(url);
    }


3.请求转发

// 请求转发
ServletContext context = this.getServletContext();
context.getRequestDispatcher("/initParam").forward(req,resp)

4.读取资源文件

  • 在java目录下新建资源文件
  • 在resources目录下新建资源文件

都会被打包放到了同一目录下:classes,这个路径统称为 classpath: 路径

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 读取配置文件
        ServletContext context = this.getServletContext();
        InputStream in = context.getResourceAsStream("/WEB-INF/classes/db.properties");
        Properties prop = new Properties();
        prop.load(in);

        String username = prop.getProperty("username");
        String password = prop.getProperty("password");

        resp.getWriter().print(username + ":" + password);
    }


7.HttpServletResponse

web服务器在接收到客户端的http请求,针对这个请求,分别创建了一个代表请求的HttpServletRequest对象,一个代表响应的HttpServletResponse对象

  • 如果要获取客户端请求的一些数据参数,使用HttpServletRequest
  • 如果要响应一些数据给客户端,使用HttpServletResponse

1.像浏览器发送数据

ServletOutputStream getOutputStream() throws IOException;

PrintWriter getWriter() throws IOException;

2.设置响应头

void setCharacterEncoding(String var1);

void setContentLength(int var1);

void setContentLengthLong(long var1);

void setContentType(String var1);



3.响应状态码

int SC_OK = 200;
int SC_FOUND = 302;
int SC_BAD_REQUEST = 400;
int SC_INTERNAL_SERVER_ERROR = 500;

4.下载文件

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1.获取图片路径
        String realPath = "D:\\How2JTest\\project\\javaweb-02-servlet\\response\\target\\response\\WEB-INF\\classes\\黄濑凉太.jpg";
        // 2.获取文件名
        String fileName = realPath.substring(realPath.lastIndexOf("/") + 1);
        System.out.println("文件下载的路径: " + realPath);
        // 3.设置响应头,URLEncoder.encode()处理文件名中文乱码问题
        response.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(fileName,"UTF-8"));
        // 4.获取文件流
        FileInputStream in = new FileInputStream(realPath);
        // 5.设置缓冲区
        int len = 0;
        byte[] buffer = new byte[1024];

        // 6.写出文件
        ServletOutputStream os = response.getOutputStream();
        while ((len = in.read(buffer)) != -1){
            os.write(buffer, 0, len);
        }

        os.close();
        in.close();
    }
8.JSP九大内置对象和作用?

1.request 用户端请求,此请求会包含来自GET/POST请求的参数

2.response 网页传回用户端的回应

3.pageContext 网页的属性是在这里管理,代表的编译后JSP内容

4.session 与请求有关的会话期

5.application servlet 正在执行的内容

6.out 用来传送回应的输出

7.config servlet的构架部件

8.page JSP网页本身

9.exception 针对错误网页,未捕捉的例外

10.微服务面试题

1.什么是微服务架构

微服务架构就是将单体的应用程序分成多个应用程序,这多个应用程序就是微服务,每个微服务都是单独的运行 并使用轻量级的机制通信。这些服务围绕业务能力来划分,并通过自动化部署机制来独立部署。这些服务可以使用不同的编程语言,不同数据库,以保证最低限度的集中式管理。

2.为什么要学习Spring Cloud

首先springcloud基于spingboot的优雅简洁,可还记得我们被无数xml支配的恐惧?可还记得 springmvc,mybatis错综复杂的配置,有了spingboot,这些东西都不需要了,spingboot好处不 再赘诉,springcloud就基于SpringBoot把市场上优秀的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理什么叫做开箱即用?即使是当年的黄金搭档dubbo+zookeeper下载配置起来也是颇费心神的!而springcloud完成这些只需要一个jar的依赖就可以了!springcloud大多数子模块都是直击痛点,像zuul解决的跨域,fegin解决的负载均衡,hystrix的熔断机制等等等等

  • 首先SpringCloud基于SpringBoot的优雅简洁 ,SpringCloud就是基于SpringBoot把市场上优秀的服务框架集成,除去复杂的配置和实现原理,开箱即用,SpringCloud的使用只需要一个jar包依赖就行 网关跨域 feing的调用和负载均衡hystrix的熔断机制等
3.Spring Cloud 是什么

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。

Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

4. SpringCloud的优缺点

优点:

1.耦合度比较低。不会影响其他模块的开发。

2.减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发。

3.配置比较简单,基本用注解就能实现,不用使用过多的配置文件。

4.微服务跨平台的,可以用任何一种语言开发。

5.每个微服务可以有自己的独立的数据库也有用公共的数据库。

6.直接写后端的代码,不用关注前端怎么开发,直接写自己的后端代码即可,然后暴露接口,通过组件进行服务通信。

缺点:

1.部署比较麻烦,给运维工程师带来一定的麻烦。

2.针对数据的管理比麻烦,因为微服务可以每个微服务使用一个数据库。

3.系统集成测试比较麻烦

4.性能的监控比较麻烦。【最好开发一个大屏监控系统】

  • 总的来说优点大过于缺点,目前看来Spring Cloud是一套非常完善的分布式框架,目前很多企业开始用微服务、Spring Cloud的优势是显而易见的。因此对于想研究微服务架构的同学来说,学习Spring Cloud是一个不错的选择。
5.SpringBoot和SpringCloud的区别?

SpringBoot专注于快速方便的开发单个个体微服务。

SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,

为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务

SpringBoot可以离开SpringCloud独立使用开发项目, 但是SpringCloud离不开SpringBoot ,属于依赖的关系

SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。

6. SpringCloud由什么组成

这就有很多了,我讲几个开发中最重要的

Spring Cloud Eureka:服务注册与发现

Spring Cloud Zuul:服务网关

Spring Cloud Ribbon:客户端负载均衡

Spring Cloud Feign:声明性的Web服务客户端

Spring Cloud Hystrix:断路器

Spring Cloud Confifig:分布式统一配置管理

等20几个框架,开源一直在更新

处。

7. Spring Cloud 和dubbo区别?
  • (1)服务调用方式:dubbo是RPC springcloud Rest Api
  • (2)注册中心:dubbo 是zookeeper springcloud是eureka,也可以是zookeeper
  • (3)服务网关,dubbo本身没有实现,只能通过其他第三方技术整合,springcloud有Zuul路由网关,作为路由服务器,进行消费者的请求分发,springcloud支持断路器,与git完美集成配置文件支持版本控制,事物总线实现配置文件的更新与服务自动装配等等一系列的微服务架构要素。
8.Eureka
1.在了解eureka之前,先了解CAP理论

**C **

Consistency,一致性的意思。
一致性就是说,我们读写数据必须是一摸一样的。
比如一条数据,分别存在两个服务器中,server1和server2。
我们此时将数据a通过server1修改为数据b。此时如果我们访问server1访问的应该是b。
当我们访问server2的时候,如果返回的还是未修改的a,那么则不符合一致性,如果返回的是b,则符合数据的一致性。

A

Availability,可用性的意思。
这个比较好理解,就是说,只要我对服务器,发送请求,服务器必须对我进行相应,保证服务器一直是可用的。

P

Partition tolerance,分区容错的意思。
一般来说,分布式系统是分布在多个位置的。比如我们的一台服务器在北京,一台在上海。可能由于天气等原因的影响。造成了两条服务器直接不能互相通信,数据不能进行同步。这就是分区容错。我们认为,分区容错是不可避免的。也就是说 P 是必然存在的。

2.eureka的基本原理

​ 服务启动后向Eureka注册,Eureka Server会将注册信息向其他Eureka Server进行同步,当服务消费者要调用服务提供者,则向服务注册中心获取服务提供者地址,然后会将服务提供者地址缓存在本地,下次再调用时,则直接从本地缓存中取,完成一次调用。

​ 当服务注册中心Eureka Server检测到服务提供者因为宕机、网络原因不可用时,则在服务注册中心将服务置为DOWN状态,并把当前服务提供者状态向订阅者发布,订阅过的服务消费者更新本地缓存。

服务提供者在启动后,周期性(默认30秒)向Eureka Server发送心跳,以证明当前服务是可用状态。Eureka Server在一定的时间(默认90秒)未收到客户端的心跳,则认为服务宕机,注销该实例。

3.eureka的自我保护机制

在默认配置中,Eureka Server在默认90s没有得到客户端的心跳,则注销该实例,但是往往因为微服务跨进程调用,网络通信往往会面临着各种问题,比如微服务状态正常,但是因为网络分区故障时,Eureka Server注销服务实例则会让大部分微服务不可用,这很危险,因为服务明明没有问题。

为了解决这个问题,Eureka 有自我保护机制,通过在Eureka Server配置如下参数,可启动保护机制。

eureka.server.enable-self-preservation=true
复制代码

它的原理是,当Eureka Server节点在短时间内丢失过多的客户端时(可能发送了网络故障),那么这个节点将进入自我保护模式,不再注销任何微服务,当网络故障回复后,该节点会自动退出自我保护模式。

Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:

  1. Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
  2. Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
  3. 当网络稳定时,当前实例新的注册信息会被同步到其它节点中
    Eureka还有客户端缓存功能(注:Eureka分为客户端程序与服务器端程序两个部分,客户端程序负责向外提供注册与发现服务接口)。 所以即便Eureka集群中所有节点都失效,或者发生网络分割故障导致客户端不能访问任何一台Eureka服务器;Eureka服务的消费者仍然可以通过 Eureka客户端缓存来获取现有的服务注册信息。甚至最极端的环境下,所有正常的Eureka节点都不对请求产生相应,也没有更好的服务器解决方案来解 决这种问题时;得益于Eureka的客户端缓存技术,消费者服务仍然可以通过Eureka客户端查询与获取注册服务信息。

默认情况下,如果Eureka Service在一定时间内没有接收到某个微服务的心跳,Eureka Service会进入自我保护模式,在该模式下Eureka Service会保护服务注册表中的信息,不在删除注册表中的数据,当网络故障恢复后,Eureka Servic 节点会自动退出自我保护模式

4.什么是Eureka
  • Eureka作为SpringCloud的服务注册功能服务器,他是服务注册中心,系统中的其他服务使用Eureka的客户端将其连接到Eureka Service中,并且保持心跳,这样工作人员可以通过Eureka Service来监控各个微服务是否运行正常。
9.网关
1.什么是网关
  • 网关相当于一个网络服务架构的入口,所有网络请求必须通过网关转发到具体的服务。
2.网关的作用是什么
  • 统一管理微服务请求,权限控制、负载均衡、路由转发、监控、安全控制黑名单和白名单等
3.网关与过滤器有什么区别
  • 网关是对所有服务的请求进行分析过滤,过滤器是对单个服务而言。
4.常用网关框架有那些?
  • Nginx、Zuul、Gateway

11.多线程面试题

1.多线程应用场景
  • 例如: 迅雷多线程下载、数据库连接池、分批发送短信等。
2.并发编程有什么缺点
  • 并发编程的目的就是为了能提高程序的执行效率,提高程序运行速度,但是并发编程并不总是能提高程序运行速度的,而且并发编程可能会遇到很多问题,比如:内存泄漏、上下文切换、线程安全、死锁等问题。
3.并发编程三个必要因素是什么?
  • 原子性:原子,即一个不可再被分割的颗粒。原子性指的是一个或多个操作要么全部执行成功要么全部执行失败。
  • 可见性:一个线程对共享变量的修改,另一个线程能够立刻看到。(synchronized,volatile)
  • 有序性:程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)
4.在 Java 程序中怎么保证多线程的运行安全?
  • 出现线程安全问题的原因一般都是三个原因:
    • 线程切换带来的原子性问题 解决办法:使用多线程之间同步synchronized或使用锁(lock)。
    • 缓存导致的可见性问题 解决办法:synchronized、volatile、LOCK,可以解决可见性问题
    • 编译优化带来
5.并行和并发的区别

并发: 两个人使用一台电脑

并行: 两个人使用两台电脑

6.什么是多线程
  • 多线程:多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务。
7.线程和进程的区别

进程: 一个内存中运行的应用程序,每个正在系统上运行的程序都是一个进程

线程:进程中的一个执行单元,他负责在程序中独立执行

7.什么是线程死锁

死锁:就是线程A拿着线程B的资源,线程B拿着线程A的资源相互争抢导致死锁

8.为什么要使用并发编程
  1. 充分的利用CPU资源
  2. 方便进行业务的拆分,提升应用性能
9.如何避免线程死锁
  1. 避免一个线程同时获得多个锁
  2. 避免一个线程在锁内同时占用多个资源,尽量保证每个锁占用一个资源
10创建线程的四种方式

1.继承Thread类

public class MyThread extends Thread {
@Override
public void run() {
    System.out.println(Thread.currentThread().getName() + " run()方法正在执行...");
}

2.实现 Runnable 接口

public class MyRunnable implements Runnable {
@Override
public void run() {
    System.out.println(Thread.currentThread().getName() + " run()方法执行中...");
}

3.实现 Callable 接口

public class MyCallable implements Callable<Integer> {
@Override
public Integer call() {
    System.out.println(Thread.currentThread().getName() + " call()方法执行中...");
    return 1;
}


4.匿名内部类方式

public class CreateRunnable {
    public static void main(String[] args) {
        //创建多线程创建开始
        Thread thread = new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("i:" + i);
                }
            }
        });
        thread.start();
    }
}


11.runnable和callable有什么区别

相同点:

  1. 都是接口
  2. 都可以编写多线程程序
  3. 都采用Thread.start()启动线程

主要的区别:

  1. runnable没有返回值
  2. callable有返回值
12.线程的run()和start()有什么区别?
  • start()用于启动线程,run()方法用于执行线程运行时代码。run()可以重复调用,start只能调用一次。
  • start()方法是用来执行线程的,真正的实现多线程,调用start()方法无需等待run()方法代码执行完毕,就可以执行其他的代码,线程处于就绪状态,并没有运行,通过Thread类调用run方法来完成运行状态,运行结束线程终止。
  • run()方法是在本线程里的,只是线程里的一个函数而不是多线程,如果直接调用run(),其实相当于调用一个普通的函数而已。
13.为什么调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法?(面试问到过)
  • new 一个Thread,线程进入新建状态,调用start()方法,会启动一个线程并使线程进入了就绪状态,分配到实践片后就可以开始运行了。start()会执行线程的相应工作,然后执行run()方法的内容,这才是多线程工作
  • 如果直接执行run方法,会把run方法当成main方法线程下的一个普通方法去执行,并不会在某个线程执行,所以不是多线程

小结:

调用start()方法可以启动线程并使线程进入就绪状态,调用start()方法后Thread方法就会自动的调用run方法

14.线程状态

1.新建状态(new):

新建线程对象

2.就绪状态(可运行状态)

线程对象创建后,当调用线程对象的start()方法,该线程就进入就绪状态,等待被线程调度选中,获取cpu的使用权

3.运行状态(running)

可运行状态线程获得了cpu实践片,执行程序代码,就绪状态是进入到运行状态的唯一入口,线程想要进入运行状态执行,首先处于就绪状态

4.阻塞状态(bolck)

处于运行状态状态中的线程由于某种原因,暂时放弃对CPU的使用,停止执行,此时进入阻塞状态,直到进入就绪状态,才有机会再次被CPU调用进入到运行状态

5.死亡状态(结束)

线程run()方法、main()方法执行结束,或者因异常退出run()方法,则线程结束生命

15.sleep()方法和wait()方法有什么区别?

两者都可以暂停线程执行

区别:

  • 类的不同:sleep()是Thread线程类的静态方法,wait()是Object类的方法
  • 是否释放锁:sleep()不释放锁,wait()释放锁
  • 用途不同:wait通常用于线程间交互/通信,sleep通常被用于暂停执行
  • 用法不同:wait()方法被调用后,线程不会自动苏醒,需要别的线程调用同一对象的notify()或者notifyAll()方法,sleep()方法执行完成后,线程自动苏醒

12.SpringBoot面试题

1.谈谈你对Spring Boot的理解?

SpringBoot主要用来简化使用Spring的难度和繁重的XML配置,它是Spring组件的一站式解决方案,采取了习惯优于配置的方法。通过.properties或者.yml文件替代了Spring繁杂的XML配置文件,同时支持@ImportResource注解加载XML配置。Spring Boot还提供了嵌入式HTTP服务器、命令行接口工具、多种插件等等,使得应用程序的测试和开发简单起来。

2.为什么需要SpringBoot?

Spring Boot 优点非常多,如:独立运行、简化配置、自动配置和无需部署war文件等等

3.SpringBoot的优点
  1. 简化开发,提高生产力
  2. SpringBoot使用javaConfig有助于避免使用XML,同时避免大量的Maven导入和版本各种冲突
  3. SpringBoot很容易与Spring生态集成,如SpringJDBC,SpringOPM,SpringData等等
  4. SpringBoot提供了嵌入式HTTP服务器,Tomcat和Jetty,可以轻松的开发和测试应用程序 Spring Boot 提供命
  5. SpringBoot令行接口工具,用于开发和测试应用程序
  6. Spring Boot 提供了多种插件,可以使用内置Maven工具开发和测试 应用程序
  7. Spring Boot 没有单独的 Web 服务器需要,这意味着不再需要启动 Tomcat或其他任何东西
  8. 开箱即用原理繁杂的配置
4. Spring Boot 的核心配置文件有哪几个?它们的区别是什么?

Spring Boot 的核心配置文件是 application 和 bootstrap 配置文件。
application 配置文件主要用于 Spring Boot 项目的自动化配置。
bootstrap 配置文件有三个应用场景。
使用Spring Cloud Config配置中心时,需要在 bootstrap 配置文件中添加连接到配置中心的配置属性,来加载外部配置中心的配置信息;

5. Spring Boot 的配置文件有哪几种格式?它们有什么区别?

主要有.properties 和 .yml格式,它们的区别主要是书写格式不同。另外,.yml 格式不支持 @PropertySource 注解导入配置。

6. 开启SpringBoot特性有哪几种方式?

继承spring-boot-starter-parent项目
导入spring-boot-dependencies项目依赖

7. 什么是Spring Boot Starter?

Starters可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,可以一站式集成 Spring 和其他技术,而不需要到处找示例代码和依赖包。Spring Boot Starter的工作原理是:Spring Boot 在启动时扫描项目所依赖的JAR包,寻找包含spring.factories文件的JAR包,根据spring.factories配置加载AutoConfigure类,根据 @Conditional注解的条件,进行自动配置并将Bean注入Spring Context

8. Spring Boot 有哪几种读取配置的方式?

使用@Value注解加载单个属性值
使用@ConfigurationProperties注解可以加载一组属性的值,针对于要加载的属性过多的情况,比@Value注解更加简洁

9. Spring Boot 支持哪些日志框架?推荐和默认的日志框架是哪个

Spring Boot 支持 Java Util Logging, Log4j2, Logback 作为日志框架,如果使用 Starters 启动器,Spring Boot 将使用 Logback 作为默认日志框架,推荐的日志框架是Log4j2

10. Spring Boot 可以兼容老 Spring 项目吗?

可以兼容,使用 @ImportResource 注解导入老 Spring 项目配置文件。

11. 保护 Spring Boot 应用有哪些方法?

在生产中使用HTTPS
使用Snyk检查依赖关系
升级到最新版本
启用CSRF保护
使用内容安全策略防止XSS攻击

12. 什么是 JavaConfig?

java config是指基于java配置的spring。传统的Spring一般都是基本xml配置的,后来spring3.0新增了许多java config的注解,特别是spring boot,基本都是清一色的java config。

13. (Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的)介绍一下 @SpringBootApplication 注解

Spring Boot 的核心注解是@SpringBootApplication,它也是启动类使用的注解,主要包含了 3 个注解:
@SpringBootConfiguration:它组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:具有打开自动配置的功能,也可以关闭某个自动配置的选项。
@ComponentScan:用于Spring组件扫描。

14. Spring Boot 自动配置原理是什么?
  1. 我们都知道SpringBoot程序的入口是通过@SpringBootApplication注解修饰的一个类,
  2. 自动装配的过程肯定是通过SpringBootApplication这个注解内部实现来完成的,
  3. SpringBootApplication这个注解是符合注解, ComponentScan :表示包扫描, EnableAutoConfiguration注解用于自动装配,和SpringBootConfiguraion表示加载SpringBoot相关的一些配置
  4. @EnableAutoConfiguration注解也是一个符合注解,其中Import()注解用于自动导入 AutoConfigurationImportSelector ,通过 loadFactoryNames ()方法扫描META-INF/spring.factories的权限定类名通过反射创建并自动的加载到Spring容器中
15. 你如何理解 Spring Boot 配置加载顺序?

Spring Boot配置加载顺序优先级是:propertiese文件、YAML文件、系统环境变量、命令行参数。

16.什么是YAML?

YAML是一种人类可读的数据序列化语言。它通常用于配置文件。与属性文件相比,如果我们想要在配置文件中添加复杂的属性,YAML文件就更加结构化,而且更少混淆。可以看出YAML具有分层配置数据。

17 .Spring Boot支持哪些嵌入式Web容器?

Spring Boot支持的嵌入式servlet容器有: Tomcat、Jetty、Undertow。

18. YAML 配置的优势在哪里 ?

配置有序
支持数组,数组中的元素可以是基本数据类型或者对象
简洁方便

19. Spring Boot 是否可以使用 XML 配置 ?

Spring Boot 推荐使用 Java 配置同时支持 XML 配置,通过 @ImportResource 注解加载 XML 配置。

20. application.properties和bootstrap.properties有何区别 ?

bootstrap比 applicaton 优先加载,配置在应用程序上下文的引导阶段生效, 而且boostrap 里面的属性不能被覆盖;
application用于 spring boot 项目的自动化配置。

21. 什么是 Spring Profiles?

Spring Profiles 允许用户根据配置文件(dev,prod,test等等)来注册 bean。当应用程序在开发环境中运行时,只有某些 bean 可以加载,而在生产环境中,某些其他 bean 也可以加载。比如要求 Swagger 文档仅适用于测试环境,并且禁用所有其他文档,可以使用配置文件来完成。

22. 如何在自定义端口上运行 Spring Boot 应用程序

可以在 application.properties 配置文件中指定端口,比如server.port = 8090

27. 什么是 CSRF 攻击?

CSRF 代表跨站请求伪造,这是一种攻击,迫使最终用户在当前通过身份验证的Web 应用程序上执行不需要的操作。CSRF 攻击专门针对状态改变请求,而不是数据窃取,因为攻击者无法查看对伪造请求的响应。

28. 如何使用 Spring Boot 实现异常处理?

Spring 通过使用 @ControllerAdvice 注解处理异常,实现一个ControllerAdvice 类来处理控制器类抛出的所有异常

13.SpringCloud面试题

1.Eureka面试题

1.服务的注册

Eureka客户端向Eureka服务端进行注册,这个过程实际就是将自己的服务名,ip地址,端口号等信息发送到服务注册中心进行保存

2.服务的发现

在服务治理框架下,服务间的调用不能通过指定的实例地址来实现,而是通过服务名称发送请求调用实现,服务调用放通过服务名从服务注册中心的服务清单中获取实例列表,在通过指定的负载均衡策列取出一个服务实例位置来进行服务调用

3.服务续约

服务注册后,服务提供方还必须要时时的向服务注册中心发送心跳(默认为30秒)该请求告诉服务注册注册中心自己还活着,防止对该服务进行剔除,将服务从列表排除这就是服务续约

4.失效剔除

有些时,我们服务实例不一定会正常下线,可能由于内存溢出,网络故障等,原因使服务不能正常运作,服务中心未收到服务下线的请求,为了服务从服务列表中将这些无法提供服务的实例删除,服务端在启动的时候会创建一个定时任务,没有续约的服务剔除出去

5.自我保护

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值