java面试题(上)

 
1:  自我介绍
  自我介绍首先描述本身的基本状况,其次是描述本身的技术亮点,作过的亮点项目或产品。若是没有作过有技术亮点的事,天天都在作增删改查功能或重复性的工做,须要好好反思下,这样下去技术上没有多少增加。若是工做中就是作这个怎么办?能够考虑利用业余时间参与开源项目或本身作一些工具或框架。
2:  HashMap怎么解决Hash冲突的
  一、HashMap的数据结构是:数组Node[](桶)与链表Node中有next Node.(继承map.entity:hashcode,key,value,next)
  二、在put元素时,先根据key的hashCode从新计算出hash值,而后再根据hash值找出这个元素在数组中的位置,若是数组的此处已经存放了数据,那么这个元素会以链表的形式保存,新的在链头,旧的在链尾(经过hash,key对比来判别两个节点是否相同);
  三、若是数组的此处没有存放数据,则直接将该元素保存在数组的此位置上。
  四、大于增加因子或一个链表超过8时,经过resize扩充一倍容量,前者是为了扩充容量,后者是为了将链表上的值经过hash取模放到别的桶上去。
  五、当列表长度超过8时则将桶转为红黑树(hashmap:当链表的长度超过8且桶的大小超过64=4*16)。
   ps:
    二叉树:
      一、每一个节点最多只有两颗子树,即节点的度最大为2
      二、左子树和右子树是有顺序的,次序不能颠倒
      三、即便某个节点只有一个子树,也要区分左右子树
    二叉查询树(BST):
      一、节点的左子树所包含的节点的值都小于该节点的值。
      二、节点的右子树所包含的节点的值都大于等于该节点的值。
      三、左子树和右子树也都是二叉查询树
      四、优势:查询方便;缺点:以便会变瘸,如7-》6-》5-》4

 

 

    平衡二叉树:
      一、任何节点的左右子树的高度之差的绝对值最可能是1

 

    红黑树:一种近似平衡二叉查找树,在进行插入和删除时经过特定操做保持二叉树的平衡,从而得到更高的查找性能。
      一、节点是红色或者黑色
      二、跟节点是黑色的
      三、每一个叶子节点都是黑色的空节点
      四、每一个红色节点的两个子节点都是黑色(根到叶子,不存在两个连续的红色节点)
      五、从任一节点到该节点的每一个叶子节点的全部路径都包含相同数目的黑色节点
      六、经过着色和旋转(左旋/右旋)修正更新后的红黑树,插入的节点必定是红色的。
        a、若是父类是黑色则不影响
        b、若是父类是红色则要进行旋转并修改父类的颜色为黑色

 

 

    
3:  ConcurrentHashMap怎么解决线程安全
  一、Segment分段锁功能,每个Segment 都想的于小的hash table而且都有本身锁,只要修改再也不同一个段上就不会引发并发问题。
  二、Segment继承了ReentrantLock,会根据hash获取指定的分段锁。
  三、1.8的分段实现是直接将锁加在段中第一个元素上。
 
4:  常见的排序有没有了解过
排序方法        平均状况        最好状况        最坏状况        辅助空间        稳定性
冒泡排序         O(n^2)           O(n)              O(n^2)            O(1)                稳定
选择排序         O(n^2)          O(n^2)            O(n^2)            O(1)              不稳定
插入排序         O(n^2)           O(n)              O(n^2)            O(1)                稳定
希尔排序O(n*log(n))~O(n^2) O(n^1.3)       O(n^2)            O(1)              不稳定
堆排序          O(n*log(n))     O(n*log(n))    O(n*log(n))       O(1)              不稳定
归并排序       O(n*log(n))     O(n*log(n))    O(n*log(n))       O(n)                稳定
快速排序       O(n*log(n))     O(n*log(n))      O(n^2)            O(1)              不稳定
 
冒泡排序通过优化之后,最好时间复杂度能够达到O(n)。设置一个标志位,若是有一趟比较中没有发生任何交换,可提早结束,所以在正序状况下,时间复杂度为O(n)。
选择排序在最坏和最好状况下,都必须在剩余的序列中选择最小(大)的数,与已排好序的序列后一个位置元素作交换,依次最好和最坏时间复杂度均为O(n^2)。
插入排序是在把已排好序的序列的后一个元素插入到前面已排好序(须要选择合适的位置)的序列中,在正序状况下时间复杂度为O(n)。
堆是彻底二叉树,所以树的深度必定是log(n)+1,最好和最坏时间复杂度均为O(n*log(n))。
归并排序是将大数组分为两个小数组,依次递归,至关于二叉树,深度为log(n)+1,所以最好和最坏时间复杂度都是O(n*log(n))。
快速排序在正序或逆序状况下,每次划分只获得比上一次划分少一个记录的子序列,用递归树画出来,是一棵斜树,此时须要n-1次递归,且第i次划分要通过n-i次关键字比较才能找到第i个记录,所以时间复杂度是\sum_{i=1}^{n-1}(n-i)=n(n-1)/2,即O(n^2)。
 
5:  一堆基本有序的数组,用哪一种排序效率最高
  归并排序
 
6:  JDK1.6到JDK1.8 GC上面最大作了什么变化
  一、去除了方法区,将常量、静态变量移到了堆中;将元数据移到了本地缓存中,称元空间。
  二、原来每一个程序都有本身的永久代,如今移到本地缓存中,能够实现多个程序的共享,提升了内存的利用率。
  三、默认状况下,元空间的大小受内存限制。经过-XX:MetaspaceSize等设置大小
  好处:
    一、元空间能够动态增加了,再也不是固定的了。
    二、提升了内存的利用率。
    三、它由元空间虚拟机管理,和原来的不一样收集器回收相比,如今使用类加载器来判断过时的对象(类加载器标记不在存活则其加载的类也被释放)
   PS:元空间虚拟机负责元空间的分配,其采用的形式为组块分配。组块的大小因类加载器的类型而异。在元空间虚拟机中存在一个全局的空闲组块列表。当一个类加载器须要组块时,它就会从这个全局的组块列表中获取并维持一个本身的组块列表。当一个类加载器再也不存活,那么其持有的组块将会被释放,并返回给全局组块列表。类加载器持有的组块又会被分红多个块,每个块存储一个单元的元信息。组块中的块是线性分配(指针碰撞分配形式)。组块分配自内存映射区域。这些全局的虚拟内存映射区域以链表形式链接,一旦某个虚拟内存映射区域清空,这部份内存就会返回给操做系统
 
7:  CMS怎么进行垃圾收集的
  CMS(Concurrent Mark Sweep)是一种以获取最短回收停顿时间为目标的收集器,工做在老年代,基于“标记-清除”算法实现,整个过程分为如下4步:
  1. 初始标记:这个过程只是标记如下GC Roots可以直接关联的对象,可是仍然会Stop The World;
  2. 并发标记:进行GC Roots Tracing的过程,能够和用户线程一块儿工做。
  3. 从新标记:用于修正并发标记期间因为用户程序继续运行而致使标记产生变更的那部分记录,这个过程会暂停全部线程,但其停顿时间远比并发标记的时间短;
  4. 并发清理:能够和用户线程一块儿工做。
  CMS收集器的缺点:
  1. 对CPU资源比较敏感,在并发阶段,虽然不会致使用户线程停顿,可是会占用一部分线程资源,下降系统的总吞吐量。
  2. 没法处理浮动垃圾,在并发清理阶段,用户线程的运行依然会产生新的垃圾对象,这部分垃圾只能在下一次GC时收集。
  3. CMS是基于标记-清除算法实现的,意味着收集结束后会形成大量的内存碎片,可能致使出现老年代剩余空间很大,却没法找到足够大的连续空间分配当前对象,不得不提早触发一次Full GC。
 
  JDK1.5实现中,当老年代空间使用率达到68%时,就会触发CMS收集器,若是应用中老年代增加不是太快,能够经过-XX:CMSInitiatingOccupancyFraction参数提升触发百分比,从而下降内存回收次数提升系统性能。
  JDK1.6实现中,触发CMS收集器的阈值已经提高到92%,要是CMS运行期间预留的内存没法知足用户线程须要,会出现一次”Concurrent Mode Failure”失败,这是虚拟机会启动Serial Old收集器对老年代进行垃圾收集,固然,这样应用的停顿时间就更长了,因此这个阈值也不能设置的过高,若是致使了”Concurrent Mode Failure”失败,反而会下降性能,至于如何设置这个阈值,还得长时间的对老年代空间的使用状况进行监控。
 
 
8:  G1怎么进行垃圾收集的
  G1(Garbage First)是JDK1.7提供的一个工做在新生代和老年代的收集器,基于“标记-整理”算法实现,在收集结束后能够避免内存碎片问题。
  G1优势:
  1. 并行与并发:充分利用多CPU来缩短Stop The World的停顿时间;
  2. 分代收集:不须要其余收集配合就能够管理整个Java堆,采用不一样的方式处理新建的对象、已经存活一段时间和经历过屡次GC的对象获取更好的收集效果;
  3. 空间整合:与CMS的”标记-清除”算法不一样,G1在运行期间不会产生内存空间碎片,有利于应用的长时间运行,且分配大对象时,不会致使因为没法申请到足够大的连续内存而提早触发一次Full GC;
  4. 停顿预测:G1中能够创建可预测的停顿时间模型,能让使用者明确指定在M毫秒的时间片断内,消耗在垃圾收集上的时间不得超过N毫秒。
  

 

  使用G1收集器时,Java堆的内存布局与其余收集器有很大区别,整个Java堆会被划分为多个大小相等的独立区域Region,新生代和老年代再也不是物理隔离了,都是一部分Region(不须要连续)的集合。G1会跟踪各个Region的垃圾收集状况(回收空间大小和回收消耗的时间),维护一个优先列表,根据容许的收集时间,优先回收价值最大的Region,避免在整个Java堆上进行全区域的垃圾回收,确保了G1收集器能够在有限的时间内尽量收集更多的垃圾。
  不过问题来了:使用G1收集器,一个对象分配在某个Region中,能够和Java堆上任意的对象有引用关系,那么如何断定一个对象是否存活,是否须要扫描整个Java堆?其实这个问题在以前收集器中也存在,若是回收新生代的对象时,不得不一样时扫描老年代的话,会大大下降Minor GC的效率。
  针对这种状况,虚拟机提供了一个解决方案:G1收集器中Region之间的对象引用关系和其余收集器中新生代与老年代之间的对象引用关系被保存在Remenbered Set数据结构中,用来避免全堆扫描。G1中每一个Region都有一个对应的Remenbered Set(记录谁引用了它),当虚拟机发现程序对Reference类型的数据进行写操做时,会产生一个Write Barrier暂时中断写操做,检查Reference引用的对象是否处于相同的Region中,若是不是,则经过CardTable把相关引用信息记录到被引用对象所属Region的Remenbered Set中。
   PS:回收过程
    一、初始化标记:G1 GC 对根进行标记
    二、根区域扫描:在初始标记的存活区扫描对老年代的引用,并标记被引用的对象
    三、并发标记:在整个堆中查找可访问的(存活的)对象
    四、最终标记:和GMS的从新标记类似
    五、筛选回收:对个Region的回收价值和成本进行派去,根据用户所指望的GC停顿时间制定回收计划。
   参见:https://www.cnblogs.com/ASPNET2008/p/6496481.html
9:  G1相比于CMS有哪些优点
  一、回收算法不一样:与CMS的”标记-清除”算法不一样,G1能够看出是基于“复制”算法的,在运行期间不会产生内存空间碎片,有利于应用的长时间运行,且分配大对象时,不会致使因为没法申请到足够大的连续内存而提早触发一次Full GC;
 
 
10: 哪些状况会致使Full GC
   一、full gc会将对中全部垃圾对象清理掉,perm区的被卸载的classloader中加载的类的数据;它是相对minor gc来讲的,后者是eden去空间不足是触发,除了回收eden区不活动的对象,还会把老对象复制到old区。
      二、System.gc()方法的调用
      三、老年代代或永生区空间空间不足
      四、统计获得的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间
      五、CMS GC时出现promotion failed和concurrent mode failure
    a、promotion failed:在进行Minor GC时,Survivor Space放不下,对象只能放入老年代,而此时老年代也放不下形成的
    b、concurrent mode failure:执行CMS GC的过程当中同时业务线程将对象放入老年代,而此时老年代空间不足,或者在作Minor GC的时候,新生代Survivor空间放不下,须要放入老年代,而老年代也放不下而产生的
      六、堆中分配很大的对象
   ps:和full gc、minor gc 并列的还有major gc,它专门用来清理老年代
11: 新new的对象放在哪里
    一、对象内容和数组类型这些须要动态申请内存的存放在堆区。
    二、基本类型和对象引用存放栈区。
    三、堆区是在运行过程当中会变化的
 
12: 哪些东西放在栈区(虚拟机栈)
  一、栈区即虚拟机栈,是线程私有的。栈会为每一个线程建立一个栈帧,每一个方法从调用到执行完成的过程,就对应栈帧一个栈帧在虚拟机中的入栈到出栈的过程。
       二、栈帧中存储局部变量表,操做数栈,动态连接、方法出入口等信息。
    三、局部变量表存放基本数据类型、及对象引用类型和returnAddress类型(程序return后须要执行的指令引用,也是一种基本数据类型),这些数据也是操做过程当中动态加载进来的。
    四、操做数栈,当对两个值进行算术运算时要先加载到这个栈中,结果值也是先放到这,结束时才放到局部变量表中。
  五、动态连接:方法字节码的引用
  六、方法出入口信息
  七、栈区的内容都是建立时就能肯定的
  ps:
  1.寄存器:最快的存储区, 由编译器根据需求进行分配,咱们在程序中没法控制;
  1. 栈:存放基本类型 的变量数据和对象的引用,但对象自己不存放在栈中,而是存放在堆(new出来的对象)或者常量池中(字符串常量对象存放的常量池中),局部变量【注意:(方法中的局部变量使用final修饰后,放在堆中,而不是栈中)】
  2.堆:存放使用new建立的对象,全局变量
  3. 静态域(方法区):存放静态成员(static定义的)
  4. 常量池(方法区):字符串常量和基本类型常量(public static final)。有时,在嵌入式系统中,常量自己会和其余部分分割离开(因为版权等其余缘由),因此在这种状况下,能够选择将其放在ROM中 ;
  5. 非RAM存储:硬盘等永久存储空间
 
