基础篇
Java有什么特性?
简单性: 语法简单,基于C语言
面向对象: 以类的方式组织代码,以对象的形式组织封装数据,就是把一类事物抽象为对象;
可移植性(跨平台): 可以编译为字节码文件进行跨平台移植达到一次编写,随处运行的效果
高性能: 可以将"热点"字节码编译为本地机器码,并进行缓存,这样重新调用的时候就可以极大的提升效率
分布式: Java是为Internet的分布式环境设计的,因为他能处理tcp/ip协议.Java还支持远程调用方法
动态性: 就是可以将某些代码添加到正在运行的程序中,反射机制,当需要把代码加入运行的程序中,动态性是一个非常重要的特征.Java的动态特性是其面向对象设计方法的扩展
多线程: 多线程可以带来更好的交互响应和实时的行为
安全性: Java适合网络/分布式环境,可以用来构建防病毒,防篡改系统
健壮性: Java吸收了C/C++的有点,去掉了影响程序件状态的部分,并且还有异常处理机制
Java的三大特性?
封装 继承 多态
封装?
把一类事物抽象到一起封装为对象,这就是封装
那就聊聊继承吧?
继承是一种联合的层次结构,目的是为了代码的重用,继承了他的类叫(子类),被基础的称之为(父类),子类可以使用父类的属性和方法,若可以变更方法也称之为方法的重写,但重写要遵循几个原则:
注意: 父类的私有方法不能被重写
-
方法名和参数列表要完全一致
-
子类的返回值类型要小于等于父类的返回值类型(指的是继承关系不是大小)
-
子类抛出的异常需要小于等于父类抛出的异常大小
-
子类的方法权限修饰符需要大于等于父类被重写的方法的权限修饰符
多态你是怎么理解的?
多态(向上造型)可以理解为父类类型指向本类对象的引用,前提是需要有1.继承关系的存在 2. 子类重写父类的方法 3. 父类引用变量指向子类对象
如果父类要调用子类的特有方法的时候就需要强转了,也称之为向下造型
Jdk Jre Jvm三者的关系是什么?
JVM就是虚拟机是虚构出来的系统,JRE是Java最小的运行环境,它包含了JVM和系统类库,JDK是Java最小的开发环境,它包含了JRE和编译运行工具
JVM的内存结构了解吗?
-
类装载子系统: 用于程序运行前,将所有的字节码数据加载到方法区(元空间)中,之后程序运行
-
程序执行引擎: 用于执行代码(按行执行),能够获取目前程序执行到的位置,且会将线程执行的行号记录到程序计数器中
-
元空间(方法区): 从JDK1.8开始的新叫法,之前是叫做方法区的,和方法区的区别是:元空间的数据是直接保存在磁盘的,物理存在,保存的数据是所有的类(.class文件)信息
-
程序计数器: 会为每个线程分配一块空间,用于记录该线程执行的代码行号,行号由程序执行引擎记录进来的
-
本地方法栈: 服务于本地方法的,若调用的方法为native()方法,则该方法中产生的局部变量均会保存到本地方法栈中,除此之外,其用法和虚拟机栈的用法是相同的
-
虚拟机栈: 每创建一个线程在虚拟机栈中会为该线程开辟一块空间,该线程中的局部变量会保存在该区域中;在线程的区域中,以栈帧的方式来保存对应方法中的局部变量,即每调用一个方法会在栈中为该方法开辟一块栈帧区域,该方法中产生的局部变量,会保存在这个栈帧区域中
-
堆: 采取分代思想(堆的作用: 存放对象实例和数组),所有新生成的对象首先是放在新生代中的
Java中常见的循环方式有哪些,你了解他们的区别吗?
for: 用于我们知道循环次数的情况下
while/do while : 当循环次数不确定的时候 do while至少会执行一次循环,while可能一次都不会执行
在分支结构中switch...case中判断条件可以是字符串类型吗?
在jdk1.7之前只能是整形,在jdk1.7开始就可以用字符串类型
在进行浮点数计算的时候可以使用double吗?
如果对精度要求不高的时候可以使用,但若对精度要求高,像涉及银行资金的时候double可能会丢失精度,所以我们可以使用BigDecimal来达到精确的计算
数组的特点?
-
数组的长度创建后是固定的不可更改
-
元素必须是相同类型
数组是相同数据类型的有序集合,数组也是对象.数组的元素相当于对象的成员变量
什么是方法的重载呢?
重载发生在同一个类中,存在方法名相同参数列表不同的方法
在方法中return的作用是啥呢?
若方法无返回值,则用于结束方法;若方法有返回值,用于返回值给调用方并结束方法
请说一下java中的final关键字?
final关键字可以用于修饰方法,变量,类
被final修饰的方法不可以被重写
被final修饰的类不可以被继承
被final修饰的变量不可以被重写赋值改变
被final修饰的参数,不能在方法内重新赋值
请问final/finally/finalize有什么区别?
final用于修饰类,变量,方法,修饰类表示该类不可被继承,修饰方法表示该方法不可被重写,修饰变量表示不可被重新赋值
finally一般用在try-catch捕获异常代码块中,不论代码是否发生异常,finally中的代码一定会执行,通常用来关闭资源使用
finalize属于Object类中的一个方法,当对象被回收的时候会调用此方法
请说一下java中的static关键字?
static修饰的变量,称之为静态变量,存在方法区(元空间)中,可以通过类名点变量来访问,当所有对象的数据一样使用
static修饰的方法,称为静态方法,存在方法区(元空间)中,可以通过类名点方法名来调用
谈谈你了解的异常有哪些呢?
Throwable是异常和错误的顶级父类,Error是错误,程序无法处理的常见的有VirtulMachineErrorJava虚拟机运行异常,Exception我们可以编码修复的错误,常见的RunTimeException,IOException,FileNotFoundException等等
你了解throws和throw的区别吗?
throws是放在方法上的用于向上抛出异常,需要由方法调用者来处理这些异常,注意这种异常只是可能
throw是用于方法内部后跟异常对象名字,表示次数抛出异常,由方法内处理,注意执行了throw一定抛出某种异常
浅谈一下抽象类和接口的区别?
总的来说,抽象类可以说是提取共性,而接口是暴露一套开发规则
抽象类是用abstract修饰的,同样也可以用于修饰方法,抽象类中可以存在抽象方法和普通方法,接口中只能存在抽象方法
接口是用interface修饰的只能包含常量,接口之间可以继承和多实现,而抽象类只能被继承,因为Java是单继承的,所有在继承的时候应该慎重
什么是内部类?内部类有哪些呢?
在Java中,可以将一个类的定义放在另一个类的定义内部,称之为内部类,可以分为四种: 成员内部类 局部内部类
匿名内部类 静态内部类
你能说说Java中的this和super关键字吗?
this表示本类对象的引用,super是发生在继承关系中的,代表父类对象的引用
不写构造方法可以吗?如果写了含参构造还会有默认构造方法吗?
不写构造方法可以,编译器会自动生成无参构造,如果写了含参构造就必须手动写上无参构造
break和continue有什么区别呢?
break用于结束循环,continue用于跳出本次循环
什么是嵌套循环?如何跳出嵌套循环呢?你觉得嵌套多了有啥问题呢?
嵌套循环指的是循环中还存在循环,外层循环控制行,内存循环控制列,外层循环执行一次,内层循环执行完所有轮次,当我想要跳出循环的时候可以使用outer: 标签来定义循环,使用break outer 来跳出整个循环,嵌套循环建议控制在3层以内,嵌套太多可读性不好,并且可能存在设计问题
谈谈序列化和反序列化?
序列化:利用ObjectOutputStream,把对象信息按照固定格式转成一串字节值输出并持久化
反序列化: 利用ObjectInputStream,读取磁盘中之前序列化好的数据重新恢复成对象
应用场景:
1. 需要序列化的文件必须实现Serializable接口,用来启用序列化功能 1. 不需要序列化的数据可以修饰为static,是因为static资源属于类资源,不随着对象被序列化输出 1. 每个被序列化的文件都有一个唯一id,如果没有添加此id,编译器会自动根据类的定义信息计算产生一个 1. 在反序列化时,若和序列化版本号不一致,则午饭完成反序列化 1. 常用与服务器之间数据传输,反序列化读取数据 1. 不需要序列化的数据也可以修饰为transient(临时的)只在程序运行期间在内存中存在,不会被序列持久化
集合和数组的区别?
数组的长度是固定的,集合的长度是可变的
数组声明了它容纳的元素的类型,而集合不声明
数组不论是效率还是类型检查都是最好的.只能放一种类型,集合存放的类型可以不只一种(不加泛型就是Object)
常见的集合类型有哪些?
常用的list集合(可重复集,有序)有ArrayList.LinkedList.Vector. ArrayList底层是数组,它查询速度快,增删慢;LinkedList底层是链表,它查询速度慢,增删快.ArrayList和LinkedList都是非线程安全的,Vector,底层是数组,它是线程安全的
通过字面量创建字符串和new一个字符串对象有什么区别?那什么是常量池呢?
首先内存分配的方式不一样,通过字面量创建的字符串会分配进常量池中,通过new一个字符串对象会分配到堆内存中. 常量池我的理解是它存在于方法区(元空间)内,使用字面量创建的字符串会直接缓存到常量池中,若在此使用该字面量创建新的字符串就不用创新对象了而是直接从常量池中获取
做字符串拼接的时候可以用String吗 StringBuilder和StringBuffer有什么区别呢?
不建议使用String做拼接,最好使用StringBuilder或StringBuffer做拼接,StringBuilder是非线程安全的,但是效率更高;StringBuffer是线程安全的,效率低一些,但是安全
子类重写父类含有抛出异常的方法时,有哪些要求呢?
-
不再抛出任何异常
-
仅抛出部分异常
-
抛出子类异常
-
不可以抛出额外异常
-
不可以抛出父类异常
HashMap的初始化长度是多少?在什么时候进行扩容?
HashMap的初始长度是16,当容量达到加载因子0.75的时候进行扩容,扩容的大小是2的N次方倍
HashMap的底层数据结构是什么?那什么时候变为红黑树?
在jdk1.7的时候是数组+链表,在jdk1.8开始变为数组+链表+红黑树,当链表的长度大于8的时候,就会变为红黑树
HashMap/HashTable/ConcurrentHashMap有什么区别
HashMap是非线程安全的(初始容量16,扩容是2的N次方),HashTable是线程安全的(初始容量是11,扩容是2*n+1)其内部使用synchronized关键字加锁,ConcurrentHashMap和HashTable,是线程安全的,在jdk1.7其内部使用了分段锁的思想来进行加锁,每一把锁只锁住部分数据,降低了锁的力度,提高了效率,在JDK1.8的时候使用了CAS算法+synchronized来保证并发的安全
注:什么是CAS算法? CAS算法是能够保证当前操作线程安全和原子性的算法.CAS算法将预期值和更新值传入进行比较,若内存值和预期值不同,那么此次操作失败,则会持续进行循环比较,直到匹配才会传入更新值,这也称之为线程的自旋,其中算法称之为CAS算法
是否能够使用任何类作为HashMap的Key?
可以的,但是要注意,如果类重写了equals方法,我们就应当连同重写hashcode方法,因为hashcode的值应当与equals的结果相对应,两个对象若equals比较为true,hashcode的值应当相同.两个对象equals比较结果为false,hashcode值最好不同,若依然相同,那么作为key存入HashMap中时会产生链表情况,影响HashMap查询性能
你能说说sleep/wait/notify的作用吗?
sleep用于线程的休眠,可以设置休眠的时长单位是毫秒,如果在一个同步块中,sleep不会释放锁
wait是Object提供的一个方法,可以对对象来进行线程的休眠,如果在一个同步块中,wait会释放锁
notify也是Object提供的一个方法,可以唤醒处于wait状态的线程,如果在一个同步块中,wait不会释放锁
你有用过线程池吗?Java中的线程池有哪些?你最常用的是哪种?
-
有用过的.Java中的线程池有四种
-
动态线程池: newCachedThreadPool
-
固定线程数的线程池: newFixedThreadPool
-
固定只有一条线程的线程池: newSingleThreadExecutorPool
-
固定频率执行的线程池: newScheduledThreadPool
一般来说这四种线程池都不用,一般是使用ThreadPoolExecutor来自定义线程池
既然用到了自定义线程池,那您能介绍下ThreadPoolExcutor各参数的含义吗?你觉得核心线程数设置多大比较合适呢?
-
核心线程数: 指的是线程池不关闭就一直存活的线程数
-
最大线程数: 指线程池能同时存活的最大线程数
-
空闲线程存活时间: 指非核心线程数,空闲线程的存活时间,过期就会销毁
-
时间单位: 存活时间的单位
-
线程工厂: 进行线程开销的工厂
-
阻塞队列: 存储等待执行的任务
-
拒绝策略: 指线程池的线程数达到了上线(最大线程数且任务队列也满了)的情况下的逻辑
最大的核心线程数建议设置为:CPU的核数*2+1 能发挥最好的性能
Spring篇
什么是Spring?
Spring是一款开源的企业级应用框架,是用于管理bean的生命周期的轻量级容器,它可以简化开发,解耦对象的依赖关系,集成其他的框架
Spring的核心功能有哪些?
SpringIOC SpringAOP SpringMVC
什么是SpringIOC? 那有哪些依赖注入方式呢?
SpringIOC就是控制反转,将bean的依赖关系交给Spring容器管理,容器通过DI依赖注入的方式来使用创建的Bean对象. 有构造器注入,set方法注入,注解注入
注:IOC是一种设计思想,而DI是它的实现形式
注解注入有哪些方式呢?它们有什么区别?
注解注入有两种方式@Autowired注解和@Resource注解
@Autowired注解是spring框架提供的注解,可以通过构造器注入也可以通过set注入,是基于类型注入的(byType)
@Resource注解是JDK提供是一个注解,只是spring能够识别它,它只能通过set注入,它是基于bean名称注入的(byName).
BeanFactory和FactoryBean有什么区别?
BeanFactory是Spring当中的一个接口,Spring当中的容器都实现了这个接口,本质上我们可以认为他就是Spring容器接口.FactoryBean是工厂bean是,工厂bean跟普通的Bean不同,它返回的对象不是指定类的实例,而是该工厂Bean的getObject()方法所返回的对象
Spring当中的BeanPostProcessor是什么?
Spring为我们提供了一个BeanPostProcessor接口,这个接口可以在bean初始化的时候让我们干预做一些操作.我们需要自己建一个类实现这个接口,接口中会有两个方法,可以在bean初始化之前或者之后做一些操作,加入具体的逻辑就行
注:postProcessBeforeInitialization 初始化前
postProcessAfterInitialization 初始化后
什么是Spring当中的Aware接口?
在开发中,我们经常要用到Spring容器它本身的功能和资源,Spring提供了一系列的Aware接口来实现具体的功能,只要我们的Bean实现了具体的Aware接口,并实现其方法,Spring容器就会把资源传给我们了
常用的Aware接口:
-
BeanNameAware 获取容器中 Bean 的名称
-
BeanFactoryAware 获取当前BeanFactory,这样就可以调用容器服务
-
ApplicationContextAware 获取当前Spring容器
Spring是如何解决循环依赖问题的?
先了解下什么是循环依赖,循环依赖顾名思义就是出现了互相依赖的情况,例如在A类中注入B类对象调用B类方法,但是B类中注入了A类对象和方法,就会产生循环依赖问题
Spring的内部有三级缓存:
也就是三个map,
singltonObject 一级缓存,用于保存实例化,注入,初始化完成的bean实例的
ealySingletonObjects 二级缓存,还没完成属性注入,是一个半成品,由三级缓存放进来的对象
singletonFactories 三级缓存,用于bean创建工厂,以便于后面扩展有机会创建代理对象
Spring中的bean是线程安全的吗?那要怎么解决呢?
当多线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题,我们可以通过@Scope("prototype")注解来指定创建模式为多例让线程安全,也可以通过ThreadLocal来解决这个问题
SpringMVC当中有几大组件?分别是哪些呢?它们是如何交互工作的?
MVC有5大组件:
-
DispatcherServlet (前端控制器)
-
HandlerMapping (请求派发控制器)
-
Controller (请求处理控制器)
-
ModeAndView (数据和视图名)
-
ViewResolver (视图解析器)
工作交互:
1.客户端发送请求到前端控制器(DispatcherServlet)
2.前端控制器(DispatcherServlet)根据请求调用请求派发器(HandlerMapping),解析后转发到请求处理控制器(Controller)
3.请求处理控制器(Controller)处理完之后会返回一个ModelAndView对象
4.ViewResolver会根据逻辑view进行视图解析
5.DispatcherServlet返回给客户端进行视图渲染
转发和重定向有什么区别?
转发就是一次请求浏览器的地址不变,可以通用共享数据,只能发送到系统内部
重定向是两次请求,浏览器的地址会变,不能通用共享数据,可以重定向内部或者外部
说说什么是SpringBoot呢?
简单来说它就是框架的框架,它内部整合了很多常用的框架,简化了Spring应用配置
SpringBoot具体有如下特性:
创建独立的Spring应用程序,通过main方法运行
内嵌Tomcat.可选择部署jar文件,也可以部署war文件
简化maven配置,SpringBoot的父工程帮我们管理了大量的maven依赖
自动配置Spring,根据核心application.properties配置文件+代码注解使用就能自动配置Sprin,无需要再繁琐的去配置xml
什么是SpringAOP?那什么是切面编程呢?
AOP是面向切面的编程,可以在不修改源码的情况对原有的方法进行扩展@Around环绕通知
切面编程是将原有业务中共通的处理逻辑抽离出来,进行独立封装,然后通过配置作用到原有传统的业务处理方法上.例如处理事务,日志处理,异常处理,权限处理等
切面的三大要素是什么?
切面: 共通功能
切入点: 对哪些方法进行切入
通知: 在方法的什么位置切入
扩展 AOP有三种配置切面的方式
通过xml+表达式进行配置
通过Aspect注解+表达式进行配置
通过Aspect注解+自定义注解配置
SpringAOP的底层原理是什么呢?Spring当中的动态代理有哪些实现方式呢?你能分别说说他们的机制吗?
SpringAOP的底层原理是动态代理
动态代理有JDK动态代理和CGLIB动态代理两种实现方式
JDK动态代理是基于实现InvocationHandler接口来实现的,当我们的代理类调用方法时,会被它的invoke方法拦截,进行方法的增强
CGLIB动态代理底层是采用的字节码技术来生成代理类,也是我们调用代理类方法时,被拦截,进行方法的增强,是采用继承的方式来实现的
什么是事务呢?事务的特征有哪些呢?
事务通常作用于数据领域,是对数据库进行读写的一组操作,事务的操作结果只有两种,要么都成功要么提交不成功回滚到之前的状态
事务的特征包含:原子性/一致性/隔离性/持久性
Spring当中的事务隔离级别有几种?分别是什么呢?
五种!
Spring当中的要比Mysql的隔离级别多出一种默认隔离级别,这是根据选择链接的数据库来决定的,例如Mysql默认的是可重复读,Oracle默认的是读已提交
数据库是以下四种:
读未提交: 一个事务能够读取到另一个事务没有提交的数据,这种隔离级别可以会产生脏读的问题,如果另一个事务回滚了,那么实际上数据库根本就没有这条数据,所以这种隔离级别几乎不用
读已提交: 一个事务提交的数据在另一个事务中才能读取到,没有提交就读取不到,可以预防脏读,但是会产生新的不可重复读的问题,就是在同一个事务中每次读取到的数据都不一样,因为有别的事务在提交事务
注: 读已提交针对的是修改操作
可重复读:一个事务提交的数据在另一个事务中依然读取不到,也就是一个事务在开始的时候只能读取到此刻的数据,后面的变化是感觉不到的,虽然解决了不可重复读,但是会带来幻读的问题就像第一次读的时候是100,但是另外一个事务已经把他变为了50了,但是第二次读的时候还是100,感觉不到;
注:因为MVCC机制,select操作不会更新版本号读取的是快照读(历史版本),但是增删改操作会更新版本号,是当前读(当前版本),这就是幻读,解决方案第一就是串行化,第二是在可重复读的机制下,使用间隙锁来解决
串行化:Serializable是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读,不可重复读,幻读问题,但是这种事务隔离级别下效率太低了,比较消耗数据库的性能,一般都是不用
Spring事务中的事务传播控制有几种?
7种
什么是SpringBoot的自动装配?
SpringBoot会读取classpath下的META-INF/spring.factories文件,其文件中我可以添加配置好的类,SpringBoot会把这些类自动配置成bean加入的Spirng容器当中
什么是Mybatis的一级缓存和二级缓存?
一级缓存是默认开启的,是SqlSession级别的缓存,在操作数据库时需要构造sqlSession对象,不同的sqlSession之间的缓存数据区域是互相不影响的,不同的sqlSession中的缓存是互相不能读取的
二级缓存是需要手动开启的,在xml中进行配置,是namespace(命名空间)级别的缓存,多个sqlSession可以共用二级缓存,二级缓存是跨SqlSession的
一级缓存的失效情况:
-
手动调用clearCache()清理Session缓存
-
同一个SqlSession两次查询期间执行了任何一次增删改操作,并commit了
-
同一个SqlSession但是查询条件不同
-
不同的SqlSession对应不同的一级缓存
二级缓存的开启:
二级缓存默认是关闭的,如果配置开启二级缓存,需要同时开启配置下面两项主要配置文件Mybatis-config.xml
<setting name="cacheEnabled" value="true"/>
需要开启二级缓存的Mapper.xml
<cache/>
注意:
用二级缓存的pojo类要实现Serializable接口
二级缓存在session提交或关闭后才生效
在Mybatis中用#{}取值和${}取值有什么区别?那什么是SQL注入?
#{}是做预编译取值,${}是做字符串拼接取值,${}可能会产生SQL注入的问题. SQL注入是指数据库被脱库,导致重要信息泄露,本质是把用户的数据当做有效代码来执行SQL语句.
Bean的生命周期你知道吗?
简单来说Bean的生命周期分为四个阶段
-
实例化
-
属性设置
-
检查aware相关的接口设置相关依赖
-
初始化
-
BeanPostProcessor的前置处理方法和后置处理方法
-
前后置处理之间会检查是否是以InitializingBean(正在初始化的Bean)调用的afterPropertiesSet方法,检查是否配置有自定义的init-method
-
销毁
-
会注册必要的Destruction(销毁)相关回调接口
-
检查是否实现了DisposableBean(可任意处理的Bean)接口
-
是否配置有自定义的destroy(销毁)方法
RabbitMq如何实现的延时队列?
rabbitmq当中本身没有提供延时队列功能推荐两种实现方式:
-
使用time to live(存活时间/过期时间)+死信队列组合实现延迟队列的效果
-
使用rabbitmq官方延迟插件,实现延迟队列效果
TTL+死信队列组合实现延迟队列效果
TTL(time to live),当消息达到存活时间后,还没有被消费,会被自动清除.rabbitmq可以对消息设置过期时间,也可以对整个队列(queue)设置过期时间,可以类比redis的TTL
死信队列,DLX(死信交换机).那如何让一个普通的队列成为死信队列呢?
消息成为死信的三种情况:
-
队列消息长度达到限制
-
消费者拒接消费消息,basicNack/basicReject并且不把消息重新放入原目标队列, 需要手动设置下 requeue=false
-
原队列存在消息过期设置,消息达到超时时间未被消费
逻辑整理:
结合TTL和死信队列,我们可以做以下思考:
-
创建队列b(普通队列)和队列a(死信队列),分别绑到交换机d,并且提供监听队列b(普通队列)的消费者c,队列a(死信队列)不提供消费者
-
生产者发送包含TTL的消息到队列a(死信队列)
-
队列a(死信队列)过期后,经由交换机d发送到队列b(普通队列)
-
消费者c消费队列b(普通队列)的信息.
如何快速插入MySQL百万级数据?
思考: 要插入这么多的数据肯定要频繁的访问数据库,什么样的机器肯定都吃不消的,能不能一次访问执行呢?
Java里面有答案: 可以用到两个关键对象 Statement PrepareStatement
Statement对象 每执行一条SQL语句都会将SQL发给数据库,数据库先编译SQL再执行
PrepareStatement对象 会先将SQL语句发送给数据库进行预编译,该对象会引用预编译后的结果.可以多次传入不同的参数给PrepareStatement对象执行,若有上万条类似的插入数据的语句,数据库也只需要编译一次,这样一来就减少了SQL语句的编译次数,提供了执行效率
要用到BaseDao工具类(依赖/jar包)
注:(重点)rewriteBatchedStatements=true,一次插入多条数据,只插入一次!!