Java面试总结

 

1、基础篇

1.一、Java基础 

  • 面向对象的特征:继承、封装和多态
    final, finally, finalize 的区别

  •  

    final用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。html

    内部类要访问局部变量,局部变量必须定义成final类型,例如,一段代码……前端

    finally是异常处理语句结构的一部分,表示老是执行。java

    finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,能够覆盖此方法提供垃圾收集时的其余资源回收,例如关闭文件等。JVM不保证此方法总被调用mysql

  • Exception、Error、运行时异常与通常异常有何异同

  •  

    exception 表示一种设计或实现问题。也就是说,它表示若是程序运行正常,从不会发生的状况android

  • error表示恢复不是不可能但很困难的状况下的一种严重问题。好比说内存溢出。不可能期望程序能处理这样的状况。程序员

    异常表示程序运行过程当中可能出现的非正常状态,运行时异常表示虚拟机的一般操做中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,可是并不要求必须声明抛出未被捕获的运行时异常。 web

  •  

    异常是指java程序运行时(非编译)所发生的非正常状况或错误,与现实生活中的事件很类似,现实生活中的事件能够包含事件发生的时间、地点、人物、情节等信息,能够用一个对象来表示,Java使用面向对象的方式来处理异常,它把程序中发生的每一个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息。面试

    Java对异常进行了分类,不一样类型的异常分别用不一样的Java类表示,全部异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error和Exception,Error表示应用程序自己没法克服和恢复的一种严重问题,程序只有死的份了,例如,说内存溢出和线程死锁等系统问题。Exception表示程序还可以克服和恢复的问题,其中又分为系统异常和普通异常,系统异常是软件自己缺陷所致使的问题,也就是软件开发人员考虑不周所致使的问题,软件使用者没法克服和恢复这种问题,但在这种问题下还可让软件系统继续运行或者让软件死掉,例如,数组脚本越界(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException)、类转换异常(ClassCastException);普通异常是运行环境的变化或异常所致使的问题,是用户可以克服的问题,例如,网络断线,硬盘空间不够,发生这样的异常后,程序不该该死掉。ajax

    java为系统异常和普通异常提供了不一样的解决方案,编译器强制普通异常必须try..catch处理或用throws声明继续抛给上层调用方法处理,因此普通异常也称为checked异常,而系统异常能够处理也能够不处理,因此,编译器不强制用try..catch处理或用throws声明,因此系统异常也称为unchecked异常。redis

     

    提示答题者:就按照三个级别去思考:虚拟机必须宕机(就是死机)的错误,程序能够死掉也能够不死掉的错误,程序不该该死掉的错误;

  • 请写出5种常见到的runtime exception

  •  

    这道题主要考你的代码量到底多大,若是你长期写代码的,应该常常都看到过一些系统方面的异常,你不必定真要回答出5个具体的系统异常,但你要可以说出什么是系统异常,以及几个系统异常就能够了,固然,这些异常彻底用其英文名称来写是最好的,若是实在写不出,那就用中文吧,有总比没有强!

    所谓系统异常,就是…..,它们都是RuntimeException的子类,在jdk doc中查RuntimeException类,就能够看到其全部的子类列表,也就是看到了全部的系统异常。我比较有印象的系统异常有:NullPointerException,ArrayIndexOutOfBoundsException,ClassCastException,SQLException,FileNotFoundException,IOException......

  • int 和 Integer 有什么区别,Integer的值缓存范围

  •  

    int是java提供的8种原始数据类型之一。Java为每一个原始类型提供了封装类,Integer是java为int提供的封装类。int的默认值为0,而Integer的默认值为null,即Integer能够区分出未赋值和值为0的区别,int则没法表达出未赋值的状况,例如,要想表达出没有参加考试和考试成绩为0的区别,则只能使用Integer。在JSP开发中,Integer的默认为null,因此用el表达式在文本框中显示时,值为空白字符串,而int默认的默认值为0,因此用el表达式在文本框中显示时,结果为0,因此,int不适合做为web层的表单数据的类型。

    在Hibernate中,若是将OID定义为Integer类型,那么Hibernate就能够根据其值是否为null而判断一个对象是不是临时的,若是将OID定义为了int类型,还须要在hbm映射文件中设置其unsaved-value属性为0。

    另外,Integer提供了多个与整数相关的操做方法,例如,将一个字符串转换成整数,Integer中还定义了表示整数的最大值和最小值的常量。

    • Boolean:(所有缓存)
    • Byte:(所有缓存)
    • Integer(-128 — 127缓存)
    • Character(<= 127缓存)
    • Short(-128 — 127缓存)
    • Long(-128 — 127缓存)
    • Float(没有缓存)
    • Doulbe(没有缓存)
  • 包装类,装箱和拆箱

  •  

    包装类(原始类型对应的类,即Boolean, Byte, Short, Integer, Long, Float, Double, Character)的自动拆、装箱分别是指以下的过程:

    装箱:是指基本类型变为相应包装类的过程,如Integer a=Integer.valueOf(100);或者int a=100;Integer b=new Integer(a);这里的Integer.valueOf(100)和new Integer(a)就是装箱,由基本数据类型构造出一个包装类的对象。

    拆箱:就是装箱的逆过程。如Integer a=new Integer(100);int b=a.intValue();这里的a.intValue()就是拆箱的过程,由一个包装类对象转换到相应的基本数据类型。

    自动装箱、拆箱:指编译器帮助开发人员完成包装类的装箱和拆箱过程,也就是在将*.java文件编译成*.class文件的过程当中完成。本文的目的是要介绍一下,编译器在什么时候才进行自动装箱、拆箱。

    包装类的自动装箱和拆箱规则以下:

    • 遇到赋值运算符“=”(包括传参数,参数传递属于隐式赋值)时,会将包装类拆箱或装箱为相应类型
    • 遇到算术运算符、位运算符和位移运算符“+, ++, --, -, *, /, %, &, |, ^, ~, <<, >>, >>>”时,对包装类进行拆箱
    • 遇到关系运算符">, <, >=, <="(不包括“==”和“!=”哦)时,对包装类进行拆箱
    • 对关系运算符"==, !="而言,遇到数字常量或算术表达式时,才对包装类进行拆箱
    其他状况下,不进行自动的装箱和拆箱,注意啊,这里说的是自动装箱和拆箱。
  • String、StringBuilder、StringBuffer

  •  

    1. 修改字符串速度 
      StringBuilder>StringBuffer>String
    2. 内容是否可变 
      只有String不可变。 
    3. 线程安全 
      只有StringBuilder是线程不安全的。

     

    String做为不可变类,是明显线程安全的,Java中全部不可变类都是线程安全的。

    StringBuffer类是可变类,可是StringBuffer类中实现的方法都是被Sychronized关键字所修饰的,所以它靠锁实现了线程安全。 
    Stringbuilder类是可变类,而且方法没有被Sychronized修饰,所以它是线程不安全的。

    JAVA平台提供了两个类:String和StringBuffer,它们能够储存和操做字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变的时候你就能够使用StringBuffer。典型地,你能够使用StringBuffers来动态构造字符数据。另外,String实现了equals方法,new String(“abc”).equals(newString(“abc”)的结果为true,而StringBuffer没有实现equals方法,因此,new StringBuffer(“abc”).equals(new StringBuffer(“abc”)的结果为false。

     

    接着要举一个具体的例子来讲明,咱们要把1到100的全部数字拼起来,组成一个串。

    StringBuffer sbf = newStringBuffer(); 

    for(int i=0;i<100;i++) {

        sbf.append(i);

    }

    上面的代码效率很高,由于只建立了一个StringBuffer对象,而下面的代码效率很低,由于建立了101个对象。

    String str = new String(); 

    for(int i=0;i<100;i++) {

        str = str + i;

    }

    String覆盖了equals方法和hashCode方法,而StringBuffer没有覆盖equals方法和hashCode方法,因此,将StringBuffer对象存储进Java集合类中时会出现问题。

    在讲二者区别时,应把循环的次数搞成10000,而后用endTime-beginTime来比较二者执行的时间差别,最后还要讲讲StringBuilder与StringBuffer的区别。(区别以下)

    StringBuffer线程安全的可变字符序列。一个相似于 String的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但经过某些方法调用能够改变该序列的长度和内容,可将字符串缓冲区安全地用于多个线程。能够在必要时对这些方法进行同步,所以任意特定实例上的全部操做就好像是以串行顺序发生的,该顺序与所涉及的每一个线程进行的方法调用顺序一致。(从 JDK 5开始,为该类补充了一个单个线程使用的等价类,即 StringBuilder。与该类相比,一般应该优先使用 StringBuilder类,由于它支持全部相同的操做,但因为它不执行同步,因此速度更快。)

    StringBuilder一个可变的字符序列。此类提供一个与 StringBuffer兼容的 API,但不保证同步。该类被设计用做 StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种状况很广泛)。(若是可能,建议优先采用该类,由于在大多数实现中,它比 StringBuffer要快。但将 StringBuilder的实例用于多个线程是不安全的。若是须要这样的同步,则建议使用 StringBuffer。)

    StringBuffer和Stringbuilder上的主要操做都是 append和 insert 方法,可重载这些方法,以接受任意类型的数据。每一个方法都能有效地将给定的数据转换成字符串,而后将该字符串的字符追加或插入到字符串缓冲区中。append方法始终将这些字符添加到缓冲区的末端;而 insert方法则在指定的点添加字符。

    例如,若是 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用z.append("le")会使字符串缓冲区包含“startle”,而 z.insert(4, "le")将更改字符串缓冲区,使之包含“starlet”。

    一般,若是 sb 引用 StringBuilder 的一个实例,则sb.append(x)和 sb.insert(sb.length(), x)具备相同的效果。只要发生有关源序列(如在源序列中追加或插入)的操做,该类就只在执行此操做的字符串缓冲区上而不是在源上实现同步。

  • 重载和重写的区别

  •  

    Overload是重载的意思,Override是覆盖的意思,也就是重写。

    重载Overload表示同一个类中能够有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不一样)。

    重写Override表示子类中的方法能够与父类中的某个方法的名称和参数彻底相同,经过子类建立的实例对象调用这个方法时,将调用子类中的定义方法,这至关于把父类中定义的那个彻底相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现。子类覆盖父类的方法时,只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,由于子类能够解决父类的一些问题,不能比父类有更多的问题。子类方法的访问权限只能比父类的更大,不能更小。若是父类的方法是private类型,那么,子类则不存在覆盖的限制,至关于子类中增长了一个全新的方法。

    至于Overloaded的方法是否能够改变返回值的类型这个问题,要看你倒底想问什么呢?这个题目很模糊。若是几个Overloaded的方法的参数列表不同,它们的返回者类型固然也能够不同。但我估计你想问的问题是:若是两个方法的参数列表彻底同样,是否可让它们的返回值不一样来实现重载Overload。这是不行的,咱们能够用反证法来讲明这个问题,由于咱们有时候调用一个方法时也能够不定义返回结果变量,即不要关心其返回结果,例如,咱们调用map.remove(key)方法时,虽然remove方法有返回值,可是咱们一般都不会定义接收返回结果的变量,这时候假设该类中有两个名称和参数列表彻底相同的方法,仅仅是返回类型不一样,java就没法肯定编程者倒底是想调用哪一个方法了,由于它没法经过返回结果类型来判断。

    override能够翻译为覆盖,从字面就能够知道,它是覆盖了一个方法而且对其重写,以求达到不一样的做用。对咱们来讲最熟悉的覆盖就是对接口方法的实现,在接口中通常只是对方法进行了声明,而咱们在实现时,就须要实现接口声明的全部方法。除了这个典型的用法之外,咱们在继承中也可能会在子类覆盖父类中的方法。在覆盖要注意如下的几点:

    一、覆盖的方法的标志必需要和被覆盖的方法的标志彻底匹配,才能达到覆盖的效果;

    二、覆盖的方法的返回值必须和被覆盖的方法的返回一致;

    三、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;

    四、被覆盖的方法不能为private,不然在其子类中只是新定义了一个方法,并无对其进行覆盖。

    overload对咱们来讲可能比较熟悉,能够翻译为重载,它是指咱们能够定义一些名称相同的方法,经过定义不一样的输入参数来区分这些方法,而后再调用时,JVM就会根据不一样的参数样式,来选择合适的方法执行。在使用重载要注意如下的几点:

    一、在使用重载时只能经过不一样的参数样式。例如,不一样的参数类型,不一样的参数个数,不一样的参数顺序(固然,同一方法内的几个参数类型必须不同,例如能够是fun(int,float),可是不能为fun(int,int));

    二、不能经过访问权限、返回类型、抛出的异常进行重载;

    三、方法的异常类型和数目不会对重载形成影响;

    四、对于继承来讲,若是某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,若是定义的话,也只是定义了一个新方法,而不会达到重载的效果。

  • 抽象类和接口有什么区别

  • 1.abstract class表示继承关系,"is-a"关系,单继承
    能够有数据成员,默认friendly
    能够有非抽象方法和抽象方法,抽象方法只能是public或protected

    2.interface表示like-a"关系,一个类能够实现多个接口
    只能有静态数据成员(默认是public staic final)
    只能有类型抽象方法,且public类型

  • 接口能够继承接口。抽象类能够实现(implements)接口,抽象类能够继承具体类。抽象类中能够有静态的main方法。

  •  

    备注:只要明白了接口和抽象类的本质和做用,这些问题都很好回答,你想一想,若是你是java语言的设计者,你是否会提供这样的支持,若是不提供的话,有什么理由吗?若是你没有道理不提供,那答案就是确定的了。

     只有记住抽象类与普通类的惟一区别就是不能建立实例对象和容许有abstract方法。

  • 说说反射的用途及实现

  • Java 反射机制是在运行状态中,对于任意一个类,都可以知道这个类的全部属性和方法;对于任意一个对象,都可以调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制

  • 在运行时构造一个类的对象;判断一个类所具备的成员变量和方法;调用一个对象的方法;生成动态代理。反射最大的应用就是框架

  •  

    Java反射机制主要用于实现如下功能。

    (1)在运行时判断任意一个对象所属的类型。

    (2)在运行时构造任意一个类的对象。

    (3)在运行时判断任意一个类所具备的成员变量和方法。

    (4)在运行时调用任意一个对象的方法,甚至能够调用private方法

  • 说说自定义注解的场景及实现

  • 垂直化编程,就是A—B—C—D…等执行下去,一个逻辑一个逻辑完了再执行下一个,可是spring 中AOP提供了一种思想,它的做用就是,当在业务不知情的状况下,对业务代码的功能的加强,这种思想使用的场景,例如事务提交、方法执行以前的权限检测、日志打印、方法调用事件等等

  • java在咱们要自定义注解的时候提供了它本身的自定义语法以及元注解,元注解(负责注解其余注解): Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型做说明。Java5.0定义的元注解: 
        1.@Target, 
        2.@Retention, 
        3.@Documented, 
        4.@Inherited 
      这些类型和它们所支持的类在java.lang.annotation包中能够找到。 
      1.@Target:用户描述注解的做用范围 
      取值(ElementType)有: 
        1.CONSTRUCTOR:用于描述构造器 
        2.FIELD:用于描述域 
        3.LOCAL_VARIABLE:用于描述局部变量 
        4.METHOD:用于描述方法 
        5.PACKAGE:用于描述包 
        6.PARAMETER:用于描述参数 
        7.TYPE:用于描述类、接口(包括注解类型) 或enum声明 
    2.@Retention:表示须要在什么级别保存该注释信息 
    取值(RetentionPoicy)有: 
        1.SOURCE:在源文件中有效(即源文件保留) 
        2.CLASS:在class文件中有效(即class保留) 
        3.RUNTIME:在运行时有效(即运行时保留)(经常使用) 
      3.@Documented:Documented是一个标记注解 
      4.@Inherited :用于声明一个注解; 

  • HTTP请求的GET与POST方式的区别

  •  

    (1) 在客户端,Get方式在经过URL提交数据,数据在URL中能够看到;POST方式,数据放置在HTML HEADER内提交。

    (2) GET方式提交的数据最多只能有1024字节,而POST则没有此限制。

    (3) 安全性问题。正如在(1)中提到,使用 Get的时候,参数会显示在地址栏上,而 Post不会。因此,若是这些数据是中文数据并且是非敏感数据,那么使用 get;若是用户输入的数据不是中文字符并且包含敏感数据,那么仍是使用 post为好。

    (4) 安全的和幂等的。所谓安全的意味着该操做用于获取信息而非修改信息。幂等的意味着对同一 URL的多个请求应该返回一样的结果。完整的定义并不像看起来那样严格。换句话说,GET请求通常不该产生反作用。从根本上讲,其目标是当用户打开一个连接时,她能够确信从自身的角度来看没有改变资源。好比,新闻站点的头版不断更新。虽然第二次请求会返回不一样的一批新闻,该操做仍然被认为是安全的和幂等的,由于它老是返回当前的新闻。反之亦然。POST请求就不那么轻松了。POST表示可能改变服务器上的资源的请求。仍然以新闻站点为例,读者对文章的注解应该经过 POST请求实现,由于在注解提交以后站点已经不一样了

  • Session与Cookie区别

  •  

    Cookie保存在客户端,未设置存储时间的cookie为会话cookie保存在浏览器的进程开辟的内存中,当浏览器关闭后会话cookie也会被删除;设置了存储时间的cookie保存在用户设备的磁盘中直到过时。

    session保存在服务器端,存储在IIS的进程开辟的内存中。

    当服务器端生成一个session时就会向客户端发送一个cookie保存在客户端,这个cookie保存的是session的sessionId。这样才能保证客户端发起请求后客户端已经登陆的用户可以与服务器端成千上万的session中准确匹配到已经保存了该用户信息的session,同时也可以确保不一样页面之间传值时的正确匹配。

    注:为了防止客户端禁用了cookie而没法使用session的状况能够把sessionId和其余用户信息重写到url中,每次请求都在url中附带sessionId和用户信息(不包含用户的敏感信息)

  • 列出本身经常使用的JDK包

  •  

    1.java.lang:语言包

    2.java.util:实用包

    3.java.awt:抽象窗口工具包

    4.javax.swing:轻量级的窗口工具包,这是目前使用最普遍的GUI程序设计包

    5.java.io:输入输出包

    6.java.net:网络函数包

    7.java.applet:编制applet用到的包(目前编制applet程序时,更多的是使用swing中的JApplet类)。

  • MVC设计思想

  • MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,M是指业务模型,V是指用户界面,C是指控制器,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑汇集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不须要从新编写业务逻辑。

    MVC是软件开发过程当中比较流行的设计思想。应该明确一点就是,MVC是设计模式,设计思想,不是一种编程技术。

    在web开发中最典型的是JSP+servlet+javabean模式,其思想的核心概念以下:

    Model:封装应用程序的数据结构和事务逻辑,集中体现应用程序的状态,当数据状态改变是,可以在试图里面体现出来。JavaBean很是适合这个角色。

    View:是Model是外在表现,模型状态改变是,有所体现,JSP很是适合这个角色。

    Controller:是对用户输入进行相应,将模型和试图联系在一块儿,负责将数据写到模型中,并调用视图。Servlet很是适合这个角色。

    MVC思想如图:


    MVC的步骤以下:

    1.用户在表单中输入,表单提交给Servlet,Servlet验证输入,而后实例化JavaBean

    2,JavaBean查询数据库,查询结果暂存在JavaBean中。

    3,Servlet跳转到JSP,JSP使用JavaBean获得它里面的查询结果,并显示出来。

  • equals与==的区别

  • 值类型(int,char,long,boolean等)都是用==判断相等性。对象引用的话,==判断引用所指的对象是不是同一个。equals是Object的成员函数,有些类会覆盖(override)这个方法,用于判断对象的等价性。例如String类,两个引用所指向的String都是"abc",但可能出现他们实际对应的对象并非同一个(和jvm实现方式有关),所以用==判断他们可能不相等,但用equals判断必定是相等的。

    (单独把一个东西说清楚,而后再说清楚另外一个,这样,它们的区别天然就出来了,混在一块儿说,则很难说清楚)

    ==操做符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用==操做符。

    若是一个变量指向的数据是对象类型的,那么,这时候涉及了两块内存,对象自己占用一块内存(堆内存),变量也占用一块内存,例如Objet obj = new Object();变量obj是一个内存,new Object()是另外一个内存,此时,变量obj所对应的内存中存储的数值就是对象占用的那块内存的首地址。对于指向对象类型的变量,若是要比较两个变量是否指向同一个对象,即要看这两个变量所对应的内存中的数值是否相等,这时候就须要用==操做符进行比较。

    equals方法是用于比较两个独立对象的内容是否相同,就比如去比较两我的的长相是否相同,它比较的两个对象是独立的。例如,对于下面的代码:

    String a=new String("foo");

    String b=new String("foo");

    两条new语句建立了两个对象,而后用a,b这两个变量分别指向了其中一个对象,这是两个不一样的对象,它们的首地址是不一样的,即a和b中存储的数值是不相同的,因此,表达式a==b将返回false,而这两个对象中的内容是相同的,因此,表达式a.equals(b)将返回true。

    在实际开发中,咱们常常要比较传递进行来的字符串内容是否等,例如,String input = …;input.equals(“quit”),许多人稍不注意就使用==进行比较了,这是错误的,随便从网上找几个项目实战的教学视频看看,里面就有大量这样的错误。记住,字符串的比较基本上都是使用equals方法。

    若是一个类没有本身定义equals方法,那么它将继承Object类的equals方法,Object类的equals方法的实现代码以下:

    boolean equals(Object o){

    return this==o;

    }

    这说明,若是一个类没有本身定义equals方法,它默认的equals方法(从Object类继承的)就是使用==操做符,也是在比较两个变量指向的对象是不是同一对象,这时候使用equals和使用==会获得一样的结果,若是比较的是两个独立的对象则总返回false。若是你编写的类但愿可以比较该类建立的两个实例对象的内容是否相同,那么你必须覆盖equals方法,由你本身写代码来决定在什么状况便可认为两个对象的内容是相同的。

  • hashCode和equals方法的区别与联系

  • hashCode 方法是基类Object中的 实例native方法,所以对全部继承于Object的类都会有该方法。

  •  

    哈希相关概念 
     咱们首先来了解一下哈希表:

    • 概念 : Hash 就是把任意长度的输入(又叫作预映射, pre-image),经过散列算法,变换成固定长度的输出(int),该输出就是散列值。这种转换是一种 压缩映射,也就是说,散列值的空间一般远小于输入的空间。不一样的输入可能会散列成相同的输出,从而不可能从散列值来惟一的肯定输入值。简单的说,就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

    • 应用–数据结构 : 数组的特色是:寻址容易,插入和删除困难; 而链表的特色是:寻址困难,插入和删除容易。那么咱们能不能综合二者的特性,作出一种寻址容易,插入和删除也容易的数据结构?答案是确定的,这就是咱们要提起的哈希表,哈希表有多种不一样的实现方法,我接下来解释的是最经常使用的一种方法——拉链法,咱们能够理解为 “链表的数组”,如图:

               这里写图片描述 
                              图1 哈希表示例

       左边很明显是个数组,数组的每一个成员是一个链表。该数据结构所容纳的全部元素均包含一个指针,用于元素间的连接。咱们根据元素的自身特征把元素分配到不一样的链表中去,也是根据这些特征,找到正确的链表,再从链表中找出这个元素。其中,将根据元素特征计算元素数组下标的方法就是散列法。

    • 拉链法的适用范围 : 快速查找,删除的基本数据结构,一般须要总数据量能够放入内存。

    • 要点 : 
      hash函数选择,针对字符串,整数,排列,具体相应的hash方法; 
      碰撞处理,一种是open hashing,也称为拉链法,另外一种就是closed hashing,也称开地址法,opened addressing

    前提: 谈到hashCode就不得不说equals方法,两者均是Object类里的方法。因为Object类是全部类的基类,因此一切类里均可以重写这两个方法。

    • 原则 1 : 若是 x.equals(y) 返回 “true”,那么 x 和 y 的 hashCode() 必须相等 ;
    • 原则 2 : 若是 x.equals(y) 返回 “false”,那么 x 和 y 的 hashCode() 有可能相等,也有可能不等 ;
    • 原则 3 : 若是 x 和 y 的 hashCode() 不相等,那么 x.equals(y) 必定返回 “false” ;
    • 原则 4 : 通常来说,equals 这个方法是给用户调用的,而 hashcode 方法通常用户不会去调用 ;
    • 原则 5 : 当一个对象类型做为集合对象的元素时,那么这个对象应该拥有本身的equals()和hashCode()设计,并且要遵照前面所说的几个原则。
  • 什么是Java序列化和反序列化,如何实现Java序列化?或者请解释Serializable 接口的做用

  • 序列化:把对象转换为字节序列的过程称为对象的序列化。
    反序列化:把字节序列恢复为对象的过程称为对象的反序列化。

  • 实现Serializable接口便可

  • transient 修饰的属性,是不会被序列化的   静态static的属性,不序列化

  • Object类中常见的方法,为何wait  notify会放在Object里边?

  •  

    a、这些方法用于同步中

    b、使用这些方法时必需要标识所属的同步的锁

    c、锁能够是任意对象,因此任意对象调用的方法必定是定义在Object类中

  • Java的平台无关性如何体现出来的

  •  Java平台无关的能力给予网络一个同构的运行环境,使得分布式系统能够围绕着“网络移动对象”开构建。好比对象序列化,RMI, Jini就是利用平台无关性。把面向对象编程从虚拟机带到了网络上。

  • JDK和JRE的区别

  • JDK,开发java程序用的开发包,JDK里面有java的运行环境(JRE),包括client和server端的。须要配置环境变量。。。。
    JRE,运行java程序的环境,JVM,JRE里面只有client运行环境,安装过程当中,会自动添加PATH。

  • Java 8有哪些新特性

  • 一、接口的默认方法二、Lambda 表达式三、函数式接口四、方法与构造函数引用五、Lambda 做用域六、访问局部变量七、访问对象字段与静态变量八、访问接口的默认方法九、Date API十、Annotation 注解

