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<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博客

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值