面试-java

文章目录

Java

Java语言的理解

平台无关系(跨平台,一次编译到处运行)
GC(垃圾回收机制,不需要去手动的释放内存)
语言特性(泛型,反射,lambda)
面向对象(封装,继承,多态)
类库(并发,集合库)
异常处理

跨平台

Javac编译,生成字节码文件,JVM解析,转换成特定平台的执行指令
Java原码首先被编译成字节码,再由不同平台的JVM进行解析,Java语言在不同的平台上运行时不需要进行重新编译,Java虚拟机在执行字节码的时候,把字节码转换成具体平台上的机器指令。

关于JVM的问题

JVM如何加载.class文件?JVM在内存里一直运行

Engine:引擎

在这里插入图片描述

反射

JAVA反射机制在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取消息以及动态调用对象方法的功能称为java语言的反射机制

类加载

类从编译到执行的过程

编译器将xxx.java源文件编译为xxx.class字节码文件
ClassLoader将字节码转换为JVM中的Class<xxx>对象
JVM利用Class<xxx>对象实例化为xxx对象

谈谈ClassLoader

ClassLoader在java中有着非常重要的作用,它主要工作在Class装载的加载阶段,其主要作用是从系统外部获得Class二进制数据流,它是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过将Class文件里的二进制数据流装载进系统,然后交给Java虚拟机进行连接、初始化等操作

ClassLoader的种类

BootStrapClassLoader:C++编写,加载核心库java.*
ExtClassLoader:Java编写,加载扩展库javax.*
AppClassLoader:Java编写,加载程序所在目录
自定义ClassLoader:Java编写,定制化加载

谈谈类加载器的双亲委派机制

在这里插入图片描述

为什么要使用双亲委派机制取加载类

避免多份同样字节码的加载

类的加载方式

隐式加载:new
显示加载:loadClass,forName等,通过构造器,可以创建带参的实例化对象

loadClass和forName的区别

类的装载过程:
# 加载
通过ClassLoader加载class文件字节码,生成class对象

# 链接
校验:检查加载的class的正确性和安全性
准备:为类变量分配存储空间并设置类变量初始值
解析:JVM将常量池内的符号引用转换为直接引用

# 初始化
执行类变量赋值和静态代码块

两者的区别:
Class.forName得到的class已经初始化完成的
ClassLoader.loadClass得到的class是还没有链接的

Java内存模型

在这里插入图片描述

程序计数器

当前线程所执行的字节码行号指示器(逻辑)
改变计数器的值来选取下一条需要执行的字节码指令
和线程是一对一的关系即“线程私有”
对Java方法计数,如果是Native方法则计数器值为undefined
不会发生内存泄露

Java虚拟机栈(Stack)

# Java方法执行的内存模型

# 包含多个栈帧
局部变量表,操作栈、动态连接。返回地址
局部变量表:包含方法执行过程中的所有变量
操作数栈:入栈、出栈、复制、交换、产生消费变量

本地方法栈

与虚拟机栈相似,主要作用于标注了native的方法

元空间(MetaSpace)与永久代(PermGen)的区别

元空间使用本地内存,而永久代使用的是jvm的内存
解决了空间不足的问题
# MetaSpace相比PermGen的优势
字符串常量池存在永久代中,容易出现性能问题和内存溢出
类和方法的信息大小难易确定,给永久代的大小指定带来困难
永久代会为GC带来不必要的复杂性
方便HotSpot与其他JVM如Jrockit的集成

Java堆(Heap)

对象实例的分配区域
GC管理的主要区域

JVM三大新能调优参数-Xmx -Xmx -Xss

java -Xms128m -Xmx128x -Xss256k -jar xxx.jar
-Xss:规定了每个线程虚拟机栈(堆栈)的大小
-Xms:堆的初始值
-Xmx:推能达到的最大值

Java内存模型中堆和栈的区别-内存分配策略

静态存储:编译时确定每个数据目标在运行时的存储空间需求
栈式存储:数据区需求在编译时未知,运行时模块入口前确定
堆式存储:编译时或运行时模块入口都无法确定,动态分配

Java内存模型中推和栈的区别

联系:引用对象、数组时,栈里定义变量保存堆中目标的首地址
Person p = new Person()
栈内存			堆内存
p	------>  使用new开辟的内存空间

管理方式:栈自动释放,堆需要GC
空间大小:栈比堆小
碎片相关:栈产生的碎片远小于堆
分配方式:栈支持静态和动态分配,而堆仅支持动态分配
效率:栈的效率比堆高

