跳转到主内容
思享编程网:思考分享,玩转编程世界!

2024Java面试题附答案(持续更新2024.3.23)

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[] tab;/*数组*/ Node 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 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)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开始初始化对象AsingletonFactories:earlySingletonObjects:singletonObjects:2调用A的构造,把A放入singletonFactoriessingletonFactories:AearlySingletonObjects:singletonObjects:3开始注入A的依赖,发现A依赖对象BsingletonFactories:AearlySingletonObjects:singletonObjects:4开始初始化对象BsingletonFactories:A,BearlySingletonObjects:singletonObjects:5调用B的构造,把B放入singletonFactoriessingletonFactories:A,BearlySingletonObjects:singletonObjects:6开始注入B的依赖,发现B依赖对象AsingletonFactories:A,BearlySingletonObjects:singletonObjects:7开始初始化对象A,发现A在singletonFactories里有,则直接获取A,把A放入earlySingletonObjects,把A从singletonFactories删除singletonFactories:BearlySingletonObjects:AsingletonObjects:8对象B的依赖注入完成singletonFactories:BearlySingletonObjects:AsingletonObjects:9对象B创建完成,把B放入singletonObjects,把B从earlySingletonObjects和singletonFactories中删除singletonFactories:earlySingletonObjects:AsingletonObjects:B10对象B注入给A,继续注入A的其他依赖,直到A注入完成singletonFactories:earlySingletonObjects:AsingletonObjects:B11对象A创建完成,把A放入singletonObjects,把A从earlySingletonObjects和singletonFactories中删除singletonFactories:earlySingletonObjects:singletonObjects:A,B12循环依赖处理结束,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)

值得注意的是,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的||双竖线连接字符串%,中间接预编译字符串。

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    trim|where|set|foreach|if|choose|when|otherwise|bind 11.mybatis list怎么写?

单个:

and deptno=#{deptno,jdbcType=DECIMAL}

多个:

and deptno in

#{item,jdbcType=DECIMAL}

12.大于、等于、不等于的写法

AND state <>'-1' ]]> 13.更新,没值不更新,防止更新为空

update dept

dname= #{dname},

where deptno= #{deptno,jdbcType=VARCHAR}

五、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 loader = ServiceLoader.load(MyInterface.class); 平时有做过间隔执行吧,java获取时间间隔有几种方法?

答:两种。

new Date().getTime() 和System.currentTimeMillis() 修改第三方jar包里的类怎么做?

答:在自己的工程里建立相同包路径、相同的类名,然后覆盖里面的方法即可。

友情链接: 程序员搞什么副业好?

程序员搞什么副业好?

_老板楼上雅座-CSDN博客

相关文章