13: 双亲委派模型, 有什么好处
  全盘负责和双亲委托加载,原则:由下向上询问加载,由上向下尝试加载。
  分类:引导类加载器、扩展类加载器、系统类加载器、用户自定义类加载器;线程上下文加载器
  双亲委派模型的优势:
  一、构造一种加载顺序,防止不可信类扮演可信任的类。  
  二、类重用,避免重复加载
14: wait和sleep有什么区别
 
 一、来自不一样的类:,sleep来自Thread类,和wait来自Object类,且sleep是Thread的静态类方法。
 二、对锁的操做:最主要是sleep方法没有释放锁,而wait方法释放了锁。
 三、唤醒方式不一样:sleep本身醒了,wait须要同一个监视器上操做唤醒
 三、使用范围:wait,notify和notifyAll是操控对象监视器的,因此只能在同步控制方法或者同步控制块里面使用,而sleep能够在任何地方使用 
     synchronized(x){ 
        x.notify() 
       //或者wait() 
     }
 
15: 线程池几个参数
 
 
ThreadPoolExecutor mExecutor = new ThreadPoolExecutor(
                corePoolSize,// 核心线程数                          
                maximumPoolSize, // 最大线程数                          
                keepAliveTime, // 闲置线程存活时间                          
                TimeUnit.MILLISECONDS,// 时间单位                         
                new LinkedBlockingDeque<Runnable>(),// 线程队列                          
                Executors.defaultThreadFactory(),// 线程工厂                          
                new AbortPolicy()// 队列已满,并且当前线程数已经超过最大线程数时的异常处理策略                 
        );  

 

线程池任务执行流程:
  1. 当线程池小于corePoolSize时,新提交任务将建立一个新线程执行任务,即便此时线程池中存在空闲线程。
  2. 当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
  3. 当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会建立新线程执行任务
  4. 当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
  5. 当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
  6. 当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭
16: 怎么评估线程数大小 
  1、cup满载角度
    一、衡量标准有三个:cpu个数、等待时间、计算时间(主要是计算所在的比重P)、CPU的利用率、线程数T
    二、目的:防止CPU过载,也就是利用率超过100%
    三、使用的公式:T=C/P
    四、CUP计算时间:ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime(),计算时间能够经过执行CPU开始减结束时间得出
    五、等待时间:
      a、要预热省去加载类什么的时间(不计入统计范围),而后调用垃圾回收器
         b、固定时间范围内一直执行任务,最后拿中的事件减去CPU时间就是等待实现。但过程当中要考虑线程切换等耗时
    六、线程等待时间所占比例越高,须要越多线程。线程CPU时间所占比例越高,须要越少线程
  2、内存容量角度
    一、计算每一个人物的内存大小,Runtime.getRuntime().totalMemory()
    ps:CPU是受操做系统调用的,BIO只会形成线程阻塞。
17: 几个线程访问同一个东西,怎么保证安全
  1、若是都是读取:无所谓了
  2、若是一个写多个读
    一、volatile
    二、读写锁
  3、即读又写
    一、同步锁
    二、并发锁
  通常说来,确保线程安全的方法有这几个:竞争与原子操做、同步与锁、可重入、过分优化。
 
  18: Spring几个特色说下
  1--核心容器
    核心容器提供spring框架的基本功能,核心容器的主要组件是BeanFactory, 他是工厂模式的实现. 
    BeanFactory使用控制反转(IOC)模式将应用程序的配置和依赖性与实际的应用程序代码分开
  2--Spring上下文
    是一个配置文件,该配置文件向spring框架提供上下文信息
  3--Spring AOP
    经过配置管理特性,Spring AOP 模块直接将面向切面(方面)编程功能集成到spring框架中
  4--spring DAO
    JDBC DAO抽象层提供了有意义的已成层次结构, 可用该结构管理异常处理和不一样数据库抛出的错误信息,极大的下降了异常代码数量
  5--Spring ORM
    spring框架插入了若干个ORM框架, 从而提供了ORM的对象工具,其中包括了Hibernate, Mybatis
  6--Spring Web
    web上下文模块创建在应用程序上下文模块之上,为基于web的应用程序提供上下文
  7--Spring MVC
    该框架是一个全功能的构建web应用程序的MVC实现. 经过策略接口,MVC框架变成高度可配置的. MVC容纳了大量视图技术. 其中包括JSP、Velocity和POI
  Spring 框架的好处
    spring是最大的工厂
      spring负责业务逻辑组件的框架和生成, 并管理业务逻辑组件的生命周期 
    spring能够生产全部实例, 从控制器、 业务逻辑组件、 持久层组件
  Spring特色
    1--下降了组件之间的耦合性, 实现了软件各个层之间的解耦
    2--能够使用spring容器提供的服务, 如: 事务管理, 消息服务
    3--容器提供单例模式支持
    4--容器提供AOP技术, 利用它很容易实现权限拦截, 运行期监控
    5--容器提供了众多的辅助类, 能加快应用的开发(org.springframework.jdbc.core.JDBCTemplate 等)
    6--spring对主流的应用框架提供了集成支持, 例如: hibernate,JPA, Struts, Mybatis(IBatis)
    7--Spring属于低侵入式设计, 代码污染度极低
    8--独立于各类应用服务器
    9--spring的DI机制下降了业务对象替换的复杂性
    10--spring的高度开发性, 并不强制应用彻底依赖于spring, 开发者能够自由选择spring的部分或者所有
19: CGLib有没有了解过(CGlib是什么? )
  CGlib是一个强大的,高性能,高质量的Code生成类库。它能够在运行期扩展Java类与实现Java接口。 
  固然这些实际的功能是asm所提供的,asm又是什么?Java字节码操控框架,具体是什么你们能够上网查一查,毕竟咱们这里所要讨论的是cglib, 
  cglib就是封装了asm,简化了asm的操做,实现了在运行期动态生成新的class。 
  可能你们还感受不到它的强大,如今就告诉你。 
  实际上CGlib为spring aop提供了底层的一种实现;为hibernate使用cglib动态生成VO/PO (接口层对象)。 
  CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。一般能够使用Java的动态代理建立代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。
  CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的全部不是final的方法。在子类中采用方法拦截的技术拦截全部父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
  CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,由于它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
  CGLIB缺点:对于final方法,没法进行代理。
 
 20: Spring支持哪几种切片
  1.前置加强(@Before):org.springframework.aop.BeforeAdvice表明前置加强,由于Spring只支持方法级的加强,因此MethodBeforeAdvice是目前可的的前置加强,表示在目标方法执行前实施加强,而BeforeAdvice是为了未来版本扩展须要而定义的;  
  2.后置加强(@After):org.springframework.aop.AfterReturningAdvice表明后加强,表示在目标方法执行后实施加强;
    a、在方法执行以后,还没法获取返回结果
  3.环绕加强(@Around):org.aopalliance.intercept.MethodInterceptor表明环绕加强,表示在目标方法执行先后实施加强;
     a、其中能够决定是否调用原来的方法
     b、整个请求处理链中只会调用一次
     c、经常使用于打印日志、设置缓存
  4.返回加强(@AfterRunning):是返回通知,能够获取返回结果
  5.异常抛出加强(@AfterThrowing):org.springframework.aop.ThrowsAdvice表明抛出异常加强,表示在目标方法抛出异常后实施加强;
  6.引介加强:org.springframework.aop.InteoductionInterceptor表明引介加强,表示在目标类中添加一些新的方法和属性;
    a、新的类实现DelegatingIntroductionInterceptor的invoke接口(这类和原类能够彻底无关系)
    b、以下配置后applicationContext中获取getCar实例,则该对象便可转为car类型也可转为新定义类的类型
    c、1-5的都是目标方法范围内织入,而引介加强是直接在类级别上添加目标未实现的接口方法

 

  ps:
    a、这些加强接口都有一些方法,经过实现这些接口方法,在接口方法中这义横切逻辑,就能够将它们织入到目标类的方法的相应链接点的位置。
    b、加强执行顺序:环绕->前置->方法执行->环绕->后置->返回
 
21: SpringBoot和Spring有什么区别
  Spring Boot是最近这几年才火起来的,那么它到底与Spring有啥区别呢?
  想了解区别,其实就是Spring Boot提供了哪些特征:
  1. 直接运行:Spring Boot能够创建独立的Spring应用程序;
  2. 简化部署:内嵌了如Tomcat,Jetty和Undertow这样的容器,也就是说能够直接跑起来,用不着再作部署工做了。
  3. 简化配置:POM,提供的POM能够简化Maven的配置;XML,无需再像Spring那样搞一堆繁琐的xml文件的配置;
  4. 能够自动配置Spring;
  5. 提供了一些现有的功能,如量度工具,表单数据验证以及一些外部配置这样的一些第三方功能;
22: SpringBoot和Spring启动有什么区别
  一、IDEA启动 SpringBootApplication
  二、命令行启动首先将命令行位置跳转到当前项目的根目录下,再输入“mvn spring-boot:run”命令,初次操做maven须要下载插件等待几分钟。
  三、命令行编译为jar启动首先命令行在当前项目根目录运行编译命令“mvn install”,以后跳转到当前项目的target文件夹下(cd target)多出两个文件
 
 
spring
  1. 首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;
  2. 其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;
  3. 再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet能够配置多个,以最多见的DispatcherServlet为例,这个servlet其实是一个标准的前端控制器,用以转发、匹配、处理每一个servlet请求。DispatcherServlet上下文在初始化的时候会创建本身的IoC上下文,用以持有spring mvc相关的bean。在创建DispatcherServlet本身的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取以前的根上下文(即WebApplicationContext)做为本身上下文的parent上下文。有了这个parent上下文以后,再初始化本身持有的上下文。这个DispatcherServlet初始化本身上下文的工做在其initStrategies方法中能够看到,大概的工做就是初始化处理器映射、视图解析等。这个servlet本身持有的上下文默认实现类也是mlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是经过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每一个servlet就持有本身的上下文,即拥有本身独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。
 
23: Spring启动生命周期 
 
 
 
一、Spring IoC容器容许BeanFactoryPostProcessor在容器实例化任何bean以前读取bean的定义(配置元数据),并能够修改它。如hsf中根据配置构造bean。
二、BeanPostProcessor,咱们想在Spring容器中完成bean实例化、配置以及其余初始化方法先后要添加一些本身逻辑处理
   a、postProcessorBeforeInitailization方法是在bean实例化,依赖注入以后及自定义初始化方法(例如:配置文件中bean标签添加init-method属性指定Java类中初始化方法、@PostConstruct注解指定初始化方法(在BeanPostProcessor中执行),Java类实现InitailztingBean接口)以前调用
   b、后置处理器的postProcessorAfterInitailization方法是在bean实例化、依赖注入及自定义初始化方法以后调用
三、InstantiationAwareBeanPostProcessor是BeanPostProcessor的子接口,能够在Bean生命周期的另外两个时期提供扩展的回调接口,即实例化Bean以前(调用postProcessBeforeInstantiation方法)和实例化Bean以后(调用postProcessAfterInstantiation方法)
四、BeanNameAware获取bean的名字
五、BeanFactoryAware获取beanfactory的引用,beanfactory中通常用做调用getBean来当即实例化bean
六、InitializingBean 设置完依赖后调用
七、InstantiationAwareBeanPostProcessorAdapter是postProcessorBeforeInitailization的实现适配类,咱们通常使用adapter对其来扩展。
八、Constructor > @PostConstruct > InitializingBean > init-method ,反过来也同样,beanPostProcessor中执行@PreDestroy
九、BeanFactoryAware后有个ApplicationContextAware
相关链接:
https://www.cnblogs.com/zrtqsk/p/3735273.html
https://www.cnblogs.com/april-chen/p/8182631.html
 
24: Spring注解@Resource和@Autowired区别对比  => 优先级不同
  一、@Autowired与@Resource均可以用来装配bean. 均可以写在字段上,或写在setter方法上。 
  二、@Autowired默认按类型装配(这个注解属于Spring),默认状况下必需要求依赖对象必须存在,若是要容许null 值,能够设置它的required属性为false,如:@Autowired(required=false) ,若是咱们想使用名称装配能够结合@Qualifier注解进行使用,以下: 
  @Autowired() @Qualifier("baseDao")     
  private BaseDao baseDao;    
   三、@Resource(这个注解属于J2EE),默认按照名称进行装配,名称能够经过name属性进行指定, 
  若是没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,若是注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。可是须要注意的是,若是name属性一旦指定,就只会按照名称进行装配。
  @Resource(name="baseDao")     
  private BaseDao baseDao;    
  我喜欢用 @Resource注解在字段上,且这个注解是属于J2EE的,减小了与spring的耦合。最重要的这样代码看起就比较优雅。
 
 
25: spring @service @controller @componet 三者区别
  @controller用来定义控制层的组件 
       @service用来定义业务层的组件
       @repository用来定义持久层的组件
       @ component用来定义不在上述范围内的通常性组件