Java垃圾回收机制

对象被判定为垃圾的标准

没有被其他对象引用

判定对象是否为垃圾的算法

引用计数算法
可达性分析算法

# 引用计数算法
判断对象的引用数量
	通过判断对象的引用数量来决定对象是否可以被回收
	每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1
	任何引用计数为0的对象实例可以被当做垃圾收集
优点:执行效率高,程序执行受影响较小
缺点:无法检测出循环引用的情况,导致内存泄露

可达性分析算法

在这里插入图片描述

通过判断对象的引用链是否可达来决定对象是否可以被回收

# 可以作为GC Roob的对象
虚拟机栈中引用的对象(栈帧总的本地变量表)
方法区中的常量引用的对象
方法区中的类静态属性引用的对象
本地方法栈中JNI(Native方法)的引用对象
活跃线程的引用对象

谈谈你了解的垃圾回收算法

# 标记-清除算法(Mark and Sweep)
标记:从根集合进行扫描,对存活的对象进行标记
清除:对堆内存从头到尾进行线性遍历,回收不可达对象内存
缺点:碎片化,导致回收之后,出现很多不连续的内存空间

# 复制算法(Copying)
分为对象面和空闲面
对象在对象面上创建
存活的对象被从对象面复制到空闲面
将对象面所有对象内存清除
# 优点
解决碎片化问题
顺序分配内存,简单高效
适用于对象存活率低的场景

# 标记-整理算法(Compacting)
标记:从根集合进行扫描,对存活的对象进行标记
清除:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收
# 优点
避免内存的不连续性
不用设置两块内存互换
适用于存活率高的场景

# 分代收集算法(Generational Collector)
垃圾回收算法的组合拳
按照对象生命周期的不同划分区域以采用不同的垃圾回收算法
目的:提高JVM的回收效率
JDK8以后分为:年轻代(存活率低,复制算法)和老年代(存活率高,标记-整理算法),没有永久区了

java垃圾回收的种类

# 垃圾回收的种类,分代收集算法
Minor GC: 年轻代GC
Full GC:老年代GC
# 年轻代
尽可能快速地收集掉那些生命周期短的对象
1个Enden区(开始创建的对象都在Enden区,当进行一次垃圾回收(Minor GC)时,把还存活的对象复制到任意的一个Survivor区)
两个Survivor区(当进行一次垃圾回收时,一个Survivor区会把还存活的对象,复制到另一个Survivor区,并且年龄会加1)

# 对象如何晋升到老年代
经历一定Minor次数依然存活的对象
Survivor区中存放不下的对象
新生成的大对象(-XX:+PretenuerSizeThreshold)
# 常用的调优参数
-XX:SurvivorRatio:Eden和Survivor的比值,默认8:1:1
-XX:NewRatio:老年代和年轻代内存大小的比例
-XX:MaxTenuringThreshold:对象从年轻代晋升到老年代经过的GC次数的最大阈值

# 老年代
存放声明周期较长的对象
标记-清理算法
标记-整理算法
Full GC和Major GC
Full GC比Major GC慢,但执行频率低、

# 触发Full GC的条件
老年代空间不足
永久代空间不足(针对jak7以前的版本,jdk8之后没有永久代空间了)
GMS GC时出现promotion failed, concurrent mode failure
Minor GC晋升到老年代的平均大小大于老年代的剩余空间
调用System.gc()
使用RMI来进行RPC或管理JDK应用,每小时执行1次Full GC

# Stop-the-World
JVM由于要执行GC而停止了应用程序的执行
任何一种GC算法中都会发生
多数GC优化通过减少Stop-the-World发生的时间来提高程序性能

# Safepoint
分析过程中对象引用关系不会发生变化的点
产生Safepoint的地方:方法调用;循环跳转;异常跳转等
安全点数量得适中

JVM运行的模式

java -version # 查看jvm运行时的模式

server:启动慢,运行速度快,重量级的虚拟机,做了更多的优化
client:启动快,运行速度慢,轻量级

年轻代常见的垃圾收集器

当执行垃圾回收时,会暂停所有用户的线程,暂停的时间100ms以内,可以接受的
# Serial收集器(-XX:+UseSerialGC,复制算法)
单线程收集,进行垃圾收集时,必须暂停所有工作线程
简单高效,Client模式下默认的年轻代收集器

