JAVA基础核心/集合/Spring/Mybatis/JVM/SQL/linux/多线程/Redis/架构设计与分布式 ---> 2024.3.23 更新 多线程
一、Java基础核心
1.String a = "123"; String b = "123";String c=new String("123"); a==b的结果是什么? a==c的结果?
答:true,false。a==b指向常量池中的同一个地址。a==c ,由于c是在堆中的对象,而a是常量池中的对象,因此是不同地址。
具体:JVM分析:String a = “123“;String b = “123“;a==b的结果是什么?为什么?_老板楼上雅座-CSDN博客
2.==和equals的区别?
答: ==比较的是对象的引用地址,equals比较的是对象内容。
3.||和&&的优先级哪个高?
答:或的优先级高,true || true && false, 先计算或 ,再计算true && false
5.volatile关键字有什么作用?
答: 保证有序性和内存可见性。多线程下需要及时更新值到主存时使用。
6.Java的静态代理和动态代理有什么差别?
答: 静态代理编译前就写好代理类,动态代理则是利用反射在运行时动态生成代理类。
7.String和StringBuffer、StringBuilder有什么区别?
答:String是不可变的,StringBuffer和StringBuilder是可变的。StringBuffer是线程安全的,StringBuilder是不安全的。
8.class.forName()和classLoader都可用来对类进行加载,它们的区别?
答:Class.forName得到的class是已经初始化完成的
Classloder.loaderClass得到的class是还没有链接的。
class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。"
9.类实例化执行顺序?
答:父子类静态代码块 static{...}--->main方法----->父子非静态代码块 {...}------>构造函数 ------->一般方法
10.访问修饰符的作用域?
答:口诀:类包子非,一二三四。 private(1)-default(2)-protected(3)-public(4) ,数字代表几个√
11.深拷贝和浅拷贝区别?
答:深拷贝会拷贝对象里的引用,比如属性是一个其他对象。浅拷贝不会拷贝属性对象,只是复制了地址。
12.什么是序列化和反序列化?怎么实现的?
答:序列化:对象--》字节;反序列化:字节--》
两种实现:
(1)java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
(2)Kryo框架,优点:快速、高性能、体积小
Kryo kryo = new Kryo();
Output output = new Output(new FileOutputStream(fileName));
kryo.writeObject(output, this.cache);
13.反射原理,注解原理?
答:反射就是在运行时对于任何一个对象都知道其所有属性及方法,并能够调用。
反射原理:每一个类都有一个Class对象。所有类都是在第一次使用时,动态加载到JVM中,当程序创建第一个对类的静态成员引用时,会加载这个类。一旦你一个类的Class对象被载入到内存,它便被用来创建这个类的所有对象。Java使用Class对象来执行其RTTI(Run-Time Type Identification),这也是反射的基础。
注解原理:获取到某个类的Class对象后,使用反射机制获取到该类的属性、方法等,判断对应的属性、方法是否使用了特定的注解,getAnnotation(xx.class)方法来获取类的注解,如果有则调用该类注解的处理器来处理。
面试回答--》反射就是运行时每个类都有class对象,然后就可以用class对象得到属性和方法信息并能够调用。注解就是利用反射得到class对象,然后getAnnotation方法得到注解,就可以根据这个注解做一些处理。
14.ClassNotFoundException 和NoClassDefFoundError的区别?
答:1.前者是exception异常,后者是error
2.前者是动态加载class时报错,Class.forName;后者是运行时找不到类,一般是调Jar包里的时候。
二、集合
1.ArrayList和LinkedList底层实现有什么差别?
答: arraylist底层是数组,linkedlist底层是链表。需要保持输入输出顺序时要linkedlist
2.HashMap的底层结构是什么?
答:数组+链表+红黑树
3.HashMap的Put()方法原理?
答:(1)调用 hash(Key) 方法计算 Key 的 hash 值,再计算得到数组下标。
(2)若key的hash在HashMap中不存在,则插入,若已经存在,发生碰撞。
(3)当发生碰撞时(两个不同的字符串key可能计算出的hash相同)
若key相等,则更新Value。
若key不想等,则加到链表的尾部。
(4)如果放链表时发现当链表长度大于8,且数组长度大于64,把键值对放红黑树(链表转红黑树)。
数组是一个Node,它是Map.Entry的子类。包含 hash key value next属性 ,红黑树TreeNode是LinkedHashMap.Entry的子类(parent,left,right,prev,red)
关键方法:属性table,resize扩容,newNode创建节点,treeifyBin转红黑树
put方法注释:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab;/*数组*/
Node<K,V> p/*Node的临时变量*/;
int n,//数组的长度
i;
if ((tab = table) == null || (n = tab.length) == 0)//table为空或节点不足,扩容
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)//如果数组索引位置是空的,新建一个Node节点
tab[i] = newNode(hash, key, value, null);
else {//产生碰撞,下面是处理碰撞
Node<K,V> e; //目标节点,也就是我们想要处理的节点
K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p; //hash碰撞,key又相等,直接覆盖
else if (p instanceof TreeNode)//判断是不是红黑树
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {//以下是链表
for (int binCount = 0; ; ++binCount) {//链表循环遍历
if ((e = p.next) == null) {//如果链表的下一个节点为空
p.next = newNode(hash, key, value, null);//把节点插到链表的尾部
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);//如果链表的长度大于8,再进去判断数组是不是大于等于64,如果大于转红黑树
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))//如果e节点存在hash值和key值都与传入的相同,则e节点即为目标节点,跳出循环
break;
p = e;//指向下一个节点
}
}
//到这里已经遍历到目标节点e了,现在要做的就是替换新的值
if (e != null) { //如果e节点不为空,则代表目标节点存在,使用传入的value覆盖该节点的value,并返回oldValue
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);//这里是空方法, 用于LinkedHashMap
return oldValue;
}
}
++modCount;
if (++size > threshold)//如果插入节点后节点数超过阈值,则调用resize方法进行扩容
resize();
afterNodeInsertion(evict);//这里是空方法, 用于LinkedHashMap
return null;
}
4.HashMap里的hashcode方法和equal方法什么时候需要重写?
答:对象作为hashmap的key时要重写equals方法,不写equals会不相等。
5.hashmap扩容机制是什么?
答:jdk1.8初始是0,put的时候扩容成16,加载因子默认0.75, 当达到容量的0.75的时候就要扩容,扩容成2倍。
6.hashmap为什么使用红黑树?为什么不直接使用红黑树?
答:数组太长插入慢,链表太长查询慢,二叉树树太高,红黑树通过左旋、右旋变色来平衡二叉树。如果树很短,没必要使用红黑树,因为平衡二叉树也需要消耗,反而更慢。
7.jdk8中对HashMap做了哪些改变?
答:jdk1.8引入了红黑树;hash碰撞时,jdk1.8从链表的尾部插入,而1.7是头插法。
8.ConcurrentHashMap怎么实现线程安全?
答:JDK 1.7 中采用 分段锁的方式;JDK 1.8 中直接采用了CAS(无锁算法)+ synchronized。
9.你知道 hash 的实现吗?为什么要这样实现?为什么要用异或运算符?
答:JDK 1.8 中,是通过 hashCode() 的高 16 位异或低 16 位实现的:(h = k.hashCode()) ^ (h >>> 16),主要是从速度,功效和质量来考虑的,减少系统的开销,也不会造成因为高位没有参与下标的计算,从而引起的碰撞。
异或运算保证了对象的 hashCode 的 32 位值只要有一位发生改变,整个 hash() 返回值就会改变。尽可能的减少碰撞。
10.简单说说红黑树?
答:根节点是黑色的,每个节点非黑即红,一条路径上不能有两个红色节点,如果有就会平衡,有一个教育网站可以动态的看到自旋过程。
11.ArrayList和LinkedList的插入和访问的时间复杂度?
答:时间复杂度为O(n),就代表数据量增大几倍,耗时也增大几倍。比如常见的遍历算法。O(1)就是最低的时空复杂度,也就是耗时/耗空间与输入数据大小无关,无论输入数据增大多少倍,耗时/耗空间都不变。哈希算法就是典型的O(1)时间复杂度,无论数据规模多大,都可以在一次计算后找到目标(不考虑冲突的话)
ArrayList没有扩张,那么插入的复杂度为O(1),扩张最差O(n),访问复杂度为O(1),LinkedList插入复杂度为O(1),访问的复杂度为O(n).
12.HashMap、LinkedHashMap、HashTable和currentHashMap的区别?
答:hashmap:底层是数组+链表+红黑树,线性不安全,性能好。
LinkedHashMap:底层和hashmap一样,里面增加了一些方法,还有Entry的after、before属性来保持遍历的时候能按照插入顺序来。
Hashtable:底层是数组,put方法加了同步锁,线程安全,但效率低不适合并发。
CurrentHashMap:引入了分割,它只锁定一个Node,也是线程安全的,效率比Hashtable高。
三、Spring
1.restcontroller 和controller有什么区别?
答:restcontroller=controller+@resposebody
2.controller是单例的吗?
答: 默认是的。scope可设置成多例,作用域:singleton(单例)、prototype(多例)
3.过滤器拦截器有什么区别?
答:过滤器依赖servlet容器,基于函数回调实现,在容器初始化时调用一次,几乎可以过滤所有资源包括静态文件。拦截器基于web框架比如springmvc,基于反射实现运用aop,可以多次调用,但只能拦截controller,无法拦截静态文件。
4.spring bean的生命周期?
答:加载bean定义、bean工厂处理器、实例化、属性赋值、aware接口扩展、beanpost前置处理器、初始化、beanpost后置处理器-bean的使用销毁
5.spring bean的初始化怎么做?
答:1.配置init-method @Bean(init-method="xx") 2.实现InitializingBean方法afterPropertiesSet,3。加@PostConstruct 顺序:postConstruct>afterPropertiesSet>init-method
6.简单说下IOC和AOP?
答:ioc就是权限控制反转,创建对象交给spring容器来做,便于管理。aop就是面向切面编程,它通过动态代理为spring对象增加功能。比如增加日志切面,spring的事务以及拦截器都是运用了aop。
7.spring的动态代理用哪种实现?
答:spring默认是采用jdk的动态代理,如果想要用cglib可以通过配置文件的方式指定。
8.jdk代理和cglib代理有什么区别?
答:jdk代理利用反射对实现接口的类生成代理,而cglib则利用字节码技术生成一个子类。前者必须是有实现接口才可以,后者则是生成子类,因此目标类不能是final的。
9.spring依赖注入方式有几种,分别是什么?
答:常见的有3种。构造器注入、set方法注入、属性注入(autowire或者resorce)、接口注入(不常用)
10.springmvc流程?
答:面试回答-->(前端控制器(DS)接收请求,然后DS调用处理器映射器HandleMapping,生成处理器对象和拦截器,DS再调用处理器适配器HandleAdapter找到对应的handle,调用handler返回模型视图,DS调用视图解析器解析视图,最后浏览器渲染给用户。)
核心组件:
前端控制器 DispacherServlet
处理器映射器 HandleMapping
处理器适配器 HandleAdapter
Handle
视图解析器 ViewResovle
1.发送请求前端控制器 DispacherServlet
2. DispacherServlet调用处理器映射器HandleMapping,生成处理器对象和拦截器
3.DispatcherServlet 再调用处理器适配器HandleAdapter找到对应的handle
4.调用handle,返回模型视图,前端控制器再调用视图解析器解析视图
5.DispatcherServlet 对视图进行渲染,返回给用户。
11.说说spring mvc用了哪些技术怎么实现的?
答:拦截器 aop技术
12.Spring MVC常用的注解有哪些?
答:@RequestMapping url映射 、@RequestBody 将请求参数转为java对象、@ResponseBody将返回参数转为json格式。
13.springmvc是怎么向前台传递数据的?
答:通过modelMap对象把内容放到里面,前端就可以通过el表达式拿到。
14.springmvc拦截器是怎么写的?
答:实现HandlerInterceptor接口或者继承适配器类
15.WebApplicationContext是什么?
答:继承了ApplicationContext 并增加了一些WEB应用必备的特有功能.
16.springboot/spring常用的注解有哪些?
答:@import @Component @RequestMapping @Pathvariable @Bean @Scope @Configuration @Autowired @RequestBody @ResposeBody
17.怎么保证spring定时任务高可用?两个定时任务同时写表怎么处理的?
答:数据库方面:表主键约束避免重复插入,如果写的频率高,可以用悲观锁的方式,更新的时候加锁for update nowait,数额类更新:如果是累加的,可以写成update a =a+b 的方式,如果本次操作不可抛弃,可以增加重试功能。如果写的频率不高,可以用乐观锁,增加版本号字段来控制,写的时候带上版本号去更新。
18.说说springboot自动配置原理?
答:面试回答-->(springboot注解会引入后缀是xxImportSelector类,里面有selectImorts方法,它会寻找META-INF/spring.factories配置文件,找到里面的自动配置类,根据自动配置类进行装配。)
详细:
@SpringBootApplication里有@EnableAutoConfiguration ,@EnableAutoConfiguration里再@Import({EnableAutoConfigurationImportSelector.class})
他的父类AutoConfigurationImportSelector里面的selectImports方法会去寻找META-INF/spring.factories配置文件,配置文件里配置的EnableAutoConfiguration的自动配置类(比如springboot-autoconfigure META-INF/spring.factories 自动配置类)
根据自动配置类进行装配
19.Spring cloud知道多少?
答:五大组件eureka、zuul、Hystrix、ribbon、springcloud config,以及服务调用feign
20.@Autowired/@Resource的区别?
答:@Autowired是Spring的注解,Autowired默认先按byType,如果发现找到多个bean,则,又按照byName方式比对,如果还有多个,则报出异常。@Resource是jdk的注解,默认按名称装配,如果装配失败,按类型。
21.BeanFactory 和ApplicationContext的区别?
答:BeanFactory提供基本功能(实例化对象和获取对象),ApplicationContext提供更多的功能包括aop。装载bean方面,BeanFactory在拿bean的时候才实例化,ApplicationContext在启动的时候全部实例化。
22.Spring 定时任务执行原理是什么?
答:Spring 将会优先查找 TaskScheduler/ScheduledExecutorService的实例,若存在将会使用。如果不存在则使用的是 JDK 自带的 ScheduledExecutorService的默认配置。
23.spring执行多个定时任务怎么做?
答:配置一个TaskScheduler,设置好线程池大小,增加到spring容器中。如果不配置,则多个任务可能会互相影响(等待)。
24.spring是如何解决循环依赖的?
答:面试回答--》简单来说,主要是有三级缓存,在里面有发现循环依赖,就会临时放到二级缓存(表示有循环依赖的),最后都放到一级缓存里面去。
1.对象A一开始会放入三级缓存( singletonFactories),发现依赖B,也放入三级缓存;2.开始注入B中的依赖A时,把A从三级移到二级(删除三级中A);3.把B放进一级缓存,删除三级,完成B的注入;4.把A也放入一级缓存,删除二级缓存,完成A的注入。至此A,B注入完成。
详细:
假设对象A中有属性是对象B,对象B中也有属性是对象A,即A和B循环依赖。
1.创建对象A,调用A的构造,并把A保存下来。
2.然后准备注入对象A中的依赖,发现对象A依赖对象B,那么开始创建对象B。
3.调用B的构造,并把B保存下来。
4.然后准备注入B的构造,发现B依赖对象A,对象A之前已经创建了,直接获取A并把A注入B(注意此时的对象A还没有完全注入成功,对象A中的对象B还没有注入),于是B创建成功。
5.把创建成功的B注入A,于是A也创建成功了。
于是循环依赖就被解决了。
singletonObjects:单例对象的cache,一级缓存
earlySingletonObjects :二级缓存,提前暴光的单例对象的Cache 。【用于检测循环引用,与singletonFactories互斥】
singletonFactories : 单例对象工厂的cache ,三级缓存
步骤 | 操作 | 三层列表中的内容 |
---|---|---|
1 | 开始初始化对象A | singletonFactories: earlySingletonObjects: singletonObjects: |
2 | 调用A的构造,把A放入singletonFactories | singletonFactories:A earlySingletonObjects: singletonObjects: |
3 | 开始注入A的依赖,发现A依赖对象B | singletonFactories:A earlySingletonObjects: singletonObjects: |
4 | 开始初始化对象B | singletonFactories:A,B earlySingletonObjects: singletonObjects: |
5 | 调用B的构造,把B放入singletonFactories | singletonFactories:A,B earlySingletonObjects: singletonObjects: |
6 | 开始注入B的依赖,发现B依赖对象A | singletonFactories:A,B earlySingletonObjects: singletonObjects: |
7 | 开始初始化对象A,发现A在singletonFactories里有,则直接获取A, 把A放入earlySingletonObjects,把A从singletonFactories删除 | singletonFactories:B earlySingletonObjects:A singletonObjects: |
8 | 对象B的依赖注入完成 | singletonFactories:B earlySingletonObjects:A singletonObjects: |
9 | 对象B创建完成,把B放入singletonObjects, 把B从earlySingletonObjects和singletonFactories中删除 | singletonFactories: earlySingletonObjects:A singletonObjects:B |
10 | 对象B注入给A,继续注入A的其他依赖,直到A注入完成 | singletonFactories: earlySingletonObjects:A singletonObjects:B |
11 | 对象A创建完成,把A放入singletonObjects, 把A从earlySingletonObjects和singletonFactories中删除 | singletonFactories: earlySingletonObjects: singletonObjects:A,B |
12 | 循环依赖处理结束,A和B都初始化和注入完成 | singletonFactories: earlySingletonObjects: singletonObjects:A,B |
表格来自:https://blog.csdn.net/lkforce/article/details/97183065
其实最关键的是,为什么会出现循环依赖?
假如说只有一级缓存singletonObjects,A对象创建后放进来,再注入B,B对象创建后也放进singletonObjects。。。。B中也有依赖A,又会创建A对象。。。。所以问题的关键是:怎么取已经创建的对象,而三级缓存的作用就是为了:可以发现是否已经有对象的实例创建好了。
25.@Transitional失效的情况?
答:springmvc下controller上的@Transactional不生效;检查型异常不回滚,如FileNotFoundException;只作用在public修饰符上
26.Springmvc 中 DispatcherServlet 初始化过程
答:入口是web.xml中配置的ds,ds继承了HttpServletBean,FrameworkServlet,通过其中的init方法进行初始化装载bean和实例,initServletBean是实际完成上下文工作和bean初始化的方法。
27.spring中如何执行接口实现类?(spring为开发者提供了很多接口,比如BeanPostProcessor)
答:1.在META-INF/services/目录下用你的接口全路径名称命名一个文件(不加后缀),然后在该文件中一行一个添加你的接口实现类的全路径名。
2.通过load方法来加载出所有的接口实现类
ServiceLoader serviceLoader = ServiceLoader.load(IService.class);
Iterator iterator = serviceLoader.iterator();
while(iterator.hasNext()){
IService service = (IService)iterator.next();
service.test();
}
方法二:spring利用 ApplicationContextAware接口扩展获取接口实现类,applicationContext.getBeansOfType(IService.class);//获取该接口类型的所有Bean实现,缺点是只能获取加入spring容器的bean,
也就是@Component注解的bean。
28.spring怎么注入接口的?
答:实际的注入的是实现类,@Autowired 默认按类型装配,如果多个类型会报错,找不到再按名称装配。
//例子:接口IService ,实现类是service1和service2
@Autowired
@Qualifier("service1")//按名称时配合Qualifier
private IService service;//如果没有用Qualifier,那么用servcie会报错,把service改成实现类的名字service1,就会按名称装配
@Resource默认按名称装配,找不到再按类型
四、Mybatis
1.说说mybatis比jdbc更好的地方
mybatis底层也是jdbc封装的,mybatis做了什么事情代替jdbc,有连接池,xml配置语句不用硬编码,预编译需要设置序号值,不利于维护。结果集也用硬编码。
1.加载数据源
2.解析xml,根据namespace和id获取sql语句
3.执行sql
为接口生成代理对象(MapperScannerConfigurer)
<!-- 自动扫描dao包下的所有接口自动生成代理对象 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.demo.dao" /><!--com.**.demo 多层目录,可以用**-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
值得注意的是,mapperscanner配置的包不要太大,不能包含@service的接口,不然,会导致service接口也被生成Mybatis代理对象导致无xml文件报错。
2.Mybatis接口代理是按类型还是按名称?
答:首先来看一个异常,接口没有绑定对应的mapper xml配置
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.demo.service.MyService.testService
值得注意的是,Mybatis是按类型装配的,因此,如果Service是按照类型装配的,就会被Mybatis扫描到,因此就会报绑定异常,如果是按照名称装配,就不会被Mybatis生成代理对象。
3.mybatis一对多怎么写,关键字是?
答:collection
4.mybatis like 怎么写?
答:用oracle的||双竖线连接字符串%,中间接预编译字符串。
<select id="select3" resultMap="myMap">
select * from student where name like '%' || #{name} || '%'
</select>
5.mybatis # 和$有什么区别
#就是类似jdbc里的问号替换 预编译,字符串会有带引号的字符串 name=‘xx’
$就是直接替换中括号里的内容,不会做类型转换,name=xx 此方法会引起sql注入
6.mybatis接口多个方法参数怎么和xml对应上?
可以用map,但不直观,接口中的方法参数用@Param注明对应xml中的预编译字符串。
7.mybatis表字段名和实体类属性名不一样怎么办?
xml中编写resultMap来映射
8.Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
xml中的namespace映射dao接口路径,接口的方法名映射xml中的每个语句标签的id属性。
方法不能重载,因为mybatis是把接口名+方法名作为唯一key去寻找xml中的语句标签的,语句标签存在mapperstatement里,唯一key-mapperstatement
9.mapper接口的工作原理?
利用jdk动态代理,为借口生成一个MapperProxy代理对象,代理对象会拦截接口方法,转而执行MapperStatement所代表的sql,然后将sql执行结果返回。
10.mybatis有哪些标签?
select|insert|updae|delete <resultMap>、<parameterMap>、<sql>、<include> trim|where|set|foreach|if|choose|when|otherwise|bind
11.mybatis list怎么写?
单个:<if test="deptno!=null and deptno!=''">
and deptno=#{deptno,jdbcType=DECIMAL}
</if>
多个:
<if test="deptnos!=null and deptnos.size>0">
and deptno in
<foreach collection="deptnos" item="item" open="(" separator="," close=")">
#{item,jdbcType=DECIMAL}
</foreach>
</if>
12.大于、等于、不等于的写法
<![CDATA[
AND state <>'-1'
]]>
13.更新,没值不更新,防止更新为空
<update id="update" parameterType="java.util.HashMap" >
update dept
<set>
<if test="dname!= null and dname!= ''">
dname= #{dname},
</if>
</set>
where deptno= #{deptno,jdbcType=VARCHAR}
</update>
五、JVM
1.JVM里,new出来的对象是在哪个区?
答:new出来的对象放在堆里,对象的引用放在栈里。
2.说说类加载有哪些步骤?
答:类加载分三步:加载、连接(验证、准备和解析)和初始化。
加载:class文件加载到JVM内存(静态变量、常量放到方法区),产生Class对象。
验证:验证class文件是否格式正确。
准备:为静态变量分配内存并设置默认的初始值
解析:将符号引用替换为直接引用 (栈帧里的动态链接也有一步符号引用转变为直接引用)
初始化:为类的静态变量赋予正确的初始值
3.说说JVM内存模型?
答:是一种规范,与线程共享有关,堆和方法区所有线程共享,需要对其制定规则规范。主内存:java所有变量都存储在主内存中。工作内存/线程本地内存:本线程用到的变量为主存中的副本拷贝。
4.说说JVM内存结构分别放什么?
答:堆:对象和数组、字符串常量池。方法区:常量、静态变量,运行时常量池(字面量和符号引用)。虚拟机栈:局部变量、对象引用。程序计数器:记录线程执行位置,以便下一次继续执行。
本地方法栈:是一些native方法(c++实现)
5.说说常用的JVM参数?
答:-Xms128m -Xmx128m Xmn1g -XX:SurvivorRatio=4 -XX:+UseG1GC
6.说说常用的JVM调优命令?
答:jps jstack jinfo jmap jstat
7.你知道反汇编吗?
答:知道。会使用javap反汇编解读i++操作原理 ,反汇编后得到JVM指令,可以通过官方文档查看JVM指令集的解释。
javap -c xx.class
8.说说高cpu的排查过程?
答:top命令找到进程号,用ps命令找到线程号,然后转为16进制,最后使用jstack命令找到该线程号堆栈位置。
9.堆栈溢出如何跟踪?
答:事先启动增加堆溢出跟踪日志参数-XX:+HeapDumpOnOutOfMemoryError ,用mat工具查看。
10.说说一次GC流程是怎么样的?
答:普通对象进入年轻代eden区,大对象直接进入老年代。
年轻代回收机制:
1.对象进入伊甸区,填满后触发一次minor gc,还存活的对象会复制到其中一个存活区s0并清空伊甸区。
2.当伊甸区再一次填满后,再次触发minor gc,jvm会把伊甸区和s0还存活的对象复制到另一个存活区s1,同时清空伊甸区和s0区。
3.如果复制的过程发现空间不够,就会搬到老年代。
4.如果minorgc达到15次就会搬到老年代。
11.什么是stop the world(STW)?
答:GC前会执行STW操作。线程进入JVM设置的“安全点”,暂停所有运行中的线程,stop the world,然后开始GC。
12.什么情况下会发生栈内存溢出?什么情况下发生堆溢出?
答:递归容易发生栈溢出。 堆溢出:对象一直增大,不释放。 比如循环里一直list.add
13.g1 和 cms 区别?
答:Cms是以获取最短回收停顿时间为目标的收集器。基于标记-清除算法实现。比较占用cpu资源,切易造成碎片。 G1是面向服务端的垃圾收集器,是jdk9默认的收集器,基于标记-整理算法实现。可利用多核、多cpu,保留分代,实现可预测停顿,可控。
14.你知道几种垃圾回收算法?
答: 引用计数法、标记--清除算法、复制算法、标记--压缩(整理)算法。
引用计数法:对象被别人引用,它的计数器加1,引用结束减1,对象的计数器值为0就回收。 缺点:循环引用就可能没发回收。
标记清除算法:根据根节点寻找可达对象,如果不可达就回收。缺点:产生空间碎片。
复制算法(新生代串行垃圾回收器使用):把空间分钟两块,回收的时候就复制到另一块区域,然后删除本区域。
标记压缩算法(老年代中使用):它在标记--清除算法的基础上做了一些优化。在一块内存空间内,标记可达的对象,压缩到内存的一边,然后删除其他对象,这样就不会产生内存碎片。
15.判断对象死亡的方法有哪几个?
答:引用计数法和可达性分析法。引用计数法就是通过计数器值控制,被引用就加1,引用失效就减1. 缺点:A引用B,B引用A就无法回收,也就是循环引用。可达性分析法就是挑选一个稳定的对象作为GCROOT,然后寻找可达的对象,不可达就回收。缺点:产生内存碎片。
16.你知道哪几种垃圾收集器?
答:Parallel Scavenge 复制算法,重要的特点就是:关注系统的吞吐量。
Parallel OLD 采用标记压缩算法
CMS收集器 标记-清除算法 回收停顿时间会比较小,但是相应的牺牲了吞吐量 适合B/S架构
G1 收集器(Garbage First) G1 收集器是目前最新的垃圾回收器,与 CMS 收集器相比,G1 收集器是基于标记--压缩算法的,它不会产生内存碎片
它将整个Java堆划分为多个大小相等的独立区域(Region),region集合定义为年轻代和老年代,但他们不再是物理隔阂。
17.JVM老年代和新生代的比例?
答:年轻代占1/3,老年代占2/3。伊甸区和存活区占比是8:1:1,都可以通过参数来指定。
18.YGC和FGC发生的具体场景?(什么时候执行YGC和FGC)
答:eden空间不足,执行 young gc;old空间不足,perm空间不足,调用方法System.gc() ,YGC时的悲观策略,dump live的内存信息时(jmap –dump:live)这些出发FGC
19.CAS的原理?Atomicinteger为什么要用CAS而不是synchronized?
答:它的作用是将指定内存地址的内容与所给的某个值相比,如果相等,则将其内容替换为指令中提供的新值,如果不相等,则更新失败。
内存中的值,旧的预期值,新值。内存值与旧值比较相等则替换。
优点:是非阻塞的。
缺点:有可能产生ABA问题,就是假如做了某些操作A 变成了B,然后又改会A,这个时候cas是发现不了的。
cas是非阻塞的,效率高,synchronized是悲观锁。
六、SQL
1.什么是三范式和反三范式?(表设计)
答:1NF:每列属性唯一;2NF主键唯一;3NF属性不能与其他表属性产生冗余。反三范式:避免过多的关联查询,可以降低范式标准,适当冗余。
2.SQL优化常见的方式?
答:避免全表扫描、合理使用索引、避免返回大量数据给客户端、避免使用游标、避免频繁创建删除临时表。
3.oracle解析器解析顺序?
答:从右到左的顺序处理FROM子句中的表名,记录数少的表写在右边作为基础表。WHERE 解析自下而上,过滤大数据量的条件放后面。
4.ORACLE分页怎么做?
答:用rownum select xx from (select xx, rownum r from test1) tt where tt.r < 100;
5.行列转换怎么做?
答:1.decode函数或者case when;2.通过map_agg函数把两个列的多行的值,映射为map;3.通过UNION语句
6.oracle怎么查树?
答:用start with conect by 。 具体:start with pid=0 connect by (prior id)=pid
7.索引查询快的原因,索引使用什么排序?
答:索引就是以空间换时间,以特定的数据结构重新存储数据,减少查询次数。mysql就是用b+树
七、linux
1.说说你常用的命令?
答:ps 、grep、find 、more 、less、 top、du、df、touch、chmod
2.怎么查找文件中的字符串?
答:如果只要找到字符串的那一行,用grep字符 文件名的方式,如果要找到字符串并且要能看到上下文,就用less,用斜杠加字符串查找,用n/N往上下查找。
less -mN a.txt 查看文件,/wzx 查找字符串,n继续往下找,N网上找。g跳到第一行,G跳到最后一行,q 退出
grep wzx a.txt 查找文件内容包含字符串
3.怎么查看java进程?
答:可以用jps,也可以用ps -ef|grep
ps -ef |grep xxx 查看进程
4.怎么查看cpu占用情况?
答:top 查看cpu占用情况
5.怎么查看磁盘占用情况?
答:df 查看磁盘占用情况
du * -sh 查看当前文件夹下空间占用(包含文件夹统计) ll -h 也可以查看文件占用
6.怎么更改环境变量?
vi /etc/profile 所有用户 。 立即生效:source /etc/profile
vi ~/.bash_profile 更改当前用户环境变量。立即生效: source ~/.bash_profile
7.你知道怎么自定义一个自己的命令吗?
答:写好命令脚本,然后再环境变量增加自定义命令文件扫描路径,并且立即生效。
八、并发多线程
1.说说常用的线程池有哪些?
答:定长线程池(FixedThreadPool)、缓存线程池(CachedThreadPool)、单线程线程池(SingleThreadExecutor)、周期线程池
2.那你知道多线程JDK8有什么新特性?
答:增加异步编程CompletableFuture。可以对返回结果进行集中处理。
3.说说ThreadPoolExecutor有几个参数,分别是什么意思?
答:7个参数。核心线程大小、最大线程数、非核心线程超时时间、单位、阻塞队列、线程工厂、拒绝策略handle
4.线程有几种拒绝策略,分别是什么?实际应该怎么使用?
答:我知道有4种。单词记不住,面试回答--》一种是直接抛异常,一直是直接丢弃任务,还有一种是抛弃等待最久的任务,另外一种我忘了。
具体:
AbortPolicy 直接抛异常,程序中断。;CallerRunsPolicy 回退调用者
DiscardOldestPolicy 抛弃队列种等待最久的任务,尝试提交 DiscardPolicy 直接丢弃任务
使用:
具体使用要根据业务来,如果业务可以支持抛异常就用抛异常,如果任务可以丢弃就用丢弃策略,没有特定的方式。我看过一个框架重写的拒绝策略是什么都不处理,具体是哪个我忘了好像是apache。
5.说说线程池工作的过程?
答:面试回答--》1.线程池添加任务,小于核心线程数,就创建执行;2.如果大于核心数,就进入阻塞队列;3.如果阻塞队列满了,就创建非核心线程执行新添加的任务;4.如果全满了,就根据拒绝策略处理。
详细:
1.线程池创建
2.执行execute()添加任务时:
执行execute()添加任务时:
当前线程数小于核心线程数,立即创建线程并运行。
否则,进入阻塞队列等待。
如果队列满了,就会创建非核心线程并立即执行。(注意,此时阻塞队列里的任务仍然在等待)。
如果还有任务进来,并且此时运行线程大于等于最大线程数,则启动饱和拒绝策略。
3.当线程中任务执行完成时,从队列取任务。
4.如果一个线程空闲,会根据空闲时间,判断是否大于核心线程数,如果大于就销毁线程,释放内存。
6.用过哪些原子类,他们的原理是什么?
答:AtomicInteger; AtomicLong; AtomicReference; AtomicBoolean;基于CAS原语实现 ,比较并交换、加载链接/条件存储,最坏的情况下是旋转锁
7.创建多少线程数合适?
答:CPU 密集型(例如:1-1000 万累加计算)则是 CUP核心数+1,如果是IO 密集型(数据库访问,远程调用)则是:最佳线程数 = (1/CPU利用率) = 1 + (I/O耗时/CPU耗时)
8.Threadlocal原理和作用?
答:作用:每个线程提供变量副本,起到线程隔离的作用。原理:每个ThreadLocal类创建一个Map,然后用线程的ID作为Map的key,实例对象作为Map的value,这样就能达到各个线程的值隔离的效果。
9.countdowlatch 和 cyclicbarrier 的作用?
答:CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它运行一个或者多个线程一直处于等待状态。 CyclicBarrier要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。
10.使用 synchronized 修饰静态方法和非静态方法有什么区别?
答:对象锁和类锁
11.死锁产生的原因?
答:两个以上线程,争抢对方手中的资源,而自身又不释放。
12.synchronized的原理是什么?
答:用javap反汇编出来,可以看到它里面有一个monitorenter和moniterexit来加锁和释放,当出现异常的时候就会走到另一个moniterexit里。
13.单机上一个线程池正在处理服务如果忽然断电怎么办(正在处理和阻塞队列里的请求怎么处理)?
答:阻塞队列持久化,正在处理事务控制。
14.线程池使用无界阻塞队列会出现什么问题?
答:如果任务处理太慢就会不断有新任务进入阻塞队列,内存会飙升,有可能会OOM.
15.你知道ReentrantLock吗?简单说说原理?
答:ReentrantLock是Java类,与synchronized相比它可以中断正在请求锁的线程等操作。内部实现是公平锁和非公平锁,公平锁会排队等待,非公平锁的实现会在一开始就cas抢锁,抢不到再等别人释放,一释放立马cas抢锁,不会排队。默认是非公平锁。
涉及类NonfairSync FairSync Sync 方法:lock 、尝试获取锁tryAcquire、compareAndSetState
参考:面试题:你知道ReentrantLock吗?回答思路_老板楼上雅座-CSDN博客
16.实现Lock加锁解锁关键的方法是什么?
答:lock和unlock方法。Lock lock=new ReentrantLock();//可重入锁 lock.lock() 加锁 finally=> 解锁:lock.unlock();
17.Synchronized 和 Lock区别?
答:1.Synchronized 内置的Java关键字,Lock是一个Java类; 2.Synchronized 无法判断获取锁的状态,Lock可以判断
3.Synchronized 会自动释放锁,lock必须要手动加锁和手动释放锁!可能会遇到死锁;
4.Synchronized 不可以中断的,非公平的 . Lock,可重入的,可以判断锁,可以自己设置公平锁和非公平锁;
5.Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码;"
18.悲观锁synchronized 的阻塞方法,有什么缺点?
答:阻塞方法用wait()和notify(),缺点是不能指定线程唤醒。
public synchronized void test(){
this.wait(); //锁等待
//xxx
this.notify();//唤醒,不能指定线程唤醒。
}
19.悲观锁ReentrantLock的阻塞方法(Condition)
答:用Condition类,调用wait方法阻塞等待,用signal方法唤醒,与notify相比可以在任意位置唤醒也可以指定线程。
ReentrantLock lock = new ReentrantLock();
Condition con = lock.newCondition();//阻塞条件
lock.lock() ;//加锁
con.wait(); //等待
con.signal();//唤醒,可以在其他方法中环境,阻塞在这个con条件
lock.unlock();//解锁
20.乐观锁的原理?(CAS、自旋)
答:CAS+自旋。for(;;)//自旋,也就是死循环。 LockSupport.park() unpark可以解决无限自旋问题。
CAS原理(compare and swap)就是有内存位置的值、预期值和新值,根据预期值与内存中的值比较,如果相等就更新新值,不相等就返回内存中的值。
Java里面有Unsafe.getUnsafe().compareAndSwapInt()方法,如果返回true就表示是原子性操作,这个方法的原理是C实现的,它是调用cpu底层指令来实现。
21.CAS的ABA问题是什么?
答:CAS ABA问题是有两个线程X、Y,X、Y都从内存位置中取出值A,X进行操作值变成了B,然后X又将B变成了A. 这个时候线程Y进行CAS操作发现内存中仍然是A就继续操作成功。这会有什么问题呢? 比如一个单向链表实现的堆栈:A--(next)-->B,现在线程T1要把栈顶替换为B,栈顶预期值是A. 这时线程T2将A、B出栈,再push D C A,此时栈顶还是A,但是B已经出栈了,A.next=C, C.next=D, B.next=null. 这个时候T1线程CAS发现栈顶的位置仍然是A,和预期值一直,于是CAS返回true操作成功,把B放到栈顶,但是B.next=null, 于是C和D就丢失了。
22.ArrayBlockingQueue的底层结构?
答:是数组,容量固定,不能扩容,没有内容的下标位置就以null填充。顺序是先进先出,入队列是从队首加元素,记录putIndex位置,出队列从队首取。用了一把锁lock
23.LinkedBlockingQueue的底层结构?
答:链表Node(next,prev…),可以指定容量,默认Integer.MAX_VALUE。它里面有两把锁:取takeLock =new ReentrantLock(); 放putLock = new ReentrantLock();实现读写分离。入队从队尾入队,记录last节点,入队从队尾入队,记录last节点。顺序也是先进先出。
24.说说锁的升级过程?
答:不是每次都有那么多线程竞争,JDK1.6引入了偏向锁,偏向锁就是一个线程进来以后,会在对象的对象头里的MarkWord记录线程ID,
下次再有一个线程进来的时候,会比较线程ID是不是当前线程,如果是,就说是同一个线程,就直接持有对象的锁。
如果不是同一个线程就会CAS竞争资源。如果竞争失败了,就升级为轻量级锁。
轻量级锁就是多个线程开始竞争资源,但是默认他是很快就能争抢到锁,会CAS自旋等待。如果一直CAS失败就会升级为重量级锁。
锁的升级是不可逆的,重量级就是操作系统里面的MutexLock锁,它是互斥锁。"‘
25.说说线程池提交任务submit和execute有什么区别?
1、submit支持callable,execute支持runnable;2、submit有返回值,execute没有返回值;3、submit会吃异常,出现异常不打印控制台,execute会打印出异常堆栈。
九、Redis
这边看你写了解一点redis,能说说你是怎么用的吗?
答:redis就是说,读的次数远大于写的次数,这个使用可以用redis作为缓存,提高读的效率。
缓存击穿、缓存穿透、缓存雪崩是什么?怎么解决?
答:缓存击穿:缓存没数据,数据库有数据,一般发生在过期的时候,给数据库压力。解决:热点数据永不过期,或者解锁,第一个进来锁上,第二个线程进来等待100ms再重新获取。
缓存穿透:缓存和数据库都找不到,用户一直去访问,造成数据库压力。解决:可以给KEY设置一个null值,然后设置一个很短的时间比如5分钟过期时间或者接口层增加id递减判断,如果小于0就拦截,或者用布隆过滤器。
缓存雪崩:就是同一时间大批量的KEY都过期了。可以分散它的过期时间来解决。
布隆过滤器的原理是什么?
答:就是把可能存在的哈希数据放到一个大的bitmap中,然后一个一定不存在的数据进来,就会被过滤掉。
十、架构设计与分布式
讲讲分布式事务?
答:二阶段提交、三阶段提交、TCC。二阶段就是预提交和正式提交,缺点是阻塞线程没有超时机制。三阶段有询问提交、预提交、提交三个阶段。改进了二阶段的预提交阶段,引入了超时机制。
TCC:就是try、confirm/cancel每个阶段都要开发写代码实现,由协调者统一调用confirm/cancel。它的每一阶段都是完整的数据库事务,数据库实际真实改变的。
接口如何处理重复请求?(幂等性)
答:首先要有一个唯一的请求流水。数据库:加主键,更新时加锁。前端可以加token
十一、其他
Http客户端你用过哪些?
答:(1) JDK 的 HttpURLConnection (2) Apache Commons HttpClient(Apache HttpClient 3.x)(3) Apache HttpComponents Client(Apache HttpClient 4.x) (4) Square 公司开源的 OkHttp (5)Spring 的 RestTemplate (6)Netflix 公司的 Feign
你知道jdk自带的SPI机制吗?
答:SPI机制是一种可插拔的接口实现。jdk会从META-INF/services/目录下取接口的实现类配置文件,然后用ServiceLoader加载。 在一些框架中常常应用于过滤器实现,开发者可以自己添加实现类,框架就会多执行一步你写的过滤器实现。
1.在META-INF/services/目录下用你的接口全路径名称命名一个文件(不加后缀),然后在该文件中一行一个添加你的接口实现类的全路径名。 文件名:com.my.MyInterface
2.通过load方法来加载出所有的接口实现类 ServiceLoader<MyInterface> loader = ServiceLoader.load(MyInterface.class);
平时有做过间隔执行吧,java获取时间间隔有几种方法?
答:两种。new Date().getTime() 和System.currentTimeMillis()
修改第三方jar包里的类怎么做?
答:在自己的工程里建立相同包路径、相同的类名,然后覆盖里面的方法即可。
友情链接:
程序员搞什么副业好?程序员搞什么副业好?_老板楼上雅座-CSDN博客