上面组件的名称默认是类名的首字母小写,若是要重命名,则这样@controller("beanName")
当在spring中配置了<context:annotation-config/> 和<context:component-scan base-package="*">时,上述四种注解的组件都会由spring容器来建立为bean并由本身来管理.
那么建立了上面这些组件后,又是如何来注入的呢,这时就由@autowired来配置了。
只须要在private的属性上加上@autowired就能够自动把接口属性的实现类的bean注入,注意不须要setter,getter方法
上面若是一个接口属性有两个实现类,怎么办,这时就要用@qualifier来特别说明要注入哪一个bean了。
 
     26: Http和Https协议有什么区别,证书了解不
  HTTPS和HTTP的区别:
    a、定义:
      http:是超文本传输协议,是客户端和服务端请求和应答的标准,过程当中明文传输。
      http:是安全版的http,以安全传输为目标,具体就是http中加入ssl层,机密传输。
    b、具体使用:
      http使用简单,http则须要身份认证,须要用到数字证书
      http默认使用80端口,https则使用443
    c、证书
       是一串身份信息按照指定的格式排列后,包含证书颁发机构进行数字签名的数据。常见的数字证书格  式X.509 V3,存储到文件上通常一种为不含私钥的DER格式,以cer或者crt为文件后缀名,另外一种为包含私钥的PKCS格式,以pfx或者p12为后缀。
       数字证书中包括(不是所有):
    (1)数字证书持有者身份信息 网站就包括所对应的URL或IP
    (2)该证书拥有者对应的该证书公钥
    (3)证书颁发者信息
    (4)证书颁发者用私钥对证书持有者身份信息和公钥的签名 使用的摘要和签名算法
   PS:
    优势
      https可认证用户和服务,确保发送正确客户机和服务器
      https协议能够确保数据传输过程当中的安全性和完整性。
      https构建的网站在各大搜索引擎中排名更高
    缺点:
      增长资源消耗:https协议握手阶段比较费时,页面加载时间延长50%,增长10%到20%的耗电。
      增长成本:ssl证书须要钱,ssl证书须要绑定IP
    握手:(4步)
      一、浏览器说“你好,我是浏览器”,将直接的加密算法和一个随机值发送服务器。
      二、服务器说“你好,我是服务器”,从客户端提供的机密算法中选择一种,并添加一个本身生成的随机值和本身的证书一块儿发送浏览器端。此阶段服务端能够要求客户端提供证书。
      三、客户端收到证书后,先从ca校验该证书的合法性,并从证书中取出服务端公钥,而后生成一个随机值,用服务端公钥加密后传给服务端。
      四、服务端使用私钥对这个随机值进行解密,而后将三次的随机值拼到一块儿做为之后沟通的加密密钥。
      五、此时的客户端也会将三次随机值拼成一个密钥。
     使用:
      一、org.springframework.web.client.RestTemplate 封装了一个http/https的请求操做       
      二、先构造一个SSLContext,其中设置相信策略TrustStrategy默认是true.       
      三、构造一个SSL的链接工厂:SSLConnectionSocketFactory
      四、将注册到注册中心中(Registry),并基于此构建一个客户端连池(PoolingHttpClientConnectionManager)
      五、构造一个org.springframework.http.client.ClientHttpRequestFactory
      六、将请求工厂注入到resTemplate中
    27: 介绍下Redis设计实现
Redis是基于内存、可持久化的日志型、Key-Value 数据库 高性能存储系统,
 
  
 
 
    28: Redis的细节源码看过没有
1. 支持5种数据结构
支持strings, hashes, lists, sets, sorted sets 
string是很好的存储方式,用来作计数存储。sets用于创建索引库很是棒;
2. K-V 存储 vs K-V 缓存
新浪微博目前使用的98%都是持久化的应用,2%的是缓存,用到了600+服务器 
Redis中持久化的应用和非持久化的方式不会差异很大: 
非持久化的为8-9万tps,那么持久化在7-8万tps左右; 
当使用持久化时,须要考虑到持久化和写性能的配比,也就是要考虑redis使用的内存大小和硬盘写的速率的比例计算;
3. 社区活跃
Redis目前有3万多行代码, 代码写的精简,有不少巧妙的实现,做者有技术洁癖 
Redis的社区活跃度很高,这是衡量开源软件质量的重要指标,开源软件的初期通常都没有商业技术服务支持,若是没有活跃社区作支撑,一旦发生问题都无处求救;
Redis基本原理
redis持久化(aof) append online file: 
写log(aof), 到必定程度再和内存合并. 追加再追加, 顺序写磁盘, 对性能影响很是小
1. 单实例单进程
Redis使用的是单进程,因此在配置时,一个实例只会用到一个CPU; 
在配置时,若是须要让CPU使用率最大化,能够配置Redis实例数对应CPU数, Redis实例数对应端口数(8核Cpu, 8个实例, 8个端口), 以提升并发: 
单机测试时, 单条数据在200字节, 测试的结果为8~9万tps;
2. Replication
过程: 数据写到master–>master存储到slave的rdb中–>slave加载rdb到内存。 
存储点(save point): 当网络中断了, 连上以后, 继续传. 
Master-slave下第一次同步是全传,后面是增量同步;、
3. 数据一致性
长期运行后多个结点之间存在不一致的可能性; 
开发两个工具程序: 
1.对于数据量大的数据,会周期性的全量检查; 
2.实时的检查增量数据,是否具备一致性;
对于主库未及时同步从库致使的不一致,称之为延时问题; 
对于一致性要求不是那么严格的场景,咱们只须要要保证最终一致性便可; 
对于延时问题,须要根据业务场景特色分析,从应用层面增长策略来解决这个问题; 
例如: 
1.新注册的用户,必须先查询主库; 
2.注册成功以后,须要等待3s以后跳转,后台此时就是在作数据同步。
    29: Redis分布式缓存
1.架构设计
因为redis是单点,项目中须要使用,必须本身实现分布式。基本架构图以下所示:
 
 
2.分布式实现
经过key作一致性哈希,实现key对应redis结点的分布。
一致性哈希的实现:
l        hash值计算:经过支持MD5与MurmurHash两种计算方式,默认是采用MurmurHash,高效的hash计算。
l        一致性的实现:经过java的TreeMap来模拟环状结构,实现均匀分布
3.client的选择
对于jedis修改的主要是分区模块的修改,使其支持了跟据BufferKey进行分区,跟据不一样的redis结点信息,能够初始化不一样的ShardInfo,同时也修改了JedisPool的底层实现,使其链接pool池支持跟据key,value的构造方法,跟据不一样ShardInfos,建立不一样的jedis链接客户端,达到分区的效果,供应用层调用
4.模块的说明
l        脏数据处理模块,处理失败执行的缓存操做。
l        屏蔽监控模块,对于jedis操做的异常监控,当某结点出现异常可控制redis结点的切除等操做。
整个分布式模块经过hornetq,来切除异常redis结点。对于新结点的增长,也能够经过reload方法实现增长。(此模块对于新增结点也能够很方便实现)
对于以上分布式架构的实现知足了项目的需求。另外使用中对于一些比较重要用途的缓存数据能够单独设置一些redis结点,设定特定的优先级。另外对于缓存接口的设计,也能够跟据需求,实现基本接口与一些特殊逻辑接口。对于cas相关操做,以及一些事物操做能够经过其watch机制来实现。(参考我之前写的 redis事物介绍
    30: 线程在频繁的Full GC 怎么排查
咱们知道Full GC的触发条件大体状况有如下几种状况: 
1. 程序执行了System.gc() //建议jvm执行fullgc,并不必定会执行 
2. 执行了jmap -histo:live pid命令 //这个会当即触发fullgc 
3. 在执行minor gc的时候进行的一系列检查
a、执行Minor GC的时候,JVM会检查老年代中最大连续可用空间是否大于了当前新生代全部对象的总大小。b、若是大于,则直接执行Minor GC(这个时候执行是没有风险的)。c、若是小于了,JVM会检查是否开启了空间分配担保机制,若是没有开启则直接改成执行Full GC。d、若是开启了,则JVM会检查老年代中最大连续可用空间是否大于了历次晋升到老年代中的平均大小,若是小于则执行改成执行Full GC。e、若是大于则会执行Minor GC,若是Minor GC执行失败则会执行Full GC
a、使用了大对象 //大对象会直接进入老年代
b、在程序中长期持有了对象的引用 //对象年龄达到指定阈值也会进入老年代
对于咱们的状况,能够初步排除a,b两种状况,最有多是d和e这两种状况。为了进一步排查缘由,咱们在线上开启了 -XX:+HeapDumpBeforeFullGC。
JVM在执行dump操做的时候是会发生stop the word事件的,也就是说此时全部的用户线程都会暂停运行。 为了在此期间也能对外正常提供服务,建议采用分布式部署,并采用合适的负载均衡算法
 
 
1)FULL GC先后Java堆大小有变化;经研究发现是因为Java应用JVM参数XMS设置为默认值,在咱们的系统环境下,Hotspot的Xms默认值为50M(-Xms默认是物理内存的1/64);每次GC时,JVM会根据各类条件调节Java堆的大小,Java堆的取值范围为[Xms, Xmx]。根据以上分析,修改Xms值与Xmx相等,这样就不会由于所使用的Java堆不够用而进行调节,通过测试后发现FULL GC次数从四位数减小至个位数。
 
2)关键词“System”让我想到了System.gc调用,System.gc调用只是建议JVM执行年老代GC,而年老代GC触发FULL GC,JVM会根据系统条件决定是否执行FULL GC,正由于系统条件很差判断,因此很难构造System.gc调用触发FULL GC,几经周折终于成功,当System.gc触发FULL  GC时都会有关键词“(System)”,而 JVM自动触发的FULL GC却不带关键词“(System)”,能够判定是Java应用存在“System.gc”代码。通过本次测试我也发现System.gc的真正含义,通俗言之,“System.gc” 就是FULL GC触发的最后一根稻草。 
 
从本次分析中,咱们能够得出以下的经验: 
1)Java应用的jvm参数Xms与Xmx保持一致,避免因所使用的Java堆内存不够致使频繁full gc以及full gc中因动态调节Java堆大小而耗费延长其周期。 
2)建议不要调用System.gc或者Runtime.getRuntime().gc,不然本次调用可能会成为“压死骆驼的最后一根稻草”。固然咱们能够经过设置jvm参数禁止这种调用生效,可是除非特别有把握该参数有必要添加,不然不推荐这么设置。
    31: JVM一些工具,jps, jmap
 -v 输出传递给JVM的参数
$> jps -v
23789 BossMain
28802 Jps -Denv.class.path=/data/aoxj/bossbi/twsecurity/java/trustwork140.jar:/data/aoxj/bossbi/twsecurity/java/:/data/aoxj/bossbi/twsecurity/java/twcmcc.jar:/data/aoxj/jdk15/lib/rt.jar:/data/aoxj/jdk15/lib/tools.jar -Dapplication.home=/data/aoxj/jdk15 -Xms8m
23651 Resin -Xss1m -Dresin.home=/data/aoxj/resin -Dserver.root=/data/aoxj/resin -Djava.util.logging.manager=com.caucho.log.LogManagerImpl -Djavax.management.builder.initial=com.caucho.jmx.MBeanServerBuilderImpl
 
jmap
JVM Memory Map命令用于生成heap dump文件,若是不使用这个命令,还能够使用-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM的时候自动生成dump文件。 jmap不只能生成dump文件,还能够查询finalize执行队列、Java堆和永久代的详细信息,如当前使用率、当前使用的是哪一种收集器等。【内存分析】
  
 
32:  海量日志数据,提取出某日访问百度次数最多的那个IP?

此题,在我以前的一篇文章算法里头有所提到,当时给出的方案是:IP的数目仍是有限的,最多2^32个,因此能够考虑使用hash将ip直接存入内存,而后进行统计。html

  再详细介绍下此方案:首先是这一天,而且是访问百度的日志中的IP取出来,逐个写入到一个大文件中。注意到IP是32位的,最多有个2^32个IP。一样能够采用映射的方法,好比模1000,把整个大文件映射为1000个小文件,再找出每一个小文中出现频率最大的IP(能够采用hash_map进行频率统计,而后再找出频率最大的几个)及相应的频率。而后再在这1000个最大的IP中,找出那个频率最大的IP,即为所求。  前端

   
34:  垃圾回收的原理
     优势:a.不须要考虑内存管理, b.能够有效的防止内存泄漏,有效的利用可以使用的内存, c.因为有垃圾回收机制,Java中的对象再也不有"做用域"的概念,只有对象的引用才有"做用域"
     原理:垃圾回收器是做为一个单独的低级别的线程运行,在不可知的状况下对内存堆中已死亡的或者长期没有使用的对象回收,可是不能实时的对某一对象或者全部对象进行垃圾回收。
     垃圾回收机制:分代复制垃圾回收、标记垃圾回收、增量垃圾回收
    35:  你写过Java的Web系统
 
    36:  简单介绍一下你的项目
 
    37:  两个有序的数组,合成一个有序的数组,怎么合并效率高 
归并排序
    38:  淘宝的登录页面,怎么保证他安全