# ParNew收集器(-XX:+UseParNewGC,复制算法)
多线程收集,其余的行为、特点和Serial收集器一样
单核执行效率不如Serial,在多核下执行才有优势

# Parallel Scavenge收集器(-XX:UseParallelGC,复制算法)
吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
比起关注用户线程停顿时间,更关注系统的吞吐量
在多核下执行才有优势,Server模式下默认的年轻代收集器 
使用参数可以进行调优,交给虚拟机自己进行调优,-XX:+UseAdaptiveSizePolicy

老年代常见的垃圾收集器

# Serial Old收集器(-XX:+UseSerialOldGC,标记-整理算法)
单线程收集,进行垃圾收集时,必须暂停所有工作线程
简单高效,Client模式下默认的老年代收集器

# Parallel Old收集器(-XX:+UseParallelOldGC,标记-整理算法)
多线程,吞吐量优先

# CMS收集器(-XX:UseConcMarkSweepGC,标记-清除算法)
初始化标记:stop-the-world
并发标记:并发追溯标记,程序不会停顿
并发预清理:查找执行并发标记阶段从年轻代晋升到老年代的对象
重写标记:暂停虚拟机,扫描CMS堆中的剩余对象
并发清理:清理垃圾对象,程序不会停顿
并发重置:重置CMS收集器的数据结构

G1收集器(年轻和老年)

# G1收集器(-XX:UseG1GC,复制+标记-整理算法)
Garbage First收集器的特点
并行和并发
分代收集
空间整理
可预测的停顿

# Garbage First收集器
将整个Java堆内存划分成多个大小相等的Region
年轻代和老年代不在物理隔离

垃圾收集器之间的联系

在这里插入图片描述

GC相关的面试题

# Object的finalize()方法的作用是否与C++的析构函数作用相同
与C++的析构函数不同,析构函数调用确定,而它的是不确定的
将未引用的对象放置于F-Queue队列
方法执行随时可能会被终止
给予对象最后一次重生的机会

# java中的强引用,软引用,弱引用,虚引用有什么用
# 强引用(Strong Reference)
最普遍的引用:Object obj = new Object()
抛出OutOfMemoryError终止程序也不会回收具有强引用的对象
通过将对象设置为null来弱化引用,使其被回收

# 软引用(Soft Reference)
对象处在有用但非必须的状态
只有当内存空间不足时,GC会回收该引用的对象的内存
可以用来实现高速缓存
String str = new String("abc");  // 强引用
SoftReference<String> softRef = new SoftReference<String>(str); // 软引用

# 弱引用(Weak Reference)
非必须的对象,比软引用更弱一些
GC时会被回收
被回收的概率也不大,因为GC线程优先级比较低
适用于引用偶尔被使用且不影响垃圾收集的对象
String str = new String("abc");  // 强引用
WeakReference<String> softRef = new WeakReference<String>(str); // 弱引用

# 虚引用(PhantomReference)
不会决定对象的声明周期
任何时候都可能被垃圾收集器回收
跟踪对象被垃圾收集器回收的活动,其哨兵作用
必须和引用队列ReferenceQueue联合使用
String str = new String("abc"); 
ReferenceQueue queue = new ReferenceQueue();
PhantomReference ref = new PhantomReference(str, queue);

引用队列(ReferenceQueue):无实际存储结构,存储逻辑依赖于内部节点之间的关系来表达

强引用 > 软引用 > 弱引用 > 虚引用
引用类型被垃圾回收时间用途生存时间
强引用从来不会对象的一般状态JVM停止运行时终止
软引用在内存不足时对象缓存内存不足时终止
弱引用在垃圾回收时对象缓存gc运行后终止
虚引用Unknown标记、哨兵Unknown

多任务

进程和线程的区别

# 进程
进程独占内存空间,保存各自运行状态,相互间不干扰且可以互相切换,为并发处理任务提供了可能

# 线程
共享进程的内存资源,相互间切换更快速,支持更细粒度的任务控制,使进程内的子任务得以并发执行

进程是资源分配的最小单位,线程是CPU调度的最小单位
所有与进程相关的资源,都被记录在PCB中
进程是抢占处理机的调度单位;线程属于某个进程,共享其资源
# PCB里有:
描述信息、控制信息、资源信息、CPU现场

# 总结
线程不能看做独立应用,而进程可看做独立应用
进程有独立的地址空间,相互不影响,线程只是进程的不同执行路径
线程没有独立的地址空间,进程的程序比多线程程序健壮
进程的切换比线程的切换开销大

进程和线程的关系

