目录
一、JVM
二、集合
三、异常
四、反射
五、IO
六、线程
七、Java8新特性
八、Spring的IOC和AOP
九、Mybatis
十、SpringMVC
十一、Springboot自动配置原理
十二、SpringCloud
十三、Mysql
十四、Redis
十五、ElasticSearch
十六、Linux
十七、Docker
十八、Nginx
十九、Kafka
二十、VUE
一、JVM
1、JVM是什么
Java虚拟机,从而让Java程序能够一次编译、到处运行,Java程序的具体运行过程Java源代码被编译器编译成字节码文件,然后JVM把字节码文件编译成对应操作系统的机器码,最后机器码调用相应操作系统的本地方法库执行相应的方法。
2、JVM组成
(1)类加载器(引申出双亲委派机制,应用加载器、扩展加载器(ext目录下)和根加载器(rt.jar包,比如我们常用的java.lang.String))(类加载生成的Class文件会存储在永久代)
(2)运行时数据区(程序计数器、栈、本地方法栈、堆(1/3是新生代、2/3是老年代,可以直接引申出垃圾回收算法)(应该还包括方法区的永久代)和方法区(包含运行时常量池,也称为永久代,用于存储常量、静态变量、类信息(即三种反射方法获取的Class文件,包括类的版本、字段、方法和接口描述)、即时编译器编译后的机器码和运行时常量池等数据)(JVM的GC分代收集会包含方法区,只是这里可以回收的对象很少))(其中只有堆和方法区是线程共享的,其他都是线程独有,所以其他区域的生命周期和线程一样,而线程共享的堆和方法区的生命周期是和jvm虚拟机一样)
(3)执行引擎(包含即时编译器和垃圾回收器,其中即时编译器就是把字节码文件转换为机器码,垃圾回收器就是调用上面提到的垃圾回收算法回收在运行过程中不再使用的对象)
(4)本地接口库(调用操作系统的本地方法库执行具体的指令操作)(注意:本地方法库是属于操作系统的)
3、gc垃圾回收
先提下如何标记对象为垃圾对象?引申出引用计数法和可达性分析法;
再提下gc垃圾回收的三种算法:复制算法、标记清除算法和标记整理算法,最后提下jvm采用分代回收算法
(1)新生代(1/3)
存放新创建的对象(不包括大小为2-128KB的大对象),8/10伊甸园区、1/10幸存者1区和1/10幸存者2区,复制算法,伊甸园区内存不够时会触发小gc,活下来的对象会进入幸存者2区并清除伊甸园区对象,之后如果伊甸园区内存又不够则再次触发小gc,此时幸存者2区的对象复制到幸存者1区一起参与小gc,如果有存活下来的对象则放入幸存者2区并清除伊甸园区和幸存者1区对象,之后调换幸存者1区和2区往复执行,如果触发了15次小gc,幸存者2区里还有对象,则进入老年代。
注:新创建的对象首先再伊甸园区,如果该对象的大小达到2-128kb,则该对象是大对象,直接进入老年代。
(2)老年代(2/3)
存储长时间存活的对象(小gc15次以上)和大对象(2-128KB)
新生代会不断把长时间存活对象给到老年代,那老年代也会满,满了怎么处理?(同花顺面试问到过)
full gc,触发条件是老年代内存空间不足或者大对象太大新生代没有连续的内存空间存储,采用的算法是标记整理算法,但full gc比较耗时,如果一次性大对象太多则会报堆内存不足异常。
(3)永久代(少量区域)
存储类加载之后的Class文件和元数据,和新生代、老年代不同的是,gc不会在程序运行期间对永久代的内存进行清理,所以如果加载的Class文件太多会出现堆内存不足异常,比如tomcat引用jar文件太多就会导致jvm堆内存不足而无法启动。
Java8之后永久代被元数据区即元空间取代,并且元数据不再使用jvm虚拟机的内存,而是操作系统的本地内存,但常量池和静态变量还是在堆中。
4、引用类型
在Java中一切皆对象,对象的操作是通过该对象的引用实现的,Java中引用对象分为以下四种:
(1)强引用
平时经常用的new一个对象然后赋值给某个变量就是强引用,强引用的对象一定是可达性状态,所以不会被垃圾回收,比如手动关流,不然会引用内存泄漏。
注:内存泄漏、内存溢出和内存不足区别:第一个是有个对象存在内存,但找不到该对象引用,一直存在无法释放;第二个是内存就这么大,但我们要需要的内存比总内存大;第三个是需要内存,但剩余内存不足。
(2)软引用
需要通过SoftReference类来实现,软引用的对象特点,会在系统内存空间不足时被回收。
(3)弱引用
需要通过WeakReference类实现,弱引用的对象特点,垃圾回收过程中一定会被回收。
(4)虚引用
需要通过PhantomReference类是实现,虚引用和引用队列联合使用,可以用于跟踪对象的垃圾回收状态。
5、垃圾回收器
针对新生代和老年代分为两种垃圾回收器
(1)新生代
A、Serial垃圾回收器:单线程、复制算法、进行垃圾回收的时候需要暂停其他所有工作线程,直到垃圾收集结束、使用场景是客户端运行的jvm新生代;
B、ParNew垃圾回收器:多线程、复制算法、垃圾回收原理和前面一样,使用场景是服务端运行的jvm新生代;
C、Parallel Scavenge垃圾回收器:多线程、复制算法、主要是优化系统吞吐量、更高效的利用CPU尽快完成gc垃圾回收任务
(2)老年代
A、Serial Old垃圾回收器:单线程、标记整理算法、可搭配新生代的Serial垃圾回收器使用,进行垃圾回收的时候也要暂停其他所有工作线程,使用场景是客户端;
B、Parallel Old垃圾回收器:多线程、标记整理算法,如果考虑系统吞吐量,可以搭配新生代第三种垃圾回收器
C、G1垃圾回收器:不牺牲系统吞吐量的情况实现短停顿垃圾回收。
6、jvm类加载机制(看情况,可以在上面引申出来)
(1)类加载分为5个阶段:加载、验证、准备、解析和初始化
(2)三种类加载器
(3)双亲委派机制
二、集合
1、List:可重复(不一定有无序)
(1)ArrayList:是否有序?
A、数据结构:数组(空间连续适合查询,不适合增删)
B、特点:不需要指定集合长度、会自动创建新集合然后把原数据复制过去
C、常用方法:add()、remove()和get()
D、线程不安全、三种处理方法
(2)LinkedList:
A、数据结构是双向链表(和数组相反)
B、线程不安全
(3)Vector:
A、数据结构和ArrayList一模一样
B、线程安全是唯一区别,原理底层方法都加上synchonzied,所以读写效率慢,不建议使用
2、Set:不可重复(不一定有无序)
(1)HashSet:HashTable实现、无序
(2)TreeSet:二叉树实现、有序
海康面试题:聊下二叉树?
(3)LinkedHashSet:HashTable实现数据存储,双向链表记录顺序
3、Map
(1)HashMap
A、数据结构:
1)jdk1.8之前是数组+链表,其中链表的每个元素是个对象,包含key、value、hash和用于指向单向链表的下一个元素的next。
2)jdk1.8之后是数组+链表+红黑树(64+8条件)(使用红黑树查询的时间复杂度从原来的n变成logn)
B、重要参数
1)默认容量16
2)加载因子0.75,超过会扩容,创建一个原来容量2倍的新数组,然后把原数组的元素全部复制到新数组,为什么一定是2的倍数?因为对2的几次方减1进行位运算可以让hash值更均匀分布。
C、线程不安全
A、原因
1)jdk1.8之前是因为头插法导致线程不安全
2)jdk1.8之后改成尾插法,但数据覆盖还是会出现线程不安全
B、解决方案
1)Collections类的synchronizedMap方法
2)使用juc包下的ConcurrentHashMap,这个采用分段锁思想实现,即把ConcurrentHashMap分成多个数据段即内部细分成多个hashMap,每个数据段都继承ReentrantLock并单独加锁,这样多线程下put和get都是在不同的数据段中,从而保证每个数据段线程安全,也就保证全局线程安全,默认分为16个段,所以最多同时支持16个线程并发执行写操作,只要这些线程写操作分布在不同段上即可,和hashMap一样,jdk1.8之后也引入红黑树。
(2)HashTable
A、继承自Dictionary类即字典类
B、线程安全,但同一个时间只能一个线程执行写操作,所以并发性不如ConcurrentHashMap
(3)TreeMap
A、数据结构:基于二叉树实现
B、有序
(4)LinkedHashMap
A、数据结构:基于HashTable
B、使用链表记录插入顺序
三、异常
1、异常概念
事与愿违,代码也可能不按正常执行,从而出现各种错误或者异常。
2、异常分类
Throwable是所有错误和异常的父类,错误包括堆内存不足等非代码层面问题,异常分为运行时异常和编译时异常,前者有空指针异常、类转换异常和数组长度溢出异常等,后者有io异常、sql异常和class找不到异常等
3、异常处理方式
(1)向外抛出异常,由调用者自己处理
A、方法上throws,后面跟着异常类
B、方法里throw ,后面跟着异常对象,一般为new xxx,需要注意的是throw下面的语句不再执行,所以一般if里用throw(finally除外)
(2)自己捕获处理
try-catch-finally
四、反射机制
1、动态语言概念
如果某个程序在运行过程中可以改变其结构,比如对属性或者方法进行修改或者删除,则这种程序所用的语言就是动态语言。
JS、Python就是动态语言,而C、C++就不是,Java因为反射机制而属于半动态语言。
2、反射概念
程序运行过程中,可以获取任意一个类的所有属性和方法,并且能创建一个对象调用任意一个方法。。
3、反射的应用
(1)向上造型
首先明确Java中的对象有两种类型,一种是编译时类型,是声明对象时采用的类型;另一种是运行时类型,是对象赋值时采用的类型。
理解这两种类型最好的例子是体现Java多态的向上造型,比如Person person = new Student();
那运行时类型的对象信息即属性和方法如何获取?反射。
(2)Spring创建对象
(3)动态代理
4、反射API
主要是以下四个类的方法
(1)Class类:表示类信息,可以获取类的属性、方法等信息(三种方法获取Class)
(2)Field类:表示类的成员变量,可以获取和设置类中的属性值
(3)Method类:表示类的方法,可以获取方法的描述信息或者执行某个方法
(4)Constructor类:表示类的构造方法,可以创建具体实例
5、反射的步骤
(1)三种方法获取想要操作的类的Class对象:
A、Class.forName(想要操作的类的全路径),最高效安全,使用场景JDBC、Spring创建对象
B、直接想要操作的类.class
C、new 一个想要操作的类的对象,然后调用该对象.getClass()
(2)获取类的Class对象之后就可以调用该对象的方法得到属性类、方法类和构造器类对象数组,然后循环遍历获取对应属性、方法或者构造器,再执行对应方法即可。
注:获取类的Class对象之后除了上面的获取类的属性、方法和构造器等类信息外,还可以创建对象,一共两种方法:
A、如果有无参构造器,则直接调用Class对象的newInstance方法即可
B、根据Class对象获取Constructor对象后,调用后者的newInstance方法,如果是有参构造,则该方法需要指定方法参数
6、Method的invoke方法
(1)获取Class对象
(2)根据Class对象获取某个Method对象
(3)根据Class对象或者构造器对象获取一个实例
(4)调用Method对象的invoke方法,方法参数是实例和Method对象需要调用的方法参数
五、IO
主要分为字节流和字符流两种,根据输入还是输出又可以分为四种,根据输入和输出的位置还可以分为多种,在工作中这些都用到过,比如文件上传接口经常用的,具体使用的时候需要设置一个buffer缓冲区,然后读取流中数据并写入缓冲区,直到读取结束。
六、线程
1、线程概念
进程和线程
2、线程的创建方式
三种:Thread类(start()是本地方法,弊端:继承了其他类就不能继承这个)、Runnable接口(静态代理)和Callbale接口(各个线程有返回值并汇总输出)
3、线程池
(1)使用线程池的原因
频繁创建和销毁线程比较耗费资源,所以使用线程池保证系统高效且安全使用,高效体现在线程复用,安全体现在控制最大线程数。
(2)线程池原理:
银行柜台例子
核心线程数、最大线程数、队列、拒绝策略(抛出异常、直接抛弃不做处理和替换队列中等待最久那个等)
(3)五种常用线程池
可缓存、固定大小、可做任务调度、单个线程和jdk1.8新增的足够大小。
4、线程的生命周期
(1)新建:new
(2)就绪:start()
(3)运行:得到CPU资源,run()
(4)阻塞:sleep()、wait()
(5)死亡:正常运行完、异常或error、stop方法(手动)
5、线程的基本方法
(1)sleep():睡眠一段时间,不释放锁
(2)wait():等待一段时间,释放锁,线程通信方式
(3)notifyAll():唤醒所有wait的线程,线程通信方式
(4)还有线程放弃CPU执行权的方法和线程中断、加入等方法
注:
面试题:
A、sleep()和wait()的区别
1)sleep()是Thread类方法,而wait()是Object类方法
2)前者不会释放锁,后者会释放锁
3)前者醒来会自动进入等待状态,后者需要notify()或notifyAll()配合
B、start()和run()的区别
1)前者是native方法,后者是Thread类方法
2)前者需要等待CPU调度才能执行
6、锁
根据不同维度可以分为不同锁,具体维度如下所示:
(1)乐观和悲观
A、乐观锁:
分为读和写两种情况,每次读取的时候乐观的认为该数据没有人修改过所有不加锁直接读,写的时候先读取当前版本号再加锁(为什么要当前版本号,解决CAS的ABA问题),如果变化则CAS,即比较and交换,比较当前版本号和上一次版本号是否发生变化,如果没有变化则直接写,使用场景mysql写锁
B、悲观锁
读写都会加上锁,所以其他人必须等待直到拿到锁才能操作数据,比如synchronized、ReentrantLock。
(A)synchronized
不仅属于独占式的悲观锁还属于可重入锁
注:什么是独占锁?锁在同一时刻只能被一个线程获取;
可重入锁?锁支持一个线程对同一个资源执行多次加锁操作,即可以连续两次对一个资源加锁。
A、synchronized的作用范围
1)成员变量、非静态方法:锁的是对象
2)静态方法:锁的是Class对象
3)代码块:代码块中配置的对象
B、synchronzied的实现
synchronizied内部有6个区域,每个区域的数据都代表锁的不同状态。
synchronizied是一个重量级操作,需要调用操作系统的相关接口,性能较低,性能低到给线程加锁的时间可能超过获取锁后具体逻辑代码的操作时间,所以jdk1.6对其进行优化,锁可以先偏向锁升级到轻量级锁再升级到重量级锁,这个升级过程也称之为锁升级。
(B)ReentrantLock
和synchronized一样,既是独占式的悲观锁,也是可重入锁;但不同的是ReentrantLock还可以实现公平锁和非公平锁。
(2)公平和非公平
A、公平锁:先到先得
B、非公平锁:JVM根据随机和就近原则分配锁,相对公平锁,这个执行效率更高,所以ReentrantLock默认是用非公平锁
面试题:synchronzied和ReentrantLock区别
1)前者是关键字,后者是Lock接口实现类
2)前者不需要手动释放锁,后者需要手动添加和释放锁
3)后者可以实现公平锁和非公平锁,前者不行
4)后者结合condition可以实现线程间通信,前者不行
5)后者可以实现响应中断等,前者不行
注:独占锁对应共享锁,应用场景是mysql读锁采用共享锁,写锁采用独占锁。
7、线程间通信
(1)使用volatile关键字
和synchronized一样,volatile也是关键字,只是后者的同步机制稍微弱一点,主要作用是保证变量可见性(计算机多个CPU有多个CPU缓存,所以加了volatile关键字的变量可以不写入CPU缓存直接写到主内存,从而保证一个线程修改变量其他线程能实时知道)和禁止指令重排。
(2)wait()和notifyAll()实现
(3)ReentrantLock和Condition实现
七、Java8新特性
Optional、lambda、stream和DateTime等时间类
八、Spring的IOC和AOP
1、IOC
(1)IOC概念
低耦合、控制反转
(2)Spring Bean的装配流程
Spring从xml配置文件或者注解读取bean配置信息,然后创建bean对象(4种方法,反射(前提无参构造,forName()、getInstance())、静态工厂、实例工厂和Spring自带工厂),创建的bean对象放入bean缓存池,该缓存池就是个hashMap,业务层使用的时候就可以从这里拿即可。
(3)Spring Bean的作用域
单例(唯一实例)、原型(每次创建一个实例)、请求、会话和全局
Springbean生命周期、属性设置两种方法、DI即依赖注入直接用注解实现最简单(@Autowire(一般注入控制层和Service层的组件)或者@Resource(一般注入DAO层的组件))
(4)Spring Bean的生命周期
1)bean实例化:4种方式
2)bean配置:主要是有参构造或者set方法
3)注入容器基础设施层面的依赖:如果某个bean实现了某些xxxAware接口则会调用对应方法
4)bean初始化:如果xml配置文件中配置初始化属性则执行初始化方法
5)bean调用:可以调用其内部各种方法
6)bean销毁:如果xml配置文件中配置销毁属性则执行销毁方法
(5)Spring依赖注入方式:构造器、set、静态工厂、实例工厂
2、AOP
(1)AOP概念
切面技术、高内聚、
(2)核心概念
切面、通知(前置、后置、成功、异常和环绕)、织入、连接点和切入点
(3)代理模式
A、静态代理:Thread和Runnable
B、动态代理:
(A)jdk本身反射包下的Proxy类(调用该类的newProxyInstance方法,方法参数有被代理者的所有类加载器、实现的所有接口和InvocationHandler接口的一个实现类,类中重写invoke方法实现动态调用被代理者的方法,然后在方法前后进行一些权限控制、日志和事务等操作即动态地将业务和技术代码合作一起)
(B)第三方包cglib(部分接口,创建一个增强器,再继承被代理者然后实现部分接口,最后调用回调方法对被代理者的方法进行增强。))
二者区别:前者必须实现被代理者的所有接口,而后者只需要部分即可,而且如果某个代理者没有实现任何接口,则只能用cglib实现,所有cglib的应用场景更广泛。
(4)AOP注解实现
@Aspect 定义一个类为切面
@Pointcut定义需要拦截的方法
@Before、@Around等实现前置通知、环绕通知等通知要执行的方法
九、Mybatis
sqlSessionFactoryBuilder
sqlSessionFactory
sqlSession:操作sql语句(一级缓存,默认开启,最多1024条sql语句)
接口+xml文件(Mapper级别,二级缓存,不同sqlSession共享)
十、SpringMVC
原理是请求过来,会先到dispatcherServlet组件,这个是个servlet,找到对应请求的HandlerMapping,再调用处理器找到对应类进行执行,执行结果返回ModelAndView,然后视图解析器对数据进行渲染响应。
十一、SpringBoot自动配置原理
Springboot有自动配置、内嵌tomcat和简化maven依赖等优点,其中自动配置是最大亮点,那其自动配置原理是什么?
启动注解@SpringbootApplication很关键,它是个组合注解:@SpringbootConfiguration,Spring中bean的xml配置文件;@ComponentScan,包扫描注解,会加载启动类所在包及其子包内所有类的注解,将其放入SpringBoot容器管理;@EnableAutoConfiguration,这个注解有个元注解@Import,会导入select结尾的类,这个类里有个方法是会扫描spring-boot-autoconfiguration-版本号.jar包里META-INF中的spring.factories文件,该文件声明了很多自动配置类,这些配置类都是有条件的,一般都是pom.xml中添加对应模块的Starter依赖就会自动配置哪些类。
所以SpringBoot出现的原因是条件注解。
十二、SpringCloud
注册中心(erueka(Restful)、nacos)、配置中心(apollo、nacos)、网关、fegin、各个微服务
注:apollo原理为分为客户端和服务端,然后我们在apollo服务端修改配置文件信息,那在Java服务中的apollo客户端如何获取信息?首先我们配置的信息,会通过meta服务更新到admin服务,而admin服务的获取的配置信息会同步到config服务,最后客户端根据meta服务找到config服务读取其配置信息。
十三、Mysql
增删改查
多表联查
group by、order by
事务(ACID 原子性、一致性、持久性、隔离性(脏读、不可重复读、幻读))
执行引擎(Mysam、Innodb)
十四、Redis
支持多种数据结构:String、List、Set和Hash
缓存穿透:很多缓存上找不到,直接去数据库找,未找到的也直接在redis中缓存,设置过期时间;布隆过滤器
雪崩:很多key过期时间都太集中,同时过期,查询大量到数据库
持久化:RDB(内存快照)和AOF(一个个记录下来)
集群:哨兵模式和cluster
十五、ElasticSearch
分布式、原理是有个协调节点存储元数据,类似仓库管理员有个账本,记录各个数据节点存储数据位置
分区分片存储数据
mapping(表结构)、索引(表)、文档(记录)
全文检索即搜索(mysql的innodb不支持)、存储大容量数据
十六、Linux
常用命令
目录:mkdir、rm
文件:vi、touch
查看:ls、ll、cat
http:curl -vv
十七、Docker
镜像、容器、仓库
常用命令:
docker ps、docker pull、dockers push
十八、Nginx
反向代理服务器
动静分离和负载均衡
nginx -s reload
十九、Kafka
生产者、消费者、主题、经纪人
二十、VUE
js框架、路由、element UI