Java面试300题(2020年版,3-5年面试题重点突破)

目录

Java知识点汇总

1 JDK、JRE、JVM关系是什么?

JDK(Java Development Kit)即为Java开发工具包,包含编写Java程序所必须的编译、
运行等开发工具以及JRE。开发工具如:用于编译java程序的javac命令、用于启动JVM运
行java程序的java命令、用于生成文档的javadoc命令以及用于打包的jar命令等等。
JRE(Java Runtime Environment)即为Java运行环境,提供了运行Java应用程序所
必须的软件环境,包含有Java虚拟机(JVM)和丰富的系统类库。系统类库即为java提前
封装好的功能类,只需拿来直接使用即可,可以大大的提高开发效率。 
JVM(Java Virtual Machines)即为Java虚拟机,提供了字节码文件
(.class)的运行环境支持。简单说,就是JDK包含JRE包含JVM。

2 继承的的特点和好处,弊端?

  • 特点:可以从以下两方面来讲:
  • 类与类之间的继承:只能单继承不能多继承,但是可以多层继承。
  • 接口与接口之间的继承:既可以单继承也可以多继承。
  • 好处:(1)提高了代码的复用性、维护性、可扩展性。(2)让类与类产生了关系,
    是多态的前提。
  • 弊端:增强了类与类的耦合性。
  • 补充:面向对象的三大、四大
  • 面向对象的三大思想特征:封装、继承、多态
  • 面向对象的四大特性:封装、继承、多态、抽象

3 局部变量和成员变量的区别

  • 在类中的位置:成员变量定义在类中方法外,局部变量定义在方法中或者方法的声明上。
  • 在内存中的位置:成员变量存放在堆内存中,局部变量存放在栈内存中。
  • 初始化值不同:局部变量没有默认值,必须先赋值才能使用;成员变量有默认初始化值。
  • 生命周期不同:成员变量随着对象的存在而存在,随着对象的消失而消失;局部变量
    随着方法的调用而存在,方法调用完毕而消失。

4 Java支持的数据类型有哪些?什么是自动拆装箱?

基本数据类型:整数值型:byte,short,int,long, 字符型:char 浮点类型:float
,double 布尔型:boolean 整数默认int型,小数默认是double型。Float和long类
型的必须加后缀。
首先知道String是引用类型不是基本类型,引用类型声明的变量是指该变量在内存中实
际存储的是一个引用地址,实体在堆中。引用类型包括类、接口、数组等。String类还
是final修饰的。 而包装类就属于引用类型,自动装箱和拆箱就是基本类型和引用类型
之间的转换,至于为什么要转换,因为基本类型转换为引用类型后,就可以new对象,从
而调用包装类中封装好的方法进行基本类型之间的转换或者toString(当然用类名直接
调用也可以,便于一眼看出该方法是静态的),还有就是如果集合中想存放基本类型,
泛型的限定类型只能是对应的包装类型。

5 类变量(静态变量)和实例变量(对象变量,成员变量)的区别?

  • 所属不同:类变量属于类,是对象的共性内容;实例变量属于对象,是对象的特性内容。
  • 在内存中位置不同:类变量存在方法区的静态区;实例变量存在堆内存中。
  • 生命周期不同:类变量随着类的加载而存在,随着类的消失而消失;实例变量随着对
    象的存在而存在,随着对象的消失而消失。
  • 调用方式不同:类变量既能被类名点的形式调用,也能通过对象点的形式调用;而实
    例变量只能通过对象点的形式调用。

6 static关键字的特点?注意事项是什么?有什么好处和弊端?

  • 静态成员随着类的加载而加载,优于对象存在
  • 存在于方法区的字节码文件中的静态区
  • 被类中所有的对象所共享
  • 能够通过“类名点”的形式调用
  • 静态只能访问静态
  • 注意事项:
    • 静态方法中不可以有this、super关键字(因为静态优先于对象存在;静态只
      能覆盖静态。)
    • 好处:能够被类名点的形式调用,简化书写。被该类下所有的对象所共享。
    • 弊端:生命周期过长。

7 如何实现对象克隆?

  • 实现Cloneable接口并重写Object类中的clone()方法;
  • 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的
    深度克隆
  • 基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可
    以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出
    异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候
    暴露出来总是优于把问题留到运行时。

8 访问修饰符public,private,protected,以及不写(默认)时的区别

  • public:当前类、同包、子类、其他包
  • protected:当前类、同包、子类
  • private:当前类
  • default:当前类、同包

9 Java中如何实现序列化,有什么意义?

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可
以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了
解决对象流读写操作时可能引发的问题(如果不进行序列化可能会存在数据乱序的问题)
。要实现序列化,需要让一个类实现Serializable接口,该接口是一个标识性接口,
标注该类对象是可被序列化的,然后使用一个输出流来构造一个对象输出流并通过
writeObject(Object)方法就可以将实现对象写出(即保存其状态);如果需要反
序列化则可以用一个输入流建立对象输入流,然后通过readObject方法从流中读取对
象。序列化除了能够实现对象的持久化之外,还能够用于对象的深度克隆

10 抽象类和接口的区别

  • 抽象类只能被继承,而且只能单继承。接口需要被实现,而且可以多实现。
  • 抽象类中可以定义非抽象方法,子类可以直接继承使用。接口中都有抽象方法,需要
    子类去实现。
  • 抽象类使用的是  is a 关系。接口使用的 like a 关系。
  • 抽象类的成员修饰符可以自定义。接口中的成员修饰符是固定的。全都是public的。

11 两个对象值相同(x.equals(y) == true),但却可有不同的 hash code,这句 话对不对?

不对,如果两个对象 x 和 y 满足 x.equals(y) == true,它们的哈希码
(hash code)应当相同。
Java 对于 eqauls 方法和hashCode方法是这样规定的:

  • 如果两个对象相同(equals 方法返回 true ),那么它们的 h ashCode 值
    一定要相同;
  • 如果两个对象的 hashCode相同,它们并不一定相同。当然,你未必要按照
    要求 去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可
    以出现在 Set 集合中,同时增加新元素 的效率会大大下降(对于使用哈希存
    储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。

12 类和对象的关系?

类是用来描述事物的属性(事物的外在的特征,成员变量)和行为(事物具有
的特性,成员方法)的一个抽象的概念。
对象是类的实例化。
比如:学生是一个类,具体某一个人就是对象。

13 封装就是私有,对吗?为什么?

不对,private(私有)仅仅是封装的一种体现形式。我们常用的类,方法,函数
也是封装。只要是对外不可见,就能达到封装的效果,比如:包与包之间的访问。
补充:get/set访问方式必须成对出现吗?
答:不是,具体看需求,需要设置值就提供set方法,如果需要访问值,就提供get方法

14 什么是封装?封装的原则?好处

封装就是隐藏对象的属性和具体实现细节,仅对外提供公共的访问方式。
原则:(1)将不需要对外暴露的信息隐藏;(2)对外提供公共的访问方式。
好处:将变化隔离;提高了安全性;提高了代码代码重用性(是对的),便于使用。

15 Java中参数传递的问题

Java中只有值传递。
如果是基本数据类型,传递的是值,
如果是引用数据类型,传递的地址值(但是String类型作为方法的形参时,传
递的是值),地址值也是值。

16 构造方法,set方法都可以给成员变量赋值,这两种赋值方式有什么区别?

构造方法主要作用是用来给对象初始化,赋值只是它的捎带工作,也可以不用赋值。
Set方法只能用来赋值,在原有对象的基础上赋值,可以用来修改值。
构造方法重新赋值,相对于重新创建对象。

17 静态代码块和构造代码块的区别?

  • 静态代码块随着类的加载而加载,一般是用来加载驱动的。只在类加载的时候
    执行一次,优先于构造方法执行
  • 构造代码块里边放的是所有构造方法的共性内容,为了简化书写,调高效率。
    每创建一次对象,就执行一次,它是优先于构造方法执行的。

18 java 中实现多态的机制是什么

靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程
序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,
也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。

19 String 是最基本的数据类型吗?

不是。Java中的基本数据类型只有8个:byte、short、int、long、float、
double、char、boolean;除了基本类型(primitive type)和枚举类型
(enumeration type),剩下的都是引用类型(reference type)。

20 float f=3.4;是否正确

不正确。3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于
下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转
换float f =(float)3.4; 或者写成float f =3.4F;。

21 int和Integer有什么区别?

Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本
数据类型,但是为了能够将这些基本数据类型当成对象操作,Java为每一个基本
数据类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer,
从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。Java 为每个
原始类型提供了包装类型:- 原始类型: boolean,char,byte,short,int,
long,float,double- 包装类型:Boolean,Character,Byte,Short,
Integer,Long,Float,Double

22 switch 是否能作用在byte 上,是否能作用在long 上,是否能作用在String上?

在Java 5以前,switch(expr)中,expr只能是byte、short、char、int。
从Java 5开始,Java中引入了枚举类型,expr也可以是enum类型,从Java 7开始,
expr还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。

23 数组有没有length()方法?String有没有length()方法?

数组没有length()方法,有length 的属性。String 有length()方法。
JavaScript中,获得字符串的长度是通过length属性得到的,这一点容易和Java混淆。

24 在Java中,如何跳出当前的多重嵌套循环?

在最外层循环前加一个标记如A,然后用break A;可以跳出多重循环。
(Java中支持带标签的break和continue语句,作用有点类似于C和C++中的goto语句,
但是就像要避免使用goto一样,应该避免使用带标签的break和continue,因为它不
会让你的程序变得更优雅,很多时候甚至有相反的作用,所以这种语法其实不知道更好)

25 构造器(constructor)是否可被重写(override)

构造器不能被继承,因此不能被重写,但可以被重载。

26 是否可以继承String类?

String 类是final类,不可以被继承。

27 String和StringBuilder、StringBuffer的区别?

Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,
它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的
字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对
象可以直接进行修改。StringBuilder是Java 5中引入的,它和StringBuffer的
方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有
被synchronized修饰,因此它的效率也比StringBuffer要高。

28 重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?

方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,
而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的
参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生
在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,
比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)
。重载对返回类型没有特殊的要求。

29 char 型变量中能不能存贮一个中文汉字,为什么?

char类型可以存储一个中文汉字,因为Java中使用的编码是Unicode(不选择任何
特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),
一个char类型占2个字节(16比特),所以放一个中文是没问题的。

30 抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本

地方法(native),是否可同时被synchronized修饰?
都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。
本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。
synchronized和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。

31 是否可以从一个静态(static)方法内部发出对非静态(non-static)方法的调用?

不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,
在调用静态方法时可能对象并没有被初始化。

32 String s = new String(“xyz”);创建了几个字符串对象?

两个对象,一个是静态区的”xyz”,一个是用new创建在堆上的对象。

33 接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?

抽象类是否可继承具体类(concrete class)?
接口可以继承接口,而且支持多重继承。抽象类可以实现(implements)接口,
抽象类可继承具体类也可以继承抽象类。

34 一个”.java”源文件中是否可以包含多个类(不是内部类)?有什么限制?

可以,但一个源文件中最多只能有一个公开类(public class)而且文件
名必须和公开类的类名完全保持一致。

35 Java 中的final关键字有哪些用法?

  • 修饰类:表示该类不能被继承;
  • 修饰方法:表示方法不能被重写;
  • 修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。

36 比较一下Java和JavaSciprt。

JavaScript 与Java是两个公司开发的不同的两个产品。Java 是
原Sun Microsystems公司推出的面向对象的程序设计语言,特别
适合于互联网应用程序开发;而JavaScript是Netscape公司的产品
,为了扩展Netscape浏览器的功能而开发的一种可以嵌入Web页面中
运行的基于对象和事件驱动的解释性语言。下面对两种语言间的异同作
如下比较:- 基于对象和面向对象:Java是一种真正的面向对象的语言
,即使是开发简单的程序,必须设计对象;JavaScript是种脚本语言,
它可以用来制作与网络无关的,与用户交互作用的复杂软件。它是一种基
于对象(Object-Based)和事件驱动(Event-Driven)的编程语言,因
而它本身提供了非常丰富的内部对象供设计人员使用。- 解释和编译:
Java的源代码在执行之前,必须经过编译。JavaScript是一种解释性编
程语言,其源代码不需经过编译,由浏览器解释执行。(目前的浏览器几
乎都使用了JIT(即时编译)技术来提升JavaScript的运行效率)- 强类
型变量和类型弱变量:Java采用强类型变量检查,即所有变量在编译之前
必须作声明;JavaScript中变量是弱类型的,甚至在使用变量前可以不作
声明,JavaScript的解释器在运行时检查推断其数据类型。- 代码格式不一样。

37 什么时候用断言(assert)?

断言在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。
一般来说,断言用于保证程序最基本、关键的正确性。断言检查通常在开发和
测试时开启。为了保证程序的执行效率,在软件发布后断言检查通常是关闭的。
断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为true;
如果表达式的值为false,那么系统会报告一个AssertionError。

38 阐述final、finally、finalize的区别。

final:修饰符(关键字)有三种用法:如果一个类被声明为final,意味着它
不能再派生出新的子类,即不能被继承,因此它和abstract是反义词。将变量
声明为final,可以保证它们在使用中不被改变,被声明为final的变量必须在
声明时给定初值,而在以后的引用中只能读取不可修改。被声明为final的方法
也同样只能使用,不能在子类中被重写。- finally:通常放在try…catch…的
后面构造总是执行代码块,这就意味着程序无论正常执行还是发生异常,这里的
代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。
finalize:Object类中定义的方法,Java中允许使用finalize()方法在垃圾
收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集
器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其
他清理工作。

39 Java中是如何支持正则表达式操作的?

Java中的String类提供了支持正则表达式操作的方法,包括:matches()、
replaceAll()、replaceFirst()、split()。此外,Java中可以用Pattern
类表示正则表达式对象,它提供了丰富的API进行各种正则表达式操作,
请参考下面面试题的代码。

40 获得一个类的类对象有哪些方式?

方法1:类型.class,例如:String.class- 方法2:对象.getClass(),
例如:”hello”.getClass()- 方法3:Class.forName(),例如:
Class.forName(“java.lang.String”)

41 什么是值传递和引用传递?

值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响
原变量. 引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个
副本, 并不是原对象本身 。 一般认为,java内的传递都是值传递. java中实
例对象的传递是引用传递 。

42 Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别?

sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前
线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁
依然保持,因此休眠时间结束后会自动恢复。wait()是Object类的方法,调
用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对
象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)
时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的
锁就可以进入就绪状态。