Java对操作系统提供的功能进行封装,包括进程和线程
运行一个程序会产生一个进程,进程包含至少一个线程
每个进程对应一个JVM实例,多个线程共享JVM里的堆
Java采用单线程编程模型,程序会自动创建主线程
主线程可以创建子线程,原则上要后于子线程完成执行

Thread中的start和run方法区别

调用start()方法会创建一个新的子线程并启动
run()方法只是Thread的一个普通方法的调用

Thread和Runnable是什么关系

Thread是实现了Runnable接口的类,使得run支持多线程
因类的单一继承原则,推荐多使用Runnable接口
MyRunnable myr1 = new MyRunnable();  // MyRunnable是实现Runnable接口的类里的run方法
Thread t1 = new Thread(myr1);
t1.start(); 

如何给run()方法传参

实现的方式主要有三种:
构造函数传参
成员变量传参
回调函数传参

如何实现处理线程的返回值

实现的方式主要有三种:
主线程等待法
使用Thread类的join()阻塞当前线程以等待子线程处理完毕
通过Callable接口实现:通过FutureTask Or 线程池获取

线程的状态

# 六个状态
新建(New):创建后尚未启动的线程的状态
运行(Runnable):包含Running和Ready
无限期等待(Waiting):不会被分配CPU执行时间,需要显示被唤醒
没有设置Timeout参数的Object.wait()方法
没有设置Timeout参数的Thread.join()方法
LockSupport.park()方法
限期等待(Timed Waiting):在一定时间后会由系统自动唤醒
阻塞(Bolcked):等待获取排它锁
结束(Terminated):已终止线程的状态,线程已经结束执行

sleep和wait的区别

# 基本差别
sleep是Thread类的方法,wait是Object类中定义的方法
sleep()方法可以在任何地方使用
wait()方法只能在synchronized方法或synchronized块中使用,只能获取锁了,此时等待才能释放锁

# 最主要的本质区别
Thread.sleep只会让出CPU,不会导致锁行为的改变
Object.wait不仅让出CPU,还会释放已经占有的同步资源锁

notify和notifyAll的区别

# 了解两个基本概念
# 锁池EntryList
假设线程A已经拥有了某个对象(不是类)的锁,而其它线程B、C想要调用这个对象的某个synchronized方法(或者块),由于B、C线程在进入对象synchronized方法(或者块)之前必须先获得该对象锁的拥有权,而恰巧对象的锁目前正被线程A所占用,此时B、C线程就会被阻塞,进入一个地方去等待锁的释放。这个地方便是该对象的锁池

# 等待池WaitSet
假设线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁,同时线程A就进入到了该对象的等待池中,进入到等待池中的线程不会去竞争该对象的锁

# notifyAll
notifyAll会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会

# notify
notify只会随机选取一个出去等待池中的线程进入锁池取竞争获取锁的机会

yield

# 概念
当调用Thread.yield()函数时,会给线程调度器一个当前线程愿意让出CPU使用的暗示,但是线程调度器可能会忽略这个暗示

如何中断线程

# 目前使用的方法
调用interrupt(),通知线程应该中断了
1.如果线程处于被阻塞状态,那么线程将立即退出被阻塞状态,并抛出一个InterruptException异常
2.如果线程处于正常活动状态,那么会将该线程的终端的标志设置为true。被设置终端标志的线程将继续正常运行,不受影响

需要被调用的线程配合中断
1.在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程
2.如果线程处于正常活动状态,那么会将该线程的中断标志设置为true。被设置终端标志的线程将继续正常运行,不受影响

在这里插入图片描述

synchronized

线程安全问题的主要诱因:
存在共享数据(也称临界资源)
存在多条线程共同操作这些共享数据

解决问题的根本方法:
同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再对共享数据进行操作
互斥锁的特性
互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程的协调机制,这样在同一时间只有一个线程对需要同步的代码块(复合操作)进行访问,互斥性也称为操作的原子性

可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于随后获取该锁的另一个线程是可见的(即在获得锁时应获得最新共享变量的值),否则另一个线程可能是在本地缓存的某个副本上继续操作,从而引起不一致

synchronized锁的不是代码,锁的都是对象
获取锁
根据获取锁的分类:获取对象锁和获取类锁
获取对象锁的两种用法
1、同步代码块(synchronized(this),synchronized(类实例对象)),锁的是小括号()中的实例对象
2、同步非静态方法(synchronized method),锁是当前对象的实例对象
public synchronized void SyncTask(){}  // 同步非静态方法

