本篇对面试的作用微乎其微
面试总结:问了我半个小时,一半多的重要的东西都答不上来,特别重要的是多线程,还有数据库。算是一场经历吧。
本篇对面试的作用微乎其微, 这里面有很大部分不会被问到.
面试需要的准备:
- 视频看两遍,第二遍总结
- 复习之前做过的Java项目
- 复习之前看过的spring视频和代码。
一、Java基础
1.JDK、JRE、JVM三者区别和联系
jdk是java开发工具,jre是java运行时环境,jvm是java虚拟机。
jdk包含jre,jre中的bin就是jvm,lib就是类库。
javac编译成class文件,class文件是字节码文件,由jvm解释为对应系统的机器码。
2.==和equals
==对比栈的值,基本类型是变量值,引用类型是堆中内存对象的地址。equals默认是采用==比较。String中已经重写了equals方法,不然由于String是不可变的原因会不一样。
3.final
final修饰变量,变量初始化后不可变;修饰类,类不可被继承;修饰方法,方法不能被子类覆盖,但是可以被重载。修饰引用类型,初始化后不能让其指向另一个对象,但引用值可以改变。
局部内部类和匿名内部类只能访问局部final变量。这是因为外部类方法结束变量就会销毁,但是内部类对象可能还存在,所以复制一份局部变量供内部类使用,为了保证这两个变量一致,所以用final修饰。
4.String、StringBuffer、StringBuilder
String 是final修饰的,不可变,每次操作会产生新对象。
StringBuffer和StringBuilder都是在原对象上操作。
StringBuffer是线程安全的,StringBuilder是线程不安全的。但是StringBuilder性能要高些。
经常需要改变字符串就使用后面两个。
优先使用StringBuilder,多线程使用共享变量使用StringBuffer
5.重载和重写
重载:在同一个类中,方法名相同,参数类型不同,个数不同,顺序不同,方法返回值,修饰符可以不同。发生在编译。
重写:发生在父子类,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符大于等于父类;如果父类方法修饰符为private则子类不能重写该方法。
6.接口和抽象类
抽象类可以存在普通成员函数,而接口只能存在public abstract方法。
抽象类的成员变量可以是各种类型的,而接口中的成员变量只能说public static final类型的。
抽象类只能继承一个,接口可以实现多个。
接口是对类的行为进行约束,约束了行为的有无,不对如何实现进行限制。对行为的抽象。is a的关系
抽象类是为了代码复用,不同的类有相同的行为,都可以派生一‘个抽象类。是对类本质的抽象。like a的关系
7.List和Set
List:有序,可以重复,按对象进入的顺序保存,允许多个null元素对象,使用迭代器遍历,还可以get(int index)
Set:无序,不可重复,最多允许有一个null对象,取元素只能用迭代器。
8.hashCode与equals
hashCode获取散列码,是int型整数,用于确定对象在hash表的索引位置。HashSet检查重复元素,就是比较hashCode看对应位置是否有值,没有就假设对象没有重复出现;有值才会调用equals方法检查两个对象是否真的相同(相同的对象hash值可能不同),相同就不会让其加入成功,不同重新散列到其他为止,这大大减少了equals次数。
- 两个对象相等,hashcode相同,equals返回true
- 两个对象hashcode相同,他们不一定相等
9.ArrayList和LinkedList
ArrayList:基于动态数组,连续内存存储,适合下标访问,扩容就是新建一个然后把原来的复制,插入数据会涉及到元素的移动。使用尾插法可以极大提高性能,比linkedlist快(创建node节点)。
LinkedList:基于链表,存储在分散的内存,适合插入和删除不适合查询。遍历使用迭代器,get(i)需要对list遍历,indexof也是会遍历整个list。
10.HashMap和HashTable
1.HashMap线程不安全,HashTable线程安全
2.HashMap允许key value为空,HashTable不允许
HashMap是数组+链表实现的。jdk8开始,数组长度超过64,链表高度超过8,链表转化为红黑树,元素以内部类Node节点存在。
- 计算key的hash值,再二次hash得到一个数,这个数对数组长度取模,于是得到一个数组下标。
- 如果没有产生hash冲突(对应下标位置没有元素),则直接创建Node存入数组
- 产生hash冲突,先equals比较,相同就取代,不同判断链表高度插入链表,链表高度达到8,并且数组长度到64就转红黑树,长度低于6转回链表。
- key为null存在下标为0的位置
11.ConcurrentHashMap原理
线程安全版本的HashMap。
jdk7:ReentrantLock+Segment+HashEntry。一个segment是一个HashEntry数组,每一个HashEntry是链表。
查询:第一次hash定位到segment,第二次hash定位到元素所在链表的头部
锁:Segment分段锁,Segment继承了ReentrantLock,锁定操作的segment,其余的Segment不受影响。并发度为segment个数,数组扩容不会影响其他segment。
jdk8:
12.如何实现IOC容器
- 配置文件,配置包扫描路径
- 定义注解,表示控制层,服务层,数据持久层,依赖注入层,配置文件注解
- 递归包路径获取class文件,将这些文件添加到set集合存储
- 遍历set,获取在类上有指定注解的类,定义一个安全map存储这些对象,反射,确定需要交给IOC管理的类
- 对需要注入的类进行依赖注入
13.字节码
在机器和编译程序中加入了一层抽象的虚拟的机器。这台虚拟的机器在任何平台上都提供给编译程序一个共同的接口。于是编译程序只需要面向虚拟机,生成虚拟机理解的代码,再由虚拟机的解释器将字节码转换为特定系统的机器码执行。
java源代码->编译器->jvm可执行的字节码->jvm->jvm的解释器->机器可执行的二进制机器码->程序运行
好处:一定程度上解决了传统解释型语言执行效率低的问题;保留了解释型语言可移植的特点。java文件无需重新编译便可在多种平台运行。
14.Java类加载器
JDK自带三个类加载器:bootstrap classloader,ExtClassLoader,AppClassLoader
bootstrap classloader是ExtClassLoader父类加载器(不是直接继承),默认加载java_home/lib下的jar包和class文件。
ExtClassLoader是AppClassLoader的父类加载器,负责加载java_home/lib/ext下的jar包和class文件
AppClassLoader是自定义类加载器的父类,加载classpath的类文件。
继承ClassLoader实现自定义加载器。
15.双亲委派(委托)模型
AppClassLoader中如果没有缓存,就往上委派,都是查找缓存,缓存没有就继续委派。到了最顶层缓存还是没有,查找加载路径,没有向下查找,继续查找加载路径。到发起加载的加载器为止。
好处:为了安全性,避免用户自己编写的类动态替换Java的一些核心类。同时避免了类重复加载,相同的class文件被不同的加载器加载就是不同的两个类。
16.Java异常体系
所有异常来自顶级父类Throwable,Throwable有两个子类:exception和error。
error是程序无法处理的错误,一旦出现,程序被迫终止
exception不会导致程序停止,又分为runtimeEception运行时异常(发生在程序运行过程中,导致程序当前线程执行失败)和checkedException检查异常(常常发生在程序编译过程中,导致程序编译不通过)。
二、Spring
1.单例Bean是单例模式吗?
但单例模式是指在一个JVM中,一个类只能构造出来一个对象
单例Bean也是以一种单例模式,但是范围很小,只是一个beanName的范围,一个beanName对应一个Bean对象,不同的beanName对应不同的Bean对象(可以同一个类)。
2.Bean的实例化和初始化有什么区别?
Spring创建Bean时,先创建一个对象,然后通过反射执行类构造方法得到一个Java对象,这个过程就是Bean的实例化。
得到对象后进行依赖注入,之后就可以初始化了。Bean的初始化就是调用前面创建出来的Java对象中特定的方法,比如Java对象实现了InitializingBean接口,那么初始化就会执行Java对象的afterPropertiesSet()方法。
3.Spring AOP如何实现?和AspectJ有什么区别?
AOP是利用的动态代理机制,如果Bean实现了接口,那么就会采用JDK动态代理来生成该接口的代理对象,如果一个Bean没有实现接口,就会采用CGLIB生成当前对象的代理对象。代理对象会代理原来的Bean对象,执行某个方法时会在原来的基础上增加一些切面逻辑,使得我们可以利用AOP来实现一些登录校验,权限控制等统一功能。
AOP和AspectJ没有特别强的关系,AOP是面向切面编程,一种编程思想。AspectJ可以实现这种思想,它在编译器对类进行增强。而Spring AOP通过动态代理实现AOP,但底层实现和AspectJ不一样,只是注解的名字是相同的(before around after)
4.Spring事务如何实现?
- 底层是基于数据库事务和AOP机制。
- 使用了@Transactional注解的Bean,会创建一个代理对象。调用代理对象的方法时,会先判断该方法是否加了@Transactional注解
- 加了的话,会利用事务管理器创建一个数据库连接,修改数据库连接的AutoCommit属性为false,禁止自动提交。
- 然后执行方法,执行方法中的sql
- 执行完后没有异常就提交
- 出现了异常,根据异常的类型是不是rollbackfor指定的,再选择是否回滚
Spring事务的隔离级别对应的就是数据库的隔离级别
5.你是如何理解Spring事务的传播机制的?底层是如何实现的?
Spring事务的传播机制是Spring事务自己实现的(propagation)。这个传播机制是基于数据库连接的,新开一个事务就是新建一个数据库连接。
6.那些情况会导致Spring事务失效?对应原因?
-
方法内自调用:因为事务是基于AOP的,使用代理对象调用方法时事务才能生效。而在一个方法中调用this.xx()这个this不是代理对象,事务就会失效。
解决办法:a 把调用的方法拆到另一个bean中
b 自己注入自己
c
-
方法是private的:Spring事务会基于CGLIB进行AOP,而CGLIB会基于父子类生效。如果父类中的某个方法是private,子类就没有办法重写,也就没办法而外为其增加逻辑。
-
方法是final的,和上者原理一样,子类不能重写父类的final方法
-
单独的线程调用方法:如果不是同一个线程,就从ThreadLocal中拿不到数据库连接对象,这样自己只能新建一个,这个新建的的AutoCommit是true。
-
没加@Configuration注解:springboot基本没有这个问题。如果是spring的话,没有加注解会导致map中存的DataSource和Mybatis和JdbcTemplate中的DataSource不相等。又会自己去创建连接。
-
异常被吃掉:事务没有捕获到异常就不会回滚了
-
类没有被Spring管理
-
数据库不支持事务
7.Spring中Bean创建的生命周期
- 推断构造方法
- 实例化
- 依赖注入(填充属性)
- 处理Aware回调
- 初始化前,处理@PostConstruct
- 初始化,处理initializingBean接口
- 初始化后,进行AOP
8.Spring中Bean是线程安全的吗
这取决于Bean本身,Bean本身是无状态,就是线程安全;本身有状态,就是线程不安全。
9.ApplicationContext和BeanFactory有什么区别
BeanFactory可以生成Bean,维护Bean。ApplicationContext继承自BeanFactory,有BeanFactory的特点,由于继承了其他接口,还有其他功能,如获取系统环境变量,国际化,事件发布等功能。
10.Spring容器的启动流程是怎样的
- 扫描,得到所有的BeanDefinition对象,存在Map中。
- 筛选单例BeanDefinition创建Bean,对于多例Bean会在每次获取Bean时利用BeanDefinition去创建。
- 利用BeanDefinition创建Bean就是Bean的创建生命周期,包括推断构造,实例化,依赖注入,初始化前,初始化,初始化后(AOP)
- 单例Bean创建完了,Spring发布一个容器启动事件。
- Spring启动结束
11.Spring为什么要用三级缓存来解决循环依赖
三、SpringBoot
1.@SpringBootApplication注解有什么用?
它是一个复合注解,是@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan三个注解的整合。
@SpringBootConfiguration:相当于@Configuration,表示这是一个配置类
@EnableAutoConfiguration:负责自动配置类的导入,将项目的自动配置类导入到Spring容器
@ComponentScan:Spring容器会进行扫描,扫描路径为当前这个类所在的包路径
2.spring.factories有什么作用?
spring.factories是SpringBoot SPI(扩展)机制实现的核心。
在SpringBoot启动过程中,会找出项目中所有的spring.factories,从而向Spring容器添加各个spring.factories中指定的ApplicationListener,ApplicationContextInitializer,配置类。使得对SpringBoot做扩展非常容易。
3.如何理解SpringBoot自动配置?
在Spring中,通常需要配置很多的Bean,比如Mybatis的SqlSessionFactory;AOP的@EnableAspectJAutoProxy注解等
在SpringBoot中,内置了很多配置类,叫做自动配置类。在依赖了spring-boot-starer-web后会间接依赖到spring-boot-autoconfigure这个jar,里面就有很多自动配置类。
4.SpringBoot启动时做了哪些事?
- 判断当前应用的类型,如是不是web应用,是servlet还是webflux应用,不同类型后续会创建不同的Spring容器。
- 根据类型创建Spring容器
- 解析启动类,进行扫描,导入自动配置类并解析。
- 启动tomcat或者jetty
- 调用ApplicationRunner或CommandLineRunner
四、SpringMVC
1.SpringMVC的处理流程是什么?
- 启动Tomcat中,创建DispatcherServlet对象,执行初始化逻辑
- DispatcherServlet初始化创建Spring容器,初始化中还要初始化HandlerMapping,HandlerAdapter等。
- SpringMVC提供了好几个HandlerMapping,其中有一个为RequestMappingHandlerMapping,它去寻找Spring容器中哪些加了@RequestMapping的方法。
- 找到这些方法后,解析注解上指定的path,把path作为key,method作为value存到一个map中。
- DispatcherServlet接收到请求后,RequestMappingHandlerMapping就会根据请求路径找到对应method。
- 解析各个参数,从请求中拿到对应值,执行method。
- 执行完得到方法返回值,SpringMVC进一步解析,如果加了@ResponseBody就直接返回值给浏览器,没有的话根据返回值找页面。