使用哈希加盐法来为密码加密
      解决的办法是将密码加密后再存储进数据库,比较经常使用的加密方法是使用哈希函数(Hash Function)。哈希函数的具体定义,你们能够在网上或者相关书籍中查阅到,简单地说,它的特性以下:  
     (1)原始密码经哈希函数计算后获得一个哈希值  
     (2)改变原始密码,哈希函数计算出的哈希值也会相应改变 
     (3) 一样的密码,哈希值也是相同的 
     (4) 哈希函数是单向、不可逆的。也就是说从哈希值,你没法推算出原始的密码是多少 
    最简单、常见的破解方式当属字典破解(Dictionary Attack)和暴力破解(Brute Force Attack)方式。这两种方法说白了就是猜密码。

    字典破解和暴力破解都是效率比较低的破解方式。若是你知道了数据库中密码的哈希值,你就能够采用一种更高效的破解方式,查表法(Lookup Tables)。还有一些方法,好比逆向查表法(Reverse Look     up Tables)、彩虹表(Rainbow Tables)等,都和查表法大同小异。如今咱们来看一下查表法的原理。java

     查表法不像字典破解和暴力破解那样猜密码,它首先将一些比较经常使用的密码的哈希值算好,而后创建一张表,固然密码越多,这张表就越大。当你知道某个密码的哈希值时,你只须要在你创建好的表中查找       该哈希值,若是找到了,你就知道对应的密码了。node

      从上面的查表法能够看出,即使是将原始密码加密后的哈希值存储在数据库中依然是不够安全的。那么有什么好的办法来解决这个问题呢?答案是加盐。mysql

      盐(Salt)是什么?就是一个随机生成的字符串。咱们将盐与原始密码链接(concat)在一块儿(放在前面或后面均可以),而后将concat后的字符串加密。采用这种方式加密密码,查表法就不灵了(由于盐是随机生成的)。 nginx

     单单使用哈希函数来为密码加密是不够的,须要为密码加盐来提升安全性,盐的长度不能太短,而且盐的产生应该是随机的。git

 
    39:  你有最新半年用户的订单,天天的用户订单量有上亿,预测下将来一周哪些商品最容易被购买
数据建模-分析
   40: 你有啥问题
“入职后有没有培训活动?”
“公司对个人指望是什么?”
“这个部门或团队有多少人?主要是负责哪方面的?”
 
一、synchronized关键字原理?
    原理:synchronized底层是经过一个monitor的对象阻塞和获取。
    对代码同步:指令执行时,monitor的进入数减1,若是减1后进入数为0,那线程退出monitor,再也不是这个monitor的全部者。其余被这个monitor阻塞的线程能够尝试去获取这个 monitor 的全部权。 
    对方法同步:常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,若是设置了,执行线程将先获取monitor,获取成功以后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其余任何线程都没法再得到同一个monitor对象。
    重量级锁:Mutex Lock 监视器锁monitor本质就是依赖于底层的操做系统的Mutex Lock来实现的。
    
 
二、hashMap底层实现。
  • HashMap 的基本组成成员
        首先,HashMap 是 Map 的一个实现类,它表明的是一种键值对的数据存储形式。Key 不容许重复出现,Value 随意。jdk 8 以前,其内部是由数组+链表来实现的,而 jdk 8 对于链表长度超过 8 的链表将转储为红黑树。大体的数据存储形式以下:
    
  从上图中能够看出,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。
   源码以下:
Java代码  
  1. /** 
  2.  * The table, resized as necessary. Length MUST Always be a power of two. 
  3.  */  
  4. transient Entry[] table;  
  5.   
  6. static class Entry<K,V> implements Map.Entry<K,V> {  
  7.     final K key;  
  8.     V value;  
  9.     Entry<K,V> next;  
  10.     final int hash;  
  11.     ……  
  12. }  
   能够看出,Entry就是数组中的元素,每一个 Map.Entry 其实就是一个key-value对,它持有一个指向下一个元素的引用,这就构成了链表。
 
  • put 方法的具体实现
Java代码  
  1. public V put(K key, V value) {  
  2.     // HashMap容许存放null键和null值。  
  3.     // 当key为null时,调用putForNullKey方法,将value放置在数组第一个位置。  
  4.     if (key == null)  
  5.         return putForNullKey(value);  
  6.     // 根据key的keyCode从新计算hash值。  
  7.     int hash = hash(key.hashCode());  
  8.     // 搜索指定hash值在对应table中的索引。  
  9.     int i = indexFor(hash, table.length);  
  10.     // 若是 i 索引处的 Entry 不为 null,经过循环不断遍历 e 元素的下一个元素。  
  11.     for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
  12.         Object k;  
  13.         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
  14.             V oldValue = e.value;  
  15.             e.value = value;  
  16.             e.recordAccess(this);  
  17.             return oldValue;  
  18.         }  
  19.     }  
  20.     // 若是i索引处的Entry为null,代表此处尚未Entry。  
  21.     modCount++;  
  22.     // 将key、value添加到i索引处。  
  23.     addEntry(hash, key, value, i);  
  24.     return null;  
  25. }  
      当咱们往HashMap中put元素的时候,先根据key的hashCode从新计算hash值,根据hash值获得这个元素在数组中的位置(即下标),若是数组该位置上已经存放有其余元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最早加入的放在链尾。若是数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。
    
    
 
  • remove 方法的具体实现。
        采用迭代器遍历,不只适用于HashMap,对其它类型的容器一样适用。
   采用这种方法的遍历,能够用下文说起的方式安全地对HashMap内的元素进行修改,并不会对后续的删除操做形成影响。
  若是使用foreach遍历方法删除HashMap中的元素,Java颇有可能会在运行时抛出异常。
    为何呢?
    一、使用iterator迭代删除时没有问题的,在每一次迭代时都会调用hasNext()方法判断是否有下一个,是容许集合中数据增长和减小的。
    二、使用forEach删除时,会报错ConcurrentModificationException,由于在forEach遍历时,是不容许map元素进行删除和增长。
    因此,遍历删除map集合中元素时,必须使用迭代iterator
 
 
 
 
  • 为何时HashMap的容量老是2的n次方?
    若是不是2的2次幂,空间浪费至关大,更糟的是这种状况中,数组能够使用的位置比数组长度小了不少,这意味着进一步增长了碰撞的概率,减慢了查询的效率!而当数组长度为16时,即为2的n次方时,2n-1获得的二进制数的每一个位上的值都为1,这使得在低位上&时,获得的和原hash的低位相同,加之hash(int h)方法对key的hashCode的进一步优化,加入了高位计算,就使得只有相同的hash值的两个值才会被放到数组中的同一个位置上造成链表。
    
    
    
 
  • 其余一些基本方法的基本介绍
 
   HashMap 包含以下几个构造器:
   HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。
   HashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap。
   HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子建立一个 HashMap。
   HashMap的基础构造器HashMap(int initialCapacity, float loadFactor)带有两个参数,它们是初始容量initialCapacity和加载因子loadFactor。
   initialCapacity:HashMap的最大容量,即为底层数组的长度。
   loadFactor:负载因子loadFactor定义为:散列表的实际元素数目(n)/ 散列表的容量(m)。
 
 
  • 什么是红黑树
红黑树本质上是一种二叉查找树,但它在二叉查找树的基础上额外添加了一个标记(颜色),同时具备必定的规则。这些规则使红黑树保证了一种平衡,插入、删除、查找的最坏时间复杂度都为 O(logn)。
 
 
 
三、TCP与UDP的区别
  一、基于链接与无链接
  二、TCP要求系统资源较多,UDP较少; 
  三、UDP程序结构较简单 
  四、流模式(TCP)与数据报模式(UDP); 
  五、TCP保证数据正确性,UDP可能丢包 
  六、TCP保证数据顺序,UDP不保证 
 
 
四、TCP三次握手说一下。
 
简单说,让双方都证明对方能发收。
知道对方能收是由于收到对方的由于收到而发的回应。
具体:
1:A发,B收, B知道A能发
2:B发,A收, A知道B能发收
3:A发,B收, B知道A能收
 
五、看你项目用到线程池,说一下线程池工做原理,任务拒接策略有哪几种?
一个线程从被提交(submit)到执行共经历如下流程:
  • 线程池判断核心线程池里是的线程是否都在执行任务,若是不是,则建立一个新的工做线程来执行任务。若是核心线程池里的线程都在执行任务,则进入下一个流程
  • 线程池判断工做队列是否已满。若是工做队列没有满,则将新提交的任务储存在这个工做队列里。若是工做队列满了,则进入下一个流程。
  • 线程池判断其内部线程是否都处于工做状态。若是没有,则建立一个新的工做线程来执行任务。若是已满了,则交给饱和策略来处理这个任务。
 
任务拒接策略?
 有4种内置的实现策略和一个用户自定义拒绝策略。
AbortPolicy       为java线程池默认的阻塞策略,不执行此任务,并且直接抛出一个运行时异常,切记ThreadPoolExecutor.execute须要try catch,不然程序会直接退出。 
DiscardPolicy        直接抛弃,任务不执行,空方法 。
DiscardOldestPolicy   从队 列里面抛弃head的一个任务,并再次execute 此task。
CallerRunsPolicy        在调用execute的线程里面执行此command,会阻塞入口 。 
用户自定义拒绝策略   实现RejectedExecutionHandler,并本身定义策略模式。
 
再次须要注意的是,ThreadPoolExecutor.submit() 函数,此方法内部调用的execute方法,并把execute执行完后的结果给返回,但若是任务并无执行的话(被拒绝了),则submit返回的future.get()会一直等到。
future 内部其实仍是一个runnable,并把command给封装了下,当command执行完后,future会返回一个值。
 
 
六、进程和线程的区别?
进程和线程的主要差异在于它们是不一样的操做系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不一样执行路径。线程有本身的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,因此多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行而且又要共享某些变量的并发操做,只能用线程,不能用进程。
 
1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
2) 线程的划分尺度小于进程,使得多线程程序的并发性高。
3) 另外,进程在执行过程当中拥有独立的内存单元,而多个线程共享内存,从而极大地提升了程序的运行效率。
4) 线程在执行过程当中与进程仍是有区别的。每一个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。可是线程不可以独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分能够同时执行。但操做系统并无将多个线程看作多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
 
 
七、ArrayList与LinkedList的区别?
  一、ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 (LinkedList是双向链表,有next也有previous)
  二、对于随机访问get和set,ArrayList以为优于LinkedList,由于LinkedList要移动指针。 
  三、对于新增和删除操做add和remove,LinedList比较占优点,由于ArrayList要移动数据。 
 
 
 
八、线程安全与非线程安全集合说一下,底层怎么实现的(hashmap,concurrenthashmap)?
     Hashmap本质是数组加链表。根据key取得hash值,而后计算出数组下标,若是多个key对应到同一个下标,就用链表串起来,新插入的在前面。
     ConcurrentHashMap:在hashMap的基础上,ConcurrentHashMap将数据分为多个segment,默认16个(concurrency level),而后每次操做对一个segment加锁,避免多线程锁的概率,提升并发效率。
    
 
九、Hashtable、ConcurrentHashMap、TreeMap、HashMap的key,value都是不为空的吗?
 
HashMap的key和value都容许为空,treeMap的value容许为空。
 
十、单例模式  
  •  有几种实现方式 ?
        5种  
1.饿汉模式(调用效率高,可是不能延时加载):
2.懒汉模式(调用效率不高,可是能延时加载):
3.双重检测锁模式(因为JVM底层模型缘由,偶尔会出问题,不建议使用):
4.静态内部类式(线程安全,调用效率高,能够延时加载):
5.枚举类(线程安全,调用效率高,不能延时加载,能够自然的防止反射和反序列化调用):
如何选用:
-单例对象 占用资源少,不须要延时加载,枚举 好于 饿汉
-单例对象 占用资源多,须要延时加载,静态内部类 好于 懒汉式 
 
  • 单例模线程安全吗?  
  不安全 。
  • 通常如何保证它线程安全 ?  
      double-check 双重检查锁定  。
  • 修饰符 volatile  有什么做用 ?
        能保证被它修饰的成员变量能够被多个线程正确的处理。Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另一个线程能读到这个修改的值。它在某些状况下比synchronized的开销更小
 
十一、 判断一个字符在一个字符串中出现的次数  ?
  StringUtils.countMatches(str, t);
 
十二、HashMap是否是有序的?
 
   不是有序的.
 
  • 有没有有顺序的Map实现类? 
有TreeMap和LinkedHashMap。
 
  • TreeMap和LinkedHashMap是如何保证它的顺序的?
          LinkedHashMap 是根据元素增长或者访问的前后顺序进行排序,而 TreeMap是基于元素的固有顺序 (由 Comparator 或者 Comparable 肯定)。
                                 
 
  • 哪一个的有序实现比较好?
      TreeMap TreeMap则实现了 SortedMap 接口。          
  • 你以为还有没有比它更好或者更高效的实现方式?
           参照TreeMap的value排序,咱们同样的也能够实现HashMap的排序。
 
1三、实现全部的线程一块儿等待某个事件的发生,当某个事件发生时,全部线程一块儿开始往下执行的话,有什么好的办法吗?
 
栅栏(Java的并发包中的CyclicBarrier)  CountDownLatch  CyclicBarrier  Semaphore
  • CountDownLatch (N个线程数量count减为0 主程序或一组程序开始执行)
        CountDownLatch是一个计数器闭锁,主要的功能就是经过await()方法来阻塞住当前线程,而后等待计数器减小到0了,再唤起这些线程继续执行。 
        这个类里主要有两个方法,一个是向下减计数器的方法:countdown(),若是取得当前的状态为0,说明这个锁已经结束,直接返回false;
        若是没有结束,而后去设置计数器减1,若是compareAndSetState不成功,则继续循环执行。 而其中的一直等待计数器归零的方法是await()。 
  • CyclicBarrier(N个线程,他们之间任何一个没有完成,全部的线程都必须等待) 
        CyclicBarrier是加计数方式,计数达到构造方法中参数指定的值时释放全部等待的线程。
 
  • Semaphore(Semaphore 是只容许必定数量的线程同时执行一段任务。)
            Semaphore,每次semaphore.acquire(),获取一个资源,每次semaphore.acquire(n),获取n个资源,
            当达到semaphore 指定资源数量时就不能再访问线程处于阻塞,必须等其它线程释放资源,semaphore.relase()每次资源一个资源,
            semaphore.relase(n)每次资源n个资源。
            