43 线程的sleep()方法和yield()方法有什么区别?

  • sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线
    程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
  • 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就
    绪(ready)状态;
  • sleep()方法声明抛出InterruptedException,而yield()方
    法没有声明任何异常;
  • sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。

44 当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?

不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。
因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,
如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在
等锁池(注意不是等待池哦)中等待对象的锁。

45 请说出与线程同步以及线程调度相关的方法。

wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方
法要处理InterruptedException异常;- notify():唤醒一个处于等待状态
的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,
而是由JVM确定唤醒哪个线程,而且与优先级无关;- notityAll():唤醒所有
处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,
只有获得锁的线程才能进入就绪状态;

46 编写多线程程序有几种实现方式?

Java 5以前实现多线程有两种实现方法:一种是继承Thread类;
另一种是实现Runnable接口。两种方式都要通过重写run()方法来定义线程的行为,
推荐使用后者,因为Java中的继承是单继承,一个类有一个父类,
如果继承了Thread类就无法再继承其他类了,显然使用Runnable接口更为灵活。

47 synchronized关键字的用法?

synchronized关键字可以将对象或者方法标记为同步,以实现对对象和方法的互斥访问,
可以用synchronized(对象) { … }定义同步代码块,或者在声明方法时将synchronized
作为方法的修饰符。

48 举例说明同步和异步。

如果系统中存在临界资源(资源数量少于竞争资源的线程数量的资源),例如正在写的数据以后
可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就必须
进行同步存取(数据库操作中的排他锁就是最好的例子)。当应用程序在对象上调用了一个需
要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,
在很多情况下采用异步途径往往更有效率。事实上,所谓的同步就是指阻塞式操作,而异步就
是非阻塞式操作。

49 启动一个线程是调用run()还是start()方法?

启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它
可以由JVM 调度并执行,这并不意味着线程就会立即运行。run()方法是线程启动后要进行回
调(callback)的方法。

50 什么是线程池(thread pool)?

在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其
它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行
垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是
一些很耗资源的对象创建和销毁,这就是”池化资源”技术产生的原因。线程池顾名思义就是事
先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,
使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。Java 5+中的
Executor接口定义一个执行线程的工具。它的子类型即线程池接口是ExecutorService。要配
置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,因此在工具类
Executors面提供了一些静态工厂方法,生成一些常用的线程池,
如下所示:- newSingleThreadExecutor:创建一个单线程的线程池。
这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。
如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务
的执行顺序按照任务的提交顺序执行。- newFixedThreadPool:创建固定大小的线程池。
每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达
到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需
要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又
可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖
于操作系统(或者说JVM)能够创建的最大线程大小。- newScheduledThreadPool:创建一个
大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。

51 线程的基本状态以及状态之间的关系?

其中Running表示运行状态,Runnable表示就绪状态(万事俱备,只欠CPU),Blocked表
示阻塞状态,阻塞状态又有多种情况,可能是因为调用wait()方法进入等待池,也可能是执
行同步方法或同步代码块进入等锁池,或者是调用了sleep()方法或join()方法等待休眠或
其他线程结束,或是因为发生了I/O中断。

52 简述synchronized 和java.util.concurrent.locks.Lock的异同?

Lock是Java 5以后引入的新的API,和关键字synchronized相比主要相同点:Lock 能完
成synchronized所实现的所有功能;主要不同点:Lock有比synchronized更精确的线程
语义和更好的性能,而且不强制性的要求一定要获得锁。synchronized会自动释放锁,而
Lock一定要求程序员手工释放,并且最好在finally 块中释放(这是释放外部资源的最好
的地方)。

53 线程和进程有什么区别?

线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程
使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个
线程都拥有单独的栈内存用来存储本地数据。

54 用Runnable还是Thread?

这个问题是上题的后续,大家都知道我们可以通过继承Thread类或者调用Runnable接口来
实现线程,问题是,那个方法更好呢?什么情况下使 用它?这个问题很容易回答,如果你
知道Java不支持类的多重继承,但允许你调用多个接口。所以如果你要继承其他类,当然是
调用Runnable接口好 了。

55 Thread 类中的start() 和 run() 方法有什么区别?

这个问题经常被问到,但还是能从此区分出面试者对Java线程模型的理解程度。start()方
法被用来启动新创建的线程,而且start()内部 调用了run()方法,这和直接调用run()方
法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程
启动,start()方法才会启动新线程。

56  Java中Runnable和Callable有什么不同?

Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就
有了,Callable是在 JDK1.5增加的。它们的主要区别是Callable的 call() 方法可以返
回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结
果的Future对象。

57 Java中的volatile 变量是什么?

volatile是一个特殊的修饰符,只有成员变量才能使用它。在Java并发程序缺少同步类的情
况下,多线程对成员变量的操作对其它线程是透明的。volatile变量可以保证下一个读取操
作会在前一个写操作之后发生,就是上一题的volatile变量规则。

58 什么是线程安全?Vector是一个线程安全类吗?

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。
如果每次运行结果和单线程运行的结果是一样的,而且其他的变量 的值也和预期的是一样的,
就是线程安全的。一个线程安全的计数器类的同一个实例对象在被多个线程使用的情况下也不
会出现计算失误。很显然你可以将集合类分 成两组,线程安全和非线程安全的。Vector 是用
同步方法来实现线程安全的, 而和它相似的ArrayList不是线程安全的。

59 一个线程运行时发生异常会怎样?

这是我在一次面试中遇到的一个很刁钻的Java面试题, 简单的说,如果异常没有被捕获该线
程将会停止执行。Thread.UncaughtExceptionHandler是用于处理未捕获异常造成线程突
然中 断情况的一个内嵌接口。当一个未捕获异常将造成线程中断的时候JVM会使用
Thread.getUncaughtExceptionHandler()来 查询线程的UncaughtExceptionHandler
并将线程和异常作为参数传递给handler的uncaughtException()方法 进行处理。

60 如何在两个线程间共享数据?

你可以通过共享对象来实现这个目的,或者是使用像阻塞队列这样并发的数据结构。
(涉及到在两个线程间共享对象)用wait和notify方法实现了生产者消费者模型。

61 Java中notify 和 notifyAll有什么区别?

这又是一个刁钻的问题,因为多线程可以等待单监控锁,Java API 的设计人员提供了
一些方法当等待条件改变的时候通知它们,但是这些方法没有完全实现。notify()方法
不能唤醒某个具体的线程,所以只有一个线程在等 待的时候它才有用武之地。而notifyAll()
唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行。

62 什么是ThreadLocal变量?

ThreadLocal是Java里一种特殊的变量。每个线程都有一个ThreadLocal就是每个线程都
拥有了自己独立的一个变量,竞争条件被 彻底消除了。它是为创建代价高昂的对象获取线
程安全的好方法,比如你可以用ThreadLocal让SimpleDateFormat变成线程安全的,因
为那个类创建代价高昂且每次调用都需要创建不同的实例所以不值得在局部范围使用它,
如果为每个线程提供一个自己独有的变量拷贝,将大大提高效率。首先,通 过复用减少了
代价高昂的对象的创建个数。其次,你在没有使用高代价的同步或者不变性的情况下获得
了线程安全。线程局部变量的另一个不错的例子是 ThreadLocalRandom类,它在多线程
环境中减少了创建代价高昂的Random对象的个数。

63 什么是FutureTask?

在Java并发程序中FutureTask表示一个可以取消的异步运算。它有启动和取消运算、查询
运算是否完成和取回运算结果等方法。只有当运算完 成的时候结果才能取回,如果运算尚
未完成get方法将会阻塞。一个FutureTask对象可以对调用了Callable和Runnable的对象
进行包 装,由于FutureTask也是调用了Runnable接口所以它可以提交给Executor来执行。

64 如何避免死锁?

java多线程中的死锁死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一
种互相等待的现象,若无外力作用,它们都将无法推进下去。这是一个严重的问题,因为死
锁会让你的程序挂起无法完成任务,死锁的发生必须满足以下四个条件:
互斥条件:一个资源每次只能被一个进程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,
规定所有的进程申请资源必须以一定的顺序(升序或降序)做操作来避免死锁。

65 Java中活锁和死锁有什么区别

这是上题的扩展,活锁和死锁类似,不同之处在于处于活锁的线程或进程的状态是不断改
变的,活锁可以认为是一种特殊的饥饿。一个现实的活锁例子是两个 人在狭小的走廊碰到,
两个人都试着避让对方好让彼此通过,但是因为避让的方向都一样导致最后谁都不能通过走
廊。简单的说就是,活锁和死锁的主要区别是前者 进程的状态可以改变但是却不能继续执行。

66 怎么检测一个线程是否拥有锁?

在java.lang.Thread中有一个方法叫holdsLock(),它返回true如果当且仅当当前线程
拥有某个具体对象的锁。

67 你如何在Java中获取线程堆栈?

对于不同的操作系统,有多种方法来获得Java进程的线程堆栈。当你获取线程堆栈时,
JVM会把所有线程的状态存到日志文件或者输出到控制台。在 Windows你可以使用
Ctrl + Break组合键来获取线程堆栈,Linux下用kill -3命令。你也可以用jstack这
个工具来获取,它对线程id进行操作,你可以用jps这个工具找到id。

68 有三个线程T1,T2,T3,怎么确保它们按顺序执行?

在多线程中有多种方法让线程按特定顺序执行,你可以用线程类的join()方法在一个线
程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应
该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成。

69 如果你提交任务时,线程池队列已满。会时发会生什么?

这个问题问得很狡猾,许多程序员会认为该任务会阻塞直到线程池队列有空位。事实上如
果一个任务不能被调度执行那么ThreadPoolExecutor’s submit()方法将会抛出一个
RejectedExecutionException异常。

70 Java线程池中submit() 和 execute()方法有什么区别?

两个方法都可以向线程池提交任务,execute()方法的返回类型是void,它定义在
Executor接口中, 而submit()方法可以返回持有计算结果的Future对象,它定义在
ExecutorService接口中,它扩展了Executor接口,其它线 程池类像ThreadPoolExecutor
和ScheduledThreadPoolExecutor都有这些方法。

71 什么是阻塞式方法?

阻塞式方法是指程序会一直等待该方法完成期间不做其他事情,ServerSocket的accept()
方法就是一直等待客户端连接。这里的阻塞是 指调用结果返回之前,当前线程会被挂起,
直到得到结果之后才会返回。此外,还有异步和非阻塞式方法在任务完成前就返回。

72 多线程中的忙循环是什么?

忙循环就是程序员用循环让一个线程等待,不像传统方法wait(), sleep() 或 yield()
它们都放弃了CPU控制,而忙循环不会放弃CPU,它就是在运行一个空循环。这么做的目的是
为了保留CPU缓存,在多核系统中,一个等待线程醒来的时候可 能会在另一个内核运行,这
样会重建缓存。为了避免重建缓存和减少等待重建的时间就可以使用它

73 如何在Java中创建线程安全的Singleton?

这是上面那个问题的后续,如果你不喜欢双检锁而面试官问了创建Singleton类的替代方法,
你可以利用JVM的类加载和静态变量初始化特征来创建Singleton实例,或者是利用枚举类型
来创建Singleton,我很喜欢用这种方法

74 写出3条你遵循的多线程最佳实践

给你的线程起个有意义的名字。这样可以方便找bug或追踪。OrderProcessor, QuoteProcessor
or TradeProcessor 这种名字比 Thread-1. Thread-2 and Thread-3 好多了,给线程起一
个和它要完成的任务相关的名字,所有的主要框架甚至JDK都遵循这个最佳实践。
避免锁定和缩小同步的范围锁花费的代价高昂且上下文切换更耗费时间空间,试试最低限度的使
用同步和锁,缩小临界区。因此相对于同步方法我更喜欢同步块,它给我拥有对锁的绝对控制权。
多用同步类少用wait 和 notify首先,CountDownLatch, Semaphore, CyclicBarrier
和 Exchanger 这些同步类简化了编码操作,而用wait和notify很难实现对复杂控制流的控制。
其次,这些类是由最好的企业编写和维护在后续的JDK中它们还会不断 优化和完善,使用这些更高
等级的同步工具你的程序可以不费吹灰之力获得优化。
多用并发集合少用同步集合这是另外一个容易遵循且受益巨大的最佳实践,并发集合比同步
集合的可扩展性更好,所以在并发编程时使用并发集合效果更好。如果下一次你需要用到map,
你应该首先想到用ConcurrentHashMap。

75 如何强制启动一个线程?

这个问题就像是如何强制进行Java垃圾回收,目前还没有觉得方法,虽然你可以使用
System.gc()来进行垃圾回收,但是不保证能成功。在Java里面没有办法强制启动一个
线程,它是被线程调度器控制着且Java没有公布相关的API。

76 Java中用到的线程调度算法是什么

抢占式。一个线程用完CPU之后,操作系统会根据线程优先级、线程饥饿情况等数据
算出一个总的优先级并分配下一个时间片给某个线程执行。

77 List、Set、Map是否继承自Collection接口?

List、Set 是,Map 不是。Map是键值对映射容器,与List和Set有明显的区别,而Set
存储的零散的元素且不允许有重复元素(数学中的集合也是如此),List是线性结构的容器,
适用于按数值索引访问元素的情形。

78 阐述ArrayList、Vector、LinkedList的存储性能和特性。

ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增
加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存
操作,所以索引数据快而插入数据慢,Vector中的方法由于添加了synchronized修饰,因
此Vector是线程安全的容器,但性能上较ArrayList差,因此已经是Java中的遗留容器。
LinkedList使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形
成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利
用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后
项即可,所以插入速度较快。Vector属于遗留容器(Java早期的版本中提供的容器,除此之外
,Hashtable、Dictionary、BitSet、Stack、Properties都是遗留容器),已经不推荐使用,
但是由于ArrayList和LinkedListed都是非线程安全的,如果遇到多个线程操作同一个容器的场景
,则可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用
(这是对装潢模式的应用,将已有对象传入另一个类的构造器中创建新的对象来增强实现)。

79 Collection和Collections的区别?

Collection是一个接口,它是Set、List等容器的父接口;Collections是个一个工具类,
提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。

80 List、Map、Set三个接口存取元素时,各有什么特点?

List以特定索引来存取元素,可以有重复元素。Set不能存放重复元素(用对象的equals()
方法来区分元素是否重复)。Map保存键值对(key-value pair)映射,映射关系可以是一
对一或多对一。Set和Map容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储的
版本理论存取时间复杂度为O(1),而基于排序树版本的实现在插入或删除元素时会按照元素
或元素的键(key)构成排序树从而达到排序和去重的效果。