获取类锁的两种用法
1、同步代码块(synchronized(类.class)),锁是小括号()中类对象(Class 对象)
2、同步静态方法(synchronized static method),锁是当前对象的类对象(Class对象)
对象锁和类锁的总结
1、有线程访问对象的同步代码块时,另外的线程可以访问该对象的非同步代码块
2、若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象的同步代码块的线程会被阻塞
3、若锁住的是同一个对象,一个线程在访问对象的同步方法时,另一个访问对象同步方法的线程会被阻塞
4、若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象同步方法的线程会被阻塞,反之亦然
5、同一类的不同对象的对象锁互不干扰
6、类锁由于也是一种特殊的对象锁,因此表现和上述1,2,3,4一致,而由于一个类只有一把对象锁,所以同一个类的不同对象使用类锁将会是同步的
7、类锁和对象锁互不干扰
synchronized底层实现原理
# Monitor
Monitor:每个java对象天生自带了一把看不见的锁
Monitor锁的竞争、获取和释放

# 什么是重入
从互斥锁的设计上来说,当一个线程视图操作一个由其他线程持有的对象锁的临界资源时,将会处于阻塞状态,但当一个线程再次请求自己持有对象锁的临界资源时,这种情况属于重入

# 为什么会对synchronized嗤之以鼻
早期版本中,synchronized属于重量级锁,依赖于Mutex Lock实现
线程之间的切换需要从用户态转换到核心态,开销较大

# 自旋锁与自适应自旋锁
自旋锁:许多情况下,共享数据的锁定状态持续时间较短,切换线程不值得
通过让线程执行忙循环等待锁的释放,不让出CPU
缺点:若锁被其他线程长时间占用,会带来许多性能上的开销
synchronized和ReentrantLock的区别
synchronized是关键字,ReentrantLock是类
ReentrantLock可以对获取锁的等待时间进行设置,避免死锁
ReentrantLock可以获取各种锁的信息
ReentrantLock可以灵活地实现多路通知
机制:sync操作Mark Word,lock调用Unsafe类的park()方法

线程池

线程池的种类
利用Executors创建不同的线程池满足不同场景的需求
1.newFixedThreadPool(int nThreads)指定工作线程数量的线程池
2.newCachedThreadPool()处理大量短时间工作任务的线程池
	a.试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程
	b.如果线程闲置的时间超过阈值,则会被终止并移出缓存
	c.系统长时间闲置的时候,不会消耗什么资源
3.newSingleThreadExecutor()创建唯一的工作者线程来执行任务,如果线程异常结束,会有另一个线程取代它
4.newSingleScheduledExecutor()与newScheduledThreadPool(int corePoolSize)定时或者周期性的工作调度,两者的区别在于单一工作线程还是多线程
5.newWorkStealingPool()内部会创建ForkJoinPool,利用working-stealing算法,并行地处理任务,不保证处理顺序
fork/join框架
把大任务分割成若干个小人物并行执行,最终汇总每个小任务结果后得到大任务结果的框架
为什么要使用线程池
降低资源消耗
提高线程的可管理性
ThreadPoolExecutor的构造函数
corePoolSize:核心线程数量
maximumPoolSize:线程不够用时能够创建的最大线程数
workQueue:任务等待队列
keepAliveTime:抢占的顺序不一定,看运气
threadFactory:创建新线程,Executors.defaultThreadFactory()
handle:线程池的饱和策略
	AbortPolicy:直接抛出异常,这是默认策略
	CallerRunsPolicy:用调用者所在的线程来执行任务
	DiscardOldestPolicy:丢弃队列中靠最前的任务,并执行当前任务
	DiscardPolicy:直接丢弃任务
	实现RejectedExecutionHandler接口的自定义handler
新任务提交execute执行后的判断
如果运行的线程少于corePoolSize,则创建新线程来处理任务,即使线程池中的其他线程是空闲的;
如果线程池中的线程数量大于等于corePoolSize且小于maximumPoolSize,则只有当workQueue满时才创建新的线程去处理任务
如果设置的corePoolSize和maximumPoolSize相同,则创建的线程池的大小是固定的,这时如果有新任务提交,若workQueue未满,则将请求workQueue中,等待有空闲的线程取从workQueue中取任务并处理
如果运行的线程数量大于等于maximumPoolSize,这时如果workQueue已经满了,则通过handler所指定的策略来处理任务;
线程池的状态
RUNNING:能接受新提交的任务,并且也能处理阻塞队列中的任务
SHUTDOWN:不在接受新提交的任务,但可以处理存量任务
STOP:不在接受新提交的任务,也不处理存量任务
TIDYING:所有的任务都已终止
TERMINATED:terminated()方法执行完后进入该状态
线程池的大小如何选定
CPU密集型:线程数=按照核数或者核数+1设定
I/O密集型:线程数=CPU核数*(1 + 平均等待时间/平均工作时间)

