Java面试题——简答题部分
文章目录
- Java面试题——简答题部分
- 1.列举几个常用的集合类并指出特点
- 2.Set里的元素是不能重复的,那么用什么方法来区分重复与否呢,是==还是equals(),有何区别?
- 3.请描述线程的生命状态,并描述sleep(),join(),yield()的区别以及interrupt()的作用
- 4.简述包装类中new方法和valueof方法的区别
- 5.内存溢出和内存泄漏
- 6.final、finally、finalize的区别是什么
- 7.Collection和Collections的区别
- 8.数据库的5种约束
- 9.请描述三大范式
- 10.什么是事务,特性有哪些
- 11.事务的隔离级别
- 12.java8新特性
- 13.Vector和List
- 14.什么是SQL注入,如何防止SQL注入
- 15.如何优化sql,提高查询效率
- 16.索引失效
- 17.如何优化数据库,提高性能
- 18.请描述HashMap的数据结构,且指出JDK1.8前后hashmap的区别
- 19.HashMap和hashtable,concurrentHashMap的区别
- 20.使用冒泡排序对数组进行升序排序
- 21.写出懒汉式单例
- 22.session和cookie的区别
- `session`对象的作用域
- `servlet`中的四大域对象(了解)
- 23.描述servlet的生命周期中涉及到哪些方法
- 24.转发(forward)和重定向(redirect)的区别
- 25.拦截器(Interceptor)和过滤器(Filter)的区别
- 26.乐观锁和悲观锁
- 27.mybatis中 ${ } 与 #{ } 的区别
- 28.请描述 SpringMVC 的五大组件并描述 SpringMVC 对请求的处理流程
- 29.Spring 中 Bean 的作用域
- 30.说说自动装配注解 Resource 和 Autowired 的区别
- 31.请描述mybatis中的一级缓存和二级缓存
- 32.mybatis的动态sql
- 33.请简述Spring的两种核心技术
- 34.请说出几个Spring中的注解以及其作用
- 动态代理机制分类
- AOP的应用
- 反射
- 35.JVM的结构以及如何调优
- JVM的结构
- JVM的优化
- JVM堆内存大小的调整
- 36.说一说年轻代、年老代的工作原理
- 年轻代
- 年老代
- 37.GC的分类
- 38.GC垃圾回收机制的常用算法
- 39.数据库的主从复制,读写分离,分库分表
- 40.redis的哨兵模式
- 41.redis的五种数据类型,分别应用场景
- 42.SpringCloud的五大组件和作用
- 43.同步通讯和异步通讯的区别
- 44.Ribbon和Feign的区别
- 45.Linux命令
1.列举几个常用的集合类并指出特点
- List集合:有序,有下标,元素可重复
ArrayList
:底层是数组实现的,所以查询元素效率很高,增删元素效率相对LinkedList
而言较低LinkedList
:底层是双向链表实现的,增删元素效率较ArrayList
较高,查询元素效率较低
- Set集合:不可重复集
LinkedHashSet
:底层是散列表 + 链表,用链表来维护元素的顺序,元素是有序的HashSet
:底层是散列表,查询效率最高,元素是无序的TreeSet
:底层是红黑树,对元素进行了排序
- Map集合:
HashMap
:底层是散列表,查询快,元素无序TreeMap
:底层是红黑树,实现了排序LinkedHashMap
:底层是散列表 + 链表,有序
2.Set里的元素是不能重复的,那么用什么方法来区分重复与否呢,是==还是equals(),有何区别?
- 使用
equals()
- 区别:
- == 两侧若为引用类型,比较地址是否相等
equals
方法是Object
提供的方法,在Object
类中,其作用等同于==;但是Java强烈建议在子类中重写该方法,使其根据有逻辑意义。重写后通常都是根据对象的内容值来比较对象是否相等
3.请描述线程的生命状态,并描述sleep(),join(),yield()的区别以及interrupt()的作用
- 新建 --> 就绪 --> 运行 --> 阻塞 --> 死亡
interrupt()
:打断线程的阻塞状态,唤醒线程- 若线程处于阻塞状态,调用该方法,线程被唤醒
- 若线程处于运行状态,调用该方法,无效
yield()
:为其他线程让步,但是调用之后,线程进入就绪状态,而不是阻塞状态join()
:用于实现线程间的同步通讯,调用进入阻塞状态- wait():调用该方法,线程阻塞,唤醒需要其他线程调用
notify()
或notifyAll()
,调用进入阻塞状态
4.简述包装类中new方法和valueof方法的区别
new
:包装类一定创建了一个新的对象valueof()
:完成的是基本数据类型到包装类型的转换,若给出的数值处于常量池的缓存范围之内,此时直接复用常量池中的对象,不再重新创建对象。若数值不处于常量池的范围内,则新创建包装类对象
5.内存溢出和内存泄漏
- 内存泄漏:分配出去的内存回收不回来,无法重新利用,这种现象叫做内存泄漏
- 内存溢出:内存剩余空间不足以分配给请求的资源
- 关系:内存泄漏累积到一定程度会造成内存溢出,但内存溢出不一定是由内存泄漏引起的,也可能是创建的对象太大引起的
- 常见内存溢出:
OutofMemoryError
(OOM):heap溢出StackOverFlowError
(SOF):stack溢出 递归
6.final、finally、finalize的区别是什么
final
:是修饰符,可以用来修饰类(类不能被继承)、方法(方法不能被重写)、变量(一旦初始化不可再改变)finally
:是try...catch
配合使用的,特点是不论之前是否有异常出现,都一定会执行finally
块中的代码,经常用于保存释放资源的代码finalize
:是Object类提供的方法,在GC回收对象前会调用该方法进行最后的资源释放
7.Collection和Collections的区别
Collection
:是接口,其常用子接口有List
、Set
Collections
:是集合操作类,提供了一系列方法用于操作集合
8.数据库的5种约束
- 主键约束
- 外键约束
- 唯一性约束
- 非空约束
- 检查约束
9.请描述三大范式
设计表时需要遵守的规则即为范式,通常遵循前三个范式,设计出的表结构就是合理的
- 1NF:所有的列必须是原子性的
- 2NF:非码属性必须完全依赖候选码
- 3NF:非主属性不能传递依赖于其他非主属性
10.什么是事务,特性有哪些
事务是数据库中执行操作的最小执行单元,不可再分,要么全都执行成功,要么全都失败
- A:原子性
- C:一致性
- I:持久性
- D:隔离性
11.事务的隔离级别
- 读未提交(
read—uncommited
)可能产生 脏读、不可重复读、幻读 - 读已提交(
read—commited
)可能产生 不可重复读、幻读;是Oracle
、SqlServer
的默认隔离级别。 - 可重复读(
repeatableRead
)可能产生 幻读;是MySQL
的默认隔离级别。(但MySQL
不会产生幻读,因为采取的是快照读) - 串行化(
Serializable
)
12.java8新特性
- lambda表达式
- 新日期LocalDateTime
- ::方法引用
- 接口中可以定义static 方法和默认方法
13.Vector和List
Vector
是向量,是list
的前身,是线程安全的集合,list
是非现场安全的集合
14.什么是SQL注入,如何防止SQL注入
- SQL注入:由于用户在客户端输入数据时拼接了sql语句,最终导致添加的sql语句作为执行sql的一部分执行,破坏了原来sql的意义,导致用户非法获取对数据的现象。即为sql注入
- 防止SQL注入:采取预编译;
Mybatis
框架:#{ }
实现了预编译- 非框架:实现预编译方式是
PrepareStatement
Mybatis
的占位符#{ }
:实现了预编译${ }
:没有实现预编译,通常用于做字符串拼接
15.如何优化sql,提高查询效率
- 尽可能的避免使用嵌套查询,尽量使用联查
- 查询语句中不要使用 *
- 建立索引
16.索引失效
- 应尽量避免在
where
子句中对字段进行null
值判断,否则将导致引擎放弃使用索引而进行全表扫描 - 应尽量避免在
where
子句中使用!=
操作符,否则将导致引擎放弃使用索引而进行全表扫描 - 应尽量避免在
where
子句中使用or
来连接条件,否则将导致引擎放弃使用索引而进行全表扫描 not in
也要慎用,否则将导致引擎放弃使用索引而进行全表扫描- 应尽量避免在
where
子句中对字段使用like
左侧模糊查询,否则将导致引擎放弃使用索引而进行全表扫描 - 应尽量避免在
where
子句中对字段进行表达式操作,否则将导致引擎放弃使用索引而进行全表扫描 - 应尽量避免在
where
子句中对字段进行函数操作,否则将导致引擎放弃使用索引而进行全表扫描
使用索引注意事项:
- 索引适用于数据量大的表,数据量小的表不适用,反而会降低其效率
- 索引适合添加给作为查询条件的字段
- 若字段的值会被频繁修改,不适合添加索引
- 索引并不是越多越好,索引固然可以提高
select
效率,但同时降低了insert
及update
效率,因为insert
或update
时有可能会重建索引,所以怎样创建索引需要慎重考虑,视情况而定,一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要
创建索引的sql:
create index in_name on table(col)
17.如何优化数据库,提高性能
- 分库分表
- 读写分离,主从复制
- 集群
18.请描述HashMap的数据结构,且指出JDK1.8前后hashmap的区别
- jdk1.8之前:数据结构为:数组 + 链表
- jdk1.8开始:数据结构为:数组 + 链表 + 红黑树(散列桶位置的链表长度达到8,提高查询效率)
HashMap存或查元素的过程:
- 根据
key
调用hashCode()
,得到Hash码值 - 再调用散列函数得到下标存入散列桶位置
- 散列桶为空直接存入,不为空进行
equals()
比较, - 结果为
true
,key
相同,value
覆盖;结果为false
,形成链表
19.HashMap和hashtable,concurrentHashMap的区别
HashMap
是非线程安全的hashtable
和concurrentHashMap
是线程安全的HashMap
的key
和value
都允许null
,hashtable
和concurrentHashMap
不允许hashtable
和concurrentHashMap
保证线程安全的方式不同hashtable
是通过给整张散列表加锁保证线程安全,这种方式并发执行效率低concurrentHashMap
保证线程安全的同时提高并发执行效率。在jdk1.8之前是采用分段锁机制(如果多线程并发访问的是不同段(segment
)是完全并发的);在jdk1.8之后采用乐观锁和synchronized
配合使用,多线程并发向同一个散列桶添加元素时,看该位置是否为null
,为null
采用乐观锁机制,不为null
采用synchronized
方式谁先访问到,先给头节点加锁从而形成链表或红黑树
20.使用冒泡排序对数组进行升序排序
for(int i = 0;i < ary.length - 1; i++){
for(int j = 0;i<ary.length - i - 1;j++){
if(ary[j] > ary[j + 1]){
int tmp = ary[j];
ary[j] = ary[j+1];
ary[j+1] = tmp;
}
}
}
21.写出懒汉式单例
// 单例核心 构造方法私有化,提供公有方法,向外提供实例,声明成员变量
// 懒汉式单例声明成员变量不初始化
public class Singleton{
private Singleton(){};
private static Singleton singleton;
public static synchronized Singleton getInstance(){
if(singleton == null)
singleton = new Singleton();
return singleton;
}
}
22.session和cookie的区别
- 作用位置不同:
session
是作用在服务器端保存数据的,而cookie
是作用在浏览器中保存数据的 - 保存数据类型不同:
session
中可以保存任意类型的数据(session.setAttribute(name,value)
),cookie
中只能保存字符串对象 - 保存数据大小不同:
session
中对数据大小没有限制,cookie
对象中最多能保存4KB数据,而浏览器中保存的cookie
对象也是有限的,不同的浏览器保存的cookie
对象数据量不同,在20~50之间 - 有效时长不同:
session
在服务器端的有效时长为从闲置开始的30min;cookie
的有效时长是一次会话(cookie.setMaxAge(int)
:参数默认-1;负数:指cookie
的有效时长是一次会话;正数:保存给定的分钟,不论中间浏览器是否关闭,表示将cookie
对象保存到磁盘指定min;0:删除该cookie
对象)
session
对象的作用域
一次会话。原因:session
对象创建后将sessionID
保存到cookie
中下发给浏览器,而cookie
默认的有效时长是一次会话,即在浏览器没关闭期间,都可以根据cookie
中的sessionID
找到对应的session
对象,从而访问。但是浏览器若关闭,则对应的cookie
对象会被销毁,sessionID
不再在浏览器中存在,所以重新打开浏览器,无法继续访问之前session
对象中的数据
servlet
中的四大域对象(了解)
作用:保存数据,需要的时候取数据
PageContext
:作用域:当前页面(jsp)HttpServletRequest
:作用域:一次请求HttpSession
:作用域:一次会话ServletContext
:作用域:整个web应用
域对象存取数据的方法都相同,均为:
setAttribute(name,val)
:存值getAttribute(name):Object
:取值
23.描述servlet的生命周期中涉及到哪些方法
servlet
默认是单例的:
-
默认情况下:
- 当请求第一次到达时,对应的
servlet
进行实例化 - 实例化后立即调用
init
方法进行初始化 - 之后,每次请求到达,都会调用
service
方法进行处理请求 - 当web应用程序即将结束时,将
servlet
销毁,调用destroy
方法
- 当请求第一次到达时,对应的
-
servlet
的实例化时机:-
默认情况下,是在请求第一次到达时进行实例化
-
还可以在web程序启动时将
servlet
进行实例化,需要配置@WebServlet(loadonstartup=true) Userservlet
-
UserServlet{
构造方法
init(){}
service(){}
doGet(){}
doPost(){}
destroy(){}
}
24.转发(forward)和重定向(redirect)的区别
- 转发(forward):
- 是由服务器端完成的
- 客户端发起的是一次请求
- 操作地址栏的地址不会发生改变
- 可以实现数据的共享
- 只能向本应用内的资源发起请求
- 重定向(redirect):
- 是由客户端完成的
- 客户端发起的是两次请求
- 地址栏的地址会发生改变
- 不能实现数据的共享
- 可以向本应用外的资源发起请求
25.拦截器(Interceptor)和过滤器(Filter)的区别
相同之处
- 作用相似,都是对请求进行拦截过滤
- 都可以形成链状结构,在链中的顺序取决于配置顺序
不同之处
- 所属框架不同:
- 过滤器是JavaEE提供,拦截器是Spring提供的
- 作用时机不同:
- 过滤器是在请求到达
Servlet
之前进行过滤,拦截器是在Servlet
之后,Controller之前对请求进行拦截过滤的
- 过滤器是在请求到达
- 内部方法数量不同:
- 过滤器:内部只有一个抽象方法,
doFilter()
,在该方法内部写过滤功能的代码实现 - 拦截器:内存有三个抽象方法,
preHandle()
、postHandle()
、afterCompletion()
preHandle()
:最常用的,当请求在servlet之后,到底controller之前调用该方法postHandle()
:DispatcherServlet
渲染视图之前执行afterCompletion()
:在请求即将结束时调用该方法,通常用于释放资源
- 过滤器:内部只有一个抽象方法,
- 配置方式不同:
- 过滤器只能配置黑名单
- 拦截器既可以配置黑名单也可以配置白名单
26.乐观锁和悲观锁
乐观锁和悲观锁都是两种思想
- 乐观锁:不是使用锁来保证线程安全的,而是使用版本号机制来保证线程安全
- 悲观锁:是真正使用锁来保证线程安全的
- 特点:保证线程安全的同时并发执行效率低
- 应用:排它锁、 Java的
synchronized
27.mybatis中 ${ } 与 #{ } 的区别
#{}
:实现类预编译${}
:没有实现预编译,通常用于做字符串拼接
28.请描述 SpringMVC 的五大组件并描述 SpringMVC 对请求的处理流程
DispatcherServlet
:中央处理器HandlerMapping
:映射器Controller
:控制器ModelAndView
:视图数据模型ViewResolver
:视图解析器
- 请求先到达
DispatcherServlet
DispatcherServlet
调用HandlerMapping
来对请求解析解析,可以找到对应的Controller
以及方法- 到达对应的
Controller
中对应的方法,对请求进行处理Controller
处理请求结束,返回ModelAndView
给DispatcherServlet
DispatcherServlet
调用ViewResolver
来对视图进行解析DispatcherServlet
进行视图的渲染
29.Spring 中 Bean 的作用域
通过属性scope来设置bean的作用域:
属性值:
-
singleton
:默认,单例 -
prototype
:原型,每次注入都会重新实例化 -
request
:每次请求中,若需要某个bean对象,都会重新实例化 -
session
:bean的作用域为session,即新创建session对象,会重新实例化,session对象销毁,该bean对象销毁 -
global-session
:在portlet容器中存在的对象,该对象作用于整个应用程序,bean作用于整个golbal session
对象中。在web应用中不存在该对象
30.说说自动装配注解 Resource 和 Autowired 的区别
- 所属框架不同
@Resource
:是由Java提供的@Autowired
:是由Spring提供的
- 注入方式不同
@Resource
:默认先按照bean的name进行注入,若name不存在则自动按照type进行注入,若通过注解指定了name,若找不到则注入失败@Autowired
:直接按照type来注入
31.请描述mybatis中的一级缓存和二级缓存
前提了解:
- 数据库中若执行单个增删改操作,会自动为其开始事务,若在
service
层为某个方法开始了事务管理,(通过spring的声明式事务管理),则数据库中不再为操作开始事务,因为业务层的事务管理就是数据库中的数据管理 - 持久层访问数据库时,每次访问均需要
sqlsession
对象,有了该对象才能访问数据库,而sqlsession
对象是由mybatis中的sqlsessionFactory
来提供的,这里使用了工厂模式,每次访问数据库时,都会从该工厂中获取一个sqlsession
对象,对数据访问 - 注意:同一个事务中共享一个
sqlsession
对象
mybatis
的一级缓存是sqlsession
级别的缓存,同一个事务中共享一个sqlsession
对象,在事务中若执行查询操作,会将查询到的数据缓存到一级缓存中,后续若再次查询相同的数据,直接从缓存中获取即可(查询操作顺序:先去一级缓存查,若有则直接返回,若无再去数据库查)但是要注意:若事务中在查询后执行update
操作,此时清空一级缓存一级缓存的意义:提高查询效率(对数据库IO操作是ms级别;对内存IO操作是ns级别)
mybatis
的二级缓存是mapper/namespace
级别的缓存,二级缓存默认关闭,若想使用,必须通过设置开启如何开启二级缓存:
- mybatis的配置文件中添加属性:cachable:true
- 在想开启二级缓存的mapper的xml文件中添加一组标签(添加为
<mapper>
的子标签)<cache></cache>
或<cache/>
二级缓存可以实现跨sqlsession数据的共享,但是若操作该mapper的多个sqlsession中,有一个执行了update操作,则立即清空二级缓存
一级缓存、二级缓存都存在时,事务的查询顺序:二级缓存 -> 一级缓存 -> 数据库
32.mybatis的动态sql
-
if
标签:<select id = "findActiveBlogLike" resultType = "Blog"> SELECT * FROM BLOG WHERE state = 'ACTIVE' <if test="title != null"> AND title like #{title} </if> <if test = "author != null and author.name != null"> AND author_name like #{author.name} </if> </select>
-
choose
、when
、otherwise
标签:<select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG WHERE state = 'ACTIVE' <choose> <when test="title != null"> AND title like #{title} </when> <when test="author != null and author.name != null"> AND author_name like #{author.name} </when> <otherwise> AND featured = 1 </otherwise> </choose> </select>
-
trim
、where
、set
标签:<select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG <where> <if test="state != null"> state =#{state} </if> <if test="title != null"> AND title like #{title} </if> <if test="author != null and author. name != null"> AND author_name like #{author.name} </if> </where> </select> <!-- where元素只会在子元素返回任何内容的情况下才插入“WHERE”子句,且子句的开头为“AND”或“OR”,where 元素也会将它们去除。 如果where元素与你期望的不太一样,你也可以通过自定义trim元素来定制where元素的功能。比如,和where元素等价的自定义trim元素为: --> <trim prefix="WHERE" prefixOverrides="AND | OR "> </trim>
-
set
标签< update id="updateAuthorIfNecessary"> update Author <set> <if test="username != null">username=#{ username},</if> <if test="password != null">password=#{ password},</if> <if test="email != null">email=#{email},</if> <if test="bio != null">bio=#{bio}</if> </set> where id=#{id} </update>
-
foreach
标签<select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P WHERE ID in <foreach item="item" index="index" collection="list" open="(" separator = "," close=")"> #{item} </foreach> </select> <!-- 各属性的意义: collection: 遍历的对象类型 item: 给出引用,指向遍历得到的元素对象 index: 序号 open: 所有遍历到的元素最开始以..开头 close: 所有元素遍历结束,以...结尾 separator: 遍历到的元素之间的分隔符 --> <!-- 你可以将任何可迭代对象(如List、set等)、Map对象或者数组对象作为集合参数传递给foreach。当使用可迭代对象或者数组时,index是当前迭代的序号,item的值是本次迭代获取到的元素。当使用Map对象(或者Map. Entry 对象的集合)时,index是键, item是值。 -->
33.请简述Spring的两种核心技术
- 组件类注解:
@Controller
@Service
@Repository
--持久层组件@Component
--通用组件@Configuration
--配置组件@Bean
- SprignMVC中的注解:
@RestController
=@Controller
+@Responsebody
@ResponseBody
--将返回的数据封装到响应体中返回@RequestMapping
--定义请求映射路径@GetMapping
--只处理get请求@PostMapping
--只处理post请求@RequestParam
--对接收到的参数进行判断
- 组件扫描注解:
@ComponentScan
@Autowired
--注入对象@Transactional
--开启事务管理@SpringBootApplication
--启动类注解
34.请说出几个Spring中的注解以及其作用
-
IOC
(Inversion of control
):将对象的创建权交给Spring去管理DI
:依赖注入IOC
和DI
的区别:IOC
描述的是现象;DI
是过程
-
AOP
(aspect orientedprogramming
):-
AOP
是一种思想,具体实现运用到反射 -
反射
reflect
:反射是用于在运行期执行一些操作(创建对象、调用方法、获取类的内部信息) -
目的:实现关注点代码和业务代码的分离解耦,从而提高关注点代码的可维护性和可扩展性
-
AOP的底层是动态代理机制:
运行期通过代理机制将关注点代码织入到业务代码中,产生的新对象叫做代理对象,运行期执行的是代理对象中融合之后的方法代码
-
动态代理机制分类
-
JDK动态代理:为实现类接口的目标对象生成代理对象
生成代理对象需要什么:
- 创建一个实现了接口的目标对象
- 创建切面类对象,保存增强代码 / 关注点代码
- 实现
InvocationHandler
接口
- 实现
- 使用JDK的动态代理机制生成代理对象
- 调用代理对象中的方法,看执行结果是否为融合后的代码
-
CGLIB动态代理:为没有实现接口的目标对象生成代理对象
- 创建一个没有实现接口的目标对象
- 创建增强类对象
- 实现
MethodInterceptor
接口
- 实现
- 使用CGLIB的api方法生成代理对象
- 调用代理对象中的目标方法,看执行的结果是否为组合后的代码
-
AOP代理机制的选择:Spring的AOP会自动根据目标对象是否实现了接口来智能的选择动态代理机制;若目标对象实现了接口,则选择JDK动态代理机制,若目标对象没有实现接口,则自动选择CGLIB动态代理机制
注意:从SpringBoot2.xx版本开始,不论目标对象是否实现了接口,均会默认选择CGLIB动态代理机制
- 专业术语:
aspect
:切面advice
:通知 切面类中的方法joinPoint
:连接点pointcut
:切点 关注点代码切入的连接点叫做切点weaving
:织入
- 通知类型:
- 前置通知:
@Before
- 后置通知:
@After
- 环绕通知:
@Around
- 返回后通知:
@AfterReturing
- 抛出异常后通知:
@AfterThrowing
- 前置通知:
AOP的应用
- Spring的声明式事务管理
- 记录日志
- 性能统计
反射
反射:反射是用于在运行期执行一些操作(创建对象、调用方法、获取类的内部信息)
反射API:
-
在运行期获取类信息
-
获取类对象:
Class cls = Test.class Class cls = 对象.getClass()
-
获取类中的所有属性:
Field[] fields = cls.getFields(); getFields() //获取所有公有成员变量 getDecalredFields(); //获取所有成员变量 getDecalredField(String name):Field; //获取类中指定名称的成员变量 getField(String name):Field; //获取类中指定名称的公有的成员变量
-
获取类中的方法:
getMethods():Method[] getDecalredMethods():Method[] //获取本类中声明的所有方法,不包括继承的 getMethod(String name,Class... paramterTypes) //通过方法名和参数列表获取公有的方法 getDecalredMethod(String name,Class... types) //通过方法名和参数列表获取声明的方法
-
-
在运行期执行操作
-
根据类名加载类对象
Class cls = Class.forName(className);
-
创建对象
Object obj = cls.newInstance();
-
调用方法
Object returnVal = method.invoke(Object target,Object... arg); //调用Method类中的方法 /* target:该方法所属的对象 args:调用方法传递的参数 */
-
35.JVM的结构以及如何调优
JVM的结构
-
元空间:从JDK1.8开始的叫法,其实就是以前的方法区,但是和方法区也有区别。方法区的数据保存在JVM的内存上;但是元空间是将数据直接保存到物理磁盘上
-
类装载子系统:所有的.class文件的数据加载到元空间,以方便运行调用
-
程序执行引擎:执行代码,并将执行到的代码的行号记录到程序计数器
-
程序计数器:
-
本地方法栈:服务于本地方法(
native
修饰的方法),本地方法内存的所有局部变量都保存在本地方法栈中,其内部结果和栈相同的,都是为调用的方法分配栈帧区域,该方法内部的局部变量保存在这个栈帧中 -
栈(JVM栈 / 线程栈):即会为每个线程分配一块区域,在该线程内部调用方法,会在该区域中为方法开辟一块栈帧区域,方法内部的局部变量都保存在该栈帧区域中
- 局部变量表:保存的一直都是所知局部变量和数据
- 操作数栈(先进后出):中间过程所可能出现的任何操作数,取需要出栈,产生需要入栈
- 动态链接:当前方法保存在元空间上的地址,想调用可在这里直接查看到地址,在元空间找到执行
- 方法出口:保存当前方法执行结束要返回到其他那个方法的那个位置去,保存其他某个方法的行号用到了程序计数器
-
堆:采取分代思想,分为年轻代(
young generation
)、年老代(old generation
),年轻代:年老代 = 1:2;-
年轻代:
Eden
区、S0
区、S1
区(Suvivor 幸存者区)Eden
:S0
:S1
= 8 :1 :1年轻代工作原理:
新创建的对象会保存到Eden
区,当Eden
区内存不足时,会触发minor gc
,会对整个年轻代进行回收,在回收之前,会使用可达性分析算法,从栈开始,标记正在使用的对象,将使用的对象保存到S0
区,然后对Eden
和S1
区进行回收,保存到S0
的对象的年代数会+1,后续若再次Eden
内存不足时,将正在使用的对象存入另一个s区(S1
区),然后对Eden
和S0
进行回收,当对象的年代数达到15,会将对象移入到年老代中 -
年老代:创建时太大的对象会直接保存到年老代;从年轻代产生,年代数达到15的对象
年老代工作原理:
年老代中会保存大的对象和年代数达到15的对象,当年老代内存不足时,会触发
full gc
,会对整个堆内存进行回收,且会造成STW
(stop the world),用户会有卡顿的体验,若频繁的触发full gc
,用户的体验非常差,此时就需要进行JVM调优
注:
minor gc
和full gc
都会触发STW
,但是minor gc
回收内存小,所以造成的卡顿几乎感觉不到,所以minor gc
触发频率可以高,但是full gc
是对整个堆内存回收,造成的卡顿用户感觉很明显,所以要尽可能的减少full gc
的触发 -
JVM的优化
- 尽可能减少大对象的产生
- 操作文件时,尽可能的多次操作
- 尽可能让对象在年轻代被回收走
- 尽可能的扩大年轻代的大小
- 直接扩大堆内存大小
- 调整年轻代比例
- 可以调整
Eden
和两个S
区的比例 - 调整年轻代和年老代的比例(慎用)
- 可以调整
- 尽可能的扩大年轻代的大小
- 更换
CMSGC
为G1GC
JVM堆内存大小的调整
修改JVM堆大小的方式:
-
找到Idea安装目录下的 --> bin -->
idea.exe.vmoptions
-server -Xms128m //堆初始大小 -Xmx512m //最大堆内存 -XX:ReservedCodeCacheSize=240m -XX:+UseConcMarkSweepGC //指定GC -XX:SoftRefLRUPolicyMSPerMB=50-ea -XX:CICompilerCount=2-Dsun.io.useCanonPrefixCache=false -Djdk.http.auth.tunneling.disabledSchemes="" -XX:+HeapDumpOnOutOfMemoryError -XX:-OmitStackTraceInFastThrow -Djdk.attach.allowAttachSelf=true -Dkotlinx.coroutines.debug=off -Djdk.module.illegalAccess.silent=true /* 堆配置: -Xms 初始堆大小 -Xmx 最大堆大小 -XX:Newsize=n: 设置年轻代大小 -XX:NewRatio=n: 设置年轻代和年老代的比值, 如:为3表示年轻代和年老代比值为1:3,年轻代占堆内存的1/4 -XX:SurvivorRatio=n: 年轻代中Eden区与两个Survivor区的比值 注意Survivor区有两个。如3表示Eden: 3 Survivor: 2,一个Survivor区占整个年轻代的1/5 -XX:MaxPermSize=n: 设置永久代大小 永久代:元空间 */
36.说一说年轻代、年老代的工作原理
年轻代
-
新创建的所有对象都会到达Eden区
-
当Eden区内存不够会触发
minor GC
,在此之前会通过可达性分析算法标记出当前正在被使用的对象,标记之后将所有标记对象复制到两个Suvivor区之一,此时的Suvivor区为 S0区。对象每移动一次,其自身年代数+1- 可达性分析算法:从栈内存入手,堆里的对象如果正在被使用,其对他的引用一定保存在栈中,根据栈中的局部变量判断有没有指向的对象,有则表示正在被使用,会对其进行标记,且对标记对象再检测判断有没有指向的对象从而再标记
-
在触发
minor GC
之后,会将Eden区和另一个Suvivor区全部进行回收,继续创建新对象,直到当一个Suvivor区内存也不足,将所有正在被使用的对象复制到另一个Suvivor区,且自身年代数+1 -
直到
Suvivor
区中的对象年代数为15时,如果仍在使用,则移入老年代;同样当一个对象过大,创建时不够存入年轻代中将直接移入老年代。
年老代
创建时太大的对象和年代数达到15的对象会保存到年老代,当年老代内存不足时会触发full GC
,会对整个堆内存进行回收,包括年轻代,且产生STW
(Stop The World),让程序除了full GC
其余所有线程暂停,所以要尽可能少产生full GC
37.GC的分类
SerialGC
:单线程ParalleGC
:并行情况作用的GCConcMarkSweepGC
:并发情况作用的GC,从JDK1.5开始默认提供的G1GC
:从JDK1.9开始默认提供的(JDK1.9版本的堆内存结构不再是之前的结构,而是使用块结构,但是依然保留分代思想)
G1GC
:JDK1.9之后堆内存结构变为块结构,当内存不足回收时按块回收,
G1GC
有一个后台线程会记录当前回收利用率最高的块,将其作为最优先回收的块,若回收后够用,则不再继续回收,大大降低STW的时间。回收后,该块下次可以作为不同的区使用,所以相当灵活
38.GC垃圾回收机制的常用算法
-
复制算法:
minor gc
使用 -
标记清除算法
-
回收后的内存不连续,内存利用率低
-
-
标记整理算法
- 回收后,将使用的对象进行整理,将可利用的内存整理到一起,实现内存连续,内存利用率变高
39.数据库的主从复制,读写分离,分库分表
数据库的读写分离,主从复制,分库分表都是为了优化数据库
比如现在只有一台数据库服务器,若某写操作会给表加锁,此时其他所有并发操作的效率都会受到影响,采用读写分离,master用于写操作,多台salve用于读操作,在写的同时不会影响读操作,且多台salve用于读,可以大大提高并发读取的效率,在读写分离的情况下必须保证master和salve上的数据一致,所以需要主从复制
分库分表也是优化数据库的措施,可以降低服务器的压力,提高执行效率
40.redis的哨兵模式
redis是非关系数据库,作用在项目的数据持久层(数据访问层)。使用redis的哨兵模式必须是实现了读写分离的主从复制
redis的哨兵模式用于在master
宕机时,快速的从slave
中选举出一个,成为新的master
,并通知其他的slave
,告知master
发生了改变,让他们重新建立关系,从而保证系统正常运行
具体细节:
哨兵系统中是由若干个哨兵实例组成的,每个哨兵实例均会和每台服务器通过心跳机制,保持联系,若某个sentinel
接收不到master
的响应,则主观认为master
宕机,但是master
不一定真的宕机,可能是网络不好引起的。此时sentinel
会向其他的sentinel
发起询问,判断master
是否真的宕机,其他的sentinel
会通过心跳机制来检测是否能收到master
的响应,若超过半数的sentinel
都收不到响应,则客观认为master
宕机,从slave
中投票选举出一台服务器作为新的master
,并通知其他的slave
和新的master
重新建立联系
-
一个
master
用于读数据,多个slave
用于写数据,多个slave
集群连接 -
sentinel
通过心跳机制连接服务器,某个sentinel
接收不到master
的响应,主观认定为宕机,会向其他的sentinel
发起询问,判断master
是否真的宕机 -
从
slave
中投票选举新的master
,并通知其他的slave
和新的master
重新建立联系
41.redis的五种数据类型,分别应用场景
redis保存数据是以key-value
的形式保存的,其中key
一定是String
类型,而value
的数据类型有5种:
-
String
:保存字符串,可以是整数,整数可以实现自增长,进行加减incr key //增1 decr key //减1
应用:保存点击量
-
hash
:特别适合用于保存对象应用:购物车
-
list
:保存字符串数据,元素有序,redis3.2之前数据结构采用ziplist
和LinkedList
来保存数据,从redis3.2版本开始采用quickList
,quickList
是ziplist
+LinkedList
,整体结构是双向链表,每个节点的元素保存的是ziplist
应用:微信点赞
-
set
:保存String
类型的无序集合,元素唯一,底层为散列表应用:黑名单
-
sorted set
:数据结构为散列表,保存字符串,必须携带分数存入,会根据分数进行升序排列;字符串作为key
,分数作为value
,字符串唯一存在,分数可以重复,分数可以相同,若存入的字符串已经存在,则后来的分数覆盖之前的分数应用:排行榜
42.SpringCloud的五大组件和作用
SpringCloud
是微服务框架,NetFlix
(奈飞)公司刚开始提供了5个独立的微服务组件,五大组件之间若想配合使用,必须进行相关的配置,五个组件都配合,配置比较繁琐,而SpringCloud
帮我们完成了这件事,相当于一个一站式服务平台
Zuul
:网关,用于路由请求 GatewayEureka
:注册和发现中心,用于注册和拉取微服务 NacosRibbon
:负载均衡,用于将请求均衡的路由到一个服务中的多台服务器上 Feign- ribbon有均衡策略实现负载均衡:默认是轮询策略
Hystrix
:熔断器,用于避免雪崩效应的- 实现方式:当服务器出现问题,进行服务降级,从而避免雪崩效应
Config
:配置中心,用于保存一些共有的配置信息 Nacos
43.同步通讯和异步通讯的区别
以请求为例:
- 同步通讯是指客户端发起的请求,必须等待之前请求结束之后才能发起
- 异步通讯是指客户端发起的请求,无需等待之前请求结束即可发起
以多线程并发为例:
- 同步通讯是指某个线程必须等待之前的线程执行结束才能执行
- 异步通讯是指某个线程无需等待其他线程执行结束即可执行
44.Ribbon和Feign的区别
相同:
Ribbon
和Feign
都是用于实现负载均衡的,Feign
是基于Ribbon
,提供了更多方便的操作- 二者均是发送HTTP请求
区别:
-
Ribbon
需要手动构建HTTP请求,Feign
无需手动构建HTTP请求,而采用接口式编程,来发起请求
45.Linux命令
详细命令请看这篇文章