81 TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素?

TreeSet要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的compareTo()方
法,当插入元素时会回调该方法比较元素的大小。TreeMap要求存放的键值对映射的键必须实现Comparable
接口从而根据键对元素进行排序。Collections工具类的sort方法有两种重载的形式,第一种要求传入的
待排序容器中存放的对象比较实现Comparable接口以实现元素的比较;第二种不强制性的要求容器中
的元素必须可比较,但是要求传入第二个参数,参数是Comparator接口的子类型(需要重写
compare方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入
比较元素大小的算法,也是对回调模式的应用(Java中对函数式编程的支持)。

82 Java集合框架是什么?说出一些集合框架的长处?

  • 每种编程语言中都有集合。最初的Java版本号包括几种集合类:Vector、
    Stack、HashTable和Array。
  • 随着集合的广泛使用。Java1.2提出了囊括全部集合接口、实现和算法的集合框架。
  • 在保证线程安全的情况下使用泛型和并发集合类,Java已经经历了非常久。
    它还包括在Java并发包中,堵塞接口以及它们的实现。
  • 集合框架的部分长处例如以下:
    • 使用核心集合类减少开发成本,而非实现我们自己的集合类。
    • 随着使用经过严格測试的集合框架类。代码质量会得到提高。
    • 通过使用JDK附带的集合类,能够减少代码维护成本。
    • 复用性和可操作性。

83 集合框架中的泛型有什么长处?

Java1.5引入了泛型。全部的集合接口和实现都大量地使用它。泛型同意我们为集合提供
一个能够容纳的对象类型,因此。假设你加入其他类型的不论什么元素,它会在编译时报错
。这避免了在执行时出现ClassCastException。由于你将会在编译时得到报错信息。
泛型也使得代码整洁,我们不须要使用显式转换和instanceOf操作符。
它也给执行时带来长处。由于不会产生类型检查的字节码指令。

84 Java集合框架的基础接口有哪些?

Collection为集合层级的根接口。
一个集合代表一组对象。这些对象即为它的元素。
Java平台不提供这个接口不论什么直接的实现。
Set是一个不能包括反复元素的集合。
这个接口对数学集合抽象进行建模。被用来代表集合,就如一副牌。
List是一个有序集合。能够包括反复元素。
你能够通过它的索引来訪问不论什么元素。List更像长度动态变换的数组。
Map是一个将key映射到value的对象.一个Map不能包括反复的key:每一个key最多仅
仅能映射一个value。

85 为何Collection不从Cloneable和Serializable接口继承?

Collection接口指定一组对象,对象即为它的元素。怎样维护这些元素由Collection
的详细实现决定。
比如。一些如List的Collection实现同意反复的元素。而其他的如Set就不同意。
非常多Collection实现有一个公有的clone方法。
然而。把它放到集合的全部实现中也是没有意义的。这是由于Collection是一个
抽象表现。重要的是实现。
当与详细实现打交道的时候,克隆或序列化的语义和含义才发挥作用。所以,详
细实现应该决定怎样对它进行克隆或序列化,或它能否够被克隆或序列化。
在全部的实现中授权克隆和序列化,终于导致更少的灵活性和很多其他的限制。
特定的实现应该决定它能否够被克隆和序列化。

86 为何Map接口不继承Collection接口

虽然Map接口和它的实现也是集合框架的一部分。但Map不是集合。集合也不是Map。
因此,Map继承Collection毫无意义,反之亦然。
假设Map继承Collection接口,那么元素去哪儿?Map包括key-value对,
它提供抽取key或value列表集合的方法,可是它不适合“一组对象”规范。

87 Iterator是什么?

Iterator接口提供遍历不论什么Collection的接口。
我们能够从一个Collection中使用迭代器方法来获取迭代器实例。迭代器代
替了Java集合框架中的Enumeration。迭代器同意调用者在迭代过程中移除元素。

88 Enumeration和Iterator接口的差别?

Enumeration的速度是Iterator的两倍,也使用更少的内存。
Enumeration是非常基础的,也满足了基础的须要。可是,与Enumeration相比,
Iterator更加安全,由于当一个集合正在被遍历的时候。它会阻止其他线程去改动集合。
迭代器代替了Java集合框架中的Enumeration。
迭代器同意调用者从集合中移除元素,而Enumeration不能做到。
为了使它的功能更加清晰,迭代器方法名已经经过改善。

89 为何没有像Iterator.add()这种方法。向集合中加入元素?

语义不明。已知的是,Iterator的协议不能确保迭代的次序。然而要注意。
ListIterator没有提供一个add操作,它要确保迭代的顺序。

90 Iterater和ListIterator之间有什么差别?

  • 我们能够使用Iterator来遍历Set和List集合,而ListIterator仅仅能遍历List。
  • Iterator仅仅能够向前遍历。而LIstIterator能够双向遍历。
  • ListIterator从Iterator接口继承,然后加入了一些额外的功能,
    比方加入一个元素、替换一个元素、获取前面或后面元素的索引位置。

91 通过迭代器fail-fast属性,你明确了什么?

每次我们尝试获取下一个元素的时候,Iterator fail-fast属性检查当
前集合结构里的不论什么改动。假设发现不论什么改动。它抛出ConcurrentModificationException。
Collection中全部Iterator的实现都是按fail-fast来设计的(ConcurrentHashMap
和CopyOnWriteArrayList这类并发集合类除外)。

92 UnsupportedOperationException是什么?

UnsupportedOperationException是用于表明操作不支持的异常。
在JDK类中已被大量运用,在集合框架java.util.Collections.UnmodifiableCollection
将会在全部add和remove操作中抛出这个异常。

93 在Java中,HashMap是怎样工作的?

HashMap在Map.Entry静态内部类实现中存储key-value对。
HashMap使用哈希算法。在put和get方法中。它使用hashCode()和equals()方法。
当我们通过传递key-value对调用put方法的时候。HashMap使用Key hashCode()和
哈希算法来找出存储key-value对的索引。
Entry存储在LinkedList中,所以假设存在entry。它使用equals()方法来检查传
递的key是否已经存在。假设存在,它会覆盖value。假设不存在。它会创建一个
新的entry然后保存。
当我们通过传递key调用get方法时,它再次使用hashCode()来找到数组中的索引,
然后使用equals()方法找出正确的Entry,然后返回它的值。下面的图片解释了详细内容。
其他关于HashMap比較重要的问题是容量、负荷系数和阀值调整。HashMap默认的
初始容量是32,负荷系数是0.75。
阀值是为负荷系数乘以容量,不管何时我们尝试加入一个entry,假设map的大小
比阀值大的时候,HashMap会对map的内容进行又一次哈希。且使用更大的容量。
容量总是2的幂。所以假设你知道你须要存储大量的key-value对,比方缓存从数据
库里面拉取的数据,使用正确的容量和负荷系数对HashMap进行初始化是个不错的做法

94 hashCode()和equals()方法有何重要性?

HashMap使用Key对象的hashCode()和equals()方法去决定key-value对的索引。
当我们试着从HashMap中获取值的时候,这些方法也会被用到。
假设这些方法没有被正确地实现,在这种情况下,两个不同Key或许会产生同样的
hashCode()和equals()输出,HashMap将会觉得它们是同样的,然后覆盖它们。
而非把它们存储到不同的地方。

95 HashMap和HashTable有何不同?

  • HashMap同意key和value为null。而HashTable不同意。
  • HashTable是同步的,而HashMap不是。所以HashMap适合单线程环境,
    HashTable适合多线程环境。
  • 在Java1.4中引入了LinkedHashMap,HashMap的一个子类,假如你想要遍历顺序,
    你非常easy从HashMap转向LinkedHashMap,可是HashTable不是这种。
    它的顺序是不可预知的。
  • HashMap提供对key的Set进行遍历。因此它是fail-fast的。但HashTable
    提供对key的Enumeration进行遍历,它不支持fail-fast。
  • HashTable被觉得是个遗留的类。假设你寻求在迭代的时候改动Map,
    你应该使用CocurrentHashMap。

96 怎样决定选用HashMap还是TreeMap?

对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。
然而。假如你须要对一个有序的key集合进行遍历,TreeMap是更好的选择。
基于你的collection的大小,或许向HashMap中加入元素会更快。将map
换为TreeMap进行有序key的遍历。

97 ArrayList和Vector有何异同点?

  • 两者都是基于索引的,内部由一个数组支持。
  • 两者维护插入的顺序,我们能够依据插入顺序来获取元素。
  • ArrayList和Vector的迭代器实现都是fail-fast的。
  • ArrayList和Vector两者同意null值。也能够使用索引值对元素进行随机訪问。
  • Vector是同步的,而ArrayList不是。然而。假设你寻求在迭代的时候对
    列表进行改变。你应该使用CopyOnWriteArrayList。
  • ArrayList比Vector快。它由于有同步。不会过载。
  • ArrayList更加通用,由于我们能够使用Collections工具类轻易地获取
    同步列表和仅仅读列表。

98 Array和ArrayList有何差别?什么时候更适合用Array?

Array能够容纳基本类型和对象,而ArrayList仅仅能容纳对象。
Array是指定大小的,而ArrayList大小是固定的。
Array没有提供ArrayList那么多功能,比方addAll、removeAll和iterator等。
虽然ArrayList明显是更好的选择。但也有些时候Array比較好用。

99 ArrayList和LinkedList有何差别?

  • ArrayList是由Array所支持的基于一个索引的数据结构,所以它提供对元
    素的随机訪问。复杂度为O(1),但LinkedList存储一系列的节点数据。每一
    个节点都与前一个和下一个节点相连接。所以。虽然有使用索引获取元素的方
    法,内部实现是从起始点開始遍历,遍历到索引的节点然后返回元素。时间复
    杂度为O(n)。比ArrayList要慢。
  • 与ArrayList相比,在LinkedList中插入、加入和删除一个元素会更快。
    由于在一个元素被插入到中间的时候,不会涉及改变数组的大小,或更新索引。
  • LinkedList比ArrayList消耗很多其他的内存,由于LinkedList中的每一
    个节点存储了前后节点的引用。

100 哪些集合类提供对元素的随机訪问?

ArrayList、HashMap、TreeMap和HashTable类提供对元素的随机訪问。

101 哪些集合类是线程安全的?

Vector、HashTable、Properties和Stack是同步类,所以它们是线程安全
的,能够在多线程环境下使用。
Java1.5并发API包括一些集合类。同意迭代时改动,由于它们都工作在集合
的克隆上。所以它们在多线程环境中是安全的。

102 并发集合类是什么?

Java1.5并发包(java.util.concurrent)包括线程安全集合类,同意在
迭代时改动集合。
迭代器被设计为fail-fast的,会抛出ConcurrentModificationException。
一部分类为:CopyOnWriteArrayList、 ConcurrentHashMap、
CopyOnWriteArraySet。

103 BlockingQueue是什么?

Java.util.concurrent.BlockingQueue是一个队列,在进行检索或移除一个
元素的时候,它会等待队列变为非空;当在加入一个元素时,它会等待队列中的可用空间。
BlockingQueue接口是Java集合框架的一部分,主要用于实现生产者-消费者模式
。我们不须要操心等待生产者有可用的空间。或消费者有可用的对象。由于它都
在BlockingQueue的实现类中被处理了。
Java提供了集中BlockingQueue的实现,比方ArrayBlockingQueue、
LinkedBlockingQueue、PriorityBlockingQueue,、SynchronousQueue等。

104 队列和栈是什么,列出它们的差别?

栈和队列两者都被用来预存储数据。java.util.Queue是一个接口,
它的实现类在Java并发包中。队列同意先进先出(FIFO)检索元素,
但并不是总是这样。Deque接口同意从两端检索元素。
栈与队列非常类似,但它同意对元素进行后进先出(LIFO)进行检索。
Stack是一个扩展自Vector的类,而Queue是一个接口。

105 Collections类是什么?

Java.util.Collections是一个工具类仅包括静态方法。它们操作或返
回集合。它包括操作集合的多态算法,返回一个由指定集合支持的新集合
和其他一些内容。这个类包括集合框架算法的方法,比方折半搜索、排序、
混编和逆序等。

106 Comparable和Comparator接口是什么?

假设我们想使用Array或Collection的排序方法时。须要在自己定义类里
实现Java提供Comparable接口。
Comparable接口有compareTo(T OBJ)方法,它被排序方法所使用。我们
应该重写这种方法,假设“this”对象比传递的对象參数更小、相等或更大
时,它返回一个负整数、0或正整数
可是。在大多数实际情况下,我们想依据不同參数进行排序。
比方。作为一个CEO。我想对雇员基于薪资进行排序。一个HR想基于年龄对
他们进行排序。这就是我们须要使用Comparator接口的情景。由于
Comparable.compareTo(Object o)方法实现仅仅能基于一个字段进行
排序,我们不能依据对象排序的须要选择字段。
Comparator接口的compare(Object o1, Object o2)方法的实现须要
传递两个对象參数,若第一个參数比第二个小,返回负整数;若第一个
等于第二个。返回0;若第一个比第二个大。返回正整数。

107 HashMap和HashSet区别

a. HashMap实现了Map接口,HashSet实现了Set接口
b.HashMap存储键值对,HashSet存储对象
c.HashMap调用put()向map中添加元素,HashSet调用add()像set中添加元素
d.HashMap使用Key计算hasncode,HashMap使用成员计算Hashcode

108 fail-fast 与 fail-safe 之间的区别?

Fail fast快速地报告任何的failure。无论何时任何一个问题都会
引发 fail fast系统fails· 在Java Fail fast 迭代器中,迭代objects集
合有时会出现并发修改异常,出现这种情况有2个原因· 如果一个线程正在迭代
一个集合,而另一个线程同时试图修改这个集合· 在调用remove()方法后,如
何我们还试图去修改集合object

109 是否可以往 TreeSet 或者 HashSet 中添加 null 元素?

可以往 hashset 中添加一个 null· TreeSet 也允许一个 null值

110 ArrayList集合加入1万条数据,应该怎么提高效率

ArrayList的默认初始容量为10,要插入大量数据的时候需要不断扩容,而扩
容是非常影响性能的。因此,现在明确了10万条数据了,我们可以直接在初始
化的时候就设置ArrayList的容量!
这样就可以提高效率了~

111 HashMap中的get操作是什么原理?

先根据key的hashcode值找到对应的链表,再循环链表,根据key的hash是否
相同且key的==或者equals比较操作找到对应的值

112 Java中什么是Exception?

异常是Java传达给你的系统和程序错误的方
式。在java中,异常功能是通过实现比如Throwable,Exception,
RuntimeException之类的类,然后还有一
些处理异常时候的关键字,比如throw,throws,try,catch,finally之
类的。 所有的异常都是通过Throwable
衍生出来的。Throwable把错误进一步划分为 java.lang.Exception
和 java.lang.Error.  java.lang.Error 用
来处理系统错误,例如java.lang.StackOverFlowError 或者
Java.lang.OutOfMemoryError 之类的。然后
 Exception用来处理程序错误,请求的资源不可用等等。

113 Java中的检查型异常和非检查型异常有什么区别?

这又是一个非常流行的Java异常面试题,会出现在各种层次的Java面试中。
检查型异常和非检查型异常的主要区别在于其处理方式。检查型异常需要使
用try, catch和finally关键字在编译期进行处理,否则会出现编译
器会报错。对于非检查型异常则不需要这样做。Java中所有继承自
java.lang.Exception类的异常都是检查型异常,所有继承自
RuntimeException的异常都被称为非检查型异常。

114 Java中的NullPointerException和ArrayIndexOutOfBoundException之间有什么相同之处?

在Java异常面试中这并不是一个很流行的问题,但会出现在不同层次的初
学者面试中,用来测试应聘者对检查型异常和非检查型异常的概念是否熟悉。
顺便说一下,该题的答案是,这两个异常都是非检查型异常,都继承自
RuntimeException。该问题可能会引出另一个问题,即Java和C的数组有
什么不同之处,因为C里面的数组是没有大小限制的,绝对不会抛出ArrayIndexOutOfBoundException。

115 在Java异常处理的过程中,你遵循的那些最好的实践是什么?

这个问题在面试技术经理是非常常见的一个问题。因为异常处理在项目设
计中是非常关键的,所以精通异常处理是十分必要的。异常处理有很多最
佳实践,下面列举集中,它们提高你代码的健壮性和灵活性:

  1. 调用方法的时候返回布尔值来代替返回null,这样可以 NullPointerException。
    由于空指针是java异常里最恶心的异常。
  2. catch块里别不写代码。空catch块是异常处理里的错误事件,因为它只是捕获了
    异常,却没有任何处理或者提示。通常你起码要打印出异常信息,当然你最好根据需求
    对异常信息进行处理。
    3)能抛受控异常(checked Exception)就尽量不抛受非控异常(checked Exception)。
    通过去掉重复的异常处理代码,可以提高代码的可读性。
  3. 绝对不要让你的数据库相关异常显示到客户端。由于绝大多数数据库和SQLException异
    常都是受控异常,在Java中,你应该在DAO层把异常信息处理,然后返回处理过的能让用户看懂
    并根据异常提示信息改正操作的异常信息。5) 在Java中,一定要在数据库连接,数据库查询,
    流处理后,在finally块中调用close()方法。