1.二、Java常见集合

  • List 和 Set 区别

  • Set和hashCode以及equals方法的联系

  • List 和 Map 区别

  • Arraylist 与 LinkedList 区别

  • ArrayList 与 Vector 区别

  • HashMap 和 Hashtable 的区别

  • HashSet 和 HashMap 区别

  • 以上问题可查看一下博客(Collection

  • HashMap 和 ConcurrentHashMap 的区别

  • HashMap 的工做原理及代码实现,何时用到红黑树

  • 多线程状况下HashMap死循环的问题

  • 以上问题可查看一下博客(HashMap

  • HashMap出现Hash DOS攻击的问题

  • ConcurrentHashMap 的工做原理及代码实现,如何统计全部的元素个数

  • 手写简单的HashMap

  •  

    1. public interface  DIYMap<K,V> {  
    2.     //Map双列集合 基本功能是 快速取  
    3.     public V put(K k,V v);  
    4.     //快速取  
    5.     public V get(K k);  
    6.   
    7.     //定义一个内部接口  
    8.     public interface Entry<K,V>{  
    9.         public K getKey();  
    10.           
    11.         public V getValue();  
    12.     }  
    13. }  
    1. import java.util.ArrayList;  
    2. import java.util.List;  
    3. /* 
    4.     了解hashmap中entry实体的结构 
    5.     crc16算法 
    6.     hashmap底层=数组+链表 
    7.     经过hash算法带来的好处, 快存快取  / 数组在存的时候是须要遍历的 
    8.     HashMap底层是怎么回事? 
    9.          
    10.  */  
    11. public class DIYHashMap<K, V> implements DIYMap<K, V>{  
    12.     //定义默认数组大小  
    13.     private  int defaultLenth=16;  
    14.     //负载因子,扩容标准    useSize/数组长度>0.75扩容  
    15.     private double defaultAddSizeFactor=0.75;  
    16.     //使用数组位置的总数  
    17.     private double useSize;  
    18.     //定义Map 骨架之一数组  
    19.     private Entry<K, V>[] table;  
    20.   
    21.     public DIYHashMap(int defaultLenth, double defaultAddSizeFactor) {  
    22.         if(defaultLenth<0){  
    23.             throw new IllegalArgumentException("数组长度为负数"+defaultLenth);  
    24.         }  
    25.         if(defaultAddSizeFactor<=0 || Double.isNaN(defaultAddSizeFactor)){  
    26.             throw new IllegalArgumentException("扩容标准必须大于0的数字"+defaultLenth);  
    27.         }  
    28.       
    29.         this.defaultLenth = defaultLenth;  
    30.         this.defaultAddSizeFactor = defaultAddSizeFactor;  
    31.           
    32.         table=new Entry[defaultLenth];  
    33.     }  
    34.   
    35.     //快速存取 hash算法  
    36.     public V put(K k, V v) {  
    37.         if(useSize>defaultAddSizeFactor*defaultLenth){  
    38.             //扩容  
    39.             up2Size();  
    40.         }  
    41.         //经过key来计算出 存储的位置  
    42.         int index=getIndex(k,table.length);  
    43.       
    44.         Entry<K, V> entry=table[index];  
    45.         Entry<K, V> newEntry=new Entry<K, V>(k, v, null);  
    46.         if(entry==null){  
    47.             table[index]=newEntry;  
    48.             useSize++;  
    49.         }else{//维护数组相同位置队列  
    50.             Entry<K, V> tmp;  
    51.             while((tmp=table[index])!=null){  
    52.                 tmp=tmp.next;  
    53.             }  
    54.             tmp.next=newEntry;  
    55.         }  
    56.         return newEntry.getValue();  
    57.     }  
    58.   
    59.     private int getIndex(K k, int length) {  
    60.         //一般hashCode 取膜法  
    61.         int m=length-1;  
    62.         int index=hash(k.hashCode()) & m;  
    63.         return index >= 0 ? index : -index;  
    64.     }  
    65.   
    66.     //建立本身的hash算法,保证计算出的位置 在数组中均匀分布  
    67.     private int hash(int hashCode) {  
    68.         hashCode=hashCode^((hashCode>>>20)^(hashCode>>>12));  
    69.         return hashCode^((hashCode>>>7)^(hashCode>>>4));  
    70.     }  
    71.   
    72.     //扩容数组  
    73.     private void up2Size() {  
    74.         Entry<K, V>[] newTable=new Entry[defaultLenth*2];  
    75.         //将原table中的entry从新,散列到新的table中  
    76.         againHash(newTable);  
    77.     }  
    78.   
    79.     //将原table中的entry从新,散列到新的table中  
    80.     private void againHash(Entry<K, V>[] newTable) {  
    81.         //数组里面对象 封装到list中,包括同一位置 有列表结构的都解析出来  
    82.         List<Entry<K,V>> entryList=new ArrayList<Entry<K,V>>();  
    83.         for(int i=0;i<table.length;i++){  
    84.             if(table[i]==null){  
    85.                 continue;  
    86.             }  
    87.             findEntryByNext(table[i],entryList);  
    88.         }  
    89.         if(entryList.size()>0){  
    90.             useSize=0;  
    91.             defaultLenth=defaultLenth*2;  
    92.             table=newTable;  
    93.             for (Entry<K, V> entry : entryList) {  
    94.                 if(entry.next!=null){  
    95.                     entry.next=null;  
    96.                 }  
    97.                 put(entry.getKey(), entry.getValue());  
    98.             }  
    99.         }  
    100.     }  
    101.   
    102.     private void findEntryByNext(Entry<K, V> entry, List<Entry<K, V>> entryList) {  
    103.         if(entry!=null && entry.next!=null){  
    104.             //这个entry对象已经造成链表结构  
    105.             entryList.add(entry);  
    106.             //递归 将链表中的entry实体 都一次封装到entryList链表中  
    107.             findEntryByNext(entry.next, entryList);  
    108.         }else{  
    109.             entryList.add(entry);  
    110.         }  
    111.     }  
    112.   
    113.     //快取  
    114.     public V get(K k) {  
    115.         //经过key来计算出 存储的位置  
    116.         int index=getIndex(k,table.length);  
    117.               
    118.         Entry<K, V> entry=table[index];  
    119.           
    120.         if(entry==null){  
    121.             throw new NullPointerException();  
    122.         }  
    123.   
    124.         return findValueByKey(k,entry);  
    125.     }  
    126.       
    127.     private V findValueByKey(K k, Entry<K, V> entry) {  
    128.           
    129.         if(k == entry.getKey() || k.equals(entry.getKey())){  
    130.             return entry.v;  
    131.         }else if(entry.next!=null){  
    132.             return findValueByKey(k,entry.next);  
    133.         }  
    134.         return null;  
    135.     }  
    136.   
    137.   
    138.     class Entry<K, V> implements DIYMap.Entry<K, V>{  
    139.   
    140.         K k;  
    141.         V v;  
    142.         //指向被this挤压下去的entry  
    143.         Entry<K, V> next;  
    144.           
    145.         public Entry(K k, V v, Entry<K, V> next) {  
    146.             this.k = k;  
    147.             this.v = v;  
    148.             this.next = next;  
    149.         }  
    150.   
    151.         @Override  
    152.         public K getKey() {  
    153.             return k;  
    154.         }  
    155.   
    156.         @Override  
    157.         public V getValue() {  
    158.             return v;  
    159.         }  
    160.           
    161.     }  
    162. }  

     

  • 看过那些Java集合类的源码

1.三、进程和线程

  • 线程和进程的概念、并行和并发的概念

  •  

    进程和线程

    进程是一个程序的实例。每一个进程都有本身的虚拟地址空间和控制线程,线程是操做系统调度器(Schduler)分配处理器时间的基础单元。

    并发:

    讲并发以前,要先看一张图:

    这里写图片描述

    1. Concurrency,是并发的意思。并发的实质是一个物理CPU(也能够多个物理CPU) 在若干道程序(或线程)之间多路复用,并发性是对有限物理资源强制行使多用户共享以提升效率。
    2. 微观角度:全部的并发处理都有排队等候,唤醒,执行等这样的步骤,在微观上他们都是序列被处理的,若是是同一时刻到达的请求(或线程)也会根据优先级的不一样,而前后进入队列排队等候执行。
    3. 宏观角度:多个几乎同时到达的请求(或线程)在宏观上看就像是同时在被处理。
    4. 通俗点讲,并发就是只有一个CPU资源,程序(或线程)之间要竞争获得执行机会。图中的第一个阶段,在A执行的过程当中B,C不会执行,由于这段时间内这个CPU资源被A竞争到了,同理,第二个阶段只有B在执行,第三个阶段只有C在执行。其实,并发过程当中,A,B,C并非同时在进行的(微观角度)。但又是同时进行的(宏观角度)。

    并行:

    一样,在讲并行以前,要先看一张图:

    这里写图片描述

    1. Parallelism,即并行,指两个或两个以上事件(或线程)在同一时刻发生,是真正意义上的不一样事件或线程在同一时刻,在不一样CPU资源呢上(多核),同时执行。
    2. 并行,不存在像并发那样竞争,等待的概念。
    3. 图中,A,B,C都在同时运行(微观,宏观)。

    经过多线程实现并发,并行:

    1. java中的Thread类定义了多线程,经过多线程能够实现并发或并行。
    2. 在CPU比较繁忙,资源不足的时候(开启了不少进程),操做系统只为一个含有多线程的进程分配仅有的CPU资源,这些线程就会为本身尽可能多抢时间片,这就是经过多线程实现并发,线程之间会竞争CPU资源争取执行机会。
    3. 在CPU资源比较充足的时候,一个进程内的多线程,能够被分配到不一样的CPU资源,这就是经过多线程实现并行。
    4. 至于多线程实现的是并发仍是并行?上面所说,所写多线程可能被分配到一个CPU内核中执行,也可能被分配到不一样CPU执行,分配过程是操做系统所为,不可人为控制。全部,若是有人问我我所写的多线程是并发仍是并行的?我会说,都有可能。
    5. 无论并发仍是并行,都提升了程序对CPU资源的利用率,最大限度地利用CPU资源。
  • 建立线程的方式及实现

  •  

    1. 继承Thread类建立线程类

     

    [java]  view plain  copy
     
    1. package com.thread;    
    2.     
    3. public class FirstThreadTest extends Thread{    
    4.     int i = 0;    
    5.     //重写run方法,run方法的方法体就是现场执行体    
    6.     public void run()    
    7.     {    
    8.         for(;i<100;i++){    
    9.         System.out.println(getName()+"  "+i);    
    10.             
    11.         }    
    12.     }    
    13.     public static void main(String[] args)    
    14.     {    
    15.         for(int i = 0;i< 100;i++)    
    16.         {    
    17.             System.out.println(Thread.currentThread().getName()+"  : "+i);    
    18.             if(i==20)    
    19.             {    
    20.                 new FirstThreadTest().run();    
    21.                 new FirstThreadTest().run();    
    22.             }    
    23.         }    
    24.     }    
    25.     
    26. }    


    2. 经过Runable接口建立线程类

     

    [java]  view plain  copy
     
    1. package com.thread;    
    2.     
    3. public class RunnableThreadTest implements Runnable    
    4. {    
    5.     
    6.     private int i;    
    7.     public void run()    
    8.     {    
    9.         for(i = 0;i <100;i++)    
    10.         {    
    11.             System.out.println(Thread.currentThread().getName()+" "+i);    
    12.         }    
    13.     }    
    14.     public static void main(String[] args)    
    15.     {    
    16.         for(int i = 0;i < 100;i++)    
    17.         {    
    18.             System.out.println(Thread.currentThread().getName()+" "+i);    
    19.             if(i==20)    
    20.             {    
    21.                 RunnableThreadTest rtt = new RunnableThreadTest();    
    22.                 new Thread(rtt,"新线程1").start();    
    23.                 new Thread(rtt,"新线程2").start();    
    24.             }    
    25.         }    
    26.     
    27.     }    
    28.     
    29. }    


    3. 经过Callable和FutureTask建立线程

        a. 建立Callable接口的实现类,并实现call()方法;
        b. 建立Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callback对象的call()方法的返回值;
        c. 使用FutureTask对象做为Thread对象的target建立并启动新线程;

        d. 调用FutureTask对象的get()方法来得到子线程执行结束后的返回值。

     

    [java]  view plain  copy
     
    1. package com.demo;  
    2.   
    3. import java.util.concurrent.Callable;    
    4. import java.util.concurrent.ExecutionException;    
    5. import java.util.concurrent.FutureTask;    
    6.     
    7. public class CallableThreadTest implements Callable<Integer>    
    8. {    
    9.     
    10.     public static void main(String[] args)    
    11.     {    
    12.         CallableThreadTest ctt = new CallableThreadTest();    
    13.         FutureTask<Integer> ft = new FutureTask<Integer>(ctt);    
    14. //        Thread thread = new Thread(ft,"有返回值的线程");  
    15. //        thread.start();  
    16.         for(int i = 0;i < 100;i++)    
    17.         {    
    18.             System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);    
    19.             if(i==20)    
    20.             {    
    21.                 new Thread(ft,"有返回值的线程").start();    
    22.             }    
    23.         }    
    24.         try    
    25.         {    
    26.             System.out.println("子线程的返回值:"+ft.get());    
    27.         } catch (InterruptedException e)    
    28.         {    
    29.             e.printStackTrace();    
    30.         } catch (ExecutionException e)    
    31.         {    
    32.             e.printStackTrace();    
    33.         }    
    34.     
    35.     }    
    36.     
    37.     @Override    
    38.     public Integer call() throws Exception    
    39.     {    
    40.         int i = 0;    
    41.         for(;i<100;i++)    
    42.         {    
    43.             System.out.println(Thread.currentThread().getName()+" "+i);    
    44.         }    
    45.         return i;    
    46.     }    
    47.     
    48. }    

     

    4. 经过线程池建立线程

    [java]  view plain  copy
     
    1. /** 
    2.  *  
    3.  */  
    4. package com.demo;  
    5.   
    6. import java.util.concurrent.ExecutorService;  
    7. import java.util.concurrent.Executors;  
    8.   
    9. /** 
    10.  * @author Maggie 
    11.  * 
    12.  */  
    13. public class ThreadPool   
    14. {  
    15.     /* POOL_NUM */  
    16.     private static int POOL_NUM = 10;  
    17.       
    18.     /** 
    19.      * Main function 
    20.      */  
    21.     public static void main(String[] args)  
    22.     {  
    23.         ExecutorService executorService = Executors.newFixedThreadPool(5);  
    24.         for(int i = 0; i<POOL_NUM; i++)  
    25.         {  
    26.             RunnableThread thread = new RunnableThread();  
    27.             executorService.execute(thread);  
    28.         }  
    29.     }  
    30. }  
    31.   
    32. class RunnableThread implements Runnable  
    33. {  
    34.     private int THREAD_NUM = 10;  
    35.     public void run()  
    36.     {  
    37.         for(int i = 0; i<THREAD_NUM; i++)  
    38.         {  
    39.             System.out.println("线程" + Thread.currentThread() + " " + i);  
    40.         }   
    41.     }  
    42. }  
  • 进程间通讯的方式(线程间通讯

  •  

    1.管道(pipe):

         管道可用于具备亲缘关系进程间的通讯,有名管道除了具备管道所具备的功能外,它还容许无亲缘关系进程间的通讯。

       2.信号(signal):

         信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通讯方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上能够说是一致得。

       3.消息队列(message queue):

         消息队列是消息的连接表,它克服了上两种通讯方式中信号量有限的缺点,具备写权限得进程能够按照必定得规则向消息队列中添加新信息;对消息队列有读权限得进程则能够从消息队列中读取信息。

       4.共享内存(shared memory):

         能够说这是最有用的进程间通讯方式。它使得多个进程能够访问同一块内存空间,不一样进程能够及时看到对方进程中对共享内存中数据得更新。这种方式须要依靠某种同步操做,如互斥锁和信号量等。

       5.信号量(semaphore):

         主要做为进程之间及同一种进程的不一样线程之间得同步和互斥手段。

       6.套接字(socket);

         这是一种更为通常得进程间通讯机制,它可用于网络中不一样机器之间的进程间通讯,应用很是普遍。

  • 说说 CountDownLatch、CyclicBarrier 原理和区别(线程间的通讯)(例子

  • 1.闭锁CountDownLatch作减计数,而栅栏CyclicBarrier则是加计数。
    2.CountDownLatch是一次性的,CyclicBarrier能够重用。
    3.CountDownLatch强调一个线程等多个线程完成某件事情。CyclicBarrier是多个线程互等,等你们都完成。
    4.鉴于上面的描述,CyclicBarrier在一些场景中能够替代CountDownLatch实现相似的功能。

    另外,值得一提的是,CountDownLatch和CyclicBarrier在建立和启动线程时,都没有明确提到同时启动所有线程,事实上这在技术上是不大可能,没必要要,不提倡的。

  • 说说 Semaphore 原理(代码

  •  

    Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它经过协调各个线程,以保证合理的使用公共资源,就比如如今的旅游景点限流。

    主要方法:
     * acquire():今后信号量获取一个许可,在提供一个许可前一直将线程阻塞,不然线程被中断。 获取一个许可(若是提供了一个)并当即返回,将可用的许可数减 1。
     * release():释放一个许可,将其返回给信号量,即:将可用的许可数增长 1protected方法。

  • 说说 Exchanger 原理(代码

  •  

    Java并发API提供了一种容许2个并发任务间相互交换数据的同步应用。更具体的说,Exchanger类容许在2个线程间定义同步点,当2个线程到达这个点,他们相互交换数据类型,使用第一个线程的数据类型变成第二个的,而后第二个线程的数据类型变成第一个的。

    用于实现两我的之间的数据交换,每一个人在完成必定的事务后想与对方交换数据,第一个先拿出数据的人将一直等待第二我的拿着数据到来时,才能彼此交换数据。

  • ThreadLocal 原理分析,ThreadLocal为何会出现OOM,出现的深层次原理(代码)(代码

  •  

    ThreadLocal类用来提供线程内部的局部变量。这些变量在多线程环境下访问(经过get或set方法访问)时能保证各个线程里的变量相对独立于其余线程内的变量,ThreadLocal实例一般来讲都是private static类型。 
    总结:ThreadLocal不是为了解决多线程访问共享变量,而是为每一个线程建立一个单独的变量副本,提供了保持对象的方法和避免参数传递的复杂性。

    ThreadLocal的主要应用场景为按线程多实例(每一个线程对应一个实例)的对象的访问,而且这个对象不少地方都要用到。例如:同一个网站登陆用户,每一个用户服务器会为其开一个线程,每一个线程中建立一个ThreadLocal,里面存用户基本信息等,在不少页面跳转时,会显示用户信息或者获得用户的一些信息等频繁操做,这样多线程之间并无联系并且当前线程也能够及时获取想要的数据。

  • 讲讲线程池的实现原理(代码

  • 多线程技术主要解决处理器单元内多个线程执行的问题,它能够显著减小处理器单元的闲置时间,增长处理器单元的吞吐能力。    
        假设一个服务器完成一项任务所需时间为:T1 建立线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。

        若是:T1 + T3 远大于 T2,则能够采用线程池,以提升服务器性能。
                    一个线程池包括如下四个基本组成部分:
                    一、线程池管理器(ThreadPool):用于建立并管理线程池,包括 建立线程池,销毁线程池,添加新任务;
                    二、工做线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,能够循环的执行任务;
                    三、任务接口(Task):每一个任务必须实现的接口,以供工做线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工做,任务的执行状态等;
                    四、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
                    
        线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提升服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
        线程池不只调整T1,T3产生的时间段,并且它还显著减小了建立线程的数目,看一个例子:
        假设一个服务器一天要处理50000个请求,而且每一个请求须要一个单独的线程完成。在线程池中,线程数通常是固定的,因此产生线程总数不会超过线程池中线程的数目,而若是服务器不利用线程池来处理这些请求则线程总数为50000。通常线程池大小是远小于50000。因此利用线程池的服务器程序不会为了建立50000而在处理请求时浪费时间,从而提升效率。

     

        代码实现中并无实现任务接口,而是把Runnable对象加入到线程池管理器(ThreadPool),而后剩下的事情就由线程池管理器(ThreadPool)来完成了

     

    Java经过ThreadPoolExecutor提供线程池:

    ThreadPoolExecutor做为java.util.concurrent包对外提供基础实现,之内部线程池的形式对外提供管理任务执行,线程调度,线程池管理等等服务; 

    线程池的参数:
    corePoolSize 为核心线程;
    maximunPoolSize为最大线程;
    keepAliveTime为最长生命时间;
    unit是其时间单位;
    workQueue任务队列;
    handler是过多线程以后的策略
    [java]  view plain  copy
     
     
     
    1. ExecutorService singleThreadPool = new ThreadPoolExecutor(4,  
    2.         10,  
    3.         0L,  
    4.         TimeUnit.MILLISECONDS,  
    5.         new LinkedBlockingQueue<Runnable>(1024),  
    6.         namedThreadFactory,  
    7.         new ThreadPoolExecutor.AbortPolicy());  
  • 线程池的几种实现方式(代码)(代码

  • newCachedThreadPool建立一个可缓存线程池,若是线程池长度超过处理须要,可灵活回收空闲线程,若无可回收,则新建线程。
    newFixedThreadPool 建立一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    newScheduledThreadPool 建立一个定长线程池,支持定时及周期性任务执行。
    newSingleThreadExecutor 建立一个单线程化的线程池,它只会用惟一的工做线程来执行任务,保证全部任务按照指定顺序(FIFO, LIFO, 优先级)执行。

  • 线程的生命周期,状态是如何转移的

  •  

    线程的生命周期开始:当Thread对象建立完成时

    线程的生命周期结束:①当run()方法中代码正常执行完毕

                                        ②线程抛出一个未捕获的异或错误时。

    线程整个生命周期的五个阶段:

    新建状态,就绪状态,运行状态,阻塞状态,死亡状态。

    新建状态:
            当Thread对象建立完成时, 使用 new 关键字和 Thread 类或其子类创建一个线程对象后,该线程对象就处于新建状态。但此时它还不能运行,仅仅由虚拟机为其分配了内存,它保持这个状态直到程序 start() 这个线程。


    就绪状态:
           当线程对象调用了start()方法以后,该线程就进入就绪状态。就绪状态的线程处于就绪队列(可运行池里)中,此时它具有了运行的条件,可否得到CPU的使用权开始运行,还须要等待系统的调度。


    运行状态:
         若是就绪状态的线程得到CPU的使用权,开始执行 run()中的线程执行体,此时线程便处于运行状态。当使用完系统分配的时间后,系统就会剥夺该线程占用的CPU资源,让其余线程得到执行的机会。处于运行状态的线程最为复杂,它能够变为阻塞状态、就绪状态和死亡状态。


    阻塞状态:
    一个正在线程的线程在某种特殊形况下,如执行了耗时的输入/输出操做时,sleep(睡眠)、suspend(挂起)等方法,失去所占用资源以后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或得到设备资源后能够从新进入就绪状态。能够分为三种:
    等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。,若想再此进入就绪状态就须要使用notify()方法唤醒该线程。
    同步阻塞:线程在获取 synchronized 同步锁失败(由于同步锁被其余线程占用)。
    其余阻塞:经过调用线程的 sleep() 、或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 方法时间到了以后,join() 新加的线程运行结束后, I/O 处理完毕,线程从新转入就绪状态。


    死亡状态:
    一个运行状态的线程完成任务或者其余终止条件发生时,该线程就切换到终止状态。

  • 可参考:《Java多线程编程核心技术

1.四、锁机制

  • 说说线程安全问题,什么是线程安全,如何保证线程安全

  •  

    线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其余线程不能进行访问直到该线程读取完,其余线程才可以使用。不会出现数据不一致或者数据污染。

     

    1. 不共享线程间的变量;
    2. 设置属性变量为不可变变量;
    3. 每一个共享的可变变量都使用一个肯定的锁保护;
    4. 1使用线程安全的类 
      2使用synchronized同步代码块,或者用Lock锁 
      3多线程并发状况下,线程共享的变量改成方法局部级变量

     

  • 重入锁的概念,重入锁为何能够防止死锁(代码)(代码

  • 所谓重入锁,指的是以线程为单位,当一个线程获取对象锁以后,这个线程能够再次获取本对象上的锁,而其余的线程是不能够的
     *synchronized 和   ReentrantLock 都是可重入锁
     *可重入锁的意义在于防止死锁
     *实现原理实现是经过为每一个锁关联一个请求计数和一个占有它的线程。
     *当计数为0时,认为锁是未被占有的。线程请求一个未被占有的锁时,jvm将记录锁的占有者,而且讲请求计数器置为1 。
     *若是同一个线程再次请求这个锁,计数将递增;
     *每次占用线程退出同步块,计数器值将递减。直到计数器为0,锁被释放。

  • 产生死锁的四个条件(互斥、请求与保持、不剥夺、循环等待)(代码

  • (1) 互斥条件:一个资源每次只能被一个进程使用。
    (2) 占有且等待:一个进程因请求资源而阻塞时,对已得到的资源保持不放。
    (3)不可强行占有:进程已得到的资源,在末使用完以前,不能强行剥夺。
    (4) 循环等待条件:若干进程之间造成一种头尾相接的循环等待资源关系。
    这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之

    一不知足,就不会发生死锁。

  • 如何检查死锁(经过jConsole检查死锁)

  •  

    Jconsole

     

    Jconsole是JDK自带的图形化界面工具。

    1. 使用JDK给咱们的的工具JConsole,能够经过打开cmd而后输入jconsole打开。

    2. 链接到须要查看的进程。

  •  

    Jstack

     

    Jstack是JDK自带的命令行工具,主要用于线程Dump分析。

    1. 咱们先用Jps来查看java进程id。 2.看一下jstack的使用。
  • volatile 实现原理(禁止指令重排、刷新内存)(代码)

  • synchronized 实现原理(对象监视器)

  • 这两段话就解释了synchronized的实现原理: 
       
      monitorenter: 
      每一个对象有一个monitor,即监视器,当且仅当monitor被占用时,这个monitor就被锁住了。线程执行monitorenter指令是为了尝试获取该monitor的全部权,过程为: 
      1) 若是一个monitor的进入数为0,那么该线程直接进入monitor,而且将monitor进入数置为1,该线程成为该monitor的全部者; 
      2) 若是该进程是已经占用该monitor,则直接进入,而且monitor进入数加1; 
      3)若是该进程未占有该monitor,即monitor被其余线程所占有,那么该线程会被阻塞,直到该monitor的进入数变为0,此时该线程会再次尝试获取该monitor。 
       
      monitorexit: 
      执行monitorexit指令的线程必须是已经拥有该monitor的线程,执行monitorexit指令后,该monitor的进入数减1,直到该monitor的进入数减为0,此时该线程再也不是该monitor的全部者,其余被阻塞进入该monitor的线程能够尝试获取该monitor的全部权。 

  • synchronized 与 lock 的区别(代码

  •  

    类别 synchronized Lock
    存在层次 Java的关键字,在jvm层面上 是一个类
    锁的释放 一、以获取锁的线程执行完同步代码,释放锁 二、线程执行发生异常,jvm会让线程释放锁 在finally中必须释放锁,否则容易形成线程死锁
    锁的获取 假设A线程得到锁,B线程等待。若是A线程阻塞,B线程会一直等待 分状况而定,Lock有多个锁获取的方式,具体下面会说道,大体就是能够尝试得到锁,线程能够不用一直等待
    锁状态 没法判断 能够判断
    锁类型 可重入 不可中断 非公平 可重入 可判断 可公平(二者皆可)
    性能 少许同步 大量同步
  • AQS同步队列(代码

  • 队列同步器(简称:同步器)AbstractQueuedSynchronizer(英文简称:AQS,也是面试官常问的什么是AQS的AQS),是用来构建锁或者其余同步组件的基础框架,它使用了一个int成员变量表示同步状态,经过内置的FIFO队列来完成资源获取线程的排队工做。

  • CAS无锁的概念、乐观锁和悲观锁(乐观锁、悲观锁)(CAS)(代码)

  • CAS的语义是“我认为V的值应该为A,若是是,那么将V的值更新为B,不然不修改并告诉V的值实际为多少”,CAS是项 乐观锁 技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知此次竞争中失败,并能够再次尝试。CAS有3个操做数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改成B,不然什么都不作。 

  • 常见的原子操做类(代码

  •  

    • AtomicBoolean:原子更新布尔变量
    • AtomicInteger:原子更新整型变量
    • AtomicLong:原子更新长整型变量

     

  • 什么是ABA问题,出现ABA问题JDK是如何解决的(代码

  • 乐观锁的业务场景及实现方式(代码

  • Java 8并法包下常见的并发类(代码

  • 偏向锁、轻量级锁、重量级锁、自旋锁的概念(各类锁

  • 可参考:《Java多线程编程核心技术

1.五、JVM

  • JVM运行时内存区域划分(代码)

  •  

    程序计数器(Program Counter Register)、Java栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)。

      如上图所示,JVM中的运行时数据区应该包括这些部分。在JVM规范中虽然规定了程序在执行期间运行时数据区应该包括这几部分,可是至于具体如何实现并无作出规定,不一样的虚拟机厂商能够有不一样的实现方式

  • 内存溢出OOM和堆栈溢出SOE的示例及缘由、如何排查与解决(代码

  • 如何判断对象是否能够回收或存活(代码

  • 常见的GC回收算法及其含义(代码

  • 常见的JVM性能监控和故障处理工具类:jps、jstat、jmap、jinfo、jconsole等(代码

  • JVM如何设置参数(代码

  • JVM性能调优(代码

  • 类加载器、双亲委派模型、一个类的生命周期、类是如何加载到JVM中的(代码

  • 类加载的过程:加载、验证、准备、解析、初始化

  •  

    1. 加载(Loading);
    2. 验证(Verification);
    3. 准备 (Preparation);
    4. 解析(Resolution);
    5. 初始化(Initialization);
    6. 使用(Using)
    7. 卸载 (Unloading)

    其中验证、准备和解析三部分称为链接,在Java语言中,类型的加载和链接过程都是在程序运行期间完成的(Java能够动态扩展的语言特性就是依赖运行期动态加载、动态链接这个特色实现的),这样会在类加载时稍微增长一些性能开销,可是却为Java应用程序提供高度的灵活性 
    加载、验证、准备、初始化和卸载这5个阶段的顺序是固定的(即:加载阶段必须在验证阶段开始以前开始,验证阶段必须在准备阶段开始以前开始等。这些阶段都是互相交叉地混合式进行的,一般会在一个阶段的执行过程当中调用或激活另外一个阶段),解析阶段则不必定,在某些状况下,解析阶段有可能在初始化阶段结束后开始,以支持Java的动态绑定(动态绑定是指在执行期间(非编译期)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。程序运行过程当中,把函数(或过程)调用与响应调用所须要的代码相结合的过程称为动态绑定。)

  • 强引用、软引用、弱引用、虚引用

  • 1.强引用 
    之前咱们使用的大部分引用实际上都是强引用,这是使用最广泛的引用。若是一个对象具备强引用,那就相似于必不可少的生活用品,垃圾回收器毫不会回收它。当内存空 间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具备强引用的对象来解决内存不足问题。 

  •  

  •  

  •  

  • 强引用: 

  • Java内存模型JMM(代码

1.六、设计模式

  • 常见的设计模式

  • 设计模式的的六大原则及其含义

  • 常见的单例模式以及各类实现方式的优缺点,哪种最好,手写常见的单例模式

  • 设计模式在实际场景中的应用

  • Spring中用到了哪些设计模式

  • MyBatis中用到了哪些设计模式

  • 你项目中有使用哪些设计模式

  • 说说经常使用开源框架中设计模式使用分析

  • 动态代理很重要!!!

1.七、数据结构

  • 树(二叉查找树、平衡二叉树、红黑树、B树、B+树)

  • 深度有限算法、广度优先算法

  • 克鲁斯卡尔算法、普林母算法、迪克拉斯算法

  • 什么是一致性Hash及其原理、Hash环问题

  • 常见的排序算法和查找算法:快排、折半查找、堆排序等

1.八、网络/IO基础

    • BIO、NIO、AIO的概念

    •  

      1.Java对BIO、NIO、AIO的支持: 
      ① Java BIO : 同步并阻塞,服务器实现模式为一个链接一个线程,即客户端有链接请求时服务器端就须要启动一个线程进行处理,若是这个链接不作任何事情会形成没必要要的线程开销,固然能够经过线程池机制改善。 
      ② Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的链接请求都会注册到多路复用器上,多路复用器轮询到链接有I/O请求时才启动一个线程进行处理。 
      ③ Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,

      2.BIO、NIO、AIO适用场景分析: 
      ① BIO方式适用于链接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4之前的惟一选择,但程序直观简单易理解。 
      ② NIO方式适用于链接数目多且链接比较短(轻操做)的架构,好比聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。 
      ③ AIO方式使用于链接数目多且链接比较长(重操做)的架构,好比相册服务器,充分调用OS参与并发操做,编程比较复杂,JDK7开始支持。

    •  

      3.Java NIO和IO的主要区别

      IO NIO
      面向流 面向缓冲
      阻塞IO 非阻塞IO
      选择器

      面向流与面向缓冲 
      Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取全部字节,它们没有被缓存在任何地方。此外,它不能先后移动流中的数据。若是须要先后移动从流中读取的数据,须要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不一样。数据读取到一个它稍后处理的缓冲区,须要时可在缓冲区中先后移动。这就增长了处理过程当中的灵活性。可是,还须要检查是否该缓冲区中包含全部您须要处理的数据。并且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里还没有处理的数据。 
      阻塞与非阻塞IO 
      Java IO的各类流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据彻底写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,可是它仅能获得目前可用的数据,若是目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,因此直至数据变的能够读取以前,该线程能够继续作其余的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不须要等待它彻底写入,这个线程同时能够去作别的事情。 线程一般将非阻塞IO的空闲时间用于在其它通道上执行IO操做,因此一个单独的线程如今能够管理多个输入和输出通道(channel)。 
      选择器(Selectors) 
      Java NIO的选择器容许一个单独的线程来监视多个输入通道,你能够注册多个通道使用一个选择器,而后使用一个单独的线程来“选择”通道:这些通道里已经有能够处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

    • 什么是长链接和短链接(代码)

    • Http1.0和2.0相比有什么区别,可参考《Http 2.0》(代码

    • Https的基本概念(代码

    • 三次握手和四次挥手、为何挥手须要四次(代码)(代码

    • 从浏览器中输入URL到页面加载的发生了什么?可参考《代码

    • TCP和UDP(代码)(代码

2、数据存储和消息队列

2.一、数据库

  • MySQL 索引使用的注意事项(点击打开连接)

  •  

    1.WHERE字句的查询条件里有 NOT IN 、<>、!=,MYSQL将没法使用索引;

    2.WHERE字句的查询条件里使用了函数,MYSQL将没法使用索引

    3.在JOIN操做中,MYSQL只有在主键和外键的数据类型相同时才能使用索引,不然即便创建了索引也不会使用

    4.使用了比较操做符LIKE和REGEXP,MYSQL只有在搜索模板的第一个字符不是通配符的状况下才能使用索引。好比说,若是查询条件是LIKE 'abc%',MYSQL将使用索引;若是条件是LIKE '%abc'或者'_abc%',MYSQL将不使用索引。

    5.在ORDER BY操做中,MYSQL只有在排序条件不是一个查询条件表达式的状况下才使用索引。尽管如此,在涉及多个数据表的查询里,即便有索引可用,那些索引在加快ORDER BY操做方面也没什么做用。

    使用order by特别提示:
    1>mysql一次查询只能使用一个索引。若是要对多个字段使用索引,创建复合索引。
    2>在ORDER BY操做中,MySQL只有在排序条件不是一个查询条件表达式的状况下才使用索引。

    6.若是某个数据列里包含着许多重复的值,就算为它创建了索引也不会有很好的效果。好比说,若是某个数据列里包含了净是些诸如“0/1”或“Y/N”等值,就没有必要为它建立一个索引。

    7.使用短索引: 对串列进行索引,若是能够就应该指定一个前缀长度。例如,若是有一个char(255)的列,若是在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不只能够提升查询速度并且能够节省磁盘空间和I/O操做。(针对hash的索引方式,对每一个值都作hash值存储I/O操做存储索引信息)

    8.若是条件中有or(而且其中有or的条件是不带索引的),即便其中有条件带索引也不会使用(这也是为何尽可能少用or的缘由)。注意:要想使用or,又想让索引生效,只能将or条件中的每一个列都加上索引

    9.若是列类型是字符串,那必定要在条件中将数据使用引号引用起来,不然不使用索引

    10.对于那些定义为text、image和bit数据类型的列不该该增长索引。由于这些列的数据量要么至关大,要么取值不多。

    11.只要列中包含有NULL值,都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此符合索引就是无效的。

    12.组合索引之最左前缀:顾名思义,就是最左优先,上例中咱们建立了name_age_course多列索引,至关于建立了(name)单列索引,(name,age)组合索引以及(name,age,course)组合索引。在单独使用(age)、(age,course)等条件下索引无效

  • DDL、DML、DCL分别指什么

  • DML(data manipulation language): 
    它们是SELECT、UPDATE、INSERT、DELETE,就象它的名字同样,这4条命令是用来对数据库里的数据进行操做的语言 
    DDL(data definition language): 
    DDL比DML要多,主要的命令有CREATE、ALTER、DROP等,DDL主要是用在定义或改变表(TABLE)的结构,数据类型,表之间的连接和约束等初始化工做上,他们大多在创建表时使用 
    DCL(Data Control Language): 
    是数据库控制功能。是用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)语句。在默认状态下,只有sysadmin,dbcreator,db_owner或db_securityadmin等人员才有权力执行DCL

  • explain命令

  • 在工做中,咱们用于捕捉性能问题最经常使用的就是打开慢查询,定位执行效率差的SQL,那么当咱们定位到一个SQL之后还不算完事,咱们还须要知道该SQL的执行计划,好比是全表扫描,仍是索引扫描,这些都须要经过EXPLAIN去完成。EXPLAIN命令是查看优化器如何决定执行查询的主要方法。能够帮助咱们深刻了解MySQL的基于开销的优化器,还能够得到不少可能被优化器考虑到的访问策略的细节,以及当运行SQL语句时哪一种策略预计会被优化器采用。须要注意的是,生成的QEP并不肯定,它可能会根据不少因素发生改变。MySQL不会将一个QEP和某个给定查询绑定,QEP将由SQL语句每次执行时的实际状况肯定,即使使用存储过程也是如此。尽管在存储过程当中SQL语句都是预先解析过的,但QEP仍然会在每次调用存储过程的时候才被肯定。

  • left join,right join,inner join(点击打开连接)

  • 数据库事物ACID(原子性、一致性、隔离性、持久性)(点击打开连接)

  • 事物的隔离级别(读未提交、读以提交、可重复读、可序列化读)(点击打开连接)

  • 一、TransactionDefinition接口中定义五个隔离级别(isolation):

  • 脏读、幻读、不可重复读

  •  脏读:一个事务读取了另外一个事务改写但还未提交的数据,若是这些数据被回滚,则读到的数据是无效的。
     不可重复读:在同一事务中,屡次读取到同一数据的结果有所不一样。也就是说,后续读取能够读到另外一个事务已经提交的更新数据。
     幻读:一个事务读取了几行记录后,另外一个事务插入一些记录,幻读就发生了,再查询的时候,第一个事务就会发现有些原来没有的记录。

  • 数据库的几大范式(点击打开连接)

  •  

    第一范式(1NF): 强调的是列的原子性,即列不可以再分红其余几列。(属性存在子集)

    第二范式(2NF): 首先是 1NF,另外包含两部份内容,一是表必须有一个主键;二是没有包含在主键中的列必须彻底依赖于主键,而不能只依赖于主键的一部分。 

     第三范式(3NF):首先是 2NF,另外非主键列必须直接依赖于主键,不能存在传递依赖。即不能存在:非主键列 A 依赖于非主键列                               B,非主键列 B 依赖于主键的状况。(A->B  C->A这里得出结论C->B就是传递依赖,箭头理解:B依赖A)

  • 数据库常见的命令

  • 说说分库与分表设计(点击打开连接)

  • 分库与分表带来的分布式困境与应对之策(如何解决分布式下的分库分表,全局表?)(点击打开连接)

  • 说说 SQL 优化之道(点击打开连接)

  •  

    *当只要一行数据时使用 LIMIT 1   **MySQL数据库引擎会在找到一条数据后中止搜索,而不是继续日后查少下一条符合记录的数据

    *千万不要 ORDER BY RAND()

    *避免 SELECT * 

     *应尽可能避免在 where 子句中对字段进行表达式操做

     *应尽可能避免在where子句中对字段进行函数操做

     *不少时候用 exists 代替 in 是一个好的选择

     *应尽可能避免在 where 子句中使用 != 或 <> 操做符

     *对于连续的数值,能用 between 就不要用 in 

     *应尽可能避免在 where 子句中使用 or 来链接条件  **若是一个字段有索引,一个字段没有索引,将致使引擎放弃使用索引而进行全表扫描

     *通常来讲,预判一下过滤条件的范围。因为数据库是从后向前解析 SQL 语句的,一般建议把能过滤最多结果的条件放在后面,(不是必定的,mysql会根据索引作一些优化),尽可能使过滤数据量大的条件先被执行

     *Join语句的优化   **尽量减小Join语句中的NestedLoop的循环总次数;“永远用小结果集驱动大的结果集”

     *ORDER BY的实现与优化  **优化Query语句中的ORDER BY的时候,尽量利用已有的索引来避免实际的排序计算(order字段尽可能出如今条件中,而且有索引),能够很大幅度的提高ORDER BY操做的性能。

    *GROUP BY的实现与优化   **因为GROUP BY实际上也一样须要进行排序操做,并且与ORDER BY相比,GROUP BY主要只是多了排序以后的分组操做。固然,若是在分组的时候还使用了其余的一些聚合函数,那么还须要一些聚合函数的计算。因此,在GROUP BY的实现过程当中,与ORDER BY同样也能够利用到索引。

  • MySQL遇到的死锁问题、如何排查与解决(点击打开连接)

  • 存储引擎的 InnoDB与MyISAM区别,优缺点,使用场景

  • 主要区别:

    • 1).MyISAM是非事务安全型的,而InnoDB是事务安全型的。
    • 2).MyISAM锁的粒度是表级,而InnoDB支持行级锁定。
    • 3).MyISAM支持全文类型索引,而InnoDB不支持全文索引。
    • 4).MyISAM相对简单,因此在效率上要优于InnoDB,小型应用能够考虑使用MyISAM。
    • 5).MyISAM表是保存成文件的形式,在跨平台的数据转移中使用MyISAM存储会省去很多的麻烦。
    • 6).InnoDB表比MyISAM表更安全,能够在保证数据不会丢失的状况下,切换非事务表到事务表(alter table tablename type=innodb)。
    应用场景:
    • 1).MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。若是应用中须要执行大量的SELECT查询,那么MyISAM是更好的选择。
    • 2).InnoDB用于事务处理应用程序,具备众多特性,包括ACID事务支持。若是应用中须要执行大量的INSERT或UPDATE操做,则应该使用InnoDB,这样能够提升多用户并发操做的性能。
  • 索引类别(B+树索引、全文索引、哈希索引)、索引的原理

  • 什么是自适应哈希索引(AHI)

  • 为何要用 B+tree做为MySQL索引的数据结构

  • 汇集索引与非汇集索引的区别(点击打开连接)

根本区别

    汇集索引和非汇集索引的根本区别是表记录的排列顺序和与索引的排列顺序是否一致。

汇集索引

    汇集索引表记录的排列顺序和索引的排列顺序一致,因此查询效率快,只要找到第一个索引值记录,其他就连续性的记录在物理也同样连续存放。汇集索引对应的缺点就是修改慢,由于为了保证表中记录的物理和索引顺序一致,在记录插入的时候,会对数据页从新排序。

非汇集索引

    非汇集索引制定了表中记录的逻辑顺序,可是记录的物理和索引不必定一致,两种索引都采用B+树结构,非汇集索引的叶子层并不和实际数据页相重叠,而采用叶子层包含一个指向表中的记录在数据页中的指针方式。非汇集索引层次多,不会形成数据重排。

例子对比两种索引

    汇集索引就相似新华字典中的拼音排序索引,都是按顺序进行,例如找到字典中的“爱”,就里面顺序执行找到“癌”。而非汇集索引则相似于笔画排序,索引顺序和物理顺序并非按顺序存放的。

  • 遇到过索引失效的状况没,何时可能会出现,如何解决

  •  

    1. 若是条件中有or,即便其中有条件带索引也不会使用(这也是为何尽可能少用or的缘由)

      注意:要想使用or,又想让索引生效,只能将or条件中的每一个列都加上索引

      2.对于多列索引,不是使用的第一部分,则不会使用索引

      3.like查询是以%开头(以%结尾是能够的)

      4.若是列类型是字符串,那必定要在条件中将数据使用引号引用起来,不然不使用索引

      5.若是mysql估计使用全表扫描要比使用索引快,则不使用索引

  • limit 20000 加载很慢怎么解决(点击打开连接

  • 如何选择合适的分布式主键方案(点击打开连接)

  • 选择合适的数据存储方案

  • 常见的几种分布式ID的设计方案(点击打开连接)

  •  

    • 1. UUID

      UUID是Universally Unique Identifier的缩写,它是在必定的范围内(从特定的名字空间到全球)惟一的机器生成的标识符,UUID是16字节128位长的数字,一般以36字节的字符串表示,好比:3F2504E0-4F89-11D3-9A0C-0305E82C3301。

      UUID经由必定的算法机器生成,为了保证UUID的惟一性,规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。UUID的复杂特性在保证了其惟一性的同时,意味着只能由计算机生成。

      优势:
      • 本地生成ID,不须要进行远程调用,时延低,性能高。
      缺点:
      • UUID过长,16字节128位,一般以36长度的字符串表示,不少场景不适用,好比用UUID作数据库索引字段。
      • 没有排序,没法保证趋势递增。
    • 2. Flicker方案

      这个方案是由Flickr团队提出,主要思路采用了MySQL自增加ID的机制(auto_increment + replace into)

    •  

      优势:
      • 充分借助数据库的自增ID机制,可靠性高,生成有序的ID。
      缺点:
      • ID生成性能依赖单台数据库读写性能。
      • 依赖数据库,当数据库异常时整个系统不可用。

     

  • 常见的数据库优化方案,在你的项目中数据库如何进行优化的(点击打开连接)

2.二、Redis

  • Redis 有哪些数据类型,可参考《Redis常见的5种不一样的数据类型详解》String list set sortset hash(点击打开连接)

  • Redis 内部结构(点击打开连接

  • Redis 使用场景(点击打开连接

  • Redis 持久化机制,可参考《使用快照和AOF将Redis数据持久化到硬盘中

  • Redis 集群方案与实现

  • Redis 为何是单线程的?(点击打开连接

  • redis利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销

  •  

    整体来讲快速的缘由以下:

    1)绝大部分请求是纯粹的内存操做(很是快速)

    2)采用单线程,避免了没必要要的上下文切换和竞争条件

    3)非阻塞IO

  • 缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级(点击打开连接

  • 使用缓存的合理性问题(点击打开连接

  • Redis常见的回收策略

  •  

    redis 提供 6种数据淘汰策略:

    1. voltile-lru:从已设置过时时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
    2. volatile-ttl:从已设置过时时间的数据集(server.db[i].expires)中挑选将要过时的数据淘汰
    3. volatile-random:从已设置过时时间的数据集(server.db[i].expires)中任意选择数据淘汰
    4. allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
    5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
    6. no-enviction(驱逐):禁止驱逐数据

2.三、消息队列

    • 消息队列的使用场景(点击打开连接)(点击打开连接

    • 消息的重发补偿解决思路

    • 1. 处理失败 指的是MessageListener的onMessage方法里抛出RuntimeException。

      2. Message头里有两个相关字段:Redelivered默认为false,redeliveryCounter默认为0。

      3. 消息先由broker发送给consumer,consumer调用listener,若是处理失败,本地redeliveryCounter++,给broker一个特定应答,broker端的message里redeliveryCounter++,延迟一点时间继续调用,默认1s。超过6次,则给broker另外一个特定应答,broker就直接发送消息到DLQ。

      4. 若是失败2次,consumer重启,则broker再推过来的消息里,redeliveryCounter=2,本地只能再重试4次即会进入DLQ。

      5. 重试的特定应答发送到broker,broker即会在内存将消息的redelivered设置为true,redeliveryCounter++,可是这两个字段都没有持久化,即没有修改存储中的消息记录。因此broker重启时这两个字段会被重置为默认值

    • 消息的幂等性解决思路

    • 消息的堆积解决思路

    • 本身如何实现消息队列

    • 如何保证消息的有序性(点击打开连接

3、开源框架和容器

3.一、SSM/Servlet

  • Servlet的生命周期(连接

  • 加载—>实例化—>服务—>销毁

  • 转发与重定向的区别(连接

  •  

    一句话,转发是服务器行为,重定向是客户端行为。为何这样说呢,这就要看两个动做的工做流程:

    转发过程:客户浏览器发送http请求----》web服务器接受此请求--》调用内部的一个方法在容器内部完成请求处理和转发动做----》将目标资源发送给客户;在这里,转发的路径必须是同一个web容器下的url,其不能转向到其余的web路径上去,中间传递的是本身的容器内的request。在客户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感受不到服务器作了转发的。转发行为是浏览器只作了一次访问请求。

    重定向过程:客户浏览器发送http请求----》web服务器接受后发送302状态码响应及对应新的location给客户浏览器--》客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址----》服务器根据此请求寻找资源并发送给客户。在这里location能够重定向到任意URL,既然是浏览器从新发出了请求,则就没有什么request传递的概念了。在客户浏览器路径栏显示的是其重定向的路径,客户能够观察到地址的变化的。重定向行为是浏览器作了至少两次的访问请求的。

  • BeanFactory 和 ApplicationContext 有什么区别(连接

  • ,二者都是经过xml配置文件加载bean,ApplicationContext和BeanFacotry相比,提供了更多的扩展功能,但其主要区别在于后者是延迟加载,若是Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;而ApplicationContext则在初始化自身是检验,这样有利于检查所依赖属性是否注入;因此一般状况下咱们选择使用ApplicationContext.

  • Spring Bean 的生命周期(点击打开连接

  • Spring IOC 如何实现(点击打开连接

  • java程序中的每一个业务逻辑至少须要两个或以上的对象来协做完成,一般,每一个对象在使用他的合做对象时,本身均要使用像new object() 这样的语法来完成合做对象的申请工做。你会发现:对象间的耦合度高了。而IOC的思想是:Spring容器来实现这些相互依赖对象的建立、协调工做。对象只须要关系业务逻辑自己就能够了。从这方面来讲,对象如何获得他的协做对象的责任被反转了(IOC、DI)。
    这是我对Spring的IOC的体会。DI其实就是IOC的另一种说法。DI是由Martin Fowler 在2004年初的一篇论文中首次提出的。他总结:控制的什么被反转了?就是:得到依赖对象的方式反转了。

  • Spring中Bean的做用域,默认的是哪个

  •  

    1.singleton

    singleton,也称单例做用域。在每一个 Spring IoC 容器中有且只有一个实例,并且其完整生命周期彻底由 Spring 容器管理。对于全部获取该 Bean 的操做 Spring 容器将只返回同一个 Bean。

    须要注意的是,若一个 Bean 未指定 scope 属性,默认也为 singleton 。

    2.prototype

    prototype,也称原型做用域。每次向 Spring IoC 容器请求获取 Bean 都返回一个全新的Bean。相对于 singleton 来讲就是不缓存 Bean,每次都是一个根据 Bean 定义建立的全新 Bean。

    Web 做用域

    1.reqeust

    request,表示每一个请求须要容器建立一个全新Bean。

    在 Spring IoC 容器,即XmlWebApplicationContext 会为每一个 HTTP 请求建立一个全新的 RequestPrecessor 对象。当请求结束后,该对象的生命周期即告结束。当同时有 10 个 HTTP 请求进来的时候,容器会分别针对这 10 个请求建立 10 个全新的 RequestPrecessor 实例,且他们相互之间互不干扰,从不是很严格的意义上说,request 能够看作 prototype 的一种特例,除了场景更加具体以外,语意上差很少。

    2.session

    session,表示每一个会话须要容器建立一个全新 Bean。好比对于每一个用户通常会有一个会话,该用户的用户信息须要存储到会话中,此时能够将该 Bean 配置为 web 做用域。

    3.globalSession

    globalSession,相似于session 做用域,只是其用于 portlet 环境的 web 应用。若是在非portlet 环境将视为 session 做用域。

  • 说说 Spring AOP、Spring AOP 实现原理(点击打开连接

  •  

    AOP技术则偏偏相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减小系统的重复代码,下降模块间的耦合度,并有利于将来的可操做性和可维护性。AOP表明的是一个横向的关系,若是说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以得到其内部的消息。而剖开的切面,也就是所谓的“方面”了。而后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

    使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特色是,他们常常发生在核心关注点的多处,而各处都基本类似。好比权限认证、日志、事务处理。Aop 的做用在于分离系统中的各类关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”

    实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法建立“方面”,从而使得编译器能够在编译期间织入有关“方面”的代码

  • 动态代理(CGLib 与 JDK)、优缺点、性能对比、如何选择(点击打开连接

  •  

    JDK动态代理是面向接口,在建立代理实现类时比CGLib要快,建立代理速度快。

    CGLib动态代理是经过字节码底层继承要代理类来实现(若是被代理类被final关键字所修饰,那么抱歉会失败),在建立代理这一块没有JDK动态代理快,可是运行速度比JDK动态代理要快。

    使用注意:

    若是要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操做(Spirng默认采用JDK动态代理实现机制)

    若是要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。

  • Spring 事务实现方式、事务的传播机制、默认的事务类别(点击打开连接)(点击打开连接)(点击打开连接

  • Spring事务机制是一种典型的策略模式,PlatformTransactionManager表明事务管理接口,但它并不知道到底如何管理事务,它只要求事务管理提供开始事务getTransaction(),提交事务commit()和回滚事务rollback()这三个方法,但具体如何实现则交给其实现类完成。编程人员只须要在配置文件中根据具体须要使用的事务类型作配置,Spring底层就自动会使用具体的事务实现类进行事务操做,而对于程序员来讲,彻底不须要关心底层过程,只须要面向PlatformTransactionManager接口进行编程便可。PlatformTransactionManager接口中提供了以下方法:getTransaction(..), commit(); rollback(); 这些都是与平台无关的事务操做。

  •  

    一、TransactionDefinition接口中定义五个隔离级别(isolation):

    说明:
     脏读:一个事务读取了另外一个事务改写但还未提交的数据,若是这些数据被回滚,则读到的数据是无效的。
     不可重复读:在同一事务中,屡次读取到同一数据的结果有所不一样。也就是说,后续读取能够读到另外一个事务已经提交的更新数据。
     幻读:一个事务读取了几行记录后,另外一个事务插入一些记录,幻读就发生了,再查询的时候,第一个事务就会发现有些原来没有的记录。

    2. 在TransactionDefinition接口中定义了七个事务传播行为(propagationBehavior):

    三、TransactionDefinition接口中定义了事务超时
     所谓事务超时,就是指一个事务所容许执行的最长时间,若是超过该时间限制但事务尚未完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。
     默认设置为底层事务系统的超时值,若是底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。
    四、事务只读属性
     只读事务用于客户代码只读但不修改数据的情形,只读事务用于特定情景下的优化,好比使用Hibernate的时候,默认为读写事务。

  •  

    隔离级别越高,越能保证数据的完整性和一致性,可是对并发性能的影响也越大。

    大多数的数据库默认隔离级别为 Read Commited,好比 SqlServer、Oracle

    少数数据库默认隔离级别为:Repeatable Read 好比: MySQL InnoDB

  • Spring 事务底层原理(点击打开连接

  • Spring事务失效(事务嵌套),JDK动态代理给Spring事务埋下的坑,可参考《JDK动态代理给Spring事务埋下的坑!

  • 如何自定义注解实现功能

  • Spring MVC 运行流程(点击打开连接

  • 在整个Spring MVC框架中,DispatcherServlet处于核心位置,它负责协调和组织不一样组件完成请求处理并返回响应的工做。具体流程为:
    1)客户端发送http请求,web应用服务器接收到这个请求,若是匹配DispatcherServlet的映射路径(在web.xml中配置),web容器将请求转交给DispatcherServlet处理;
    2)DispatcherServlet根据请求的信息及HandlerMapping的配置找处处理该请求的Controller;
    3)Controller完成业务逻辑处理后,返回一个ModelAndView给DispatcherServlet;
    4)DispatcherServlet借由ViewResolver完成ModelAndView中逻辑视图名到真实视图对象View的解析工做;
    5)DispatcherServlet根据ModelAndView中的数据模型对View对象进行视图渲染,最终客户端获得的响应消息多是一个普通的html页面,也多是一个xml或json串,甚至是一张图片或一个PDF文档等不一样的媒体形式。

  •  

    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    文字叙述

    1 用户浏览器发起请求

    2 前端控制器DispatcherServlet首先会去请求Handler(也就是Controller),

    怎么请求Handler----经过查找HandlerMapping(里面有xml或者注解方式配置的Handler映射信息信息)来匹配用户请求url对应的Handler,

    将查找到的请求信息,放入到执行链HandlerExecutionChain中,而后在放入该url对应的拦截器信息。

    而后将执行链HandlerExecutionChain返回给前端控制器DispatcherServlet

    3 前端控制器DispatcherServlet经过请求到的handler,再请求处理器适配器HandlerAdapter去执行handler,

               ::: 执行以前须要先请求执行链中的拦截器的preHandle方法进行某些请求校验等。

    4 处理器适配器执行handler后返回给前端控制器DispatcherServlet一个ModelAndView(里面放有视图信息,模型数据信息)

              ::: 执行拦截器的postHandle方法

    5 前端控制器DispatcherServlet请求视图解析器解析视图,根据逻辑名(xxxx/xxxx/xxxx.jsp)解析成真正的视图view(jsp,ftl等)

    6 视图解析器解析完成后,返回给前端控制器DispatcherServlet一个View

    7 前端控制器DispatcherServlet进行视图渲染,将模型数据填充到request中

    8 响应用户请求,展现jsp等视图信息。

  • Spring MVC 启动流程

  • Spring 的单例实现原理(点击打开连接

  •  

    咱们来看看Spring中的单例实现,当咱们试图从Spring容器中取得某个类的实例时,默认状况下,Spring会才用单例模式进行建立。
    <bean id="date" class="java.util.Date"/>
    <bean id="date" class="java.util.Date" scope="singleton"/> (仅为Spring2.0支持)
    <bean id="date" class="java.util.Date" singleton="true"/>
    以上三种建立对象的方式是彻底相同的,容器都会向客户返回Date类的单例引用。那么若是我不想使用默认的单例模式,每次请求我都但愿得到一个新的对象怎么办呢?很简单,将scope属性值设置为prototype(原型)就能够了
    <bean id="date" class="java.util.Date" scope="prototype"/>
    经过以上配置信息,Spring就会每次给客户端返回一个新的对象实例。
    那么Spring对单例的底层实现,究竟是饿汉式单例仍是懒汉式单例呢?呵呵,都不是。Spring框架对单例的支持是采用单例注册表的方式进行实现的,源码以下:
  • 刚才的源码中,你们真正要记住的是Spring对bean实例的建立是采用单例注册表的方式进行实现的,而这个注册表的缓存是HashMap对象,若是配置文件中的配置信息不要求使用单例,Spring会采用新建实例的方式返回对象实例
  • Spring 框架中用到了哪些设计模式

  •  

    1.工厂模式,这个很明显,在各类BeanFactory以及ApplicationContext建立中都用到了;

    2.模版模式,这个也很明显,在各类BeanFactory以及ApplicationContext实现中也都用到了;

    3.代理模式,在Aop实现中用到了JDK的动态代理;

    4.策略模式,第一个地方,加载资源文件的方式,使用了不一样的方法,好比:ClassPathResourece,FileSystemResource,ServletContextResource,UrlResource但他们都有共同的借口Resource;第二个地方就是在Aop的实现中,采用了两种不一样的方式,JDK动态代理和CGLIB代理;

    5.单例模式,这个好比在建立bean的时候

  • Spring 其余产品(Srping Boot、Spring Cloud、Spring Secuirity、Spring Data、Spring AMQP 等)

  • 有没有用到Spring Boot,Spring Boot的认识、原理

  • 它的核心功能是由@EnableAutoConfiguration这个注解提供的/Spring boot出现以后,得益于“习惯优于配置”这个理念,再也没有繁琐的配置、难以集成的内容(大多数流行第三方技术都被集成在内)。

  • Hibernate的原理(点击打开连接

  • 1.经过Configuration config = new Configuration().configure();//读取并解析hibernate.cfg.xml配置文件
    2.由hibernate.cfg.xml中的<mapping resource="com/xx/User.hbm.xml"/>读取并解析映射信息
    3.经过SessionFactory sf = config.buildSessionFactory();//建立SessionFactory
    4.Session session = sf.openSession();//打开Sesssion
    5.Transaction tx = session.beginTransaction();//建立并启动事务Transation
    6.persistent operate操做数据,持久化操做
    7.tx.commit();//提交事务
    8.关闭Session
    9.关闭SesstionFactory

  •  

    1. 面向对象设计的软件内部运行过程能够理解成就是在不断建立各类新对象、创建对象之间的关系,调用对象的方法来改变各个对象的状态和对象消亡的过程,无论程序运行的过程和操做怎么样,本质上都是要获得一个结果,程序上一个时刻和下一个时刻的运行结果的差别就表如今内存中的对象状态发生了变化。

    2.为了在关机和内存空间不够的情况下,保持程序的运行状态,须要将内存中的对象状态保存到持久化设备和从持久化设备中恢复出对象的状态,一般都是保存到关系数据库来保存大量对象信息。从Java程序的运行功能上来说,保存对象状态的功能相比系统运行的其余功能来讲,应该是一个很不起眼的附属功能,java采用jdbc来实现这个功能,这个不起眼的功能却要编写大量的代码,而作的事情仅仅是保存对象和恢复对象,而且那些大量的jdbc代码并无什么技术含量,基本上是采用一套例行公事的标准代码模板来编写,是一种苦活和重复性的工做。

    3.经过数据库保存java程序运行时产生的对象和恢复对象,其实就是实现了java对象与关系数据库记录的映射关系,称为ORM(即Object Relation Mapping),人们能够经过封装JDBC代码来实现了这种功能,封装出来的产品称之为ORM框架,Hibernate就是其中的一种流行ORM框架。使用Hibernate框架,不用写JDBC代码,仅仅是调用一个save方法,就能够将对象保存到关系数据库中,仅仅是调用一个get方法,就能够从数据库中加载出一个对象。

    4.使用Hibernate的基本流程是:配置Configuration对象、产生SessionFactory、建立session对象,启动事务,完成CRUD操做,提交事务,关闭session。

    5.使用Hibernate时,先要配置hibernate.cfg.xml文件,其中配置数据库链接信息和方言等,还要为每一个实体配置相应的hbm.xml文件,hibernate.cfg.xml文件中须要登记每一个hbm.xml文件。

    6.在应用Hibernate时,重点要了解Session的缓存原理,级联,延迟加载和hql查询。

  • Hibernate与 MyBatis的比较(点击打开连接)、

  • springmvc与struts2比较点击打开连接

    struts2是类级别的拦截, 一个类对应一个request上下文, 
    springmvc是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应
    因此说从架构自己上 spring3 mvc就容易实现restful url 
    而struts2的架构实现起来要费劲 
    由于struts2 action的一个方法能够对应一个url 
    而其类属性却被全部方法共享,这也就没法用注解或其余方式标识其所属方法了 

    =================================== 
    spring3mvc的方法之间基本上独立的,独享request response数据 
    请求数据经过参数获取,处理结果经过ModelMap交回给框架 
    方法之间不共享变量 

    而struts2搞的就比较乱,虽然方法之间也是独立的,但其全部Action变量是共享的 
    这不会影响程序运行,却给咱们编码 读程序时带来麻烦 

    ==================================== 
    spring3 mvc的验证也是一个亮点,支持JSR303 
    处理ajax的请求更是方便 只需一个注解@ResponseBody ,而后直接返回响应文本便可 
  • 可参考《为何会有Spring

  • 可参考《为何会有Spring AOP

3.二、Netty

  • 为何选择 Netty

  • 说说业务中,Netty 的使用场景

  • 原生的 NIO 在 JDK 1.7 版本存在 epoll bug

  • 什么是TCP 粘包/拆包

  • TCP粘包/拆包的解决办法

  • Netty 线程模型

  • 说说 Netty 的零拷贝

  • Netty 内部执行流程

  • Netty 重连实现

3.三、Tomcat

4、分布式

4.一、Nginx

  • 请解释什么是C10K问题或者知道什么是C10K问题吗?(点击打开连接)

  • Nginx简介,可参考《Nginx简介》(点击打开连接)(点击打开连接)

  • 正向代理和反向代理.

  • Nginx几种常见的负载均衡策略

  • Nginx服务器上的Master和Worker进程分别是什么

  • 使用“反向代理服务器”的优势是什么?

4.二、分布式其余

  • 谈谈业务中使用分布式的场景

  • Session 分布式方案

  • Session 分布式处理

  • 分布式锁的应用场景、分布式锁的产生缘由、基本概念

  • 分布是锁的常看法决方案

  • 分布式事务的常看法决方案

  • 集群与负载均衡的算法与实现

  • 说说分库与分表设计,可参考《数据库分库分表策略的具体实现方案

  • 分库与分表带来的分布式困境与应对之策

4.三、Dubbo

  • 什么是Dubbo,可参考《Dubbo入门

  • 什么是RPC、如何实现RPC、RPC 的实现原理,可参考《基于HTTP的RPC实现

  • Dubbo中的SPI是什么概念

  • Dubbo的基本原理、执行流程

5、微服务

5.一、安全问题

  • 如何防范常见的Web攻击、如何方式SQL注入

  • 服务端通讯安全攻防

  • HTTPS原理剖析、降级攻击、HTTP与HTTPS的对比

5.二、性能优化

  • 性能指标有哪些

  • 如何发现性能瓶颈

  • 性能调优的常见手段

  • 说说你在项目中如何进行性能调优

6、其余

6.一、设计能力

6.二、业务工程

  • 说说你的开发流程、如何进行自动化部署的

  • 你和团队是如何沟通的

  • 你如何进行代码评审

  • 说说你对技术与业务的理解

  • 说说你在项目中遇到感受最难Bug,是如何解决的

  • 介绍一下工做中的一个你认为最有价值的项目,以及在这个过程当中的角色、解决的问题、你以为大家项目还有哪些不足的地方

6.三、软实力

  • 说说你的优缺点、亮点

  • 说说你最近在看什么书、什么博客、在研究什么新技术、再看那些开源项目的源代码

  • 说说你以为最有意义的技术书籍

  • 工做之余作什么事情、平时是如何学习的,怎样提高本身的能力

  • 说说我的发展方向方面的思考

  • 说说你认为的服务端开发工程师应该具有哪些能力

  • 说说你认为的架构师是什么样的,架构师主要作什么

  • 如何看待加班的问题

  •  

    市场经济机遇与挑战并存,企业每临重要关头,为争取主动抢占先机而组织加班,做为员工应该充分理解,主动请战保质保量加入突击。固然做为企业应该努力避免此种情况发生,突击必然紧张 紧张产生忙乱,接二连三地加班容易引发员工心理疲劳懈怠情绪,反而影响质量与效率。企业必须在管理上下功夫,努力作到高效八小时,达到紧凑有序 均衡生产的目的。
相关文章
相关标签/搜索