你知道它的实现原理吗?
继续问,你还知道其它的实现方式吗?
 
继续问,你以为这些方式里哪一个方式更好?
 
若是让你来写的话,你以为还有比它更好的实现方式吗?
 
1四、IO包和NIO包 熟悉吗?
  • NIO模型 其中的selector 职责和实现原理
传统的socket IO中,须要为每一个链接建立一个线程,当并发的链接数量很是巨大时,线程所占用的栈内存和CPU线程切换的开销将很是巨大。使用NIO,再也不须要为每一个线程建立单独的线程,能够用一个含有限数量线程的线程池,甚至一个线程来为任意数量的链接服务。因为线程数量小于链接数量,因此每一个线程进行IO操做时就不能阻塞,若是阻塞的话,有些链接就得不处处理,NIO提供了这种非阻塞的能力。
一、增长了一个角色,要有一个专门负责收集客人需求的人。NIO里对应的就是Selector。
二、由阻塞服务方式改成非阻塞服务了,客人吃着的时候服务员不用一直侯在客人旁边了。传统的IO操做,好比read(),当没有数据可读的时候,线程一直阻塞被占用,直到数据到来。NIO中没有数据可读时,read()会当即返回0,线程不会阻塞。
     NIO中,客户端建立一个链接后,先要将链接注册到Selector,至关于客人进入餐厅后,告诉前台你要用餐,前台会告诉你你的桌号是几号,而后你就可能到那张桌子坐下了,SelectionKey就是桌号。当某一桌须要服务时,前台就记录哪一桌须要什么服务,好比1号桌要点菜,2号桌要结账,服务员从前台取一条记录,根据记录提供服务,完了再来取下一条。这样服务的时间就被最有效的利用起来了。
  • NIO的核心是什么 (Selector)
Selector类是NIO的核心类,Selector可以检测多个注册的通道上是否有事件发生,若是有事件发生,便获取事件而后针对每一个事件进行相应的响应处理。这样一来,只是用一个单线程就能够管理多个通道,也就是管理多个链接。这样使得只有在链接真正有读写事件发生时,才会调用函数来进行读写,就大大地减小了系统开销,而且没必要为每一个链接都建立一个线程,不用去维护多个线程,而且避免了多线程之间的上下文切换致使的开销。
  与Selector有关的一个关键类是SelectionKey,一个SelectionKey表示一个到达的事件,这2个类构成了服务端处理业务的关键逻辑。
 
1五、虚拟机JVM 组成部分
    
程序计数器
指示当前程序执行到了哪一行,执行JAVA方法时纪录正在执行的虚拟机字节码指令地址;执行本地方法时,计数器值为undefined
虚拟机栈
用于执行JAVA方法。栈帧存储局部变量表、操做数栈、动态连接、方法返回地址和一些额外的附加信息。程序执行时栈帧入栈;执行完成后栈帧出栈
本地方法栈
用于执行本地方法,其它和虚拟机栈相似
着重说一下虚拟机栈中的局部变量表,里面存放了三个信息:
  • 各类基本数据类型(boolean、byte、char、short、int、float、long、double)
  • 对象引用(reference)
  • returnAddress地址
这个returnAddress和程序计数器有什么区别?前者是指示JVM的指令执行到哪一行,后者则是你的代码执行到哪一行。
私有内存区伴随着线程的产生而产生,一旦线程停止,私有内存区也会自动消除,所以讨论的内存回收主要是针对共享内存区。
JAVA堆
既然GC主要发生在堆内存中,这部分咱们会对堆内存进行比较详细的描述。
堆内存是由存活和死亡的对象组成的。存活的对象是应用能够访问的,不会被垃圾回收。死亡的对象是应用不可访问尚且尚未被垃圾收集器回收掉的对象。一直到垃圾收集器把这些对象回收掉以前,他们会一直占据堆内存空间。堆是应用程序在运行期请求操做系统分配给本身的向高地址扩展的数据结构,是不连续的内存区域。用一句话总结堆的做用:程序运行时动态申请某个大小的内存空间。 
 
新生代:刚刚新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1。S0和Eden被清空,而后下一轮S0与S1交换角色,如此循环往复。若是对象的复制次数达到16次,该对象就会被送到老年代中。
  • 为何新生代要分出两个survivor区?
        设置两个Survivor区最大的好处就是解决了碎片化
老年代:若是某个对象经历了几回垃圾回收以后还存活,就会被存放到老年代中。老年代的空间通常比新生代大。
 
GC名称
介绍
Minor GC
发生在新生代,频率高,速度快(大部分对象活不过一次Minor GC)
Major GC
发生在老年代,速度慢
Full GC
清理整个堆空间
  • java中垃圾回收机制?
JAVA 并无给咱们提供明确的代码来标注一块内存并将其回收。或许你会说,咱们能够将相关对象设为 null 或者用 System.gc()。然而,后者将会严重影响代码的性能,由于通常每一次显式的调用 system.gc() 都会中止全部响应,去检查内存中是否有可回收的对象。这会对程序的正常运行形成极大的威胁。另外,调用该方法并不能保证 JVM 当即进行垃圾回收,仅仅是通知 JVM 要进行垃圾回收了,具体回收与否彻底由 JVM 决定。这样作是费力不讨好。
 
  • 垃圾回收算法概述
    一、追踪回收算法(tracing collector) 
    从根结点开始遍历对象的应用图。同时标记遍历到的对象。遍历完成后,没有被标记的对象就是目前未被引用,能够被回收。
    二、压缩回收算法(Compacting Collector) 
    把堆中活动的对象集中移动到堆的一端,就会在堆的另外一端流出很大的空闲区域。这种处理简化了消除碎片的工做,但可能带来性能的损失。
    三、复制回收算法(Coping Collector) 
把堆均分红两个大小相同的区域,只使用其中的一个区域,直到该区域消耗完。此时垃圾回收器终端程序的执行,经过遍历把全部活动的对象复制到另外一个区域,复制过程当中它们是紧挨着布置的,这样也能够达到消除内存碎片的目的。复制结束后程序会继续运行,直到该区域被用完。 
可是,这种方法有两个缺陷:
对于指定大小的堆,须要两倍大小的内存空间,
须要中断正在执行的程序,下降了执行效率
    四、按代回收算法(Generational Collector) 
    为何要按代进行回收?这是由于不一样对象生命周期不一样,每次回收都要遍历全部存活对象,对于整个堆内存进行回收无疑浪费了大量时间,对症下药能够提升垃圾回收的效率。主要思路是:把堆分红若搞个子堆,每一个子堆视为一代,算法在运行的过程当中优先收集“年幼”的对象,若是某个对象通过屡次回收仍然“存活”,就移动到高一级的堆,减小对其扫描次数。
  •     垃圾回收器有哪些?
    串行回收器(serial collector)
    并行回收器
    CMS回收器
    G1回收器
    
    
    JAVA性能优化
    真正影响JAVA程序性能的,就是碎片化。碎片是JAVA堆内存中的空闲空间,多是TLAB剩余空间,也多是被释放掉的具备较长生命周期的小对象占用的空间。
  1. 减小new对象。每次new对象以后,都要开辟新的内存空间。这些对象不被引用以后,还要回收掉。所以,若是最大限度地合理重用对象,或者使用基本数据类型替代对象,都有助于节省内存;
  2. 多使用局部变量,减小使用静态变量。局部变量被建立在栈中,存取速度快。静态变量则是在堆内存;
  3. 避免使用finalize,该方法会给GC增添很大的负担;
  4. 若是是单线程,尽可能使用非多线程安全的,由于线程安全来自于同步机制,同步机制会下降性能。例如,单线程程序,能使用HashMap,就不要用HashTable。同理,尽可能减小使用synchronized
  5. 用移位符号替代乘除号。eg:a*8应该写做a<<3
  6. 对于常常反复使用的对象使用缓存;
  7. 尽可能使用基本类型而不是包装类型,尽可能使用一维数组而不是二维数组;
  8. 尽可能使用final修饰符,final表示不可修改,访问效率高
  9. 单线程状况下(或者是针对于局部变量),字符串尽可能使用StringBuilder,比StringBuffer要快;
  10. String为何慢?由于String 是不可变的对象, 所以在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,而后将指针指向新的 String 对象。若是不能保证线程安全,尽可能使用StringBuffer来链接字符串。这里须要注意的是,StringBuffer的默认缓存容量是16个字符,若是超过16,apend方法调用私有的expandCapacity()方法,来保证足够的缓存容量。所以,若是能够预设StringBuffer的容量,避免append再去扩展容量。若是能够保证线程安全,就是用StringBuilder。
 
 
 
1六、ArrayList遍历时正确删除元素?
 
删除元素请使用Iterator方式,若是并发操做,须要对Iterator对象加锁。
 
1七、对一个List的进行subList后,原有list进行增、删、改,再操做subList会怎样?
 
     子 List 的元素和原 List 中的后一部分是重合的, 而子 List 还在遍历过程当中时, 向原 List 中新增元素, 这样给子 List 的遍历过程形成了干扰甚至困扰, 因而就抛出了并发修改异常将会抛出java.util.ConcurrentModificationException
 
 
1八、web应用安全问题?
    一、跨站脚本攻击(CSS or XSS, Cross Site Scripting) 
     方案:输入或输出时对其进行字符过滤或转义处理。
  2、SQL注入攻击(SQL injection)
方案:输入输出都是过滤、合法性检查和长度限制等通用方法。
  3、远程命令执行(Code execution,我的以为译成代码执行并不确切) 
     方案:严格限制运行Web服务的用户权限。
  4、目录遍历(Directory traversal) 
    方案:一、一样是限制Web应用在服务器上的运行  2、进行严格的输入验证,控制用户输入非法路径。
  5、文件包含(File inclusion)
    方案:对文件来源进行审查
  6、脚本代码暴露(Script source code disclosure) 
  7、Http请求头的额外的回车换行符注入(CRLF injection/HTTP response splitting)
  8、跨帧脚本攻击(Cross Frame Scripting)
  9、PHP代码注入(PHP code injection)
  十、XPath injection
  十一、Cookie篡改(Cookie manipulation)
  十二、URL重定向(URL redirection)
  1三、Blind SQL/XPath injection for numeric/String inputs
  1四、Google Hacking
1五、表单、AJAX提交必须执行CSRF安全过滤。
1六、URL外部重定向传入的目标地址必须执行白名单过滤。
 
1九、简单介绍下spring的ioc和aop?
  •  控制反转(Inversion of Control,英文缩写为IOC);
    ioc就是典型的工厂模式,经过sessionfactory去注入实例。依赖注入    。
  本身实现用什么方式?    反射原理  
其实就是经过解析xml文件,经过反射建立出咱们所须要的bean,再将这些bean挨个放到集合中,而后对外提供一个getBean()方法,以便咱们得到这bean。
通俗来说就如同婚姻介绍所,只须要告诉它找个什么样的女友,而后婚介就会按照咱们的要求,提供一个mm,若是婚介给咱们的人选不符合要求,咱们就会抛出异常。
 
  •  面向切面编程(Aspect Oriented Programming,英文缩写为AOP)
    AOP就是典型的代理模式的体现。 实现拦截器  日志 统一权限校验 。
    spring的IoC容器是spring的核心,spring AOP是spring框架的重要组成部分。
    
 实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法建立“方面”,从而使得编译器能够在编译期间织入有关“方面”的代码.简单点解释,比方说你想在你的biz层全部类中都加上一个打印‘你好’的功能,这时就能够用aop思想来作.你先写个类写个类方法,方法经实现打印‘你好’,而后Ioc这个类 ref=“biz.*”让每一个类都注入便可实现。
 
20、并发问题 
丢失更新  用户A把6改为2  用户B把2改为6  则用户A丢失了更新
脏读问题  用户A,B 看到的都是6  用户B把6改成2  则用户A读的值仍然是6
 
2一、乐观锁  悲观锁
 
悲观  屏蔽一切违反数据操做完整性
乐观  只是在提交的时候检查是否违反数据完整性
 
2二、sql优化
    • 复杂sql避免模糊匹配
    • 索引问题 惟一索引  和普通索引
    • 复杂操做
    • 在能够使用UNION ALL的语句里,使用了UNION  
    • 字段长度小于5000用varchar,超过用TEXT,独立一张表,用主键来对应。
    • 在varchar字段上创建索引时,必须指定索引长度,不必对全字段创建索引。
 
2四、介绍下使用的持久层框架? 为何要选择这个(有什么好处)?
MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎全部的JDBC代码和参数的手工设置以及结果集的检索。
MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
 
2五、mybatis  $和#的区别
  一、效果来看:SELECT * FROM USER WHERE ID = #{id} /${id} ,从效果上来看均可以替换成功,只不过前置默认会把传入的参数当成字符串处理(它还能够经过,jdbcType=Number等指明类型),后者只是简单的替换(不加‘’号)。
  二、概念上:${}是一个动态SQL编译,#{}是预编译
  三、功能上:#{}由于是预编译,因此能够防止SQL注入,但${}不能。
  四、使用上:#{}经常使用来处理参数传入,${}用来处理动态SQL构造,如select * from ${} 
  五、原则:尽量的使用#{}
  PS:
  1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,若是传入的值是111,那么解析成sql时的值为order by "111", 若是传入的值是id,则解析成的sql为order by "id". 
        2. $将传入的数据直接显示生成在sql中。如:order by $user_id$,若是传入的值是111,那么解析成sql时的值为order by user_id,  若是传入的值是id,则解析成的sql为order by id. 
        3. #方式可以很大程度防止sql注入。 
        4.$方式没法防止Sql注入。 
        5.$方式通常用于传入数据库对象,例如传入表名.  
        6.通常能用#的就别用$. 