116 error和exception的区别?

Error类和Exception类的父类都是Throwable类
Error类一般是指与虚拟机相关的问题,如系统奔溃,虚拟机错误,内存空间不足,方法调用
栈溢出等。错误导致的应用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误,建议程序终止。
Exception类表示程序可以处理的异常,可以捕获且有可能恢复。遇到这种类异常,应尽可能
处理异常,使程序恢复运行,而不是随意终止异常。

117 Java异常处理机制

Java对异常类进行了分类,不同类型的异常分类用不用的Java类表示,所有异常类的根
类是java.lang.Throwable,Throwable下面有两个派生的子类Error和Exception,Error
表示应用程序本身无法克服和恢复的一种严重问题。Exception表示程序还能恢复和克服的
问题,其中分为系统异常和普通异常,系统异常是软件本身缺陷导致的问题,也就是开发人
员考虑不周到所导致的问题,软件使用者无法克服和恢复,但是这种问题下还可以让系统运
行或者让软件死掉,例如,数组下标越界(ArrayIndexOfBoundsException),空指针异常
等;普通异常是运行环境的变化或异常导致的问题,是用户能够克服的问题,例如,网络断
线,硬盘空间不够,发生这样的异常后,程序不应该死掉。
Java为系统异常和普通异t常提供了不同的解决方案,编译器强制普通异常必须try…catch
处理或用throws声明继续上抛给调用方法处理,所以普通异常也称为checked异常,而系
统异常可以处理也可以不处理,所以,编译器不强制用try…catch处理或throws声明,所以
系统异常处理也称为uncheched异常。

118 写出常见的5个RuntimeException

(1)java.lang.NullPointerException空指针异常,出现原因:调用了未经初始化的对
性爱那个或者不存在的对象。
(2)ClassNoFoundException 指定类找不到,出现原因:类的名称和路径加载错误,通
常是试图通过字符串来加载某个类时可能引发异常。
(3)NumberFormatException字符串转换为数字异常,出现原因:字符串数据中包含非
数字型字符。
(4)IndexOutOfBoundsException数组下标越界异常
(5)IllegalArgumentException 方法传递参数错误

119 throw和throws区别

throw:
    (1)throw语句用在方法体内,表示抛出异常,由方法体内的语句处理
    (2)throw是具体向外抛出异常的动作,所以它抛出的是一个异常实例,执行throw
一定是抛出了某种异常
throws:
    (1)throws语句是用在方法声明后面,表示如果抛出异常,由该方法的调用者来进
行异常处理。
    (2)throws主要是声明这个方法会抛出某种类型的异常,让它的使用者要知道需要
捕获的异常的类型。
    (3)throws表示出现异常的一种可能性,并不一定会发生这种异常。

120 什么是“异常链”?

“异常链”是Java中非常流行的异常处理概念,是指在进行一个异常处理时抛出了另外一个
异常,由此产生
了一个异常链条。该技术大多用于将“ 受检查异常” ( checked exception)封装成
为“非受检查异常”
(unchecked exception)或者RuntimeException。顺便说一下,如果因为因为异常你
决定抛出一个新的异常,
你一定要包含原有的异常,这样,处理程序才可以通过getCause()和initCause()方法
来访问异常最终的根源。

121 你曾经自定义实现过异常吗?怎么写的?

然,我们绝大多数都写过自定义或者业务异常,像AccountNotFoundException。在面
试过程中询问
这个Java异常问题的主要原因是去发现你如何使用这个特性的。这可以更准确和精致的
去处理异常,当然这也跟
你选择checked 还是unchecked exception息息相关。通过为每一个特定的情况创建一
个特定的异常,你就为
调用者更好的处理异常提供了更好的选择。相比通用异常(general exception),我
更倾向更为精确的异常。大
量的创建自定义异常会增加项目class的个数,因此,在自定义异常和通用异常之间维
持一个平衡是成功的关键。

122 Java异常处理中有哪些关键字?

throw:有时我们需要显式地创建并抛出异常对象来终止程序的正常执行。throw关键字
用来抛出并处理运行时异常。
throws:当我们抛出任何“被检查的异常(checked exception)”并不处理时,需要在
方法签名中使用关键字throws来告知调用程序此方法可能会抛出的异常。调用方法可
能会处理这些异常,或者同样用throws来将异常传给上一级调用方法。throws关键字
后可接多个潜在异常,甚至是在main()中也可以使用throws。
try-catch:我们在代码中用try-catch块处理异常。当然,一个try块之后可以有多个
catch子句,try-catch块也能嵌套。每个catch块必须接受一个(且仅有一个)代表
异常类型的参数。

123 描述一下异常的层级。

Java异常是层级的,并通过继承来区分不同种类的异常。
Throwable是所有异常的父类,它有两个直接子对象Error,Exception,其中Exception
又被继续划分为“被检查的异常(checked exception)”和”运行时的异常
(runtime exception,即不受检查的异常)”。 Error表示编译时和系统错误,
通常不能预期和恢复,比如硬件故障、JVM崩溃、内存不足等。
被检查的异常(Checked exception)在程序中能预期,并要尝试修复,
如FileNotFoundException。我们必须捕获此类异常,并为用户提供有用信
息和合适日志来进行调试。Exception是所有被检查的异常的父类。
运行时异常(Runtime Exception)又称为不受检查异常,源于糟糕的编程。
比如我们检索数组元素之前必须确认数组的长度,否则就可能会抛出
ArrayIndexOutOfBoundException运行时异常。RuntimeException是所有运行时异常的父类。
闭,因此,我们能使用finally进行关闭。不管异常有没有出现,finally块总会被执行。

124 Java异常类有哪些的重要方法?

String getMessage():方法返回Throwable的String型信息,当异常通过构造器创建后可用。
String getLocalizedMessage():此方法通过被重写来得到用本地语言
表示的异常信息返回给调用程序。Throwable类通常只是用getMessage()方法来实现返回异常信息。
synchronized Throwable getCause():此方法返回异常产生的原因,
如果不知道原因的话返回null。(原文有拼写错误 应该是if 不是id)
String toString():方法返回String格式的Throwable信息,此信息
包括Throwable的名字和本地化信息。
void printStackTrace():该方法打印栈轨迹信息到标准错误流。
该方法能接受PrintStream 和PrintWriter作为参数实现重载,这样就能实现打印栈轨迹到文件或流中。

125 Error和Exception有什么区别?

Error表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难
的情况下的一种严重问题;比如内存溢出,不可能指望程序能处理这样的情况;
Exception表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题;
也就是说,它表示如果程序运行正常,从不会发生的情况。

126 try{}里有一个return语句,那么紧跟在这个try后的finally{}里的代码会不会被执行,什么时候被执行,在return前还是后?

会执行,在方法返回调用者前执行。

127 Java语言如何进行异常处理,关键字:throws、throw、try、catch、finally分别如何使用?

Java通过面向对象的方法进行异常处理,把各种不同的异常进行分类,
并提供了良好的接口。在Java中,每个异常都是一个对象,它是Throwable
类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中
包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行
处理。Java的异常处理是通过5个关键词来实现的:try、catch、throw、
throws和finally。一般情况下是用try来执行一段程序,如果系统会抛出
(throw)一个异常对象,可以通过它的类型来捕获(catch)它,或通过总
是执行代码块(finally)来处理;try用来指定一块预防所有异常的程序;
catch子句紧跟在try块后面,用来指定你想要捕获的异常的类型;throw语
句用来明确地抛出一个异常;throws用来声明一个方法可能抛出的各种异常
(当然声明异常时允许无病呻吟);finally为确保一段代码不管发生什么异
常状况都要被执行;try语句可以嵌套,每当遇到一个try语句,异常的结构就
会被放入异常栈中,直到所有的try语句都完成。如果下一级的try语句没有对某
种异常进行处理,异常栈就会执行出栈操作,直到遇到有处理这种异常的try语
句或者最终将异常抛给JVM。

128 阐述final、finally、finalize的区别。

final:修饰符(关键字)有三种用法:如果一个类被声明为final,意味着它
不能再派生出新的子类,即不能被继承,因此它和abstract是反义词。将变量声
明为final,可以保证它们在使用中不被改变,被声明为final的变量必须在声明
时给定初值,而在以后的引用中只能读取不可修改。被声明为final的方法也同
样只能使用,不能在子类中被重写。- finally:通常放在try…catch…的后面构
造总是执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要
JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。- finalize:Object
类中定义的方法,Java中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之
前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize()
方法可以整理系统资源或者执行其他清理工作。

129 什么是比特(Bit),什么是字节(Byte),什么是字符(Char),它们长度是多少,各有什么区别

Bit最小的二进制单位 ,是计算机的操作部分 取值0或者1Byte是计算机操作数据
的最小单位由8位bit组成 取值(-128-127)Char是用户的可读写的最小单位,在Java
里面由16位bit组成 取值(0-65535)
Bit 是最小单位 计算机 只能认识 0或者1 
8个字节 是给计算机看的字符 是看到的东西  一个字符=二个字节

130 什么是流,按照传输的单位,分成哪两种流,并且他们的父类叫什么流是指数据的传输

字节流,字符流 字节流:InputStream OutputStream字符流:Reader Writer

131 流按照传输的方向可以分为哪两种,分别举例说明

输入输出相对于程序输入流InputStream,输出流OutputStream

132 OutputStream里面的write()是什么意思,write(byte b[], int off, int len)这个方法里面的三个参数分别是什么意思

write将指定字节传入数据源Byte b[ ]是byte数组b[off]是传入的第一个
字符b[off+len-1]是传入的最后的一个字符 len是实际长度

133 流一般需要不需要关闭,如果关闭的话在用什么方法,一般要在那个代码块里面关闭比较好,处理流是怎么关闭的,如果有多个流互相调用传入是怎么关闭的?

流一旦打开就必须关闭,使用close方法放入finally语句块中(finally 
语句一定会执行)调用的处理流就关闭处理流多个流互相调用只关闭最外层的流

134 Java中的所有的流可以分为几大类,它们的名字是什么,各代表什么

分为 字节输入流 InputStream 字节输出流 OutputStream字符输入流 Reader字
符输出流 Writer所有流都是这四个流的子类

135 io流怎样读取文件的

使用File对象获取文件路径,通过字符流Reader加入文件,使用字符缓存流BufferedReader处
理Reader,再定义一个字符串,循环遍历出文件。代码如下:
File file = new File(“d:/spring.txt”);try {Reader reader = 
new FileReader(file);BufferedReader buffered = 
new BufferedReader(reader);String data = null;
while((data = buffered.readLine())!=null){System.out.println(data);}} 
catch (FileNotFoundException e) {e.printStackTrace();} catch 
(IOException e) {e.printStackTrace();}

136  PrintStream、BufferedWriter、PrintWriter的比较?

PrintStream类的输出功能非常强大,通常如果需要输出文本内容,都应该将输
出流包装成PrintStream后进行输出。它还提供其他两项功能。与其他输出流不
同,PrintStream 永远不会抛出 IOException;而是,异常情况仅设置可通过
checkError 方法测试的内部标志。另外,为了自动刷新,可以创建一个
PrintStreamBufferedWriter:将文本写入字符输出流,缓冲各个字符从而提
供单个字符,数组和字符串的高效写入。通过write()方法可以将获取到的字符
输出,然后通过newLine()进行换行操作。BufferedWriter中的字符流必须通过
调用flush方法才能将其刷出去。并且BufferedWriter只能对字符流进行操作。
如果要对字节流操作,则使用BufferedInputStream。 PrintWriter的println
方法自动添加换行,不会抛异常,若关心异常,需要调用checkError方法看是否有
异常发生,PrintWriter构造方法可指定参数,实现自动刷新缓存(autoflush)