String StringBuffer StringBuilder

String:线程安全,节省空间
StringBuffer:高效,但线程不安全
StringBuilder:高效且线程安全

Java异常

异常梳理机制主要回答了三个问题

what:异常类型回答了上面被抛出异常
where:异常堆栈跟踪回答了在哪抛出
why:异常信息回答了为什么被抛出

Error和Exception的区别

Error:程序无法处理的系统错误,编译器不做检查
Exception:程序可以处理的异常,捕获后可能恢复
总结:前者是程序无法处理的错误,后者是可以处理的异常

从责任角度看:
1、Error数据JVM需要负担的责任;
2、RuntimeException是程序应该负担的责任;
3、Checked Exception可检查异常是Java编译器应该负担的责任

Java的异常处理机制

抛出异常:创建异常对象,交由运行时系统处理  throws
捕获异常:寻找合适的异常护理器处理异常,否则终止运行 try...catch

try-catch的性能

Java异常处理消耗性能的地方:
try-catch块影响JVM的优化
异常对象实例需要保存栈块照等信息,开销较大

微服务

# Spring Cloud
eureka:注册中心
ribbon: 负载均衡,放注册到eureka里面的服务,均衡起来,例如server1里对应两个ip,访问两次,每个执行一次
hystrix:熔断,防止一个服务出问题,一致卡主,出现雪崩,在一定的时间,就返回
feign:服务调用
zuul:网关,拦截不合理的请求
客户端访问ip,先经过nginx反向代理到网关zuul,然后这里有负载均衡ribbon和熔断hystrix,去eureka里面调取相应的服务;

数据结构考点

数组和链表的区别
链表的操作,如反转,链表环路检测,双向链表,循环链表相关操作
队列,栈的应用
二叉树的遍历方式及其递归和非递归的实现
红黑树的旋转

算法考点

内部排序:如递归排序,交换排序(冒泡、快排)、选择排序、插入排序;
外部排序:应掌握如何利用有限的内存配合海量的外部存储来处理超大的数据集,写不出也要有相关的思路

考点扩展
那些排序是不稳定的,稳定意味着什么
不同数据集,各种排序最好或最差的情况
如何优化算法

Mybatis的基本原理

# 工厂模式
# 生成SqlSession使用了工厂模式
优势:解耦(降低类之间的依赖关系)

# 代理模式
# 创建Dao接口实现类使用了代理模式
优势:不修改原码的基础上对已有的方法进行增强


# 1.读取配置文件
# InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
//通过类加载器来读取配置文件
is = MybatisTest.class.getClassLoader().getResourceAsStream("SqlMapConfig.xml");

# 2.创建SqlSessionFactory对象
builder = new SqlSessionFactoryBuilder();
factory = builder.build(is);

# 3.使用工厂生产SqlSession对象
sqlSession = factory.openSession();  # 里面加true,就可以自动提交,但有时候不好

# 4.使用SqlSession创建IUserDao接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);

FastDFS文件存储系统

中间件

RMQ[RabbitMQ]

# AMQP协议
高级消息队列协议

发布订阅模式
通过交换机与队列绑定的key,发布到队列里
交换机常用的有三种:
direct,点对点,需要全部配备到key了,才把消息发到该队列里
fanout,发布的消息,绑定的队列都收到消息
topic,匹配,发布的消息,与该key匹配上的队列收到消息

解决消息丢失
# ack(消息确认)
# 持久化
# 发送消息前,将消息持久化到数据库,并记录消息状态(可靠消息服务)
# 生产者确认(publisher confirm)

zookeeper

协调中心,调度中心

kafka

生产消费模式,订阅一个topic,发布的消息到该topic,订阅该topic的就收到了该消息
高吞吐,kafka集群

ES[elasticsearch]

储存数据用的,存储的数据类型为json
put /index/type/1 存储数据,可以写json的格式去查询
get /index/type/1 获取数据
delete /index/type/1 删除数据
head /index/type/1 判断数据是否存在,返回状态码,200或404

# 分词
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值