2六、HashMap和Hashtable有什么区别?
     HashMap和Hashtable都实现了Map接口,所以不少特性很是类似。可是,他们有如下不一样点:
     HashMap容许键和值是null,而Hashtable不容许键或者值是null。
     Hashtable是同步的,而HashMap不是。所以,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
     HashMap提供了可供应用迭代的键的集合,所以,HashMap是快速失败的。另外一方面,Hashtable提供了对键的列举(Enumeration)。
     通常认为Hashtable是一个遗留的类。
2七、hashCode()和equals()方法的重要性体如今什么地方?
 
    经过hashCode和equals方法保证元素的惟一性,当重写equals方法时,必须重写hashCode方法,由于若是不重写这两个方法,就会默认使用Object的方法,通常是不相同的,因此就会致使存储了重复值,与hashset、hashmap等性质冲突。
 
 
2八、Vector、ArrayList和LinkedList有什么区别?
 
ArrayList和LinkedList都实现了List接口,他们有如下的不一样点:
 
ArrayList是基于索引的数据接口,它的底层是数组。它能够以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每个元素都和它的前一个和后一个元素连接在一块儿,在这种状况下,查找某个元素的时间复杂度是O(n)。
 
相对于ArrayList,LinkedList的插入,添加,删除操做速度更快,由于当元素被添加到集合任意位置的时候,不须要像数组那样从新计算大小或者是更新索引。
 
LinkedList比ArrayList更占内存,由于LinkedList为每个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。
 
也能够参考ArrayList vs. LinkedList。 
 
 
2九、数据库事务及隔离级别说一下。
  •     数据库事务的几个特性:
    原子性(Atomicity )、一致性( Consistency )、隔离性或独立性( Isolation)和持久性(Durabilily),简称就是ACID。
  •     数据库事务怎么保证一致性?
    数据库进行任何写入操做的时候都是要先写日志的,一样的道理,咱们在执行事务的时候数据库首先会记录下这个事务的redo操做日志,而后才开始真正操做数据库,
    在操做以前,首先会把日志文件写入磁盘,那么当忽然断电的时候,即便操做没有完成,在从新启动数据库时候,数据库会根据当前数据的状况进行undo回滚或者是redo前滚,
    这样就保证了数据的强一致性。
  •     数据库隔离级别:
    ① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
  ② Repeatable read (可重复读):可避免脏读、不可重复读的发生。
  ③ Read committed (读已提交):可避免脏读的发生。
  ④ Read uncommitted (读未提交):最低级别,任何状况都没法保证。
 
30、synchronized和lock区别,可重入锁与非可重入锁的区别
  •     synchronized和lock区别:
  1. Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
  2. synchronized在发生异常时,会自动释放线程占有的锁,所以不会致使死锁现象发生;而Lock在发生异常时,若是没有主动经过unLock()去释放锁,则极可能形成死锁现象,所以使用Lock时须要在finally块中释放锁;
  3. Lock可让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不可以响应中断;
  4. 经过Lock能够知道有没有成功获取锁,而synchronized却没法办到。
  5. Lock能够提升多个线程进行读操做的效率。
  6. 在性能上来讲,若是竞争资源不激烈,二者的性能是差很少的,而当竞争资源很是激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。因此说,在具体使用时要根据适当状况选择。
  •     可重入锁与非可重入锁的区别:
     可重入锁,也叫作递归锁,指的是同一线程 外层函数得到锁以后 ,内层递归函数仍然有获取该锁的代码,但不受影响。
在JAVA环境下 ReentrantLock 和synchronized 都是 可重入锁。可重入锁最大的做用是避免死锁。
 
 
3一、aop代理模式
AOP 全称 Aspect Oriented Programming,面向切面编程,和 OOP 同样也是一种编程思想。AOP 出现的缘由是为了解决 OOP 在处理 侵入性业务上的不足。
代理模式分为静态代理和动态代理两种。
静态代理:一般用于对原有业务逻辑的扩充。建立一个代理类实现和方法相同的方法,经过让代理类持有真实对象,而后在原代码中调用代理类方法,来达到添加咱们须要业务逻辑的目的。
动态代理:动态代理底层是使用反射实现的,是在程序运行期间动态的建立接口的实现。
 
3二、jdk1.8新特性
1. 速度更快 – 红黑树 
HashMap中的红黑树
HashMap中链长度大于8时采起红黑树的结构存储。
红黑树,除了添加,效率高于链表结构。
 
2. 代码更少 – Lambda 
    • Lambda表达式的基础语法:Java8引入了一个新的操做符“->”,该操做符成为箭头操做符或者Lambda操做符,箭头操做符将Lambda表达式拆分红两部分
    • 左侧:Lambda表达式的参数列表 
    • 右侧:Lambda表达式中所需执行的功能,即Lambda体。
3. 强大的Stream API – Stream 
一系列流水线式的中间操做。
流是数据渠道,用于操做数据源(集合、数组等)所生成的元素序列。
注意: 
①Stream本身不会存储元素。 
②Stream不会改变源对象。相反,会返回持有新结果的新Stream。 
③Stream操做是延迟执行的。这意味着他们会等到须要结果的时候才执行。
 
4. 便于并行 – Parallel 
        在必要的状况下,将一个大任务进行必要的拆分Fork成若干个小任务,再将小任务的运算结果进行Join汇总。
5. 最大化减小空指针异常 – Optional
         是一个容器类,表明一个值存在或不存在,原来用null 表示一个值不存在,如今Optional 能够更好的表达这个概念。而且能够避免空指针异常。
六、ConcurrentHashMap
    • Jdk1.7时隔壁级别CocnurrentLevel(锁分段机制)默认为16。
    • JDK1.8采起了CAS算法
    • Jdk1.8没有永久区,取而代之的是MetaSpace元空间,用的是物理内存。
 
3三、java的4种引用 强软弱虚
   强引用        
    a、new一个对象,强引用不会被GC回收。
    b、默认的就是强引用
  软引用(SoftReference)、
    一、内存不足的时候会被回收
    二、会影响对象的回收

 

  弱引用(WeakReference)        
    一、当引用的对象被回收时,reference的引用为null
    二、它不影响对象的回收

 

  弱引用与软引用的区别在于:
    只具备弱引用的对象拥有更短暂的生命周期。        
    在垃圾回收器线程扫描它所管辖的内存区域的过程当中,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存。
  虚引用(PhantomReference)        
    虚引用主要用来跟踪对象被垃圾回收器回收的活动。
  虚引用与软引用和弱引用的一个区别在于:
    虚引用必须和引用队列 (ReferenceQueue)联合使用。        
    当垃圾回收器准备回收一个对象时,若是发现它还有虚引用,就会在回收对象的内存以前,把这个虚引用加入到与之关联的引用队列中
  PS:
    一、在对应引用A设置成null调用gc时并不会立刻回收,如有对A对象的引用则更不会回收了
    二、这些引用会保存着对象设置时的值,因此并非只持有引用。
    三、threadlocal中的entity就是使用了weakreference
 
3四、分布式服务 解决了哪些问题,本身设计一个分布式框架 会用到哪些技术?
        须要拆分应用进行服务化,以提升开发效率,调优性能,节省关键竞争资源
        当服务愈来愈多时,服务的URL地址信息就会爆炸式增加,配置管理变得很是困难,F5硬件负载均衡器的单点压力也愈来愈大。 
        当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪一个应用要在哪一个应用以前启动,架构师都不能完整的描述应用的架构关系。 
        接着,服务的调用量愈来愈大,服务的容量问题就暴露出来,这个服务须要多少机器支撑?何时该加机器?等等…
            
        用到哪些技术: dubbo(分布式框架), zookeeper(开源的分布式协调服务) ,redis(缓存), ssdb, nsq, nginx(负载均衡), Kafka,hessian ,RPC,netty。
            
3五、Zookeeper服务的注册和发现?
        1. init获取Zookeeper的服务注册信息,并缓存在service_repos
        2. get_service_repos方法获取实例变量service_repos
        3. get_service_endpoint根据init构建好的service_repos,以及lb_strategy提供的负载均衡策略返回某个服务的URL地址
        4. update_service_repos经过Zookeeper的Watcher机制来实时更新本地缓存service_repos
        5. heartbeat_monitor是一个心跳检测线程,用来进行服务提供者的健康存活检测,若是出现问题,将该服务提供者从该服务的提供者列表中移除;
        反之,则加入到服务的提供者列表中LoadBalanceStrategy定义了根据服务提供者的信息返回对应的服务Host和IP,即决定由那台主机+端口来提供服务。
 
3六、主流的分布式框架?
 dubbo,dubbox, spring-cloudfinaglethrift
 
3七、redis的原理 和存储结构   持久化和非持久
 
    Redis存储机制分红两种Snapshot和AOF。不管是那种机制,Redis都是将数据存储在内存中。
    Snapshot工做原理: 是将数据先存储在内存,而后当数据累计达到某些设定的伐值的时候,就会触发一次DUMP操做,将变化的数据一次性写入数据文件(RDB文件)。
    AOF 工做原理: 是将数据也是先存在内存,可是在存储的时候会使用调用fsync来完成对本次写操做的日志记录,这个日志揭露文件实际上是一个基于Redis网络交互协议的文本文件。
    
3八、类加载过程
JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面咱们就分别来看一下这五个过程。
 
1. 虚拟机在首次加载Java类时,会对静态代码块、静态成员变量、静态方法进行一次初始化(静态间按顺序执行)。
2. 只有在调用new方法时才会建立类的实例。
3. 类实例建立过程:父子继承关系,先父类再子类。父类的静态->子类的静态->父类的初始化块->父类的构造方法->子类的初始化块->子类的构造方法
4. 类实例销毁时候:首先销毁子类部分,再销毁父类部分。
 PS:
  一、加载:类加载器加载类文件
  二、验证:确保class文件结构没有被串改
  三、准备:为类的讲台变量分配内存,将其初始化默认值。
  四、解析:常量池内的符号引用转为直接引用,将法中对其余方法的应用缓存方法区中对内存地址的引用。
 
 
3九、String,StringBuffer,StringBuilder有什么不一样?
  一、线程安全方面:stringbuffer是线程安全的,其余都不是。
  二、速度:stringbuilder是最快的。
  三、内存空间:string每次操做都会建立一个新对象(另string常量池优化),其余都是可变类(有缓存会自增容量)
40、String和StringBuffer的实现?
4一、Volatile关键字做用?除了保证数据可见性,还有其余什么使用方式?  
  一、做用主要有两个:保持内存的可见性,防止指令重排序
  二、保持内存可见性:经过集中原子操做完成工做内存和主内存的交互 
    a、unlock:做用于主内存,解除独占状态。
    b、read:做用主内存,把一个变量的值从主内存传输到线程的工做内存。
    c、load:做用于工做内存,把read操做传过来的变量值放入工做内存的变量副本中。
    d、use:做用工做内存,把工做内存当中的一个变量值传给执行引擎。
    e、assign:做用工做内存,把一个从执行引擎接收到的值赋值给工做内存的变量。
    f、store:做用于工做内存的变量,把工做内存的一个变量的值传送到主内存中。
    g、write:做用于主内存的变量,把store操做传来的变量的值放入主内存的变量中。
    h、lock:做用于主内存,把变量标识为线程独占状态。
    valatile的特殊规则:
      a、read、load、use动做必须连续出现 :每次读取前必须先从主内存刷新最新的值。
      b、assign、store、write动做必须连续出现 : 每次写入后必须当即同步回主内存当中。
  三、防止指令重排序:编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理重排序。
    a、valitile写操做:storestore 写操做 storeload
    b、volatile读操做: loadload 读操做 loadstore
   PS:
    一、内存屏障:解决指令重排序问题,多CPU的情形下能够强制同步cpu中缓存不一致的状况。
    二、缓存一致性原则:
    三、锁、内存屏障与一致性
      a、只有一个cpu是,cup只会从本身的缓存中读取数据,加入缓存丢失,从主内存读取数据到缓存中,最终内存数据都是一致的。
      b、多核状况下,每一个CPU都有本身的缓存,须要读到最新数据就要解决某CPU更新了缓存端但未写回内存,其余cpu看不到的问题。
      c、此时就引入了缓存一致性协议(做用域缓存行):
        modifiled(该cup已经独占了该缓存段,并作了修改,其余核心读取时,此数据必须刷入主存),
        exclusive(数据已经读入cacheline,而且只有该CPU拥有它,能够直接修改数据)
        shared(多个cpu共享某内存的数据,当cpu须要修改数据时,须要提交RFO请求获取数据的独占全,即进入exclusive状态才能进行修改)
        invalid(share 状态下,当前cpu赞成了其余cpu申请写的时候,变成该状态)
        即:当前cpu起始状态(shared),发起rfo,(被接收后当前cpu变成exclusive,其余接收的cpu变成invalid),当前cpu修改了数据就变成modified(这个状态要求读取当前缓存段前,当前数据必须同步到主存)
      d、问题:若是当前cpu忙,没接收别的cpu的RFO,则那个发起请求的cpu就无事可作了,这就下降了性能。
      e、为了解决上述问题,添加了两个相似缓存的东西,Store buffer和invalidate queue。
        这样当cpu须要写缓存行时就将写指令丢入storebuff,去干别的事,等RFO受到回应时,该指令才执行。
        cup收到其余核心的RFO指令后,会当即回应但相应的失效操做指令(较忙时)会放入到invalidate queue中
      f、这套机制实现了异步,带了性能提高的同时也带来了问题,在发去RFO请求的cpu执行写时(先读store buffer,再读缓存)只对当前CPU可见(其余cpu可能没有执行本身invalidate queue中的失效指令)。
      g、为了解决这个问题,引入了读写屏障。写屏障保证写屏障前在全部store buffer的指令都真正的写入到缓存,读屏障保证在读屏障前全部invalidate queue中全部的无效化指令都执行。这保证了不一样核心上,缓存的强同步。
      h、在锁的实现上,通常lock都会加入读屏障,保证后续代码能够读到别的cpu未回写的缓存数据( 应该是作缓存失效吧?),而unlock会加入写屏障,将全部未回写的缓存回写( 确保到modified状态吧)。
    四、内存屏障保证缓存的一致性
  详参:http://gocode.cc/project/9/article/128