137 阐述JDBC操作数据库的步骤。

138 Statement和PreparedStatement有什么区别?哪个性能更好?

与Statement相比,①PreparedStatement接口代表预编译的语句,它主要的优势在
于可以减少SQL的编译错误并增加SQL的安全性(减少SQL注射攻击的可能性);
②PreparedStatement中的SQL语句是可以带参数的,避免了用字符串连接拼接
SQL语句的麻烦和不安全;③当批量处理SQL或频繁执行相同的查询时,
PreparedStatement有明显的性能上的优势,由于数据库可以将编译优
化后的SQL语句缓存起来,下次执行相同结构的语句时就会很快(不用再次编译和生成执行计划)。

139 使用JDBC操作数据库时,如何提升读取数据的性能?如何提升更新数据的性能?

要提升读取数据的性能,可以指定通过结果集(ResultSet)对象的setFetchSize()
方法指定每次抓取的记录数(典型的空间换时间策略);要提升更新数据的性能可以
使用PreparedStatement语句构建批处理,将若干SQL语句置于一个批处理中执行。

140 在进行数据库编程时,连接池有什么作用?

由于创建连接和释放连接都有很大的开销(尤其是数据库服务器不在本地时,每
次建立连接都需要进行TCP的三次握手,释放连接需要进行TCP四次握手,造成的
开销是不可忽视的),为了提升系统访问数据库的性能,可以事先创建若干连接
置于连接池中,需要时直接从连接池获取,使用结束时归还连接池而不必关闭连接
,从而避免频繁创建和释放连接所造成的开销,这是典型的用空间换取时间的策
略(浪费了空间存储连接,但节省了创建和释放连接的时间)。池化技术在Java
开发中是很常见的,在使用线程时创建线程池的道理与此相同。基于Java的开源
数据库连接池主要有:C3P0、Proxool、DBCP、BoneCP、Druid等

141 什么是DAO模式?

DAO(Data Access Object)顾名思义是一个为数据库或其他持久化机制提供了
抽象接口的对象,在不暴露底层持久化方案实现细节的前提下提供了各种数据访问
操作。在实际的开发中,应该将所有对数据源的访问操作进行抽象化后封装在一个
公共API中。用程序设计语言来说,就是建立一个接口,接口中定义了此应用程序
中将会用到的所有事务方法。在这个应用程序中,当需要和数据源进行交互的时候
则使用这个接口,并且编写一个单独的类来实现这个接口,在逻辑上该类对应一个
特定的数据存储。DAO模式实际上包含了两个模式,一是Data Accessor(数据访
问器),二是Data Object(数据对象),前者要解决如何访问数据的问题,而后
者要解决的是如何用对象封装数据。

142 事务的ACID是指什么

原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作的失
败都会导致整个事务的失败;- 一致性(Consistent):事务结束后系统状态是
一致的;- 隔离性(Isolated):并发执行的事务彼此无法看到对方的中间
状态;- 持久性(Durable):事务完成后所做的改动都会被持久化,即使发
生灾难性的失败。通过日志和同步备份可以在故障发生后重建数据。

143 脏读

A事务读取B事务尚未提交的数据并在此基础上操作,而B事务执行回滚,那
么A读取到的数据就是脏数据。

144 不可重复读

事务A重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务
B修改过了。

145 幻读

事务A重新执行一个查询,返回一系列符合查询条件的行,发现其中插入了被
事务B提交的行。

146 第1类丢失更新

事务A撤销时,把已经提交的事务B的更新数据覆盖了

147 第2类丢失更新

事务A覆盖事务B已经提交的数据,造成事务B所做的操作丢失

148 JDBC中如何进行事务处理?

Connection提供了事务处理的方法,通过调用setAutoCommit(false)可以
设置手动提交事务;当事务完成后用commit()显式提交事务;如果在事务处理
过程中发生异常则通过rollback()进行事务回滚。除此之外,从JDBC 3.0中还引
入了Savepoint(保存点)的概念,允许通过代码设置保存点并让事务回滚到指定的保存点。

149 JDBC能否处理Blob和Clob?

Blob是指二进制大对象(Binary Large Object),而Clob是指大字符对
象(Character Large Objec),因此其中Blob是为存储大的二进制数据
而设计的,而Clob是为存储大的文本数据而设计的。JDBC的PreparedStatement
和ResultSet都提供了相应的方法来支持Blob和Clob操作。

150 Java 中,编写多线程程序的时候你会遵循哪些最佳实践?

a)给线程命名,这样可以帮助调试。b)最小化同步的范围,而不是将整个方法
同步,只对关键部分做同步。c)如果可以,更偏向于使用 volatile 而不
是 synchronized。d)使用更高层次的并发工具,而不是使用 wait() 和 notify() 来
实现线程间通信,如 BlockingQueue,CountDownLatch 及 Semeaphore。e)优先使用
并发集合,而不是对集合进行同步。并发集合提供更好的可扩展性。

151 说出几点 Java 中使用 Collections 的最佳实践

a)使用正确的集合类,例如,如果不需要同步列表,使用 ArrayList 而不是 Vector。b)优
先使用并发集合,而不是对集合进行同步。并发集合提供更好的可扩展性。c)使用接口代表和
访问集合,如使用List存储 ArrayList,使用 Map 存储 HashMap 等等。d)使用迭代器来循
环集合。e)使用集合的时候使用泛型。

152 说出至少 5 点在 Java 中使用线程的最佳实践。

a)对线程命名b)将线程和任务分离,使用线程池执行器来执行 Runnable 或 Callable。c)使用线程池

153 说出 5 条 IO 的最佳实践

a)使用有缓冲区的 IO 类,而不要单独读取字节或字符。b)使用 NIO 和 NIO2c)在 finally
块中关闭流,或者使用 try-with-resource 语句。d)使用内存映射文件获取更快的 IO。

154 列出 5 个应该遵循的 JDBC 最佳实践

a)使用批量的操作来插入和更新数据b)使用 PreparedStatement 来避免 SQL 异常,并
提高性能。c)使用数据库连接池d)通过列名来获取结果集,不要使用列的下标来获取。

155 在多线程环境下,SimpleDateFormat 是线程安全的吗?

不是,非常不幸,DateFormat 的所有实现,包括 SimpleDateFormat 都不是线程安全
的,因此你不应该在多线程序中使用,除非是在对外线程安全的环境中使用,如
将 SimpleDateFormat 限制在 ThreadLocal 中。如果你不这么做,在解析或
者格式化日期的时候,可能会获取到一个不正确的结果。因此,从日期、时间处理的
所有实践来说,我强力推荐 joda-time 库。

156 Java 中如何格式化一个日期?如格式化为 ddMMyyyy 的形式?

Java 中,可以使用 SimpleDateFormat 类或者 joda-time 库来格式日期。DateFormat 类
允许你使用多种流行的格式来格式化日期。参见答案中的示例代码,代码中演示了将日期格式化
成不同的格式,如 dd-MM-yyyy 或 ddMMyyyy。

157 嵌套静态类与顶级类有什么区别?

一个公共的顶级类的源文件名称与类名相同,而嵌套静态类没有这个要求。一个嵌套类位于顶
级类内部,需要使用顶级类的名称来引用嵌套静态类,如 HashMap.Entry 是一个嵌套静态
类,HashMap 是一个顶级类,Entry是一个嵌套静态类。

158 Java 中,Serializable 与 Externalizable 的区别?

Serializable 接口是一个序列化 Java 类的接口,以便于它们可以在网络上传输或者可
以将它们的状态保存在磁盘上,是 JVM 内嵌的默认序列化方式,成本高、脆弱而且
不安全。Externalizable 允许你控制整个序列化过程,指定特定的二进制格式,增加安全机制。

159 Java 中,DOM 和 SAX 解析器有什么不同?

DOM 解析器将整个 XML 文档加载到内存来创建一棵 DOM 模型树,这样可以更快的查
找节点和修改 XML 结构,而 SAX 解析器是一个基于事件的解析器,不会将整个 XML 文
档加载到内存。由于这个原因,DOM 比 SAX 更快,也要求更多的内存,不适合于解析大 XML 文件。

160 说出 JDK 1.7 中的三个新特性?

虽然 JDK 1.7 不像 JDK 5 和 8 一样的大版本,但是,还是有很多新的
特性,如 try-with-resource 语句,这样你在使用流或者资源的时候,就不需
要手动关闭,Java 会自动关闭。Fork-Join 池某种程度上实现 Java 版的 Map-reduce。允
许 Switch 中有 String 变量和文本。菱形操作符(<>)用于类型推断,不再需要在变量声
明的右边申明泛型,因此可以写出可读写更强、更简洁的代码。另一个值得一提的特性是
改善异常处理,如允许在同一个 catch 块中捕获多个异常。

161 说出 5 个 JDK 1.8 引入的新特性?

Java 8 在 Java 历史上是一个开创新的版本,下面 JDK 8 中 5 个主要的特
性:Lambda 表达式,允许像对象一样传递匿名函数Stream API,充分利用现代多
核 CPU,可以写出很简洁的代码Date 与 Time API,最终,有一个稳定、简单
的日期和时间库可供你使用扩展方法,现在,接口中可以有静态、默认方法。重复
注解,现在你可以将相同的注解在同一类型上使用多次。

162 a==b”和”a.equals(b)”有什么区别?

如果 a 和 b 都是对象,则 a==b 是比较两个对象的引用,只有当 a 和 b 指向的
是堆中的同一个对象才会返回 true,而 a.equals(b) 是进行逻辑比较,所以通常需
要重写该方法来提供逻辑一致性的比较。例如,String 类重写 equals() 方法,所
以可以用于两个不同对象,但是包含的字母相同的比较。

163 a.hashCode() 有什么用?与 a.equals(b) 有什么关系?

hashCode() 方法是相应对象整型的 hash 值。它常用于基于 hash 的集合类,
如 Hashtable、HashMap、LinkedHashMap等等。它与 equals() 方法关系特别
紧密。根据 Java 规范,两个使用 equal() 方法来判断相等的对象,必须具有相
同的 hash code。

164 JVM为什么可以跨平台

JVM能跨计算机体系结构(操作系统)来执行Java字节码(JVM字节码指令集),屏
蔽可与各个计算机平台相关的软件或者硬件之间的差异,使得与平台相关的耦合统一
由JVM提供者来实现。
指令集:计算机所能识别的机器语言的命令集合。
每个运行中的Java程序都是一个JVM实例

165 描述JVM体系结构

(1)类加载器:JVM启动时或者类运行时将需要的class加载到JVM中。每个被装载的
类的类型对应一个Class实例,唯一表示该类,存于堆中。
(2)执行引擎:负责执行JVM的字节码指令(CPU)。执行引擎是JVM的核心部分,作
用是解析字节码指令,得到执行结果(实现方式:直接执行,JIT(just in time)
即时编译转成本地代码执行,寄存器芯片模式执行,基于栈执行)。本质上就是一个
个方法串起来的流程。每个Java线程就是一个执行引擎的实例,一个JVM实例中会有
多个执行引擎在工作,有的执行用户程序,有的执行JVM内部程序(GC).
(3)内存区:模拟物理机的存储、记录和调度等功能模块,如寄存器或者PC指针记录器
。存储执行引擎执行时所需要存储的数据。
(4)本地方法接口:调用操作系统本地方法返回结果。

166 描述JVM工作机制

机器如何执行代码:源代码-预处理器-编译器-汇编程序-目标代码-链接器-可执行程
序。
Java编译器将高级语言编译成虚拟机目标语言。
JVM执行字节码指令是基于栈的架构,所有的操作数必须先入栈,然后根据操作码选择
从栈顶弹出若干元素进行计算后将结果压入栈中。
通过Java编译器将源代码编译成虚拟机目标语言,然后通过JVM执行引擎执行。

167 为何JVM字节码指令选择基于栈的结构

JVM要设计成平台无关性,很难设计统一的基于寄存器的指令。
为了指令的紧凑性,让编译后的class文件更加紧凑,提高字节码在网络上的传输效率

168 描述执行引擎的架构设计

创建新线程时,JVM会为这个线程创建一个栈,同时分配一个PC寄存器(指向第一行可
执行的代码)。调用新方法时会在这个栈上创建新的栈帧数据结构。执行完成后方法对
应的栈帧将消失,PC寄存器被销毁,局部变量区所有值被释放,被JVM回收。

169 描述javac编译器的基本结构

Javac编译器的作用是将符合Java语言规范的的源代码转换成JVM规范的Java字节码。
(1)词法分析器组件:找出规范化的Token流
(2)语法分析器组件:生成符合Java语言规范的抽象语法树
(3)语义分析器组件:将复杂的语法转化成最简单的语法,注解语法树
(4)代码生成器组件:将语法树数据结构转化成字节码数据结构

170 ClassLoader(类加载器)有哪些

1)Bootstrap ClassLoader(启动类加载器):完全由JVM控制,加载JVM自身工作需要
的类(JAVA_HOME/lib)
(2)Extension ClassLoader(扩展类加载器):属于JVM自身一部分,不是JVM自身实
现的(JAVA_HOME/lib/ext)
(3)Appclication ClassLoader(应用程序类加载器):父类是Extension ClassLoader,
加载Classpath(用户类路径)上的类库

171 描述ClassLoader的作用(什么是类加载器)和加载过程

将Class文件加载到JVM中、审查每个类由谁加载(父优先的等级加载机制)、将Class字
节码重新解析成JVM统一要求的对象(Class对象)格式。
.class->findclass->Liking:Class规范验证、准备、解析->类属性初始化赋值
(static块的执行)->Class对象(这也就是为什么静态块只执行一次)

172 描述JVM类加载机制

ClassLoader首先不会自己尝试去加载类,而是把这个请求委托给父类加载器完成,
每一个层次都是。只有当父加载器反馈无法完成请求时(在搜索范围内没有找到所需
的类),子加载器才会尝试加载(等级加载机制、父优先、双亲委派)。
好处:类随着它的加载器一起具有一种带有优先级的层次关系;保证同一个类只能
被一个加载器加载。

173 JVM加载class文件到内存的两种方式

(1)隐式加载:继承或者引用的类不在内存中
(2)显式加载:代码中通过调用ClassLoader加载

174 加载类错误分析及其解决

