一 基础篇
1.1 Java基础
-
面向对象的特征
抽象:将一类对象的共同特征总结出来构建类的过程。 继承:对已有类的一种复制。 多态:不同子类型的对象对同一种消息做出不同的回应。 封装:把数据和操作数据的方法绑定起来,对数据访问时只能通过定义的接口
-
final、finally、finalize的区别
final:修饰变量,被修饰的变量值不能改变 finally:使用try捕获异常时候,一定会执行的代码块 finalize:垃圾回收前,会调用finalize方法,释放对象持有的资源
-
Exception、Error、运行时异常与一般异常有何异同
Exception与Error都是throwable的子类 Eroor:系统内部错误与资源耗尽错误,程序不抛出这种错误 Exception又分为runtimeException与I/OException。程序本身错误时runtimeException,Io就属于其他异常。
-
请写出5种常见到的runtime exception,IOException
1.NullPointerException (空指针异常) 2.IndexOutBoundsException(下标越界异常) 3.ArithmeticException(算数异常) 4.SecurityException(安全异常) 5.ArrayStoreException(数组存放类型异常) 6.NumberFormatException(String转数字类型异常) 1.FileNotFountException 2.IOException 3.EOFException
-
int 和 Integer 有什么区别,Integer的值缓存范围
int是基本数据类型,Integer是包装数据类型,包装数据类型是对象,可以使用静态方法。 Integer的值缓存范围-128-127
-
包装类,拆箱和装箱
拆箱和装箱的过程就是包装类型与基本数据类型相互转化的过程。
-
String、StringBuilder、StringBuffer
提供了两种类型的字符串String和StringBuilder/StringBuffer都可以存储和操作字符串。
String 是只读字符串,StringBuilder是单线程下使用的,所以方面都没有被Synchronized修饰。效率更高。 -
重载和重写的区别
都是实现多态:重载发生在同一个类下,相同的方法名,参数不同。而重写发生在子类与父类之间,方法名参数返回类型都相同。 重载是编译时的多态性,而重写是运行时的多态性。
-
抽象类与接口的区别
抽象类与接口都不能被实例化,一个类继承了某个抽象类或者实现了某个接口,都需要对他们的所有方法进行实现。抽象类可以定义构造器,有抽象方法、具体方法,所有接口更抽象。 抽象类中的成员可以是public、private的,但是接口中成员全部都是public,接口中定义的成员变量其实是常量。
-
说说反射的用途及实现
-
自定义注解的实现
-
get请求与post请求的区别
get请求查询字符串在url上拼接着,post请求的是单独的。get请求能够被缓存,存放书签等
-
Session与Cookie区别
cookie存放在浏览器本地,session存放在服务器,浏览器可以禁止cookie,但是不能禁止session,
-
列出常用的jdk包
1.java.lang 2.java.util 3.java.sql 4. java.math 5.java.nio 6.java.net
-
MVC设计思想
-
equals与==的区别
==:比较的是栈中存放的对象的内存地址,判断是否指向同一个对象。 equals:Object.equals()实际调用的依然是 ==,只有其他地方进行重写String、Integer
-
hashCode和equals方法的区别与联系
hashCode计算出实例的hash码,并返回hash码,equals判断两个对象的地址是不是同一个。 hashcode相同,equals不一定相同;equals相同,hashcode一定相同。 hashMap中,hashcode定位到应该存放的位置,如果该位置已经存在元素了,再调用equals。
-
什么是Java序列化和反序列化,如何实现Java序列化?或者请解释Serializable 接口的作用。
无论什么样的数据都是以二进制的形式在网络上传送的。把Java对象转化为字节码序列的过程就称为对象的序列化,把字节码转为Java对象就叫做反序列化。 调用OutputStream的writeObject方法
-
Object类中常见的方法,为什么wait notify会放在Object里边?
wait本来就是暂停获取锁的对象
1.2、Java常见集合
-
List 和 Set 区别
list和set都是继承了Collection接口。 list可以允许重复的对象,允许插入多个null元素,是一个有序容器,保留插入的顺序。常用的实现类有ArrayList LinkedList和Vector.arraylist提供了索引的随意访问,而linkedList对经常需要添加删除的场合更加合适。 set不允许有重复的元素,无序容器,TreeSet通过Comparator或者Comparable维护排序。只允许一个null元素。set接口实现的类有HashSet,LinkedHashSet以及TreeSet.TreeSet还实现了Sorted接口,因此是一个根据compare()与compareTo()的定义进行排序的有序容器。
-
Set和hashCode以及equals方法的联系
HashSet通过hashCode与equals维护元素的唯一性。
-
List 和 Map 区别
list继承conllection,而map是接口,map以key-value的形式,value可能相同,但是key一定唯一,null-key唯一。
-
Arraylist 与 LinkedList 区别
ArrrayList实现了基于动态数组的数据结构,而LinkedList是基于链表的数据结构,并且是双向链表。 对于随机访问get set,A>L,因为L需要移动指针。 对于新增删除来说L>A,因为A要移动数据。
-
ArrayList 与 Vector 区别
Vector的方法都是同步的、线程安全的,而ArrayList不是,所以ArrayList的性能较好。 当容量达到初始大小后,Vector会翻倍,而Arraylst会上涨0.5倍。更加节省空间
-
HashMap 和 Hashtable 的区别
都是基于哈希表的,HashMap继承的是map 而HashTable继承的是Dictionary。HashMap的初始容量是16,HashTable 11,填充因子0.75,2n 2n+1
-
HashSet 和 HashMap 区别
HashSet实现了set接口,不容许重复,存储对象是需要重写hashCode与equals方法。使用add添加元素,hashCode计算时利用成员对象 HasgMap实现了map接口,key允许一个null。存储key value,使用put方法添加元素,使用key来计算hashcode
-
HashMap 和 ConcurrentHashMap 的区别
想要线程安全的HashMap可以通过Collections的静态方法SynchronizedMap来获得。CouncurrecnHashMap 分段锁,默认16个。 使用put的时候,根据key的hash值得到在数组中的位置,然后放到位置中,如果已经有元素,那么将以链表的形式存放。新插入的在前面 1.8之后先插入的在后面。
-
HashMap 的工作原理及代码实现,什么时候用到红黑树
1.6 1.7采用的是位桶加链表的形式,1.8采用位桶加链表加红黑树。当链表的长度超过阀值(8)时采用红黑树,将减少查找的时间。 根据key计算hash得到在位桶(数组)中的位置,元素放到后面列表。如果已有元素添加到后面。
-
多线程情况下HashMap死循环的问题
rehash的时候链表结构发生变化,会发生闭合的回路。
-
HashMap出现Hash DOS攻击的问题
-
ConcurrentHashMap 的工作原理及代码实现,如何统计所有的元素个数
-
手写简单的HashMap
-
看过那些Java集合类的源码
1.3、进程和线程
-
线程和进程的概念、并行和并发的概念
进程:是一个程序的实例,每个进程都有自己的虚拟地址空间和控制线程。 线程:线程是操作系统调度器分配处理器的基础单元。 并行:一个cpu同步处理多个进程。 并发:一个cpu可以异步的处理多个进程。
-
创建线程的方式及实现
Thread是实现了Runnable的。 有三种,1.继承Thread 2.实现了runnable 3.使用callable和future创建线程。
-
进程间通信的方式 !!!!!!!!!!!
线程之间通信方式: 1.共享变量 --必须指向同一个共享实例的引用(synchronized) 2.wait/notify机制 3.Lock/Condition 4.管道(缺点:只能两个线程之间,只能单向通信) 创建管道输出流PipedOutputStream pos 与输入流PipedInputStream pis 将pos与pis匹配,pos.connect(pis); pos赋给信息输入线程,pis赋给信息获取线程。 进程之间的通信方式: 1.管道 2.命名管道 3.信号 4.消息队列 5.共享内存 6.内存映射 7.信号量 8.套接口
-
说说 CountDownLatch、CyclicBarrier 原理和区别
CountDownLatch:一个线程等待其他多个线程完成。CountDownLatch有一个构造方法,两个常用的方法 await() --等待变为0和countdown() ---减一 CyclicBarrier:所有线程到达某个状态,再一起执行 await()加一
-
说说 Semaphore 原理
Semaphore:一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。 AQS控制
-
说说 Exchanger 原理
两个工作线程之间的交换数据,等同时到达某一个状态 进行交换数据
-
ThreadLocal 原理分析,ThreadLocal为什么会出现OOM,出现的深层次原理
ThreadLocal:线程本地变量或线程本地存储。 ThreadLocal在每个线程中会对该变量创建一个副本(资源消耗的问题),先set再get,如果不set,可以重写initalValue方法
-
讲讲线程池的实现原理
多线程技术主要解决处理器单元内多个线程执行的问题。可以显著的减少处理器单元的闲置时间。增加啊处理器单元的吞吐能力。T1+T3>>>T2 1.线程池管理器(ThreadPool):用于创建并管理线程池,包括创建线程,销毁线程,添加新任务 2.工作线程(PoolWorker):线程池中线程,在没有任务时处于等待章台,可以循环的执行任务。 3.任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,主要规定了任务的入口、任务执行完的收尾工作,任务的执行状态 4.任务队列(taskQueue):用于存放没有处理的任务,一种缓冲机制。 常见的线程池:Executors中的静态方法 1.newSingleThreadExecutor 单个线程,串行执行 2.newFixedThreadExecutor(n) 固定数量的线城市 3.newCacheThreadExecutor 可缓存线程池,当线程池大小超过了处理任务所需的线程,就会回收线程,有新任务来时,添加新的线程来执行 4.newScheduleThreadExecutor 大小无限制的线程池,支持定时和周期性的执行线程 newWorkStealingPool 多个队列的线程池 减少连接数 重要的类: ExecutorService --真正的线程池接口 ScheduledExecutorService --和Timer/TimerTask类似,解决需要重复执行的问题 ThredPoolExecutor --ExecutorService的默认实现 ScheduledThredPoolExecutor --继承ScheduledExecutorService接口时间,周期性任务调度的类实现 queue有三种类型,即排毒有三种通用策略: 直接提交:工作队列默认是SynchronousQueue。当命令超过队列所能处理的平均数连续到达数,此策略允许无界线程具有增长的可能性。使用这种策略,要求maxnumPoolSize是无界的,可以保证A2一定在A1后执行。 无界队列:不具有预定义容量的LinkedBlockingQueue,corePoolSize线程都忙时新任务在队列中等待,当超过corePoolSize,新任务始终添加到队列中 有界队列:ArrayBlockingQueue. 拒绝策略,够了就不添加到队列中去了 可选择的饱和策略RejectExceptionHandler 1.AbortPolicy 中止策略,饱和时会抛出RejectedExcutionException 2.discardPolicy 抛弃策略,不做任何处理 直接抛弃 3.discardOldestPolicy 抛弃旧任务策略 优先级队列,会抛弃优先级较高的,不建议 4.CallerRunsPolicy 调用者运行,调用调用者的主线程来执行任务。所以主线程无法再提交新任务。 线程池最核心的一个类 java.util.concurrent.ThreadPoolExcutor类 Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,从字面意思可以理解,就是用来执行传进去的任务的; 然后ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等; 抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法; 然后ThreadPoolExecutor继承了类AbstractExecutorService。 在ThreadPoolExecutor类中有几个非常重要的方法: execute() submit() shutdown() shutdownNow() execute()方法实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。 submit()方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果(Future相关内容将在下一篇讲述)。
-
线程池的几种实现方式
-
线程的生命周期,状态是如何转移的
(1)New:创建线程对象后,该线程处于新建状态,此时它不能运行,和其他Java对象一样,仅仅有Java虚拟机为其分配了内存,没有表现出任何线程的动态特征; (2)Runnable:线程对象调用了start()方法后,该线程就进入了就绪状态(也称可运行状态)。处于就绪状态的线程位于可运行池中,此时它只是具备了运行的条件,能否获得CPU的使用权开始运行,还需要等待系统的调度; (3)Runing:处于就绪状态的线程获得了CPU使用权,开始执行run()方法中的线程执行体,则线程处于运行状态。当一个线程启动后,它不能一直处于运行状态(除非它的线程执行体足够短,瞬间结束),当使用完系统分配的时间后,系统就会剥脱该线程占用的CPU资源,让其他线程获得执行的机会。只有处于就绪状态的线程才可能转换到运行状态。 (4)Blocked:一个正在执行的线程在某些特殊情况下,如执行耗时的输入/输出操作时,会放弃CPU的使用权,进入阻塞状态。线程进入阻塞状态后,就不能进入排队队列。只有当引用阻塞的原因,被消除后,线程才可以进入就绪状态。 ——当线程试图获取某个对象的同步锁时,如果该锁被其他线程所持有,则当前线程进入阻塞状态,如果想从阻塞状态进入就绪状态必须得获取到其他线程所持有的锁。 ——当线程调用了一个阻塞式的IO方法时,该线程就会进入阻塞状态,如果想进入就绪状态就必须要等到这个阻塞的IO方法返回。 ——当线程调用了某个对象的wait()方法时,也会使线程进入阻塞状态,notify()方法唤醒。 ——调用了Thread的sleep(long millis)。线程睡眠时间到了会自动进入阻塞状态。 ——一个线程调用了另一个线程的join()方法时,当前线程进入阻塞状态。等新加入的线程运行结束后会结束阻塞状态,进入就绪状态。 线程从阻塞状态只能进入就绪状态,而不能直接进入运行状态,即结束阻塞的线程需要重新进入可运行池中,等待系统的调度。 (5)Terminated:线程的run()方法正常执行完毕或者线程抛出一个未捕获的异常(Exception)、错误(Error),线程就进入死亡状态。一旦进入死亡状态,线程将不再拥有运行的资格,也不能转换为其他状态。
-
可参考:《Java多线程编程核心技术》
1.4、锁机制
-
说说线程安全问题,什么是线程安全,如何保证线程安全
线程安全:多个线程同时运行,每次结果与单线程的结果一样,变量也与预期一样。线程安全问题都是由全局变量以及静态变量引起的 1.保证原子性:锁和同步 lock() synchronized CAS AtomicInteger 2.保证可见性:volatile happens-before原则(先行发生原则)
-
重入锁的概念,重入锁为什么可以防止死锁
重入锁:同一个线程再次进入同步代码块的时候,使用自己已获取的锁 synchronized 和 ReentrantLock 都是可重入锁
-
产生死锁的四个条件(互斥、请求与保持、不可剥夺、循环等待)
-
如何检查死锁(通过jConsole检查死锁)
-
volatile 实现原理(禁止指令重排、刷新内存)
volatile保证可见性的原理是在每次访问变量时都会进行一次刷新,因此每次访问都是主内存中最新的版本。所以volatile关键字的作用之一就是保证变量修改的实时可见性。 还有另一个重要的作用:在JDK1.5之后,可以使用volatile变量禁止指令重排序。
-
synchronized 实现原理(对象监视器)
Java对象头和monitor是实现synchronized的基础
-
synchronized 与 lock 的区别
1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类; 2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁; 3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁; 4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了; 5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可) 6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
-
synchronized 与 ReentrantLock的区别
由于ReentrantLock是java.util.concurrent包下提供的一套互斥锁,相比Synchronized,ReentrantLock类提供了一些高级功能,主要有以下3项: 1.等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。 2.公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。 3.锁绑定多个条件,一个ReentrantLock对象可以同时绑定对个对象。
-
AQS同步队列
-
CAS无锁的概念、乐观锁和悲观锁
-
常见的原子操作类
-
什么是ABA问题,出现ABA问题JDK是如何解决的
-
乐观锁的业务场景及实现方式
-
Java 8并法包下常见的并发类
-
偏向锁、轻量级锁、重量级锁、自旋锁的概念
1.5、JVM
-
JVM运行时内存区域划分
内存区划分为:堆、Java栈、本地方法栈、程序计数器、方法区 所有线程共享的区域:堆、方法区 程序计数器:记录执行的指令的地址(native方法) Java栈:存放的是一个个的栈帧,对应的调用方法,在栈帧中包括局部变量表、操作数栈、当前方法所属类的常量尺码的引用,方法返回地址。!!当线程执行一个方法时,就会创建一个对应的栈帧,并压栈,方法执行完毕后就会出栈。 本地方法栈:是执行本地方法服务的 堆:用来存储对象本身以及数组(数组引用存放在Java栈中) 方法区:存储了每个类的信息(类的名称、方法信息、字段信息)、静态变量、常量和编译后的代码
-
内存溢出OOM和堆栈溢出SOE的示例及原因、如何排查与解决
OOM:过多的静态变量、大量递归、大量循环、sql查询超过十万条,集合中存放对象的引用而不是对象、非字面量字符串+操作 栈溢出:递归调用、大量循环、全局变量过多、数组 list map数据是否过大,DDMS工具
-
如何判断对象是否可以回收或存活
引用计数法:引用加1,引用失效减1 但无法解决对象之间的循环引用的问题 可达性分析:通过一系列被称为"GC ROOTS"的对象作为起始点,搜索所走过的路径称为引用链。当没有引用链的时候,就认为不可达 可以被回收。
-
常见的GC回收算法及其含义
1.引用计数法: 2.标记-清除法: 3.copy: 4.标记-整理 5.分区:对于栈来说
-
常见的JVM性能监控和故障处理工具类:jps、jstat、jmap、jinfo、jconsole等
-
JVM如何设置参数--tomcat的配置文件中
-
JVM性能调优
-
类加载器、双亲委派模型、一个类的生命周期、类是如何加载到JVM中的
.java文件通过编译器(javac)编译成为.class的二进制文件,通过类加载器加载到内存中。 类的生命周期:加载、验证、准备、解析、初始化、使用、卸载。 加载:通过类的全限定名来获取定义此类的二进制字节流,将字节流代表的静态存储结构转化为区域运行时的数据结构。在Java堆中生成一个代表这个类的java.lang.class对象,作为方法区数据的访问入口。 验证:分为四个阶段文件格式、元数据、字节码、符号引用 解析:是将常量池的符号引用转化为直接引用,四种类型的引用解析:类或接口的解析、字段解析、方法解析、接口方法解析 初始化:根据程序员制定的计划去初始化。包括static{} 构造函数、父类初始化等。 双亲委派模型:机制:类加载器(bootstrap)-->标准扩展(Extension)类加载器-->系统加载器-->上下文加载器。从上到下加载,先将加载任务委托给父类加载器、依次递归,如果父类成功完成,就返回成功、无法完成时才去自己加载。
-
类加载的过程:加载、验证、准备、解析、初始化
-
强引用、软引用、弱引用、虚引用
强引用:只要引用存在,永远不会被回收 软引用:非必须引用,内存溢出之前回收,类似缓存 弱引用:第二次垃圾回收时回收 监控被垃圾回收器标记为即将被回收的垃圾 虚引用:垃圾回收时回收,无法通过引用取到对象值。检测是否从内存中删除
-
Java内存模型JMM
JMM的主要目标是定义程序中各个变量的访问规则,虚拟机中将变量存储到内存和从内存中取出变量的细节。
1.6、设计模式
-
常见的设计模式
1.单例模式 2.工厂模式 3.建造者模式 4.门面模式 5.策略模式
-
设计模式的的六大原则及其含义
1.开闭原则(抽象类和接口):对扩展开放,对修改关闭 2.里氏代换原侧(使用抽象类继承):基类可以被子类替换 3.依赖倒转原则(针对接口编程):要依赖抽象 4.接口隔离(建立最小接口):多接口更好 5.迪米特法则:尽量减少与其他实体相互作用 6.合成复用法则:减少继承
-
常见的单例模式以及各种实现方式的优缺点,哪一种最好,手写常见的单利模式
饿汉:定义静态,如果实例没有被引用,会造成资源浪费。 懒汉:synchronized 并发效率低。
-
设计模式在实际场景中的应用
-
Spring中用到了哪些设计模式
-
MyBatis中用到了哪些设计模式
-
你项目中有使用哪些设计模式
-
说说常用开源框架中设计模式使用分析
-
动态代理很重要InvocationHandler
手写代理模式:1.先写接口,定义接口内方法 2.写主人类,实现接口内的方法。 3.写代理类,实现InvocationHandler 利用到了反射
1.7、数据结构
- 树(二叉查找树、平衡二叉树、红黑树、B树、B+树
-
深度有限算法、广度优先算法
-
克鲁斯卡尔算法、普林母算法、迪克拉斯算法
-
什么是一致性Hash及其原理、Hash环问题
-
常见的排序算法和查找算法:快排、折半查找、堆排序等
2.1、数据库
-
MySQL 索引使用的注意事项
索引的目的在于提高查询效率。索引的类型:1.index 2.unique 3.primary key 4.fulltext 5.组合索引 创建索引:1.ALTER TABLE table_name ADD INDEX index_name (col_name1,col_name2) 2. CREATE INDEX index_name ON table_name(col_name) 删除索引:1.DROP INDEX index_name ON table_name; 2.ALTER TABLE table_name DROP PRIMART KEY; 注意事项:EXPLAIN可以帮助开发人员分析sql的问题。 1.索引在提高查询毒素的同时,也降低了更新表的速度,不仅要保存数据,还需要保存索引文件 2.建立索引会占用磁盘空间的索引文件
-
DDL、DML、DCL分别指什么
DML:有select、update、insert、delete 对数据库的数据进行操作的数据 DDL:有create、alter、drop 主要是定义或改变表结构,表建立时使用 DCL:数据库控制功能,用来设置数据库用户或角色权限的语句
-
explain命令
-
left join,right join,inner join
-
数据库事物ACID(原子性、一致性、隔离性、持久性)
操作序列要么都执行要么都不执行。 原子性:事务是一个不可分割的工作单位、要么都执行要么都不执行。 一致性:开始与结束后,数据库的完整性没有被破坏。解决办法,约束
-
事物的隔离级别(读未提交、读以提交、可重复读、可序列化读)
读未提交: 读以提交:等到提交完在读取数据。 可重复读:执行事务的时候不允许其他事务进入 可序列化读:Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
-
脏读、幻读、不可重复读
脏读:读取到还未确认的数据 ===>事务提交前,对其他事务不可读取其修改的值。 幻读:对同一行数据做出修改,更新丢失 ===>对行加锁,只允许并发一个更新事务。 不可重复读:多次读取不一致 ====> 只有当修改事务完全提交后才可以读取数据。 幻象读:读取后新增操作 ===>在操作完成数据处理之前,任何事务都不可以在添加新数据。
-
数据库的几大范式(三大范式)
1NF: 原子性,列不应该再分为几列(电话|家庭电话/手机) 2NF: 1.必须有主键2.完全依赖于主键 数据冗余的问题 3NF: 不能传递依赖,反作用依赖,非主键依赖于主键
-
数据库常见的命令
--- 数据库的扩展:业务拆分、主从复制、数据库的分库分表
-
说说分库与分表设计
单表的记录行数过大:采用Master-Slave复制模式的mysql架构。写操作集中在Master,并且挂在的Slave受到master能力和负载的限制。 拆表可以用取余的办法,表的数量一般是2的N次方,分表一般解决单表数量过大带来的查询效率问题。无法解决并发问题,所以还得分库。 分库分表的策略:1.中间变量=user_id%(库容量*每个库中表的数量); 2.库序号=取整(中间变量/每个库中表的数量) 3.表序号=中间变量%每个库中表的数量 先计算出中间变量,库序号取整、表序号取模,都是针对单个库中表的数量。
-
分库与分表带来的分布式困境与应对之策(如何解决分布式下的分库分表,全局表?)
1.数据迁移与扩容问题 2.表关联的问题 3.分页与排序的问题 4.分布式事务的问题 5.分布式全局唯一ID 跨库Join的几种解决办法: 1.全局表:将这类表在其他每个数据库均保存一份 2.字段冗余:反范式,把需要的字段加到列中。 3.数据同步,关联的库同步了 4.数据组装
-
说说 SQL 优化之道
1.explain 查询 十万条数据分水岭 2.创建正确的索引 3.不使用默认配置 > 1 innodb_buffer_pool_size > 2 innodb_log_file_size 单个innodb日志文件大小 > 3 最大连接数 4 数据库转入到内存中 5 固态硬盘
-
MySQL遇到的死锁问题、如何排查与解决
死锁成因:两个事务相互等待 innodb_lock_wait_timeout wait-for graph:是否形成环形有向图、 1.2 innodb隔离级别、索引与锁 索引与锁的关系:无索引查询是全表扫描,二级索引会先锁住二级索引,接着会锁住相对应的主键对应的记录 3.一级索引会直接锁住那一行 隔离级别: 1.未提交读: 2.提交读: 3.重复读: 4.可串行读: 定位死锁:根据日志找到相应事务对应的sql,确定数据库的隔离级别,innodb的级别是RC,排除gar锁。 查看死锁的日志:show innodb status
-
存储引擎的 InnoDB与MyISAM区别,优缺点,使用场景
myISAM:不支持事务处理、外键和行级锁。执行大量的select
-
索引类别(B+树索引、全文索引、哈希索引)、索引的原理
常用的索引类型:主键索引、唯一索引、全文索引、普通索引和组合索引 常用的索引结构:b+树索引、哈希索引 索引的原理:
-
什么是自适应哈希索引(AHI)
InnoDB存储引擎会监控表上各索引页的查询。检测到如果检测到建立哈希索引带来更高的速度,就建立哈希索引,称之为自适应哈希索引。
-
为什么要用 B+tree作为MySQL索引的数据结构
1.文件很大,不可能全部存储在内存中,所以存储到磁盘里 2.索引的结构组织要尽量减少查到磁盘中I/O的存取次数 3.红黑树 比较深
-
聚集索引与非聚集索引的区别
-
遇到过索引失效的情况没,什么时候可能会出现,如何解决
1.or语句 2.like 以%开头的 3.多列索引,不是使用的第一部分 4.列类型是字符串,需要在条件中用引号引用起来
-
limit 20000 加载很慢怎么解决
1.order by 加索引 2.反向查找 超过一般,偏移逆转
-
如何选择合适的分布式主键方案
1.UUID 2.自然主键
-
选择合适的数据存储方案
-
常见的几种分布式ID的设计方案
1.Twitter的snowflake算法 2.利用zookeeper生成唯一ID 3.利用redis生成唯一ID
-
常见的数据库优化方案,在你的项目中数据库如何进行优化的
1.选择最有效率的表名顺序(笔试常考) 从右到左顺序处理 2.WHERE子句中的连接顺序 3.SELECT子句中避免使用*号 4.删除全表,利用Truncate 5.尽量多使用COMMIT 6.用WHERE子句替换HAVING子句 7.多使用内部函数提高SQL效率 8.使用别名
2.2、Redis
-
Redis 有哪些数据类型,可参考《Redis常见的5种不同的数据类型详解》
string(最大可以512M,文件图片都可以)、list、set、hash(key-value)、zset(不同的是每个元素都会关联一个double类型的分数)
-
Redis 内部结构
-
Redis 使用场景
1.为什么使用 解决应用服务器的cpu与内存压力 减少IO操作,减轻IO压力 关系型数据库扩展性不强,难以改变表结构 2.优点 nosql数据库没有关联关系,数据结构简单 读取速度快,对较大数据处理快 3.适用场景 数据高并发的读写 海量数据的读写 扩展性能高的数据 4.不适用的场景 需要事物支持 关系复杂,需要sql结构化查询存储的。 5使用场景 1.配合关系型数据库做高速缓存 缓存高频访问的数据,降低数据库io 分布式架构,做session缓存 2.可以持久化特定数据 利用zset类型可以存储排行榜 利用list的自然时间排序存储n个最新数据 redis基础知识:端口6379 默认16个数据库,下标从0开始, redis:单线程+io多路复用:检查文件描述的就绪状态 memchached:多线程+锁
-
Redis 持久化机制,可参考《使用快照和AOF将Redis数据持久化到硬盘中》
redis提供了两种不同方式的持久化:1.快照(RDB) 2.只追加文件(AOF) 一、创建快照的方式 (1)客户端向redis发送BGSAVE命令 save 60 1000(当60秒内有1000次写入操作时) (2)客户端向redis发送SAVE命令,会阻断其他命令,shutdown前调用 注意:可以考虑关闭自动快照,选择适当的时间通过命令保存 二、AOF持久化(将被执行的写命令写道AOF文件的末尾) appendonly yes:打开AOF appendfsync everysec(每秒执行一次)/always(每个命令都执行,会降低redis的速度)/no(系统决定) 三、重写/压缩AOF文件 BGREWRITEAOF 与BGSAVE一样会创建子线程 自动压缩文件条件:auto-aof-rewrite-percentage auto-aof-rewrite-min-size
-
Redis 集群方案与实现
Redis Cluster,阿里云redis
-
Redis 为什么是单线程的?
对于redis来说,cpu不是瓶颈,而是来自机器内存或者网络带宽
-
缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级
缓存雪崩:原有缓存时效,新缓存未到,大量请求到了数据库 (加锁排队,请求量不大的时候)(设置过期标志更新缓存)(为key设置不同的缓存失效时间) 缓存穿透:缓存没有、数据库也没有,两次无用查询 (bitmap:布隆过滤器)(对空结果进行缓存) 缓存预热:系统上线后,提前把相关的缓存数据加载到缓存系统中 缓存更新:定时清理 或者等用户请求 缓存降级:一般、警告、严重、严重错误
-
使用缓存的合理性问题
-
Redis常见的回收策略(6)
根据过期时间:
1.volatile-lru:淘汰最少使用的
2.volatile-ttl:淘汰即将要过期的
3.volatile-random:任意淘汰
从数据集
4.allkeys-lru:数据集中最少使用的
5.allkeys-random:数据集中任意淘汰6.no-envication:禁止驱逐
2.3、消息队列
-
消息队列的使用场景
主要解决:应用耦合、异步消息、流量削峰、日志等问题 使用场景:异步处理,应用解耦,流量削锋和消息通讯四个场景。
-
消息的重发补偿解决思路
处理失败:onMessageListener的onMessage方法抛出RuntimeException Message头里有两个相关字段:Redelivered默认false RedeliveryCounter默认0 消息先由broker发送给consumer,consumer调用listener,如果处理失败,本地redeliveryCounter++,给broker一个特定的应答,broker端的message里redeliveryCount++,延迟一点时间继续调用,默认1s。超过六次,给Broker另一个特定应答,broker直接发送消息到DLD 如果失败2次,consumer重启,redeliveryCount=2 本地再重试四次进入DLQ
-
消息的幂等性解决思路
上半场幂等:MQ-client没有收到消息回执,再次发送===> 对每条消息,MQ系统内部必须生成一个inner-msg-id,作为去重与幂等的依据, 1.全局唯一2.MQ生成,与业务无关 下半场幂等:MQ-server没有拿到ack,服务端超时后重发消息。===》保证业务幂等,业务消息体中必须由一个biz-id,1.对于同一个业务场景,全局唯一2.由业务消息发送方生成,业务相关,对mq透明。3.判重也是由业务消息方判断。
-
消息的堆积解决思路
消息发送的效率远远大于消费的效率。默认情况下rabbitmq消费者为单线程串行消费。可以通过设置并发消费提高消费的效率。 concurrentConsumers:设置并发消费者的个数 prefetchCount:每次一次性从broker里面取得待消费的个数
-
自己如何实现消息队列
-
如何保证消息的有序性
调用一个master的consumer,其他slave消费,利用zookeeper进行选主。 脑裂问题:zookeeper可以用来选主,但不能保证某一时间内只有一个主。exclusive consumer。全部消息会发给这个consumer,如果这个挂了,会自动选择另外一个
三、开源框架和容器
3.1、SSM/Servlet
-
Servlet的生命周期
生命周期:加载--->实例化--->服务--->销毁 init():只会执行一次init,在服务器装入servlet时进行,负责初始化servlet对象 service():是servlet的核心,负责响应客户的请求 ServletRequest ServletResponse destroy():仅执行一次,服务端停止且卸载Servlet时执行该方法。
-
转发与重定向的区别
1.转发是服务器行为,重定向是客户端行为, 2.重定向是两次request,传输的信息会丢失
-
BeanFactory 和 ApplicationContext 有什么区别
BeanFactory是比较原始的Factory,无法支持spring的很多插件,例如AOP功能,web应用。 ApplictionContext是由BeanFactory派生而来,以一种更面向框架的方式工作以及对上下文分层,实现继承。 ApplictionContext还提供以下功能: * MessageSource,提供国际化的消息访问(i18n) * 资源访问,url和文件 * 事件传播 * 载入多个上下文,使每一个上下文都专注于一个特定的层次。 1.利用MessagSource进行国际化(i18n) 2.强大的事件机制(Event)3.底层资源的访问(ResourceLoader)4.对web应用的支持 其他:BeanFactory是延迟加载的,这样不能及时发现问题 手动注册,而ApplicationContext在容器启动时,一次性创建所有的Bean 自动注册
-
Spring Bean 的生命周期
-----beanFactory----
* Bean的建立,由BeanFactory读取bean的定义文件,生成各个实例。
* Setter注入,执行Bean的属性依赖注入
-----beanFactory----* 实例化一个Bean new * 按照spring上下文对实例化的Bean进行配置,也就是IOC注入。 * BeanNameAware的setBeanName(),如果实现该接口,则执行setBeanName方法。 * BeanFactoryAware的setBeanFactory(),如果实现该接口,则执行setBeanFactory方法。 -----ApplicationContext---- * ApplicationContextAware接口,则执行其setApplicationContext()方法 -----ApplicationContext---- * BeanPostProcessor的processBeforeInitialization(),如果有关联的processor,则在初始化之前都会执行这个实例的processBeforeInitialization()方法。 * InitializingBean的afterPropertiesSet(),实现了该接口,则执行其afterPropertieSet()方法。 * Bean定义文件中定义init-method * BeanPostProcessors的processAfterInitialization(),如果有关联的processor,则在Bean初始化之前都会执行这个实例的processAfterInitialization()方法 * DisposableBean的destory(),在容器关闭时,如果bean类实现了该接口,则会执行destroy()方法 * Bean定义文件定义了destroy-method。在容器关闭时,可以在Bean定义文件中使用"destroy-method"定义的方法。
-
Spring IOC 如何实现
IOC是一种思想,而DI(依赖注入)是实现IoC的一种方法:将对象的创建转移给第三方。 实现方式:1.xml 2.注解 注入方式:1.构造方法注入 2.setter注入 3.接口注入
-
Spring中Bean的作用域,默认的是哪一个
spring中注入的bean默认是配置为单例模式,四种作用域: ConfigurableBeanFactory.SCOPE_xxx 1.singleton:单例 singleton 整个应用只创建一个实例 2.prototype: 原型 prototype 每次注入时都创建一个实例 3.session: 会话 session 为每次会话创建一个 4. request:请求 request 为每个请求创建一个实例 5.globalsession::每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效 单例对象需要注入请求对象,请求对象并没有被创建,此时会先注入一个代理对象,当这个对象被使用时,则会委托给真正的bean去完成。 <bean id="xxx" class=""xx.xxx.xxx" scope="session"> <aop:scoped-proxy/> </bean> <aop:scoped-proxy/> @Scope(value=WebApplicationContext.SCOPE_SESSION,proxyMode=ScopeProxyMode.INTERFACES)
-
说说 Spring AOP、Spring AOP 实现原理
面向切面编程,aop分为静态代理(AspectJ 编译时增强 在编译阶段将代码织入)与动态代理(Spring AOP) Spring AOP的动态方式有两种,JDK动态代理与CGLIB动态代理,JDK动态代理通过反射来接收InvocationHandler与Proxy。如果类被final标记,则无法使用CGLIB做动态代理
-
动态代理(CGLib 与 JDK)、优缺点、性能对比、如何选择
使用JDK动态代理,目标类必须实现某个接口,如果没有实现接口则无法生成代理对象。 CGLIB的原理是针对目标类生成一个子类,覆盖其中的方法,所以目标修不能被声明为final类型。 从执行效率上看CgLib动态代理效率更高。
-
Spring 事务实现方式、事务的传播机制、默认的事务类别
事物的配置有五种方式,spring中配置文件中关于事物的配置有三个部分组成。分别是DataSource、TransactionManager和代理机制三个部分,变化的只是代理机制这部分。 Spring配置文件=====>实现方式: 方式一:通过事务代理工厂bean进行配置 xml方式 1.引入一系列的约束头文件与标签 2.配置C3P0数据源,以及dao、service 3.配置事物管理器以及事物代理工厂bean.,测试类getBean获取代理工厂id 方式二:注解 <tx:annotation-driven transaction-manager-"mytx"/> @Transactional(rollbackFor=StockExption.class) 方式三:Aspecth AOP配置事物 <tx:advice id="txAdvice" transaction-manager="mytx"> <tx:attributes> <tx:method name="add* islation="DEFAULT" ..../> </tx:attributes> </tx:advice> <aop:config> </aop:config> 事物的传播机制:多个事物同时存在时,srping应该如何处理这些事物的行为(七个) (默认)PROPAGATION_REQUIRED:支持当前事物,如果当前没有事物,就创建一个事物,默认的事物的传播 PROPAGATION_REQUIRED_NEW:新建事物,如果当前事物存在,就将当前事物挂起,是两个独立的事物,外层事物失败回滚,不能回滚内层事物执行的结果,内存事物失败抛出异常,外层捕获异常,也可以不回滚。 PROPAGATION_SUPPORTS:支持当前事物,如果当前没有事物,就以非事物的方式执行。 PROPAGATION_MANDATORY:支持当前事物,如果没有则抛出异常 PROPAGATION_NOT_SUPPORTS:以非事物方式执行操作,如果当前存在事物,就把当前事物挂起。 PROPAGATION_NEVER:以非事物方式执行,如果当前存在事物,则抛出异常 PROPAGATION_NEWTES:如果一个活动的事物存在就,就运行在一个嵌套的事物中,如果没有事物,就按照默认模式执行. 七大事物传播行为的使用: 充值行为 不论扣款和创建订单只要有一个异常,不允许提交直接混滚:PROPAGATION_MANDATORY 不管充值是否成功,必须记录日志:PROPAGATION_REQUIRES_NEW 业务完成后需要统计,统计耗时长不适合事物:PROPAGATION_NEVER 办理银行卡,等级成功与否不影响卡片的创建:新增卡片的行为回滚,登记就没意义了,也需要回滚,但是登记的回滚不影响新增卡片的行为。所以保存银行卡的事物传播行为可以设置为PARPAGATION_REQUIRED,PARPAGATION_MANDATORY. Spring中的隔离级别: ISOLATION_DEFAULT:使用数据库默认的事物隔离级别 ISOLATION_READ_UNCOMMITTED: ISOLATION_READ_COMMITTED: ISOLATION_REPEATABLE_READ: ISOLATION_SERIALIZABLE:
-
Spring 事务底层原理
Spring的事务管理是基于TransactionManager
-
Spring事务失效(事务嵌套),JDK动态代理给Spring事务埋下的坑,可参考《JDK动态代理给Spring事务埋下的坑!》
-
如何自定义注解实现功能
元注解有四个:@Document @Targrt @Retention @Inherited
-
Spring MVC 运行流程
1.DispatchServlet前端控制器接收发过来的请求,交给handleMapping处理映射器。 2.HandleMapping处理映射器,根据请求路径找到相对于的HandlerAdapter处理适配器(拦截器或者)controller 3.HandleAdapter处理器适配器,处理一些功能请求,返回一个ModelAndView 4.ViewResolver视图解析器,根据modelAndView中的设置View解析具体视图, 5.将Model模型中的数据渲染到View上。
-
Spring MVC 启动流程 需要看的
SpringMVC启动过程一局两个配置大致分为两个过程 1.ContextLoaderListener初始化,实例化IoC容器,并将此容器实例注册到ServletContent中 2.DispatcherServlet初始化,建立上下文,也注册到ServletContext.
-
Spring 的单例实现原理
懒汉、饿汉、静态内部类、枚举,这些单例模式的构造方法都是私有的,不可继承,spring为实现单例类可继承,使用的是单例注册表。 1、使用map实现注册表 2.使用protected修饰构造方法
-
Spring 框架中用到了哪些设计模式
1.代理模式——在AOP和remoting中被使用的比较多,同样用到反射 2.单例模式——单例注册表,在配置文件中定义的bean默认为单例模式 3.模板方法——用来解决代码重复的问题,比如RestTemplate JmsTemplate 4.工厂方法——BeanFactory用来创建对象的实例。 5.适配器——spring aop 6.装饰器——spring data hashmapper 7.观察者——spring 时间驱动模型,applicationListener 8.回调—— spring ResourceLoadAware回调接口 9.策略模式
-
Spring 其他产品(Srping Boot、Spring Cloud、Spring Secuirity、Spring Data、Spring AMQP 等)
1.Srping Boot: 2.Spring Cloud: 3.Spring Secuirity: 4.Spring Data: 5.Spring AMQP:
-
有没有用到Spring Boot,Spring Boot的认识、原理
自动配置
-
MyBatis的原理
mybatis是什么: mybatis是一个优秀的持久层框架,对JDBC操作数据库的过程进行了封装,使开发者只需要关注sql本身,不去关注例如注册驱动,加载连接,得到statement ,处理结果集等过程。 mabatis通过xml或者注解的方式,将需要执行的各种sql语句配置起来,并通过Java对象与statement中的sql语句映射生成最终的sql语句,交给mybatis框架执行sql语句,并将结果映射Java对象返回 工作原理: mybatis通过配置文件创建sqlsessionFactory,根据配置文件来源于1.xml 2.注解 获取sqlSession。sqlSession包含了执行sql语句的所有方法。 可以通过sqlSession直接运行映射的sql语句,完成对数据的增删改查和事务的提交工作,用完之后关闭sqlsession. 工作流程: mapper接口: 接口的全类名是xml文件中namespace的值。 接口的方法名是xml文件中mapperstatement的id值。 接口中的方法的参数就是传递给sql的参数 mapper接口是没有实现类的,当调用一个方法时,接口的全类名定位一个配置文件,接口的方法名定位这个配置文件中的一个namestatement,所以mapper的方法名是不能 是不能被重载的, 因为mapperStatement的保存和寻找策略。 mapper接口的工作原理,mybatis会使用JDK动态代理接口创建proxy对象,代理对象会拦截接口中的防范,转而执行mapperstatment锁代表的sql语句,然后将捷酷封装返回。 mybatis解决的问题: 1.使用数据库连接池管理链接,避免频繁创建、关闭连接带来的性能资源问题。 2.xml管理sql语句,让Java代码与sql分离,代码更加容易维护。 3.条件参数,将Java对象颜射到sql语句,通过statement的parameterType定义输入的参数的类型。 4.将结果集封装成Java对象,通过statement的resyltType定义输出的类型。避免因为sql变化,对结果集厂里麻烦的问题。
3.2、Netty
-
为什么选择 Netty
-
说说业务中,Netty 的使用场景
-
原生的 NIO 在 JDK 1.7 版本存在 epoll bug
-
什么是TCP 粘包/拆包
-
TCP粘包/拆包的解决办法
-
Netty 线程模型
-
说说 Netty 的零拷贝
-
Netty 内部执行流程
-
Netty 重连实现
3.3、Tomcat
-
Tomcat的基础架构(Server、Service、Connector、Container)
-
Tomcat如何加载Servlet的
-
Pipeline-Valve机制
-
可参考:《四张图带你了解Tomcat系统架构!》
网友评论