4二、ThreadLocal作什么的?如何使用?源码是如何实现的?get()方法?  
  一、并发背景下,经过将相关对象封闭到执行线程中来解决并发问题,是一种以空间换时间的作法。
  二、具体实现就是为当前执行线程维护一个ThreadLocalMap对象(维护到执行线程中,每一个线程建立一个),其中维护一个entity对象数组(继承weakReference),其中threadlocal对象为key,设置的对象为value,以实现thread中维护多个threadLocal对象
  三、通常在类中使用final static 修饰
  四、get方法使用当前threadlocal对象实例从当前执行线程的threadlocalmap中获取存放的对象。
  五、线程的threadLocalMap中之索引维护一个entity数组,是由于一个线程能够持有多个threadlocal
  PS:
    一、threadLocalMap中维护的entity继承了weakReference,功能就是当threadlocal被回收后就变成null->value
    二、threadLocal在get时会清除key为null的entity
  
4三、ConcurrentHashMap源码?JDK1.6,1.7,1.8中分别有什么不一样?
  一、负载因子0.75,默认容量16,当大于16*0.75时扩容一倍。
   二、1.6和1.7无太大差异,只是在new hashmap时1.6确实开辟了内存空间,1.7采用的是懒汉式,在put时才构造。
   三、1.7对待并发采用的是基于分片+链表数组结构,要通过两次hash碰撞,分片是用的是可重入锁;1.8则摒弃了分片直接使用synchronized加到桶中第一个元素上,cas用于交换元素。
   四、1.8对待增加,当链表长度大于8时但桶大小小于64时扩充容量,大于64时将桶转为红黑树操做。
  PS:
    一、node中的value和next都用volatile修饰。
    二、hash碰撞就是两个对象的key的hashcode同样,这时候如何获取他的value。
    三、1.8中的spread方法对hash作了扩展,将高16位和当前hash作异或操做,解决选择桶的下标时老是与低4位运算,形成的表的长度较小问题,从而减小系统的开销(hash碰撞状况)
    四、桶的大小超过64时,使用红黑树也是当发生较大碰撞时下降冲突的考虑。
4四、分布式程序调用链
  全链路性能监控从总体维度到局部维度展现各项指标,将跨应用的全部调用链性能信息集中展示,可方便度量总体和局部性能,而且方便找到故障产生的源头,生产上可极大缩短故障排除时间。
Google Dapper
  一、背景:随着微服务的应用,业务调用链愈来愈复杂,一个请求可能涉及到几十个服务的系统服务,涉及到多个团队的业务系统。当遇到问题须要定位时,也会产生一系列麻烦。
  二、解决方案:经过调用链,把一次请求调用过程串联起来,实现对请求路径的监控,便于快速定位。
  三、调用链显示内容:各个调用环节的性能分析(如各API使用时间、使用堆栈)、调用个环节依赖关系还原、SQL打印、IP显示。
  四、通用框架:google的Dapper,淘宝的鹰眼,京东的九头蛇。
  五、调用链原理:
    a、请求生成一个全局TranceId,经过TraceId能够串联起整个调用链,一个tranceId表明一次请求。
    b、除了TranceId,还须要SpanId记录调用的父子关系,span是本身生成,透传子调用成为parentId
    c、一个没有parentId的span是调用链入口
    e、这个调用过程当中每一个请求都要透传tranceId和spanId
    f、要查看某次完整的调用链只要根据TranceId查出全部调用记录,而后经过parentId和spanId组织起整个调用父子关系。
  PS:具体参见
    https://blog.csdn.net/Damon__Wang/article/details/81782911
    https://blog.csdn.net/Damon__Wang/article/details/82051631
 
4五、生产环境如何定位内存溢出?CPU使用率太高?Linux命令?
 一、内存溢出的常见状况分几种:堆溢出(java heap space),PermGen space(方法区),不能建立本地线程(unable to create new native thread),回收执行了太长时间、超过限制(GC overhead limit exceeded)。初步判断区域
    二、关键还在于分析dump文件。这个能够提早设置+HeapDumpOnOutOfMemeryError,或jmp
    三、使用Jprofile打开dump文件,这里能够看到大对象和具体的引用
 四、固然结合gc的日志更好(-XX:+PrintGC)
 
4六、Netty 
    Netty 是一个基于NIO的客户、服务器端编程框架。
   Netty是什么?
    1)本质:JBoss作的一个Jar包
    2)目的:快速开发高性能、高可靠性的网络服务器和客户端程序
    3)优势:提供异步的、事件驱动的网络应用程序框架和工具
    4)特色:
      a、并发高
      b、传输快:领拷贝,使用直接缓冲区
      c、封装好:比较简洁,使用链式调用
    通俗的说:一个好使的处理Socket的东东
 
4七、kafka 事务 性能
4八、内存屏障 
     Java内存模型中volatile变量在写操做以后会插入一个store屏障,在读操做以前会插入一个load屏障。一个类的final字段会在初始化后插入一个store屏障,来确保final字段在构造函数初始化完成并可被使用时可见。
 PS:
  一、为了提升性能,处理器设计了多级缓存,cpu的缓存和共享的缓存。cpu把处理结果发到缓存后就能够作其余处理了,但这也形成了可见性问题。
  二、缓存操做是分红缓存行,缓存一致性原则(MESI)经过定义独占、共享、修改、失效等缓存行的状态来协调多个处理器对其的操做。(内存级别)当共享的同一缓存端的数据发生变化时其余cpu都会获得通知。
  三、为了杜绝内存不一致的状况(如指令重排序,cpu和编译阶段都会),又引入了内存屏障来确保一致性。内存屏障分为LoadLoad屏障,LoadStore屏障,StoreStore屏障,StoreLoad屏障,都是确保后一个操做前前一个操做必须完成(指令在中间,分割对应的操做指令,肯定先后的一个关系)。(指令级别)
  四、lock前缀指令具有内存屏障功能(load&store)的的CUP指令,执行时锁住子系统来确保执行顺序,甚至跨多个CPU。
  五、JVM中,除了内存屏障,还使用先行发生原则来确保指令的先后关系。如对象锁释放必须先于加锁发生,start先于thread内全部指令执行前发生。
  六、cas操做能够理解为是lock指令(锁着内存)+系统cas指令来实现的。
  七、
 
4九、redis面试题

  一、Redis有哪些数据结构?程序员

       a、基本类型:字符串,数值         
    b、集合:字典Hash、列表List、集合Set、有序集合SortedSet,链表      
    c、其余:若是你是Redis中高级用户,还须要加上下面几种数据结构HyperLogLog、Geo、   Pub/Sub。Redis Module,像BloomFilter,RedisSearch,Redis-ML。

  二、使用过Redis分布式锁么,它是什么回事?github

      a、先拿setnx来争抢锁,抢到以后,再用expire给锁加一个过时时间防止锁忘记了释放。

  三、若是在setnx以后执行expire以前进程意外crash或者要重启维护了,那会怎么样?web

      a、这个锁就永远得不到释放了。
   b、set指令有很是复杂的参数,这个应该是能够同时把setnx和expire合成一条指令来用的! 如redis的lua脚本

  四、假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,若是将它们所有找出来?

      a、使用keys指令能够扫出指定模式的key列表。

       五、若是这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?

         a、redis关键的一个特性:redis的单线程的。keys指令会致使线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。         
    b、这个时候能够使用scan指令,scan指令能够无阻塞的提取出指定模式的key列表,可是会有必定的重复几率,         
    c、在客户端作一次去重就能够了,可是总体所花费的时间会比直接用keys指令长。

  六、使用过Redis作异步队列么,你是怎么用的?

      a、通常使用list结构做为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。

  七、可不能够不用sleep呢?

      a、list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。

  八、能不能生产一次消费屡次呢?

      a、使用pub/sub主题订阅者模式,能够实现1:N的消息队列。publish/subscribe

     九、pub/sub有什么缺点?

      a、在消费者下线的状况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。(消息是即时的,不作存储)

  十、redis如何实现延时队列?

      a、使用sortedset和string,数据做为拿时间戳做为score,消息内容做为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒以前的数据轮询进行处理。 
   b、string中uuid做为key,data做为value存放数据。
   c、sortedset中,string中的uuid做为key,时间戳做为value存储。获取的时候使用zrangebyscore排序回去最先的数据。
   d、应对的场景就是:并发对数据库更新时,锁表会致使效率低,使用这种延迟操做能够解决这种效率低的问题。

  十一、若是有大量的key须要设置同一时间过时,通常须要注意什么?

      a、若是大量的key过时时间设置的过于集中,到过时的那个时间点,redis可能会出现短暂的卡顿现象。
   b、通常须要在时间上加一个随机值,使得过时时间分散一些。(过时时间=固定时间+随机值)

  十二、Redis如何作持久化的?

      a、bgsave作镜像全量持久化,aof作增量持久化。     
   b、由于bgsave会耗费较长时间,不够实时,在停机的时候会致使大量丢失数据,因此须要aof来配合使用。    
   c、 在redis实例重启时,会使用bgsave持久化文件从新构建内存,再使用aof重放近期的操做指令来实现完整恢复重启以前的状态。

   1三、若是忽然机器掉电会怎样?

      a、取决于aof日志sync属性的配置,若是不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。     
   b、可是在高性能的要求下每次都sync是不现实的,通常都使用定时sync,好比1s1次,这个时候最多就会丢失1s的数据。

  1四、bgsave的原理是什么?

      a、fork和cow。fork是指redis经过建立子进程来进行bgsave操做,cow指的是copy on write,     
   b、子进程建立后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。

  1五、Pipeline有什么好处,为何要用pipeline?

      a、能够将屡次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有因果相关性。     
   b、使用redis-benchmark进行压测的时候能够发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。

  1六、Redis的同步机制了解么?

      a、Redis能够使用主从同步,从从同步。     
   b、第一次同步时,主节点作一次bgsave,并同时将后续修改操做记录到内存buffer,待完成后将rdb文件全量同步到复制节点,     复制节点接受完成后将rdb镜像加载到内存。     
   c、加载完成后,再通知主节点将期间修改的操做记录同步到复制节点进行重放就完成了同步过程

  1七、是否使用过Redis集群,集群的原理是什么?

      a、Redis Sentinal着眼于高可用,在master宕机时会自动将slave提高为master,继续提供服务。    
   b、 Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。
    
50、动态代理有几种,Jdk与Cglib区别
   一、实现方式:
    a、jdk经过反射机制生成一个实现代理接口(参数中interfaces里全部接口且继承了Proxy的代理类)的匿名类,在调用具体方法前调用invokeHandler的invoke处理
      a.1,Proxy.newProxyInstance(classloader,target.getclass.getInterfaces(),target(implemants InvocationHandler)
      a.二、生成一个实现了参数中interfaces里全部接口且继承了Proxy的代理类的字节码,而后用参数中的classloader加载这个代理类。
      a.三、使用代理类父类的构造函数Proxy(invocationHandler)来建立一个代理类实例,将咱们自定义的InvocationHandler的子类传入。
      a.四、返回这个代理类的实例。
    b、cglib利用ASM开源包,直接修改代理类class的字节码生成子类来重写其方法。
   二、目标类的限制:
    a、jdk只能正对实现了接口的类
    b、cglib针对有误实现接口的都行
    c、cglib不能重写final类或方法
   三、性能:jdk是愈来愈快
  ps:
    一、Java 字节码操控框架。它能被用来动态生成类或者加强既有类的功能
    二、spring中的应用:<aop:aspectj-autoproxy proxy-target-class="true"/>:cglib
5一、数据库三大范式
    一、字段不能够再分:也就是说一个列名下的值只能是一种类型,如号码中既有手机号、座机号等都不行
  保证字段的原子性,也是关系型数据库的标准(面向对象)
  二、有主键,非主键字段依赖主键(有一个主题):一个主键表明一条记录,只能有一个主题;也就是说一条记录中不能即存在学生的记录信息也存在课程的主题信息
  惟一性
  三、非主键字段不能相互依赖(都是平级的):每列都与主键有直接关系,不存在传递依赖。
5二、左链接和右链接说一下,内链接呢
  一、左链接:使用left join on,匹配时匹配表中没能匹配上的也显示,驱动表中只显示匹配的上的
  二、右链接:使用right join on,效果和左链接相反
  三、内连接:使用(inner) join on ,匹配表和驱动表都只显示匹配的上的。
  四、优化:都须要优化驱动表
  五、性能:左右关联比内关联要好一点。
5三、数据库索引有几种
5四、数据库引擎你认识几种,innodb 和myisam 区别,你的项目用到哪一个引擎
 

  PS:

    一、指数据库事务正确执行的四个基本要素:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)    
    二、数据库引擎是用于存储、处理和保护数据的核心服务。利用数据库引擎可控制访问权限并快速处理事务,从而知足企业内大多数须要处理大量数据的应用程序的要求。
    三、查询数据库支持的引擎:show engines
    四、查询数据库使用的引擎:show variables like '%storage_engine%';