1)ClassNotFoundException:没有找到对应的字节码(.class)文件;检查
classpath下有无对应文件
(2)NoClassDefFoundError:隐式加载时没有找到,ClassNotFoundException引
发NoClassDefFoundError;确保每个类引用的类都在classpath下
(3)UnsatisfiedLinkError:(未满足链接错误)删除了JVM的某个lib文件或者解
析native标识的方法时找不到对应的本地库文件
(4)ClassCastException:强制类型转换时出现这个错误;容器类型最好显示指明其
所包含对象类型、先instanceof检查是不是目标类型,再类型转换
(5)ExceptionInitializerError:给类的静态属性赋值时

175 Java应不应该动态加载类(JVM能不能动态加载类)

JVM中对象只有一份,不能被替换,对象的引用关系只有对象的创建者持有和使用,
JVM不可干预对象的引用关系,因为JVM不知道对象是怎么被使用的,JVM不知道对象
的运行时类型,只知道编译时类型。
但是可以不保存对象的状态,对象创建和使用后就被释放掉,下次修改后,对象就是
新的了(JSP)。

176 Java中哪些组件需要使用内存

(1)Java堆:存储Java对象
(2)线程:Java运行程序的实体
(3)类和类加载器:存储在堆中,这部分区域叫永久代(PermGen区)
(4)NIO:基于通道和缓冲区来执行I/O的新方式。
(5)JNI:本地代码可以调用Java方法,Java方法也可以调用本地代码

177 描述JVM内存结构及内存溢出

JVM是按照运行时数据的存储结构来划分内存结构的。
PC寄存器数据:严格来说是一个数据结构,保存当前正在执行的程序的内存地址。
为了线程切换后能恢复到正确的执行位置,线程私有。不会内存溢出。
(1)Java栈:方法执行的内存模型,存储线程执行所需要的数据。线程私有。
–OutOfMemoryError:JVM扩展栈时无法申请到足够的空间。一个不断调用自身而
不会终止的方法。
–StackOverflowError:请求的栈深度大于JVM所允许的栈深度。创建足够多的线程。
(2)堆:存储对象,每一个存在堆中Java对象都是这个对象的类的副本,复制包括
继承自他父类的所有非静态属性。线程共享。
–OutOfMemoryError:对象数量到达堆容量限制。可通过不断向ArrayList中添加
对象实现。

178 描述JVM内存结构及内存溢出

(3)方法区:存储类结构信息。包括常量池(编译期生产的各种字面量和符号引用)
和运行时常量池。线程共享。
–OutOfMemoryError:同运行时常量池。
(4)本地方法栈:与Java栈类似,为JVM运行Native方法准备的空间。线程私有。
(C栈)OutOfMemoryError和StackOverflowError同JVM栈。
(5)运行时常量池:代表运行时每个class文件中的常量表。运行期间产生的新的常
量放入运行时常量池。
–OutOfMemoryError:不断向List中添加字符串,然后String.inern(),
PermGen Space(运行时常量池属于方法区)。
(6)本地直接内存:即NIO。
–OutOfMemoryError:通过直接向操作系统申请分配内存。

179 描述JVM内存分配策略

(1)对象优先分配在Eden
(2)大对象直接进入老年代
(3)长期存活的对象将进入老年代
(4)幸存区相同年龄对象的占幸存区空间的多于其一半,将进入老年代
(5)空间担保分配(老年代剩余空间需多于幸存区的一半,否则要Full GC)

180 描述JVM如何检测垃圾

通过可达性分析算法,通过一些列称为GC Roots的对象作为起始点,从这些起始
点向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引
用链相连(GC Roots到这个对象不可达),则证明这个对象是不可用的。
使用可达性分析算法而不是引用计数算法。因为引用计数算法很难解决对象之间相
互循环引用的问题。

181 哪些元素可作为GC Roots

(1)JVM栈(栈帧中的本地变量表)中的引用
(2)方法区中类静态属性引用
(3)方法区中常量引用
(4)本地方法栈中JNI(一般的Native方法)引用

182 描述分代垃圾收集算法的思路

把对象按照寿命长短来分组,分为年轻代和老年代,新创建的在老年代,经历几次
回收后仍然存活的对象进入老年代,老年代的垃圾频率不像年轻代那样频繁,减少
每次收集都去扫描所有对象的数量,提高垃圾回收效率。

183 描述基于分代的堆结构及其比例

(1)年轻代(Young区-1/4):Eden+Survior(1/8,这个比例保证只有10%的空间被
浪费,保证每次回收都只有不多于10%的对象存活)=From+To,存放新创建的对象.
(2)老年代(Old区 ):存放几次垃圾收集后存活的对象
(3)永久区(Perm区):存放类的Class对象

184 描述垃圾收集算法

1)标记-清除算法:首先标记处所要回收的对象,标记完成后统一清除。缺点:标记
效率低,清除效率低,回收结束后会产生大量不连续的内存碎片(没有足够连续空间
分配内存,提前触发另一次垃圾回收)。适用于对象存活率高的老年代。
(2)复制算法(Survivor的from和to区,from和to会互换角色):
将内存容量划分大小相等的两块,每次只使用其中一块。一块用完,就将存活的对象
复制到另一块,然后把使用过的一块一次清除。不用考虑内存碎片,每次只要移动顶
端指针,按顺序分配内存即可,实现简单运行高效。适用于新生代。
缺点:内存缩小为原来的一般,代价高。浪费50%的空间。
(3)标记-整理算法:
标记完成后,将存活的对象移动到一端,然后清除边界以外的内存。适用于对象存活
率高的老年代。

185 描述新生代和老年代的回收策略

Eden区满后触发minor GC,将所有存活对象复制到一个Survivor区,另一Survivor
区存活的对象也复制到这个Survivor区中,始终保证有一个Survivor是空的。
Toung区Survivor满后触发minor GC后仍然存活的对象存到Old区,如果Survivor
区放不下Eden区的对象或者Survivor区对象足够老了,直接放入Old区,如果Old
区放不下则触发Full GC。
Perm区满将触发Major GC。

186 描述CMS垃圾收集器

CMS 收集器:Concurrent Mark Sweep 并发标记-清除。重视响应速度,适用于互
联网和B/S系统的服务端上。初始标记还是需要Stop the world 但是速度很快。缺
点:CPU资源敏感,无法浮动处理垃圾,会有大量空间碎片产生。

187 MySQL的复制原理以及流程

基本原理流程,3个线程以及之间的关联;

  1. 主:binlog线程——记录下所有改变了数据库数据的语句,放进master上的binlog中;
  2. 从:io线程——在使用start slave 之后,负责从master上拉取 binlog 内
    容,放进 自己的relay log中;
  3. 从:sql执行线程——执行relay log中的语句;

188 MySQL中myisam与innodb的区别

1>.InnoDB支持事物,而MyISAM不支持事物
2>.InnoDB支持行级锁,而MyISAM支持表级锁
3>.InnoDB支持MVCC, 而MyISAM不支持
4>.InnoDB支持外键,而MyISAM不支持
5>.InnoDB不支持全文索引,而MyISAM支持。

189 innodb引擎的4大特性

插入缓冲(insert buffer),二次写(double write),自适应哈希索引(ahi),预读(read ahead)

190 MySQL中varchar与char的区别以及varchar(50)中的50代表的涵义

1)、varchar与char的区别char是一种固定长度的类型,varchar则是一种可变
长度的类型(2)、varchar(50)中50的涵义最多存放50个字符,varchar(50)和
(200)存储hello所占空间一样,但后者在排序时会消耗更多内存,因为order by col采
用fixed_length计算col长度(memory引擎也一样)(3)、int(20)中20的涵义是指显
示字符的长度但要加参数的,最大为255,比如它是记录行数的id,插入10笔资料,它
就显示00000000001 ~~~00000000010,当字符的位数超过11,它也只显示11位,如果
你没有加那个让它未满11位就前面加0的参数,它不会在前面加020表示最大显示宽度为
20,但仍占4字节存储,存储范围不变;(4)、mysql为什么这么设计对大多数应用没有
意义,只是规定一些工具用来显示字符的个数;int(1)和int(20)存储和计算均一样

191 innodb有多少种日志

错误日志:记录出错信息,也记录一些警告信息或者正确的信息。查询日志:记录所
有对数据库请求的信息,不论这些请求是否得到了正确的执行。慢查询日志:设置一个
阈值,将运行时间超过该值的所有SQL语句都记录到慢查询的日志文件中。二进制日志:记录
对数据库执行更改的所有操作。中继日志:事务日志

192 事物的4种隔离级别

隔离级别读未提交(RU)读已提交(RC)可重复读(RR)串行

193 如何设计一个高并发的系统

  • 数据库的优化,包括合理的事务隔离级别、SQL语句优化、索引的优化
  • 使用缓存,尽量减少数据库 IO
  • 分布式数据库、分布式缓存
  • 服务器的负载均衡

194 锁的优化策略

  • 读写分离
  • 分段加锁
  • 减少锁持有的时间
  • 多个线程尽量以相同的顺序去获取资源

195 索引的底层实现原理和优化

B+树,经过优化的B+树
主要是在所有的叶子结点中增加了指向下一个叶子节点的指针,因此InnoDB建议为大部
分表使用默认自增的主键作为主索引。

196 什么情况下设置了索引但无法使用

  • 以“%”开头的LIKE语句,模糊匹配
  • OR语句前后没有同时使用索引
  • 数据类型出现隐式转化(如varchar不加单引号的话可能会自动转换为int型)

197 实践中如何优化MySQL

  • SQL语句及索引的优化
  • 数据库表结构的优化
  • 系统配置的优化
  • 硬件的优化

198 简单描述mysql中,索引,主键,唯一索引,联合索引的区别,对数据库的性能有什么影响

索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着
对数据表里所有记录的引用指针。普通索引(由关键字KEY或INDEX定义的索引)的唯一任
务是加快对数据的访问速度。普通索引允许被索引的数据列包含重复的值。如果能确定
某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该用关键字
UNIQUE把它定义为一个唯一索引。也就是说,唯一索引可以保证数据记录的唯一性。主
键,是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯一标识一条
记录,使用关键字PRIMARY KEY 来创建。索引可以覆盖多个数据列,如像
INDEX(columnA, columnB)索引,这就是联合索引。索引可以极大的提高数据的
查询速度,但是会降低插入、删除、更新表的速度,因为在执行这些写操作时,还要操作
索引文件

199 数据库中的事务是什么?

事务(transaction)是作为一个单元的一组有序的数据库操作。如果组中的所有操作都
成功,则认为事务成功,即使只有一个操作失败,事务也不成功。如果所有操作完成,事
务则提交,其修改将作用于所有其他数据库进程。如果一个操作失败,则事务将回滚,该
事务所有操作的影响都将取消。ACID 四大特性,原子性、隔离性、一致性、持久性

200 了解XSS攻击吗?如何防止?

XSS是跨站脚本攻击,首先是利用跨站脚本漏洞以一个特权模式去执行攻击者构造的脚
本,然后利用不安全的Activex控件执行恶意的行为。使用htmlspecialchars()函数
对提交的内容进行过滤,使字符串里面的特殊符号实体化

201 SQL注入漏洞产生的原因?如何防止

SQL注入产生的原因:程序开发过程中不注意规范书写sql语句和对特殊字符进行过滤,
导致客户端可以通过全局变量POST和GET提交一些sql语句正常执行。
防止SQL注入的方式:
开启配置文件中的magic_quotes_gpc 和 magic_quotes_runtime设置
执行sql语句时使用addslashes进行sql语句转换
Sql语句书写尽量不要省略双引号和单引号。
过滤掉sql语句中的一些关键词:update、insert、delete、select、 * 。
提高数据库表和字段的命名技巧,对一些重要的字段根据程序的特点命名,取不易被猜到的。
Php配置文件中设置register_globals为off,关闭全局变量注册
控制错误信息,不要在浏览器上输出错误信息,将错误信息写到日志文件中

202 解释MySQL外连接、内连接与自连接的区别

先说什么是交叉连接: 交叉连接又叫笛卡尔积,它是指不使用任何条件,直接将一个表的
所有记录和另一个表中的所有记录一一匹配。
内连接 则是只有条件的交叉连接,根据某个条件筛选出符合条件的记录,不符合条件的记
录不会出现在结果集中,即内连接只连接匹配的行。外连接 其结果集中不仅包含符合连
接条件的行,而且还会包括左表、右表或两个表中的所有数据行,这三种情况依次称之为左
外连接,右外连接,和全外连接。
左外连接,也称左连接,左表为主表,左表中的所有记录都会出现在结果集中,对于那些
在右表中并没有匹配的记录,仍然要显示,右边对应的那些字段值以NULL来填充。右外连
接,也称右连接,右表为主表,右表中的所有记录都会出现在结果集中。左连接和右连接可
以互换,MySQL目前还不支持全外连接

203 Myql中的事务回滚机制概述

事务是用户定义的一个数据库操作序列,这些操作要么全做要么全不做,是一个不可分割的工
作单位,事务回滚是指将该事务已经完成的对数据库的更新操作撤销。
要同时修改数据库中两个不同表时,如果它们不是一个事务的话,当第一个表修改完,可能
第二个表修改过程中出现了异常而没能修改,此时就只有第二个表依旧是未修改之前的状
态,而第一个表已经被修改完毕。而当你把它们设定为一个事务的时候,当第一个表修改
完,第二表修改出现异常而没能修改,第一个表和第二个表都要回到未修改的状态,这就
是所谓的事务回滚

204 什么是存储过程?用什么来调用?

存储过程是一个预编译的SQL语句,优点是允许模块化的设计,就是说只需创建一次,以后
在该程序中就可以调用多次。如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语
句执行要快。可以用一个命令对象来调用存储过程

205 MySQL数据库作发布系统的存储,一天五万条以上的增量,预计运维三年,怎么优化?

a. 设计良好的数据库结构,允许部分数据冗余,尽量避免join查询,提高效率。
b. 选择合适的表字段数据类型和存储引擎,适当的添加索引。
c. mysql库主从读写分离。
d. 找规律分表,减少单表中的数据量提高查询速度。
e。添加缓存机制,比如memcached,apc等。
f. 不经常改动的页面,生成静态页面。
g. 书写高效率的SQL。比如 SELECT * FROM TABEL 改为 SELECT field_1,
field_2, field_3 FROM TABLE.

206  对于大流量的网站,您采用什么样的方法来解决各页面访问量统计问题

