美文网首页
2018-06-27

2018-06-27

作者: essential_note | 来源:发表于2018-06-27 06:22 被阅读0次

    一 基础篇

    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系统架构!》

    相关文章

      网友评论

          本文标题:2018-06-27

          本文链接:https://www.haomeiwen.com/subject/hxivyftx.html