5五、若hashcode方法永远返回1会产生什么结果
  一、首先、编程时就坚持修改了equals就要修改hashchode要求
  二、hashcode在对象的对比和一些集合类中会被用到,如hashmap
  三、hashcode的存在也是一种优化程序的体现,如hashmap中的桶,若是全部的hashcode相同,那hashcode会构成一个线性表,致使性能降级
  
  
  PS:为何选择31作乘数     
    一、不大不小:为了不hash重复须要选择一个大一点的质数作乘数,不然就会致使hash值重复的较多,100以上的数乘下来容易超过int的范围。
       二、31,33,37,39,41中31能够被jvm优化,做为位移计算,这种很高效。
 
5六、Error与RuntimeException的区别
  两者的不一样之处:
    Exception:
      1.能够是可被控制(checked) 或不可控制的(unchecked)
      2.表示一个由程序员致使的错误 
      3.应该在应用程序级被处理
    Error:
      1.老是不可控制的(unchecked)
      2.常常用来用于表示系统错误或低层资源的错误
      3.如何可能的话,应该在系统级被捕捉
 
5七、引用计数法与GC Root可达性分析法区别
  一、引用计数:相似给对象添加一个计数器,当对象被引用的时候就在本身的计数器上加一,当某个引用的对象被回收后。引用为零的对象就会被回收
    优势:简单、高效
    缺点:相互引用不能被识别(a.instance=c,b.instance=a,这两项都再也不被使用,但却没法回收)
  二、可达性分析:从根开始遍历他的引用,当某个对象到根不可达时则对象该引用能够回收
    根节点:方法区的常量、静态变量
        虚拟机栈区的变量列表的引用
        本地方法栈中的引用
        本地方法栈中引用的对象
  PS:
    一、五大分区:
         
      a、程序计数器:记录当前执行程序的位置,改变计数器的值来肯定执行下一条指令,如循环、分支、方法跳转    
      b、虚拟机栈:每一个线程都会建立一个虚拟机栈,经过压栈出栈的方式执行方法调用。分局部变量表、操做数栈、动态连接、方法出口等。
      c、本地方法栈:native方法
      d、堆:存放对象实例
      e、方法区:用于存放已被虚拟机加载的类信息,常量,静态变量等数据。
      f、直接内存:并非虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域。

    二、各区域的使用

      

      

5八、双亲委派机制说一下
  一、某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,若是父类加载器能够完成类加载任务,就成功返回;只有父类加载器没法完成此加载任务时,才本身去加载。
  二、全盘责任委托:一个类若是被某个类加载器加载,那么除非指定别的加载器,不然这个类关联的类也有这个类加载器加载。
  三、由下向上询问是否加载,由上向下尝试加载。
  四、线程上下文加载器:针对java的spi状况,也就是引导类加载的类型须要使用二方包的状况,如jdk
5九、算法题:找出一个数组中第100个小的数字(堆思想解决)  
60、看你项目用到策略模式和工厂模式,说一下区别
  一、比喻:去必胜客吃披萨,工厂模式关注的是最终能吃到披萨,策略模式关注在关注的是披萨是如何作的。
    a、用途不一样
      工厂是建立型模型,他的做用是建立对象
      策略是行为型模型,他的做用是让一个对象在许多行为中选择一种行为。
    b、关注点不一样
      一个关注对象的建立
      一个关注行为的封装
    c、解决不一样的问题
      工厂模式,它接受指令,建立出符合要求的实例。它主要解决的是资源的统一分配,将对象的建立独立出来,让对象的建立和具体的使用客户无关。
      策略模式,它为了解决策略的切换与扩展,让策略模式的变化独立于使用策略模式的用户。
    d、工厂至关于黑盒子,策略至关于白盒子
  PS:
    一、 设计模式有三种类型:建立型(解决:对象的建立和具体的使用解耦),行为型(描述了对象和类的模式,以及它们之间的通讯模式)
  ,组合型(解决怎样组装现有的类,设计他们的交互方式,从而达到实现必定的功能的目的)。
    二、项目中:缓存的使用,定义一个公用的缓存操做页面,在缓存工厂中经过配置缓存类别获取具体的缓存实现,redis,tair
    三、简述:
    

    

    

 四、工厂模式有三种:简单工厂,工厂方法,抽象工厂。
    a、简单工厂(静态工厂):将类的实例化转交一个工厂,具体的行为由子类决定(即如何构造该实例);解决的问题:怎么构造一个对象有工厂内部决定,如是否设置某个属性等,用于隐藏实现细节。如计算器,获取单例等
    b、工厂方法:定义一个用于建立对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。不一样的对象使用不一样的工厂
    c、抽象工厂:提供一个建立一系列相关或相互依赖对象的接口,而无需指定他们具体的类。解决的问题:切换数据库
  五、策略模式:它定义了算法家族,分别封装起来,让他们之间能够相互替换,此模式让算法的变化,不会影响到使用算法的客户

 

 

6一、模板方法模式
   定义:一个模板结构,将具体内容延迟到子类去实现。
   解决的问题:
    一、将复用性高的代码抽取到抽象父类中,具体的操做在继承的子类中定义。
    二、父类调用子类操做,子类扩展不一样的行为,这样即实现了控制反转也符合开闭原则。
   使用场景
    试卷,apache velocity
6二、开闭原则懂吗,说一下
一、设计模式中提到的一个概念。
二、目的指导咱们如何创建一个稳定的、灵活的系统。
三、开闭原则的定义是:软件中的对象(类、模块、函数等)应该对于扩展是开放的,可是,对于修改是封闭的。(能够新增、尽量少的修改)
 
6三、NIO说一下你的理解
  一、NIO也就是非阻塞IO,相对于BIO而言,固然也有AIO
  二、打个比喻水工接水的比方:BIO就是多个多个水笼统,每一个水龙头都有一个接水工,每一个接水工只有接到水才才会作后续处理;NIO是为每一个水龙头添加了一个水缸且只有一个水工负责查看接到水刚,当某个水缸接满水,水工就先处理这个水缸的后续处理。
  三、这个水缸就缓冲区buffer,水工就是选择器Select,水工就看查看某个水龙头时就等于链接了通道channel。
  四、具体的实现时,全部的通道都注册到选择器中,选择器轮循查看通道中的数据是否准备就绪,而读出和写入都是直接到了缓冲区。
  五、这个地方提供了一个新的概念:直接缓冲区,绕过内核地址空间(系统空间),直接将数据的物理地址映射到用户地址空间(JVM)。
  ps:以前的数据拷贝都是先写到物理内存,而后再拷贝到jvm中。
6四、AtomicInteger底层原理
  一、AtomicInteger是一个提供原子操做的Integer类,经过线程安全的方式操做加减。
  二、其中存储的value使用了volatile修饰,操做时使用cas无锁算法。
  ps:
    一、具体实现就是使用Unsafe。
    二、它有以下功能:内存管理(分配、释放内存),很是规的对象实例化(无需调用构造器),操做类、对象、变量(指针偏移获取),数组操做(指针偏移),多线程同步(对象锁机制,cas操做),线程挂起与恢复,内存屏障(loadFence、storeFence、fullFence)
    
6五、CAS机制是什么?有什么缺点,会出现什么问题?
CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。
CAS机制当中使用了3个基本操做数:内存地址V,旧的预期值A,要修改的新值B,若预期值A和内存值V相同就把内存值修改为新值B
CAS的缺点:
1.CPU开销较大
在并发量比较高的状况下,若是许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。
2.不能保证代码块的原子性
CAS机制所保证的只是一个变量的原子性操做,而不能保证整个代码块的原子性。好比须要保证3个变量共同进行原子性的更新,就不得不使用Synchronized了。
3.ABA问题
这是CAS机制最大的问题所在。

  PS:

  什么是ABA问题?

 引用原书的话:若是在算法中的节点能够被循环使用,那么在使用“比较并交换”指令就可能出现这种问题,在CAS操做中将判断“V的值是否仍然为A?”,而且若是是的话就继续执行更新操做,在某些算法中,若是V的值首先由A变为B,再由B变为A,那么CAS将会操做成功。
      • 怎么避免ABA问题?
Java中提供了 java.util.concurrent.atomic中AtomicStampedReference和AtomicMarkableReference来解决ABA问题。 
  • 底层实现?
多CPU的状况下的cas操做是CPU提供的支持。
一、这和volatile的底层实现是相同的
二、底层:这个读取、对比以及设置的操做私用lock前缀保证惟一性。
6六、本地缓存过时策略怎么设置,一致性怎么保证?
  1、一致性(以下是主动的状况)
    一、当数据时效性要求比较高时,须要保证缓存与数据库保存一致,并且须要保证缓存节点和副本中的数据也要保存一致,不能出现差别现象。
    二、这就比较依赖缓存过时和更新策略,通常会在数据发生更改时,主动更新缓存中的数据或者移除对应的缓存。
    三、通常的缓存使用
      

 

    方案一(先更新缓存,再更新数据库):      
      一、是不可用的      
      二、首先库存是以数据库为准的,若是缓存更新完成但数据库未更新完成且库存少于缓存则会形成负库存。
      三、若缓存更新成功,数据库更新失败则缓存一直都是脏数据。
    方案二(新更新数据库,再更新缓存)
      一、不可取
      二、若是两个线程并发执行,会存在A更新数据库,B更新数据库,B更新缓存,A更新缓存(网络缘由),则数据库中的就是脏数据。
      三、针对那种依赖前值计算后更新的场景,无疑是浪费性能。
    方案三(先删除缓存,再更新数据库:更新数据库失败对业务也没什么影响)
      一、待优化
      二、存在A删除了缓存,B发现缓存不存在从数据库查询到旧值写到了缓存,A将新值写入到数据库:此时缓存和数据库不一致。
      三、能够采用延时双删策略:先淘汰缓存,再写入数据库,休眠1秒,再次淘汰缓存。此时能够确保上述B的写入被删除。(休眠是为了确保读请求结束,写请求能够删除对请求形成的脏数据)
      四、第二次删除能够采用新线程来作以免下降吞吐。
      五、第二次删除失败了的解决方案详见方案四
    方案四(先更新数据库,再删除缓存)
      一、待优化
      二、缓存恰好失效,A查询数据库获得旧值,b将新值写入数据库并删除缓存,A将旧值写入缓存(若b的写入数据库操做要足够端以致于B删除缓存早于A写入缓存发生:几率低)
      三、2的解决方案:异步延时删除, 缓存设置有效时间
      四、针对缓存更新失败的解决方案:
        

      删除失败后,将删除key的消息发送到消息队列,重试删除直到成功(这对业务代码有侵入)

    

        使用mysql的中间件如Canal,单启一个独立的程序去处理。

  2、过时策略
    一、缓存过时策略大体分两种:能够经过过时时间来控制内容过时的状况和没法经过过时时间来控制内容过时的状况。
    二、能够经过过时时间来控制内容过时的状况
         a、设置绝对过时时间(秒杀商品)。
         三、没法经过过时时间来控制过时的状况
        a、设置滑动过时(针对时效性不强的):在读取缓存的时候将该缓存项的过时时间在当前时间的基础上延后指定长度的事件。(如文章有评论就延长过时时间的状况)
  
  ps:
    一、缓存满了,从缓存中移除数据的策略
      a、新进先出算法
      b、最久未使用算法
      c、最少使用算法
    二、缓存并发问题
      a、缓存过时后将尝试从后端数据库获取数据,当数据获取到更新完成这段会有多个线程到数据库后去数据,对数据库形成极大的冲击,甚至致使血崩。
      b、此时就要加锁,到后台数据库请求数据要先尝试获取锁,未获取锁的线程只能等待。
      c、针对缓存过时也要选择一个范围内随机过时,不能所有集中到某个时间段。
    三、缓存穿透(不存在的key,缓存不起做用)
      a、查询一个不存在的数据,因为缓存是不被命中被动写的(若是没有就查数据库),而且出于容错考虑,若是存储层查不到数据就不写入缓存,这将致使每次都要查询存储层查询,失去了缓存的意义。在流量大的时候,可能db就挂掉了。
      b、缓存空对象,过时时间段,不超过五分钟。
      c、单独过滤处理:将对应数据为空的key进行统一存放
       d、布隆过滤器:将全部可能存在的数据哈希到一个足够大的bitmap中,一个必定不存在的数据会被 这个bitmap拦截掉
    四、缓存颠簸:缓存节点故障致使,使用hash算法解决
    五、缓存雪崩
      a、缓存采用了相同的过时时间,致使缓存再同一时刻同时失效,DB瞬间压力过大崩溃。
      b、在缓存失效的基础上添加1-5分钟的随机值
      c、从应用架构角度,咱们能够经过限流、降级、熔断等手段来下降影响,也能够经过多级缓存来避免这种灾难
    六、缓存击穿(一个存在的key,在缓存过时的一刻,同时有大量的请求)
      a、使用互斥锁:使用setnx设置值,成功消息返回的才去查询数据库(成功后设置有效期)
    七、缓存无底洞现象
    八、其余
      a、目前主流的数据库、缓存、Nosql、搜索中间件等技术栈中,都支持“分片”技术,来知足“高性能、高并发、高可用、可扩展”等要求
      b、命中:能够直接经过缓存获取到须要的数据。
 
  3、cache的使用
  4、掌医的实现
  5、商品秒杀逻辑
相关文章
相关标签/搜索