a. 确认服务器是否能支撑当前访问量。
b. 优化数据库访问。
c. 禁止外部访问链接(盗链), 比如图片盗链。
d. 控制文件下载。
e. 使用不同主机分流。
f. 使用浏览统计软件,了解访问量,有针对性的进行优化。

207 存储时期

Datatime:以 YYYY-MM-DD HH:MM:SS 格式存储时期时间,精确到秒,占用8个字节
得存储空间,datatime类型与时区无关
Timestamp:以时间戳格式存储,占用4个字节,范围小1970-1-1到2038-1-19,显示
依赖于所指定得时区,默认在第一个列行的数据修改时可以自动得修改timestamp列得值
Date:(生日)占用得字节数比使用字符串.datatime.int储存要少,使用date只需
要3个字节,存储日期月份,还可以利用日期时间函数进行日期间得计算
Time:存储时间部分得数据
注意:不要使用字符串类型来存储日期时间数据(通常比字符串占用得储存空间小,
在进行查找过滤可以利用日期得函数)
使用int存储日期时间不如使用timestamp类型

208 Hibernate工作原理

1.读取并解析配置文件 2.读取并解析映射信息,创建SessionFactory 3.打开
Sesssion 4.创建事务Transation 5.持久化操作 6.提交事务 7.关闭Session 
8.关闭SesstionFactory (finally进行执行)

209 Hibernate中get和load有什么不同之处

把get和load放到一起进行对比是Hibernate面试时最常问到的问题,这是因为只有
正确理解get()和load()这二者后才有可能高效地使用Hibernate。get和load的最
大区别是,如果在缓存中没有找到相应的对象,get将会直接访问数据库并返回一个
完全初始化好的对象,而这个过程有可能会涉及到多个数据库调用;而load方法在缓
存中没有发现对象的情况下,只会返回一个代理对象,只有在对象getId()之外的其它
方法被调用时才会真正去访问数据库,这样就能在某些情况下大幅度提高性能

210 Hibernate中save、persist和saveOrUpdate这三个方法的不同之处

除了get和load,这又是另外一个经常出现的Hibernate面试问题。 所有这三个方法,
也就是save()、saveOrUpdate()和persist()都是用于将对象保存到数据库中的方法,
但其中有些细微的差别。例如,save()只能INSERT记录,但是saveOrUpdate()可以
进行 记录的INSERT和UPDATE。还有,save()的返回值是一个Serializable对象,
而persist()方法返回值为void

211 Hibernate中的命名SQL查询指的是什么

Hibernate的这个面试问题同Hibernate提供的查询功能相关。命名查询指的是
用标签在影射文档中定义的SQL查询,可以通过使用Session.getNamedQuery()
方法对它进行调用。命名查询使你可以使用你所指定的一个名字拿到某个特定的查询。
Hibernate中的命名查询可以使用注解来定义,也可以使用我前面提到的xml影射问句
来定义。在Hibernate中,@NameQuery用来定义单个的命名查询,@NameQueries用来定义多个命名查询。

212 Hibernate中的SessionFactory有什么作用? SessionFactory是线程安全的吗

这也是Hibernate框架的常见面试问题。顾名思义,SessionFactory就是一个用于
创建Hibernate的Session对象的工厂。SessionFactory通常是在应用启动时创建
好的,应用程序中的代码用它来获得Session对象。作为一个单个的数据存储,它也
是 线程安全的,所以多个线程可同时使用同一个SessionFactory。Java JEE应用
一般只有一个SessionFactory,服务于客户请求的各线程都通过这个工厂来获得
Hibernate的Session实例,这也是为什么SessionFactory接口的实现必须是线
程安全的原因。还有,SessionFactory的内部状态包含着同对象关系影射有关的所
有元数据,它是 不可变的,一旦创建好后就不能对其进行修改了。

213 Hibernate中的Session指的是什么? 可否将单个的Session在多个线程间进行共享

前面的问题问完之后,通常就会接着再问这两个问题。问完SessionFactory的问题
后就该轮到Session了。Session代表着Hibernate所做的一小部分工作,它负责维
护者同数据库的链接而且 不是线程安全的,也就是说,Hibernage中的Session不能
在多个线程间进行共享。虽然Session会以主动滞后的方式获得数据库连接,但是
Session最好还是在用完之后立即将其关闭。

214 hibernate中sorted collection和ordered collection有什么不同

Hibernate中,对象具有三种状态:transient、persistent和detached。同
Hibernate的session有关联的对象是persistent对象。对这种对象进行的所有
修改都会按照事先设定的刷新策略,反映到数据库之中,也即,可以在对象的任何
一个属性发生改变时自动刷新,也可以通过调用Session.flush()方法显式地进行
刷新。如果一个对象原来同Session有关联关系,但当下却没有关联关系了,这样的
对象就是detached的对象。你可以通过调用任意一个session的update()或者
saveOrUpdate()方法,重新将该detached对象同相应的seesion建立关联关系。
Transient对象指的是新建的持久化类的实例,它还从未同Hibernate的任何
Session有过关联关系。同样的,你可以调用persist()或者save()方法,将
transient对象变成persistent对象

215 Hibernate中Session的lock()方法有什么作用

这是一个比较棘手的Hibernate面试问题,因为Session的lock()方法重建了关联
关系却并没有同数据库进行同步和更新。因此,你在使用lock()方法时一定要多加
小心。顺便说一下,在进行关联关系重建时,你可以随时使用Session的update()
方法同数据库进行同步。有时这个问题也可以这么来问:Session的lock()方法和
update()方法之间有什么区别?。这个小节中的关键点也可以拿来回答这个问题。

216 Hibernate中二级缓存指的是什么

这是同Hibernate的缓存机制相关的第一个面试问题,不出意外后面还会有更多这方
面的问题。二级缓存是在SessionFactory这个级别维护的缓存,它能够通过节省几
番数据库调用往返来提高性能。还有一点值得注意,二级缓存是针对整个应用而不是
某个特定的session的。

217 Hibernate中的查询缓存指的是什么

这个问题有时是作为上个Hibernate面试问题的后继问题提出的。查询缓存实际上保
存的是sql查询的结果,这样再进行相同的sql查询就可以之间从缓存中拿到结果了。
为了改善性能,查询缓存可以同二级缓存一起来使用。Hibernate支持用多种不同的
开源缓存方案,比如EhCache,来实现查询缓存

218 为什么在Hibernate的实体类中要提供一个无参数的构造器这一点非常重要

每个Hibernate实体类必须包含一个 无参数的构造器, 这是因为Hibernate框架要
使用Reflection API,通过调用Class.newInstance()来创建这些实体类的实例。
如果在实体类中找不到无参数的构造器,这个方法就会抛出一个InstantiationException异常

219 可不可以将Hibernate的实体类定义为final类

是的,你可以将Hibernate的实体类定义为final类,但这种做法并不好。因为
Hibernate会使用代理模式在延迟关联的情况下提高性能,如果你把实体类定义成
final类之后,因为 Java不允许对final类进行扩展,所以Hibernate就无法再使
用代理了,如此一来就限制了使用可以提升性能的手段。不过,如果你的持久化类实
现了一个接口而且在该接口中声明了所有定义于实体类中的所有public的方法轮到
话,你就能够避免出现前面所说的不利后果

220 hibernate的三种状态之间如何转换

当对象由瞬时状态(Transient)一save()时,就变成了持久化状态; 当我们在
Session里存储对象的时候,实际是在Session的Map里存了一份, 也就是它的缓
存里放了一份,然后,又到数据库里存了一份,在缓存里这一份叫持久对象
(Persistent)。 Session 一 Close()了,它的缓存也都关闭了,整个Session也
就失效了,这个时候,这个对象变成了游离状态(Detached),但数据库中还是存在
的。 当游离状态(Detached)update()时,又变为了持久状态(Persistent)。
当持久状态(Persistent)delete()时,又变为了瞬时状态(Transient), 此时,
数据库中没有与之对应的记录

221 Hibernate是如何延迟加载

当Hibernate在查询数据的时候,数据并没有存在与内存中,当程序真正对数据的
操作时,对象才存在与内存中,就实现了延迟加载,他节省了服务器的内存开销,
从而提高了服务器的性能

222 解Hibernate中怎样实现类之间的关系

类与类之间的关系主要体现在表与表之间的关系进行操作,它们都是对对象进行操
作,我们程序中把所有的表与类都映射在一起,它们通过配置文件中的
many-to-one、one-to-many、many-to-many

223 如何优化Hibernate

1.使用双向一对多关联,不使用单向一对多 2.灵活使用单向一对多关联 
3.不用一对一,用多对一取代 4.配置对象缓存,不使用集合缓存 5.一对多
集合使用Bag,多对多集合使用Set 6. 继承类使用显式多态 7. 表字段要少,
表关联不要怕多,有二级缓存撑腰

224 Hibernate的五个核心接口

Configuration 接口:配置Hibernate,根据其启动hibernate,创建
SessionFactory 对象; SessionFactory 接口:初始化Hibernate,充当
数据存储源的代理,创建 session 对象,sessionFactory 是线程安全的,
意味着它的同一个实例可以被应 用的多个线程共享,是重量级、二级缓存;
Session 接口:负责保存、更新、删除、加载和查询对象,是线程不安全的,
避免多个线程共享同一个session,是轻量级、一级缓存; Transaction 接口:
管理事务; Query 和Criteria 接口:执行数据库的查询

225 #{}和${}的区别是什么

是 P r o p e r t i e s 文 件 中 的 变 量 占 位 符 , 它 可 以 用 于 标 签 属 性 值 和 s q l 内 部 , 属 于 静 态 文 本 替 换 , 比 如 {}是Properties文件中的变量占位符,它可以用于标签属性值和sql内部, 属于静态文本替换,比如 Propertiessql{driver}会被静态替换为com.mysql.jdbc.Driver。#{}是sql
的参数占位符,Mybatis会将sql中的#{}替换为?号,在sql执行前会使用
PreparedStatement的参数设置方法,按序给sql的?号占位符设置参数值,
比如ps.setInt(0, parameterValue),#{item.name}的取值方式为使用反
射从参数对象中获取item对象的name属性值,相当于param.getItem().getName()。

226 Xml映射文件中,除了常见的select|insert|updae|delete标签之外,还有哪些标签

还有很多其他的标签,、、、、
,加上动态sql的9个标签,trim|where|set|foreach|if|choose|when|otherwise|bind等,
其中为sql片段标签,通过标签引入sql片段,为不支持自增的主键生成策略标签

227 最佳实践中,通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗

Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的
值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法内的参数,就
是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方
法名拼接字符串作为key值,可唯一定位一个MappedStatement,举例:
com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到
namespace为com.mybatis3.mappers.StudentDao下面id = findStudentById的
MappedStatement。在Mybatis中,每一个、、、
标签,都会被解析为一个MappedStatement对象。
Dao接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。
Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生
成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代
表的sql,然后将sql执行结果返回

228 Mybatis是如何进行分页的?分页插件的原理是什么

Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,
而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以
使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截
方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句
和物理分页参数。
举例:select * from student,拦截sql后重写为:select t.* from (select

  • from student)t limit 0,10

229 为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?

Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对
象时,可以根据对象关系模型直接获取,所以它是全自动的。而Mybatis在查询关联对
象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具

230 Mybatis比IBatis比较大的几个改进是什么

a.有接口绑定,包括注解绑定sql和xml绑定Sql ,
b.动态sql由原来的节点配置变成OGNL(对象图导航语言)表达式,
c. 在一对一,一对多的时候引进了association,在一对多的时候引入了collection节
点,不过都是在resultMap里面配置

231 接口绑定有几种实现方式,分别是怎么实现的

接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上
@Select@Update等注解里面包含Sql语句来绑定,另外一种就是通过xml里面写SQL来绑定,
在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名.

232 MyBatis实现一对一有几种方式?具体怎么操作的

有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次,
通过在resultMap里面配置association节点配置一对一的类就可以完成;
嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查
询数据,也是通过association配置,但另外一个表的查询通过select属性配置

233 MyBatis的缓存

MyBatis的缓存分为一级缓存和二级缓存,
一级缓存放在session里面,默认就有,二级缓存放在它的命名空间里,默认是打开的
, 使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态)
,可在它的映射文件中配置<cache/

234 什么是spring?

Spring 是个java企业级应用的开源开发框架。Spring主要用来开发Java应用,但
是有些扩展是针对构建J2EE平台的web应用。Spring 框架目标是简化Java企业级应
用开发,并通过POJO为基础的编程模型促进良好的编程习惯。

235 使用Spring框架的好处是什么

轻量:Spring 是轻量的,基本的版本大约2MB
控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是
创建或查找依赖的对象们
面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统
服务分开
容器:Spring 包含并管理应用中对象的生命周期和配置
MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品
事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全
局事务(JTA)
异常处理:Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate
or JDO抛出的)转化为一致的unchecked 异常

236 Spring由哪些模块组成

Core module
Bean module
Context module
Expression Language module
JDBC module
ORM module
OXM module
Java Messaging Service(JMS) module
Transaction module
Web module
Web-Servlet module
Web-Struts module
Web-Portlet module

237 核心容器(应用上下文) 模块

这是基本的Spring模块,提供spring 框架的基础功能,BeanFactory 是 任何
以spring为基础的应用的核心。Spring 框架建立在此模块之上,它使Spring成为一个容器

238 BeanFactory – BeanFactory 实现举例

Bean 工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和
依赖从正真的应用代码中分离。
最常用的BeanFactory 实现是XmlBeanFactory 类

239 XMLBeanFactory

最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,
它根据XML文件中的定义加载beans。该容器从XML 文件读取配置元数据并用它去
创建一个完全配置的系统或应用

240 解释AOP模块

AOP模块用于发给我们的Spring应用做面向切面的开发, 很多支持由AOP联盟提供,
这样就确保了Spring和其他AOP框架的共通性。这个模块将元数据编程引入Spring

241 什么是Spring IOC 容器

Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,
并且管理这些对象的整个生命周期

242 IOC的优点是什么

IOC 或 依赖注入把应用的代码量降到最低。它使应用容易测试,单元测试不再需要单例
和JNDI查找机制。最小的代价和最小的侵入性使松散耦合得以实现。IOC容器支持加载
服务时的饿汉式初始化和懒加载

243 ApplicationContext通常的实现是什么

FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,
XML Bean 配置文件的全路径名必须提供给它的构造函数。
ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,
这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。
WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用
的所有bean

244 Bean 工厂和 Application contexts  有什么区别?

Application contexts提供一种方法处理文本消息,一个通常的做法是加载文件资源
(比如镜像),它们可以向注册为监听器的bean发布事件。另外,在容器或容器内的对
象上执行的那些不得不由bean工厂以程序化方式处理的操作,可以在Application
contexts中以声明的方式处理。Application contexts实现了MessageSource接口,
该接口的实现以可插拔的方式提供获取本地化消息的方

245 什么是Spring的依赖注入

依赖注入,是IOC的一个方面,是个通常的概念,它有多种解释。这概念是说你不用创建
对象,而只需要描述它如何被创建。你不在代码里直接组装你的组件和服务,但是要在
配置文件里描述哪些组件需要哪些服务,之后一个容器(IOC容器)负责把他们组装起来

246 有哪些不同类型的IOC(依赖注入)方式

构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系
列参数,每个参数代表一个对其他类的依赖。
Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂 方法
实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入

247 Spring 的优点

(1)spring属于低侵入式设计,代码的污染极低;
(2)spring的DI机制降低了业务对象替换的复杂性;
(3)容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能;
(4)降低了组件之间的耦合性 ,实现了软件各层之间的解耦; 
(5)容器提供单例模式支持;
(6)可以使用容器提供的众多服务,如事务管理,消息服务等;
(7)容器提供了众多的辅助类,能加快应用的开发;
(8)spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等 
(9)独立于各种应用服务器 
(10)Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择
spring的部分或全部。

248 Spring的AOP理解

AOP,一般称为面向方面(切面)编程,作为面向对象的一种补充,用于解剖封装好的
对象内部,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,
这个模块被命名为“切面”(Aspect),切面将那些与业务无关,却被业务模块共同调用
的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了
系统的可维护性。可用于权限认证、日志、事务处理。
AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代
理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
(1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP
代理类,因此也称为编译时增强,他会在编译阶段将AspectJ织入到Java字节码中,
运行的时候就是增强之后的AOP对象。
(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码
,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对
象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
①JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。
JDK动态代理的核心是InvocationHandler接口和Proxy类。生成的代理对象的方法
调用都会委托到InvocationHandler.invoke()方法,当我们调用代理类对象的方
法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数
method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。
②如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。
CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动
态的生成指定类的一个子类对象,并覆盖其中特定方法,覆盖方法时可以添加增强代
码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为
final,那么它是无法使用CGLIB做动态代理的。
(3)静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的
静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring
AOP则无需特定的编译器处理

249 Spring的IoC理解

(1)IOC就是控制反转。就是对象的创建权反转交给Spring,由容器控制程序之间
的依赖关系,作用是实现了程序的解耦合,而非传统实现中,由程序代码直接操控。
(依赖)控制权由应用代码本身转到了外部容器,由容器根据配置文件去创建实例并管
理各个实例之间的依赖关系,控制权的转移,是所谓反转,并且由容器动态的将某种
依赖关系注入到组件之中。BeanFactory 是Spring IoC容器的具体实现与核心接口,
提供了一个先进的配置机制,使得任何类型的对象的配置成为可能,用来包装和管理
各种bean。
(2)最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,
这里用的就是java的反射机制,通过反射在运行时动态的去创建、调用对象。spring
就是根据配置文件在运行时动态的去创建对象,并调用对象的方法的。
(3)Spring的IOC有三种注入方式 :第一是根据属性注入,也叫set方法
注入;第二种是根据构造方法进行注入;第三种是根据注解进行注入。
详细的说:
(4)IoC,控制反转:将对象交给容器管理,你只需要在spring
配置文件总配置相应的bean,以及设置相关的属性,让spring容器
生成类的实例对象以及管理对象。在spring容器启动的时候,spring会
把你在配置文件中配置的bean都初始化以及装配好,然后在你需要调用的时候
,就把它已经初始化好的那些bean分配给你需要调用这些bean的类。就是将
对象的控制权反转给spring容器管理。
(5)DI机制(Dependency Injection,依赖注入):可以说是IoC的其中一
个内容,在容器实例化对象的时候主动的将被调用者(或者说它的依赖对象)注
入给调用对象。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码
来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要
一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在
系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注
射到A当中,这样就完成了对各个对象之间关系的控制。
IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功
能分离出来形成可重用的功能组件

250 BeanFactory和ApplicationContext有什么区别

BeanFactory和ApplicationContext是Spring的两大核心接口,而其中
ApplicationContext是BeanFactory的子接口。它们都可以当做Spring的
容器,生成Bean实例的,并管理容器中的Bean。
(1)BeanFactory:是Spring里面最底层的接口,提供了最简单的容器的功
能,负责读取bean配置文档,管理bean的加载与实例化,维护bean之间的依
赖关系,负责bean的生命周期,但是无法支持spring的aop功能和web应用。
(2)ApplicationContext接口作为BeanFactory的派生,因而具有BeanFactory
所有的功能。而且ApplicationContext还在功能上做了扩展,以一种更面向框架的
方式工作以及对上下文进行分层和实现继承,相较于BeanFactorty,
ApplicationContext还提供了以下的功能: ①默认初始化所有的Singleton,
也可以通过配置取消预初始化。
②继承MessageSource,因此支持国际化。
③资源访问,比如访问URL和文件。
④事件机制。
⑤同时加载多个配置文件。
⑥以声明式方式启动并创建Spring容器。
⑦载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层
次,比如应用的web层。
(3)①BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到
某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就
不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,
BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
②而ApplicationContext则相反,它是在容器启动时,一次性创建了所有
的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,
这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入
所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用
等待,因为它们已经创建好了。
③相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存
空间。当应用程序配置Bean较多时,程序启动较慢。
(4)BeanFactory通常以编程的方式被创建,ApplicationContext还能以声
明的方式创建,如使用ContextLoader。
(5)BeanFactory和ApplicationContext都支持BeanPostProcessor、
BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory
需要手动注册,而ApplicationContext则是自动注册

251 解释Spring支持的几种bean的作用域。

Spring容器中的bean可以分为5个范围:
(1)singleton:这种bean范围是默认的,这种范围确保不管接受到多少个
请求,每个容器中只有一个bean的实例,单例的模式由bean factory自身来维护。
(2)prototype:原形范围与单例范围相反,为每一个bean请求提供一个实例。
(3)request:在请求bean范围内会每一个来自客户端的网络请求创建一个实例,
在请求完成以后,bean会失效并被垃圾回收器回收。
(4)Session:与请求范围类似,确保每个session中有一个bean的实例,
在session过期后,bean会随之失效。(5)global-session:global-session
和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。
如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储
在global-session中。全局作用域与Servlet中的session作用域效果相同

252 请解释Spring Bean的生命周期

首先说一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;
 Spring上下文中的Bean生命周期也类似,如下:
(1)实例化一个Bean--也就是我们常说的new;
(2)按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;
(3)如果这个Bean已经实现了BeanNameAware接口,会调用它实现的
setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值;
(4)如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的
setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自
身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);
(5)如果这个Bean已经实现了ApplicationContextAware接口,会调用
setApplicationContext(ApplicationContext)方法,传入Spring上下
文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext
是BeanFactory的子接口,有更多的实现方法);
(6)如果这个Bean关联了BeanPostProcessor接口,将会调用
postProcessBeforeInitialization(Object obj, String s)方法,
BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean
初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;
(7)如果Bean在Spring配置文件中配置了init-method属性会自动调用其配
置的初始化方法。
(8)如果这个Bean关联了BeanPostProcessor接口,将会调用
postProcessAfterInitialization(Object obj, String s)方法、;

253  Spring中bean的加载过程

(1)获取配置文件资源;
(2)对获取的xml资源进行一定的处理检验;
(3)处理包装资源;
(4)解析处理包装过后的资源;
(5)加载提取bean并注册(添加到beanDefinitionMap中)

254  Spring框架中的单例Beans是线程安全的么

Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安
全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变
的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安
全的。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线
程安全。最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”

255  Spring如何处理线程并发问题

Spring使用ThreadLocal解决线程安全问题。
我们知道在一般情况下,只有有状态的Bean才可以在多线程环境下共享,在Spring中,绝
大部分Bean都可以声明为singleton作用域。就是因为Spring对一些
Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)
中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,
因为有状态的Bean就可以在多线程中共享了。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
(1)在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。
这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变
量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,
程序设计和编写难度相对较大。
(2)而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为
每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为
每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal
提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
(3)概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,
而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,
而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响

256 请解释Spring自动装配模式的区别

在Spring框架中共有5种自动装配:
(1)no:这是Spring框架的默认设置,在该设置下自动装配是关闭的,开发者需要自行
在bean定义中用标签明确的设置依赖关系。
(2)byName:该选项可以根据bean名称设置依赖关系。当向一个bean中自动装配一个
属性时,容器将根据bean的名称自动在配置文件中查询一个匹配的bean。如果找到的话,
就装配这个属性,如果没找到的话就报错。
(3)byType:该选项可以根据bean类型设置依赖关系。当向一个bean中自动装配一个属性
时,容器将根据bean的类型自动在在配置文件中查询一个匹配的bean。如果找到的话,就装
配这个属性,如果没找到的话就报错。
(4)constructor:构造器的自动装配和byType模式类似,但是仅仅适用于与有构造器相同
参数的bean,如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。
(5)autodetect:该模式自动探测使用构造器自动装配或者byType自动装配。首先,首先
会尝试找合适的带参数的构造器,如果找到的话就是用构造器自动装配,如果在bean内部没
有找到相应的构造器或者是无参构造器,容器就会自动选择byTpe的自动装配方式

257 Spring 控制器的加载过程

(1)Web容器创建;
(2)上下文创建,但未初始化;
(3)监听器创建,并注册到Context上;
(4)上下文初始化;
(5)通知到监听者,Spring配置文件/@Configuration加载;
(6)Load-on-startup>0的ServletConfig创建,springMVC的DispatcherServlet此时创建

258 Spring 框架中都用到了哪些设计模式

(1)代理模式—在AOP和remoting中被用的比较多。(2)单例模式—在spring配置文件中
定义的bean默认为单例模式。(3)工厂模式—BeanFactory用来创建对象的实例。(4)模
板方法—用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
(5)前端控制器—Spring提供了DispatcherServlet来对请求进行分发。(6)视图帮
助(View Helper )—Spring提供了一系列的JSP标签,高效宏来辅助将分散的代码整合在
视图里。(7)依赖注入—贯穿于BeanFactory / ApplicationContext接口的核心理念

259 Spring事务的种类和各自的区别

spring支持编程式事务管理和声明式事务管理两种方式:
(1)编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。
对于编程式事务管理,spring推荐使用TransactionTemplate。
(2)声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建
或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点
就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需
在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规
则应用到业务逻辑中。
(3)显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声
明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事
务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别
,无法做到像编程式事务那样可以作用到代码块级别

260 Spring事务的实现方式和实现原理

(1)划分处理单元——IOC:
由于spring解决的问题是对单个数据库进行局部事务处理的,具体的实现首相用spring中的
IOC划分了事务处理单元。并且将对事务的各种配置放到了ioc容器中(设置事务管理器,设
置事务的传播特性及隔离机制)。
(2)AOP拦截需要进行事务处理的类:
Spring事务处理模块是通过AOP功能来实现声明式事务处理的,具体操作(比如事务实行的
配置和读取,事务对象的抽象),用TransactionProxyFactoryBean接口来使用AOP功能,
生成proxy代理对象,通过TransactionInterceptor完成对代理方法的拦截,将事务处理的
功能编织到拦截的方法中。 
读取ioc容器事务配置属性,转化为spring事务处理需要的内部数据结构
(TransactionAttributeSourceAdvisor),转化为TransactionAttribute表示的数据对象。 
(3)对事物处理实现(事务的生成、提交、回滚、挂起):
spring委托给具体的事务处理器实现。实现了一个抽象和适配。适配的具
体事务处理器:DataSource数据源支持、hibernate数据源事务处理支持、
JDO数据源事务处理支持,JPA、JTA数据源事务处理支持。这些支持都是通过设
计PlatformTransactionManager、AbstractPlatforTransaction一系列事
务处理的支持。为常用数据源支持提供了一系列的TransactionManager。
(4)结合:
PlatformTransactionManager实现了TransactionInterception接口,让
其与TransactionProxyFactoryBean结合起来,形成一个Spring声明式事务
处理的设计体系

261 解释一下Spring AOP里面的几个名词

(1)切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事
务管理是J2EE应用中一个关于横切关注点的很好的例子。 在Spring AOP中,切面可
以使用通用类(基于模式的风格) 或者在普通类中以 @Aspect 注解(@AspectJ风格)
来实现。
(2)连接点(Joinpoint):在程序执行过程中某个特定的点,比如某方法调用的
时候或者处理异常的时候。 在Spring AOP中,一个连接点 总是 代表一个方法的
执行。 通过声明一个org.aspectj.lang.JoinPoint类型的参数可以使通知(Advice)
的主体部分获得连接点信息。
(3)通知(Advice):在切面的某个特定的连接点(Joinpoint)上执行的动作。通
知有各种类型,其中包括“around”、“before”和“after”等通知。 通知的类型将在后
面部分进行讨论。许多AOP框架,包括Spring,都是以拦截器做通知模型, 并维护一个
以连接点为中心的拦截器链。
(4)切入点(Pointcut):匹配连接点(Joinpoint)的断言。通知和一个切入点表达
式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。 
切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。
(5)引入(Introduction):(也被称为内部类型声明(inter-type declaration))。
声明额外的方法或者某个类型的字段。 Spring允许引入新的接口(以及一个对应的实现)
到任何被代理的对象。例如,你可以使用一个引入来使bean实现 IsModified 接口,以便
简化缓存机制。
(6)目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)
的对象。也有人把它叫做 被通知(advised) 对象。 既然Spring AOP是通过运行时代理
实现的,这个对象永远是一个 被代理(proxied) 对象
(7)织入(Weaving):把切面(aspect)连接到其它的应用程序类型或者对象上,并创
建一个被通知(advised)的对象。 这些可以在编译时(例如使用AspectJ编译器),类加
载时和运行时完成。 Spring和其他纯Java AOP框架一样,在运行时完成织入。
切入点(pointcut)和连接点(join point)匹配的概念是AOP的关键,这使得AOP不同于
其它仅仅提供拦截功能的旧技术。 切入点使得定位通知(advice)可独立于OO层次。 
例如,一个提供声明式事务管理的around通知可以被应用到一组横跨多个对象中的方法上

©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页