java面试题总结一

基础概念

1、面向对象的三个特征

封装,继承,多态,这个应该是人人皆知,有时候也会加上抽象。

2、多态的好处

允许不同类对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用)。主要有以下优点:

  1. 可替换性:多态对已存在代码具有可替换性

  2. 可扩充性:增加新的子类不影响已经存在的类结构

  3. 接口性:多态是超类通过方法签名,向子类提供一个公共接口,由子类来完善或者重写它来实现的。

  4. 灵活性

  5. 简化性

3、代码中如何实现多态

实现多态主要有以下三种方式:
1. 接口实现 
2. 继承父类重写方法 
3. 同一类中进行方法重载

4、虚拟机是如何实现多态的

动态绑定技术(dynamic binding),执行期间判断所引用对象的实际类型,根据实际类型调用对应的方法

5、接口的意义

接口的意义用三个词就可以概括:规范,扩展,回调。

6、抽象类的意义

抽象类的意义可以用三句话来概括:

  1. 为其他子类提供一个公共的类型

  2. 封装子类中重复定义的内容

  3. 定义抽象方法,子类虽然有不同的实现,但是定义时一致的

7、接口和抽象类的区别

比较抽象类接口
默认方法抽象类可以有默认的方法实现java 8之前,接口中不存在方法的实现.
实现方式子类使用extends关键字来继承抽象类.如果子类不是抽象类,子类需要提供抽象类中所声明方法的实现.子类使用implements来实现接口,需要提供接口中所有声明的实现.
构造器抽象类中可以有构造器,接口中不能
和正常类区别抽象类不能被实例化接口则是完全不同的类型
访问修饰符抽象方法可以有public,protected和default等修饰接口默认是public,不能使用其他修饰符
多继承一个子类只能存在一个父类一个子类可以存在多个接口
添加新方法想抽象类中添加新方法,可以提供默认的实现,因此可以不修改子类现有的代码如果往接口中添加新方法,则子类中需要实现该方法.

8、父类的静态方法能否被子类重写

不能。重写只适用于实例方法,不能用于静态方法,而子类当中含有和父类相同签名的静态方法,我们一般称之为隐藏。

9、什么是不可变对象

不可变对象指对象一旦被创建,状态就不能再改变。任何修改都会创建一个新的对象,如 String、Integer及其它包装类

10、静态变量和实例变量的区别?

静态变量存储在方法区,属于类所有。实例变量存储在堆当中,其引用存在当前线程栈。

11、能否创建一个包含可变对象的不可变对象?

当然可以创建一个包含可变对象的不可变对象的,你只需要谨慎一点,不要共享可变对象的引用就可以了,如果需要变化时,就返回原对象的一个拷贝。最常见的例子就是对象中包含一个日期对象的引用。

12、java 创建对象的几种方式

  1. 采用new

  2. 通过反射

  3. 采用clone

  4. 通过序列化机制

前2者都需要显式地调用构造方法。造成耦合性最高的恰好是第一种,因此你发现无论什么框架,只要涉及到解耦必先减少new的使用。

13、switch中能否使用string做参数

在jdk 1.7之前,switch只能支持byte, short, char, int或者其对应的封装类以及Enum类型(jdk1.5)。从idk 1.7之后switch开始支持String。

14、switch能否作用在byte, long上?

 switch可作用于char byte short int
 switch可作用于char byte short int对应的包装类
 switch不可作用于long double float boolean,包括他们的包装类
 switch中可以是字符串类型,String(jdk1.7之后才可以作用在string上)
 switch中可以是枚举类型

15、String s1=”ab”, String s2=”a”+”b”, String s3=”a”, String s4=”b”, s5=s3+s4请问s5==s2返回什么?

返回false。在编译过程中,编译器会将s2直接优化为”ab”,会将其放置在常量池当中,s5则是被创建在堆区,相当于s5=new String(“ab”);

16、你对String对象的intern()熟悉么?

intern()方法会首先从常量池中查找是否存在该常量值,如果常量池中不存在则现在常量池中创建,如果已经存在则直接返回。
比如 
String s1=”aa”; 
String s2=s1.intern(); 
System.out.print(s1==s2);//返回true

17、Object中有哪些公共方法?

  1. equals()

  2. clone()

  3. getClass()

  4. notify(),notifyAll(),wait()

  5. toString

18、java当中的四种引用

强引用,软引用,弱引用,虚引用。不同的引用类型主要体现在GC上:

  1. 强引用:如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。

  2. 软引用:在使用软引用时,如果内存的空间足够,软引用就能继续被使用,而不会被垃圾回收器回收,只有在内存不足时,软引用才会被垃圾回收器回收。

  3. 弱引用:具有弱引用的对象拥有的生命周期更短暂。因为当 JVM 进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象。

  4. 虚引用:顾名思义,就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。

19、WeakReference与SoftReference的区别?

这点在四种引用类型中已经做了解释,这里简单说明一下即可: 
虽然 WeakReference 与 SoftReference 都有利于提高 GC 和 内存的效率,但是 WeakReference ,一旦失去最后一个强引用,就会被 GC 回收,而软引用虽然不能阻止被回收,但是可以延迟到 JVM 内存不足的时候。

20、为什么要有不同的引用类型

不像C语言,我们可以控制内存的申请和释放,在Java中有时候我们需要适当的控制对象被回收的时机,因此就诞生了不同的引用类型,可以说不同的引用类型实则是对GC回收时机不可控的妥协。有以下几个使用场景可以充分的说明:

  1. 利用软引用和弱引用解决OOM问题:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题.

  2. 通过软引用实现Java对象的高速缓存:比如我们创建了一Person的类,如果每次需要查询一个人的信息,哪怕是几秒中之前刚刚查询过的,都要重新构建一个实例,这将引起大量Person对象的消耗,并且由于这些对象的生命周期相对较短,会引起多次GC影响性能。此时,通过软引用和 HashMap 的结合可以构建高速缓存,提供性能。

21、java中==和eqauls()的区别,equals()和`hashcode的区别

==是运算符,用于比较两个变量是否相等,而equals是Object类的方法,用于比较两个对象是否相等。默认Object类的equals方法是比较两个对象的地址,此时和==的结果一样。换句话说:基本类型比较用==,比较的是他们的值。默认下,对象用==比较时,比较的是内存地址,如果需要比较对象内容,需要重写equal方法。

hashCode()方是Object类的一个方法,返回一个哈希值。如果两个对象根据equal()方法比较相等,那么调用这两个对象中任意一个对象的hashCode()方法必须产生相同的哈希值。
如果两个对象根据eqaul()方法比较不相等,那么产生的哈希值不一定相等(碰撞的情况下还是会相等的)。

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

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

将对象放入到集合中时,首先判断要放入对象的hashcode是否已经在集合中存在,不存在则直接放入集合。如果hashcode相等,然后通过equal()方法判断要放入对象与集合中的任意对象是否相等:如果equal()判断不相等,直接将该元素放入集合中,否则不放入。

23、有没有可能两个不相等的对象有相同的hashcode

有可能,两个不相等的对象可能会有相同的 hashcode 值,这就是为什么在 hashmap 中会有冲突。如果两个对象相等,必须有相同的hashcode 值,反之不成立。

24、可以在hashcode中使用随机数字吗?

不行,因为同一对象的 hashcode 值必须是相同的

25、a==b与a.equals(b)有什么区别

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

26、3*0.1==0.3返回值是什么

false,因为有些浮点数不能完全精确的表示出来

27、a=a+b与a+=b有什么区别吗?

+=操作符会进行隐式自动类型转换,此处a+=b隐式的将加操作的结果类型强制转换为持有结果的类型,而a=a+b则不会自动进行类型转换。如:
byte a = 127; 
byte b = 127; 
b = a + b; // error : cannot convert from int to byte 
b += a; // ok 
(译者注:这个地方应该表述的有误,其实无论 a+b 的值为多少,编译器都会报错,因为 a+b 操作会将 a、b 提升为 int 类型,所以将 int 类型赋值给 byte 就会编译出错)

28、short s1= 1; s1 = s1 + 1; 该段代码是否有错,有的话怎么改?

有错误,short类型在进行运算时会自动提升为int类型,也就是说s1+1的运算结果是int类型。

29、short s1= 1; s1 += 1; 该段代码是否有错,有的话怎么改?

+=操作符会自动对右边的表达式结果强转匹配左边的数据类型,所以没错。

30、& 和 &&的区别

首先记住&是位操作,而&&是逻辑运算符。另外需要记住逻辑运算符具有短路特性,而&不具备短路特性。

31、一个java文件内部可以有类?(非内部类)

只能有一个public公共类,但是可以有多个default修饰的类。

32、如何正确的退出多层嵌套循环?

  1. 使用标号和break;

  2. 通过在外层循环中添加标识符

33、内部类的作用

内部类可以有多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立.在单个外围类当中,可以让多个内部类以不同的方式实现同一接口,或者继承同一个类.创建内部类对象的时刻不依赖于外部类对象的创建。内部类并没有令人疑惑的”is-a”管系,它就像是一个独立的实体。

内部类提供了更好的封装,除了该外围类,其他类都不能访问。

34、final, finalize和finally的不同之处

final 是一个修饰符,可以修饰变量、方法和类。如果 final 修饰变量,意味着该变量的值在初始化后不能被改变。finalize 方法是在对象被回收之前调用的方法,给对象自己最后一个复活的机会,但是什么时候调用 finalize 没有保证。finally 是一个关键字,与 try 和 catch 一起用于异常的处理。finally 块一定会被执行,无论在 try 块中是否有发生异常。

35、clone()是哪个类的方法?

java.lang.Cloneable 是一个标示性接口,不包含任何方法,clone 方法在 object 类中定义。并且需要知道 clone() 方法是一个本地方法,这意味着它是由 c 或 c++ 或 其他本地语言实现的。

36、深拷贝和浅拷贝的区别是什么?

浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。

深拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深拷贝把要复制的对象所引用的对象都复制了一遍。

37、static都有哪些用法?

几乎所有的人都知道static关键字这两个基本的用法:静态变量和静态方法。也就是被static所修饰的变量/方法都属于类的静态资源,类实例所共享。

除了静态变量和静态方法之外,static也用于静态块,多用于初始化操作:


public calss PreCache{
   static{
       //执行相关操作
   }
}

此外static也多用于修饰内部类,此时称之为静态内部类。

最后一种用法就是静态导包,即import static.import static是在JDK 1.5之后引入的新特性,可以用来指定导入某个类中的静态资源,并且不需要使用类名。资源名,可以直接使用资源名,比如:


import static java.lang.Math.*;
 
public class Test{
 
   public static void main(String[] args){
       //System.out.println(Math.sin(20));传统做法
       System.out.println(sin(20));
   }
}

38、final有哪些用法

final也是很多面试喜欢问的地方,能回答下以下三点就不错了:
1.被final修饰的类不可以被继承 
2.被final修饰的方法不可以被重写 
3.被final修饰的变量不可以被改变。如果修饰引用,那么表示引用不可变,引用指向的内容可变。
4.被final修饰的方法,JVM会尝试将其内联,以提高运行效率 
5.被final修饰的常量,在编译阶段会存入常量池中。

回答出编译器对final域要遵守的两个重排序规则更好:
1.在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
2.初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。

 

数据类型相关

39、java中int char,long各占多少字节?

类型字节数位数
short216
int432
long864
float432
double864
char216

40、64位的JVM当中,int的长度是多少?

Java 中,int 类型变量的长度是一个固定值,与平台无关,都是 32 位。意思就是说,在 32 位 和 64 位 的Java 虚拟机中,int 类型的长度是相同的。

41、int和Integer的区别

Integer是int的包装类型,在拆箱和装箱中,二者自动转换。int是基本类型,直接存数值,而integer是对象,用一个引用指向这个对象。

42、int 和Integer谁占用的内存更多?

Integer 对象会占用更多的内存。Integer是一个对象,需要存储对象的元数据。但是 int 是一个原始类型的数据,所以占用的空间更少。

43、String, StringBuffer和StringBuilder区别

String是字符串常量,final修饰:StringBuffer字符串变量(线程安全);
StringBuilder 字符串变量(线程不安全)。

44、String和StringBuffer

String和StringBuffer主要区别是性能:String是不可变对象,每次对String类型进行操作都等同于产生了一个新的String对象,然后指向新的String对象。所以尽量不在对String进行大量的拼接操作,否则会产生很多临时对象,导致GC开始工作,影响系统性能。

StringBuffer是对对象本身操作,而不是产生新的对象,因此在有大量拼接的情况下,我们建议使用StringBuffer。

但是需要注意现在JVM会对String拼接做一定的优化:
String s=“This is only ”+”simple”+”test”会被虚拟机直接优化成String s=“This is only simple test”,此时就不存在拼接过程。

45、StringBuffer和StringBuilder

StringBuffer是线程安全的可变字符串,其内部实现是可变数组。StringBuilder是jdk 1.5新增的,其功能和StringBuffer类似,但是非线程安全。因此,在没有多线程问题的前提下,使用StringBuilder会取得更好的性能。

46、什么是编译器常量?使用它有什么风险?

公共静态不可变(public static final )变量也就是我们所说的编译期常量,这里的 public 可选的。实际上这些变量在编译时会被替换掉,因为编译器知道这些变量的值,并且知道这些变量在运行时不能改变。这种方式存在的一个问题是你使用了一个内部的或第三方库中的公有编译时常量,但是这个值后面被其他人改变了,但是你的客户端仍然在使用老的值,甚至你已经部署了一个新的jar。为了避免这种情况,当你在更新依赖 JAR 文件时,确保重新编译你的程序。

47、java当中使用什么类型表示价格比较好?

如果不是特别关心内存和性能的话,使用BigDecimal,否则使用预定义精度的 double 类型。

48、如何将byte转为String

可以使用 String 接收 byte[] 参数的构造器来进行转换,需要注意的点是要使用的正确的编码,否则会使用平台默认编码,这个编码可能跟原来的编码相同,也可能不同。

49、可以将int强转为byte类型么?会产生什么问题?

我们可以做强制转换,但是Java中int是32位的而byte是8 位的,所以,如果强制转化int类型的高24位将会被丢弃,byte 类型的范围是从-128到128

 

垃圾回收

50、你知道哪些垃圾回收算法?

垃圾回收从理论上非常容易理解,具体的方法有以下几种: 
1. 标记-清除 
2. 标记-复制 
3. 标记-整理 
4. 分代回收 

http://blog.csdn.net/dd864140130/article/details/50084471

51、如何判断一个对象是否应该被回收

这就是所谓的对象存活性判断,常用的方法有两种:1.引用计数法; 2.对象可达性分析。由于引用计数法存在互相引用导致无法进行GC的问题,所以目前JVM虚拟机多使用对象可达性分析算法。

52、简单的解释一下垃圾回收

Java 垃圾回收机制最基本的做法是分代回收。内存中的区域被划分成不同的世代,对象根据其存活的时间被保存在对应世代的区域中。一般的实现是划分成3个世代:年轻、年老和永久。内存的分配是发生在年轻世代中的。当一个对象存活时间足够长的时候,它就会被复制到年老世代中。对于不同的世代可以使用不同的垃圾回收算法。进行世代划分的出发点是对应用中对象存活时间进行研究之后得出的统计规律。一般来说,一个应用中的大部分对象的存活时间都很短。比如局部变量的存活时间就只在方法的执行过程中。基于这一点,对于年轻世代的垃圾回收算法就可以很有针对性。

53、调用System.gc()会发生什么?

通知GC开始工作,但是GC真正开始的时间不确定。

 

进程,线程相关

54、说说进程,线程,协程之间的区别

简而言之,进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程。进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高。线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位。同一进程中的多个线程之间可以并发执行。

55、你了解守护线程吗?它和非守护线程有什么区别

程序运行完毕,jvm会等待非守护线程完成后关闭,但是jvm不会等待守护线程。守护线程最典型的例子就是GC线程。

56、什么是多线程上下文切换

多线程的上下文切换是指CPU控制权由一个已经正在运行的线程切换到另外一个就绪并等待获取CPU执行权的线程的过程。

57、创建两种线程的方式?他们有什么区别?

通过实现java.lang.Runnable或者通过扩展java.lang.Thread类。相比扩展Thread,实现Runnable接口可能更优.原因有二:

  1. Java不支持多继承。因此扩展Thread类就代表这个子类不能扩展其他类。而实现Runnable接口的类还可能扩展另一个类。

  2. 类可能只要求可执行即可,因此继承整个Thread类的开销过大。

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

start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。

59、怎么检测一个线程是否持有对象监视器

Thread类提供了一个holdsLock(Object obj)方法,当且仅当对象obj的监视器被某条线程持有的时候才会返回true,注意这是一个static方法,这意味着”某条线程”指的是当前线程。

60、Runnable和Callable的区别

Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。 
这其实是很有用的一个特性,因为多线程相比单线程更难、更复杂的一个重要原因就是因为多线程充满着未知性,某条线程是否执行了?某条线程执行了多久?某条线程执行的时候我们期望的数据是否已经赋值完毕?无法得知,我们能做的只是等待这条多线程的任务执行完毕而已。而Callable+Future/FutureTask却可以方便获取多线程运行的结果,可以在等待时间太长没获取到需要的数据的情况下取消该线程的任务。

61、什么导致线程阻塞

阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪),学过操作系统的同学对它一定已经很熟悉了。Java 提供了大量方法来支持阻塞,下面让我们逐一分析。

方法说明
sleep()sleep() 允许 指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。 典型地,sleep() 被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止
suspend() 和 resume()两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume() 被调用,才能使得线程重新进入可执行状态。典型地,suspend() 和 resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用 resume() 使其恢复。
yield()yield() 使当前线程放弃当前已经分得的CPU 时间,但不使当前线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程
wait() 和 notify()两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种允许 指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用。

62、wait(),notify()和suspend(),resume()之间的区别

初看起来它们与 suspend() 和 resume() 方法对没有什么分别,但是事实上它们是截然不同的。区别的核心在于,前面叙述的所有方法,阻塞时都不会释放占用的锁(如果占用了的话),而这一对方法则相反。上述的核心区别导致了一系列的细节上的区别。

首先,前面叙述的所有方法都隶属于 Thread 类,但是这一对却直接隶属于 Object 类,也就是说,所有对象都拥有这一对方法。初看起来这十分不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。而调用 任意对象的notify()方法则导致从调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在 synchronized 方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException 异常。

wait() 和 notify() 方法的上述特性决定了它们经常和synchronized关键字一起使用,将它们和操作系统进程间通信机制作一个比较就会发现它们的相似性:synchronized方法或块提供了类似于操作系统原语的功能,它们的执行不会受到多线程机制的干扰,而这一对方法则相当于 block 和wakeup 原语(这一对方法均声明为 synchronized)。它们的结合使得我们可以实现操作系统上一系列精妙的进程间通信的算法(如信号量算法),并用于解决各种复杂的线程间通信问题。

关于 wait() 和 notify() 方法最后再说明两点: 
第一:调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。

第二:除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。

谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend() 方法和不指定超时期限的 wait() 方法的调用都可能产生死锁。遗憾的是,Java 并不在语言级别上支持死锁的避免,我们在编程中必须小心地避免死锁。

以上我们对 Java 中实现线程阻塞的各种方法作了一番分析,我们重点分析了 wait() 和 notify() 方法,因为它们的功能最强大,使用也最灵活,但是这也导致了它们的效率较低,较容易出错。实际使用中我们应该灵活使用各种方法,以便更好地达到我们的目的。

63、产生死锁的条件

1.互斥条件:一个资源每次只能被一个进程使用。 
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 
3.不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。 
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

64、为什么wait()方法和notify()/notifyAll()方法要在同步块中被调用

这是JDK强制的,wait()方法和notify()/notifyAll()方法在调用前都必须先获得对象的锁

65、wait()方法和notify()/notifyAll()方法在放弃对象监视器时有什么区别

wait()方法和notify()/notifyAll()方法在放弃对象监视器的时候的区别在于:wait()方法立即释放对象监视器,notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象监视器。

66、wait()与sleep()的区别

关于这两者已经在上面进行详细的说明,这里就做个概括好了:

  • sleep()来自Thread类,和wait()来自Object类。调用sleep()方法的过程中,线程不会释放对象锁。而 调用 wait 方法线程会释放对象锁

  • sleep()睡眠后不出让系统资源,wait让其他线程可以占用CPU

  • sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒.而wait()需要配合notify()或者notifyAll()使用

67、为什么wait, nofity和nofityAll这些方法不放在Thread类当中

一个很明显的原因是JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了。简单的说,由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。

68、怎么唤醒一个阻塞的线程

如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它;如果线程遇到了IO阻塞,无能为力,因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统。

69、什么是多线程的上下文切换

多线程的上下文切换是指CPU控制权由一个已经正在运行的线程切换到另外一个就绪并等待获取CPU执行权的线程的过程。

70、synchronized和ReentrantLock的区别

synchronized是和if、else、for、while一样的关键字,ReentrantLock是类,这是二者的本质区别。既然ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量,ReentrantLock比synchronized的扩展性体现在几点上: 
(1)ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁 
(2)ReentrantLock可以获取各种锁的信息 
(3)ReentrantLock可以灵活地实现多路通知 
另外,二者的锁机制其实也是不一样的:ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的应该是对象头中mark word。

71、FutureTask是什么

这个其实前面有提到过,FutureTask表示一个异步运算的任务。FutureTask里面可以传入一个Callable的具体实现类,可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操作。当然,由于FutureTask也是Runnable接口的实现类,所以FutureTask也可以放入线程池中。

72、一个线程如果出现了运行时异常怎么办?

如果这个异常没有被捕获的话,这个线程就停止执行了。另外重要的一点是:如果这个线程持有某个某个对象的监视器,那么这个对象监视器会被立即释放。

73、Java当中有哪几种锁

  1. 自旋锁: 自旋锁在JDK1.6之后就默认开启了。基于之前的观察,共享数据的锁定状态只会持续很短的时间,为了这一小段时间而去挂起和恢复线程有点浪费,所以这里就做了一个处理,让后面请求锁的那个线程在稍等一会,但是不放弃处理器的执行时间,看看持有锁的线程能否快速释放。为了让线程等待,所以需要让线程执行一个忙循环也就是自旋操作。在jdk6之后,引入了自适应的自旋锁,也就是等待的时间不再固定了,而是由上一次在同一个锁上的自旋时间及锁的拥有者状态来决定。

  2. 偏向锁: 在JDK1.之后引入的一项锁优化,目的是消除数据在无竞争情况下的同步原语。进一步提升程序的运行性能。 偏向锁就是偏心的偏,意思是这个锁会偏向第一个获得他的线程,如果接下来的执行过程中,改锁没有被其他线程获取,则持有偏向锁的线程将永远不需要再进行同步。偏向锁可以提高带有同步但无竞争的程序性能,也就是说他并不一定总是对程序运行有利,如果程序中大多数的锁都是被多个不同的线程访问,那偏向模式就是多余的,在具体问题具体分析的前提下,可以考虑是否使用偏向锁。

  3. 轻量级锁: 为了减少获得锁和释放锁所带来的性能消耗,引入了“偏向锁”和“轻量级锁”,所以在Java SE1.6里锁一共有四种状态,无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,它会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。

74、如何在两个线程间共享数据

通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAll、await/signal/signalAll进行唤起和等待,比方说阻塞队列BlockingQueue就是为线程之间共享数据而设计的。

75、如何正确的使用wait()?使用if还是while?

wait() 方法应该在循环调用,因为当线程获取到 CPU 开始执行的时候,其他条件可能还没有满足,所以在处理前,循环检测条件是否满足会更好。下面是一段标准的使用 wait 和 notify 方法的代码:

synchronized (obj) {

   while (condition does not hold)

     obj.wait(); // (Releases lock, and reacquires on wakeup)

     ... // Perform action appropriate to condition

}

76、什么是线程局部变量ThreadLocal

线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。Java提供ThreadLocal类来支持线程局部变量,是一种实现线程安全的方式。但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。

77、ThreadLoal的作用是什么?

简单说ThreadLocal就是一种以空间换时间的做法在每个Thread里面维护了一个ThreadLocal.ThreadLocalMap把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了。

78、生产者消费者模型的作用是什么?

(1)通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率,这是生产者消费者模型最重要的作用。
(2)解耦,这是生产者消费者模型附带的作用,解耦意味着生产者和消费者之间的联系少,联系越少越可以独自发展而不需要收到相互的制约。

79、写一个生产者-消费者队列

可以通过阻塞队列实现,也可以通过wait-notify来实现。

使用阻塞队列来实现


//消费者
public class Producer implements Runnable{
   private final BlockingQueue<Integer> queue;
 
   public Producer(BlockingQueue q){
       this.queue=q;
   }
 
   @Override
   public void run() {
       try {
           while (true){
               Thread.sleep(1000);//模拟耗时
               queue.put(produce());
           }
       }catch (InterruptedException e){
 
       }
   }
 
   private int produce() {
       int n=new Random().nextInt(10000);
       System.out.println("Thread:" + Thread.currentThread().getId() + " produce:" + n);
       return n;
   }
}
//消费者
public class Consumer implements Runnable {
   private final BlockingQueue<Integer> queue;
 
   public Consumer(BlockingQueue q){
       this.queue=q;
   }
 
   @Override
   public void run() {
       while (true){
           try {
               Thread.sleep(2000);//模拟耗时
               consume(queue.take());
           }catch (InterruptedException e){
 
           }
 
       }
   }
 
   private void consume(Integer n) {
       System.out.println("Thread:" + Thread.currentThread().getId() + " consume:" + n);
 
   }
}
//测试
public class Main {
 
   public static void main(String[] args) {
       BlockingQueue<Integer> queue=new ArrayBlockingQueue<Integer>(100);
       Producer p=new Producer(queue);
       Consumer c1=new Consumer(queue);
       Consumer c2=new Consumer(queue);
 
       new Thread(p).start();
       new Thread(c1).start();
       new Thread(c2).start();
   }

使用wait-notify来实现:该种方式应该最经典,这里就不做说明了。

79、如果你提交任务时,线程池队列已满,这时会发生什么

如果你使用的LinkedBlockingQueue,也就是无界队列的话,没关系,继续添加任务到阻塞队列中等待执行,因为LinkedBlockingQueue可以近乎认为是一个无穷大的队列,可以无限存放任务;如果你使用的是有界队列比方说ArrayBlockingQueue的话,任务首先会被添加到ArrayBlockingQueue中,ArrayBlockingQueue满了,则会使用拒绝策略RejectedExecutionHandler处理满了的任务,默认是AbortPolicy。

80、为什么要使用线程池

避免频繁地创建和销毁线程,达到线程对象的重用。另外,使用线程池还可以根据项目灵活地控制并发的数目。

81、java中用到的线程调度算法是什么

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

82、Thread.sleep(0)的作用是什么

由于Java采用抢占式的线程调度算法,因此可能会出现某条线程常常获取到CPU控制权的情况,为了让某些优先级比较低的线程也能获取到CPU控制权,可以使用Thread.sleep(0)手动触发一次操作系统分配时间片的操作,这也是平衡CPU控制权的一种操作。

83、什么是CAS

CAS,全称为Compare and Swap,即比较-替换。假设有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,才会将内存值修改为B并返回true,否则什么都不做并返回false。当然CAS一定要volatile变量配合,这样才能保证每次拿到的变量是主内存中最新的那个值,否则旧的预期值A对某条线程来说,永远是一个不会变的值A,只要某次CAS操作失败,永远都不可能成功。

84、什么是乐观锁和悲观锁

乐观锁:乐观锁认为竞争不总是会发生,因此它不需要持有锁,将比较-替换这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。

悲观锁:悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,不管三七二十一,直接上了锁就操作资源了。

85、ConcurrentHashMap的并发度是什么?

ConcurrentHashMap的并发度就是segment的大小,默认为16,这意味着最多同时可以有16条线程操作ConcurrentHashMap,这也是ConcurrentHashMap对Hashtable的最大优势,任何情况下,Hashtable能同时有两条线程获取Hashtable中的数据吗?

86、ConcurrentHashMap的工作原理

ConcurrentHashMap在jdk 1.6和jdk 1.8实现原理是不同的。

jdk 1.6:

ConcurrentHashMap是线程安全的,但是与Hashtablea相比,实现线程安全的方式不同。Hashtable是通过对hash表结构进行锁定,是阻塞式的,当一个线程占有这个锁时,其他线程必须阻塞等待其释放锁。ConcurrentHashMap是采用分离锁的方式,它并没有对整个hash表进行锁定,而是局部锁定,也就是说当一个线程占有这个局部锁时,不影响其他线程对hash表其他地方的访问。 
具体实现:ConcurrentHashMap内部有一个Segment.

jdk 1.8:

在jdk 8中,ConcurrentHashMap不再使用Segment分离锁,而是采用一种乐观锁CAS算法来实现同步问题,但其底层还是“数组+链表->红黑树”的实现。

87、CyclicBarrier和CountDownLatch区别

这两个类非常类似,都在java.util.concurrent下,都可以用来表示代码运行到某个点上,二者的区别在于:

  • CyclicBarrier的某个线程运行到某个点上之后,该线程即停止运行,直到所有的线程都到达了这个点,所有线程才重新运行;CountDownLatch则不是,某线程运行到某个点上之后,只是给某个数值-1而已,该线程继续运行。

  • CyclicBarrier只能唤起一个任务,CountDownLatch可以唤起多个任务

  • CyclicBarrier可重用,CountDownLatch不可重用,计数值为0该CountDownLatch就不可再用了。

88、java中的++操作符线程安全么?

不是线程安全的操作。它涉及到多个指令,如读取变量值,增加,然后存储回内存,这个过程可能会出现多个线程交差。

89、你有哪些多线程开发良好的实践?

  1. 给线程命名

  2. 最小化同步范围

  3. 优先使用volatile

  4. 尽可能使用更高层次的并发工具而非wait和notify()来实现线程通信,如BlockingQueue,Semeaphore

  5. 优先使用并发容器而非同步容器.

  6. 考虑使用线程池

 

volatile关键字

90、可以创建Volatile数组吗?

Java 中可以创建 volatile类型数组,不过只是一个指向数组的引用,而不是整个数组。如果改变引用指向的数组,将会受到volatile 的保护,但是如果多个线程同时改变数组的元素,volatile标示符就不能起到之前的保护作用了。

91、volatile能使得一个非原子操作变成原子操作吗?

一个典型的例子是在类中有一个 long 类型的成员变量。如果你知道该成员变量会被多个线程访问,如计数器、价格等,你最好是将其设置为 volatile。为什么?因为 Java 中读取 long 类型变量不是原子的,需要分成两步,如果一个线程正在修改该 long 变量的值,另一个线程可能只能看到该值的一半(前 32 位)。但是对一个 volatile 型的 long 或 double 变量的读写是原子。

一种实践是用 volatile 修饰 long 和 double 变量,使其能按原子类型来读写。double 和 long 都是64位宽,因此对这两种类型的读是分为两部分的,第一次读取第一个 32 位,然后再读剩下的 32 位,这个过程不是原子的,但 Java 中 volatile 型的 long 或 double 变量的读写是原子的。volatile 修复符的另一个作用是提供内存屏障(memory barrier),例如在分布式框架中的应用。简单的说,就是当你写一个 volatile 变量之前,Java 内存模型会插入一个写屏障(write barrier),读一个 volatile 变量之前,会插入一个读屏障(read barrier)。意思就是说,在你写一个 volatile 域时,能保证任何线程都能看到你写的值,同时,在写之前,也能保证任何数值的更新对所有线程是可见的,因为内存屏障会将其他所有写的值更新到缓存。

92、volatile类型变量提供什么保证?

volatile 主要有两方面的作用:1.避免指令重排2.可见性保证.例如,JVM 或者 JIT为了获得更好的性能会对语句重排序,但是 volatile 类型变量即使在没有同步块的情况下赋值也不会与其他语句重排序。 volatile 提供 happens-before 的保证,确保一个线程的修改能对其他线程是可见的。某些情况下,volatile 还能提供原子性,如读 64 位数据类型,像 long 和 double 都不是原子的(低32位和高32位),但 volatile 类型的 double 和 long 就是原子的。

 

集合

93、Java中的集合及其继承关系

关于集合的体系是每个人都应该烂熟于心的,尤其是对我们经常使用的List,Map的原理更该如此.这里我们看这张图即可: 

更多内容可见集合类总结:

http://write.blog.csdn.net/postedit/40826423

94、poll()方法和remove()方法区别?

poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。

95、LinkedHashMap和PriorityQueue的区别

PriorityQueue 是一个优先级队列,保证最高或者最低优先级的的元素总是在队列头部,但是 LinkedHashMap 维持的顺序是元素插入的顺序。当遍历一个 PriorityQueue 时,没有任何顺序保证,但是 LinkedHashMap 课保证遍历顺序是元素插入的顺序。

96、WeakHashMap与HashMap的区别是什么?

WeakHashMap 的工作与正常的 HashMap 类似,但是使用弱引用作为 key,意思就是当 key 对象没有任何引用时,key/value 将会被回收。

97、ArrayList和LinkedList的区别?

最明显的区别是 ArrrayList底层的数据结构是数组,支持随机访问,而 LinkedList 的底层数据结构是双向循环链表,不支持随机访问。使用下标访问一个元素,ArrayList 的时间复杂度是 O(1),而 LinkedList 是 O(n)。

98、ArrayList和Array有什么区别?

  1. Array可以容纳基本类型和对象,而ArrayList只能容纳对象。

  2. Array是指定大小的,而ArrayList大小是固定的

99、ArrayList和HashMap默认大小?

在 Java 7 中,ArrayList 的默认大小是 10 个元素,HashMap 的默认大小是16个元素(必须是2的幂)。这就是 Java 7 中 ArrayList 和 HashMap 类的代码片段。


private static final int DEFAULT_CAPACITY = 10;
 
//from HashMap.java JDK 7
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

100、Comparator和Comparable的区别?

Comparable 接口用于定义对象的自然顺序,而 comparator 通常用于定义用户定制的顺序。Comparable 总是只有一个,但是可以有多个 comparator 来定义对象的顺序。

101、如何实现集合排序?

你可以使用有序集合,如 TreeSet 或 TreeMap,你也可以使用有顺序的的集合,如 list,然后通过 Collections.sort() 来排序。

102、如何打印数组内容

你可以使用 Arrays.toString() 和 Arrays.deepToString() 方法来打印数组。由于数组没有实现 toString() 方法,所以如果将数组传递给 System.out.println() 方法,将无法打印出数组的内容,但是 Arrays.toString() 可以打印每个元素。

103、LinkedList的是单向链表还是双向?

双向循环列表,具体实现自行查阅源码。

104、TreeMap是实现原理

采用红黑树实现,具体实现自行查阅源码。

105、遍历ArrayList时如何正确移除一个元素

该问题的关键在于面试者使用的是 ArrayList 的 remove() 还是 Iterator 的 remove()方法。这有一段示例代码,是使用正确的方式来实现在遍历的过程中移除元素,而不会出现 ConcurrentModificationException 异常的示例代码。

106、什么是ArrayMap?它和HashMap有什么区别?

ArrayMap是Android SDK中提供的,非Android开发者可以略过。
ArrayMap是用两个数组来模拟map,更少的内存占用空间,更高的效率。
具体参考这篇文章:ArrayMap VS HashMap:http://lvable.com/?p=217%5D

107、HashMap的实现原理

1. HashMap概述: HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 
2. HashMap的数据结构: 在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。

当我们往Hashmap中put元素时,首先根据key的hashcode重新计算hash值,根绝hash值得到这个元素在数组中的位置(下标),如果该数组在该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放入链尾.如果数组中该位置没有元素,就直接将该元素放到数组的该位置上.

需要注意Jdk 1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率,从原来的O(n)到O(logn)

108、你了解Fail-Fast机制吗?

Fail-Fast即我们常说的快速失败,

更多内容参看fail-fast机制:http://blog.csdn.net/chenssy/article/details/38151189

109、Fail-fast和Fail-safe有什么区别

Iterator的fail-fast属性与当前的集合共同起作用,因此它不会受到集合中任何改动的影响。Java.util包中的所有集合类都被设计为fail->fast的,而java.util.concurrent中的集合类都为fail-safe的。当检测到正在遍历的集合的结构被改变时,Fail-fast迭代器抛出ConcurrentModificationException,而fail-safe迭代器从不抛出ConcurrentModificationException。

 

日期

110、SimpleDateFormat是线程安全的吗?

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

111、如何格式化日期?

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

 

异常

112、简单描述java异常体系

相比没有人不了解异常体系,关于异常体系的更多信息可以见

白话异常机制:http://blog.csdn.net/dd864140130/article/details/42504189

113、什么是异常链

详情直接参见上面的白话异常机制,不做解释了。

114、throw和throws的区别

throw用于主动抛出java.lang.Throwable 类的一个实例化对象,意思是说你可以通过关键字 throw 抛出一个 Error 或者 一个Exception,如:throw new IllegalArgumentException(“size must be multiple of 2″)
而throws 的作用是作为方法声明和签名的一部分,方法被抛出相应的异常以便调用者能处理。Java 中,任何未处理的受检查异常强制在 throws 子句中声明。

 

序列化

115、Java 中,Serializable 与 Externalizable 的区别

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

 

JVM

116、JVM特性

平台无关性. 
Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用模式Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。

117、简单解释一下类加载器

有关类加载器一般会问你四种类加载器的应用场景以及双亲委派模型,

更多的内容参看深入理解JVM加载器:

http://blog.csdn.net/dd864140130/article/details/49817357

118、简述堆和栈的区别

VM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。

119、简述JVM内存分配

  1. 基本数据类型比变量和对象的引用都是在栈分配的。

  2. 堆内存用来存放由new创建的对象和数组。

  3. 类变量(static修饰的变量),程序在一加载的时候就在堆中为类变量分配内存,堆中的内存地址存放在栈中。

  4. 实例变量:当你使用java关键字new的时候,系统在堆中开辟并不一定是连续的空间分配给变量,是根据零散的堆内存地址,通过哈希算法换算为一长串数字以表征这个变量在堆中的”物理位置”,实例变量的生命周期–当实例变量的引用丢失后,将被GC(垃圾回收器)列入可回收“名单”中,但并不是马上就释放堆中内存。

  5. 局部变量: 由声明在某方法,或某代码段里(比如for循环),执行到它的时候在栈中开辟内存,当局部变量一但脱离作用域,内存立即释放。

 

补充(持续补充)

120、XML解析的几种方式和特点

DOM, SAX, PULL三种解析方式:

  • DOM:消耗内存:先把xml文档都读到内存中,然后再用DOM API来访问树形结构,并获取数据。这个写起来很简单,但是很消耗内存。要是数据过大,手机不够牛逼,可能手机直接死机

  • SAX:解析效率高,占用内存少,基于事件驱动的:更加简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。

  • PULL:与 SAX 类似,也是基于事件驱动,我们可以调用它的next()方法,来获取下一个解析事件(就是开始文档,结束文档,开始标签,结束标签),当处于某个元素时可以调用XmlPullParser的getAttributte()方法来获取属性的值,也可调用它的nextText()获取本节点的值。

121、JDK 1.7特性

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

122、JDK 1.8特性

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

123、Maven和ANT有什么区别?

虽然两者都是构建工具,都用于创建 Java 应用,但是 Maven 做的事情更多,在基于“约定优于配置”的概念下,提供标准的Java 项目结构,同时能为应用自动管理依赖(应用中所依赖的 JAR 文件。

124、JDBC最佳实践

  • 优先使用批量操作来插入和更新数据

  • 使用PreparedStatement来避免SQL漏洞

  • 使用数据连接池

  • 通过列名来获取结果集

125、IO操作最佳实践

  • 使用有缓冲的IO类,不要单独读取字节或字符

  • 使用NIO和NIO 2或者AIO,而非BIO

  • 在finally中关闭流

  • 使用内存映射文件获取更快的IO

126、什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”?

Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。 
Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。

127、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。

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

基本数据类型: 
整数值型:byte,short,int,long, 
字符型:char 
浮点类型:float,double 
布尔型:boolean 
整数默认int型,小数默认是double型。Float和long类型的必须加后缀。

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


129、面向对象是什么?

面向对象是一种思想,世间万物都可以看做一个对象,这里只讨论面向对象编程(OOP),Java是一个支持并发、基于类和面向对象的计算机编程语言,面向对象软件开发的优点:

  • 代码开发模块化,更易维护和修改;
  • 代码复用性强;
  • 增强代码的可靠性和灵活性;
  • 增加代码的可读性。

面向对象的四大基本特性:
抽象:提取现实世界中某事物的关键特性,为该事物构建模型的过程。对同一事物在不同的需求下,需要提取的特性可能不一样。得到的抽象模型中一般包含:属性(数据)和操作(行为)。这个抽象模型我们称之为类。对类进行实例化得到对象。

封装:封装可以使类具有独立性和隔离性;保证类的高内聚。只暴露给类外部或者子类必须的属性和操作。类封装的实现依赖类的修饰符(public、protected和private等)

继承:对现有类的一种复用机制。一个类如果继承现有的类,则这个类将拥有被继承类的所有非私有特性(属性和操作)。这里指的继承包含:类的继承和接口的实现。

多态:多态是在继承的基础上实现的。多态的三个要素:继承、重写和父类引用指向子类对象。父类引用指向不同的子类对象时,调用相同的方法,呈现出不同的行为;就是类多态特性。多态可以分成编译时多态和运行时多态。

抽象、封装、继承和多态是面向对象的基础。在面向对象四大基础特性之上,我们在做面向对象编程设计时还需要遵循有一些基本的设计原则。

面向对象的七大设计原则:

SOLID原则(单一职责原则、开放关闭原则、里氏替换原则、接口隔离原则和依赖倒置原则) 
迪米特法则 
组合优于继承原则(合成复用原则)。 
在遵循这些面向对象设计原则基础上,前辈们总结出一些解决不同问题场景的设计模式,以四人帮的gof23最为知名。

24种设计模式(gof23+1):

创建型模式: 
1.简单工厂模式(不包含在gof23中) 
2.工厂模式 
3.抽象工厂模式 
4.单例模式 
5.原型模式 
创建者模式 
6.结构型模式: 
7.组合模式 
8.装饰者模式 
9.外观模式 
10.适配器模式 
11.代理模式 
12.享元模式 
13.桥接模式 
行为型模式: 
14.观察者模式 
15.策略模式 
16.状态模式 
17.中介模式 
18.模板方法 
19.命令模式 
20.备忘录模式 
21.访问者模式 
22.解释器模式 
23.迭代器模式 
24.职责链模式 
这里只是简单描述了定义和特征以及设计模式的关系,具体细节不讨论。


130、请写出下面几个表达式的结果,答案可以用10进制或16进制书写

1). 0xaa | 0x55 
2). 15 & 240 
3). 10 ^ 12 
4). -2 >> 1 
5). -2 >>> 1 
1). 分析:十六进制数用0x……来表示,后面一个十六进制位是四位,两个十六进制位为一个字节,最多后面可以有8个十六进制位,32个字节,如:0xFFFFFFFF。 或(“ | ”)运算,全0为0,其他为1。 
所以:0xaa 用二进制表示为 10101010 ,0x55 用二进制表示为 01010101 ,按位或之后为 11111111 ,十进制数为255,十六进制数为 0xFF 。

2). 分析:10进制转换成2进制,用该数字除以2,记录商和余数,利用商再次除以2,记录商和余数……直到上为0或余数为0停止,余数逆序组成二进制的从低到高位(最后的余数为二进制最低位)。与(“ & ”)运算,全1为1,其他为0 。 
所以: 15 等于1111 ,240等于 11110000,15前面用0补齐为00001111 ,按位与之后为 00000000 ,即结果为0

3). 分析: 亦或(“ ^ ”)运算,相同取0,不同取1 。 
所以:1010 ^ 1100 =0110 , 十进制表示为6,十六进制表示为 0x06 。

4). 分析: 带符号右移(“ >> ”),即有符号位时,负数符号位补1,正数符号位补0, -2 的二进制求法是正数取反加1,因此 2 的二进制表示为0000 0000 0000 0000 0000 0000 0000 0010 ,取反加一为 
1111 1111 1111 1111 1111 1111 1111 1110 ,即 -2 的二进制表示。 
注: >> , << , >>> , 运算符只针对int型和long型,byte ,short ,char型需要转换成Int型在进行操作。 
所以: 带符号右移之后为 1111 1111 1111 1111 1111 1111 1111 1111 ,除符号位之外,减一取反,得到带符号十进 制数为 -1 。

5). 分析:无符号右移 (“ >>> ”) ,即无论正负数,右移之后符号位均补 0 。 
所以: -2 的二进制无符号右移一位之后为 0111 1111 1111 1111 1111 1111 1111 1111,即 2^31 - 1,二的三十一次方减一。 
注:右移和无符号右移主要区别就在于左面最高位补 0 还是补 1 的问题,无符号右移任何时候最高位都补 0 , 有符号右移则是正数补 0 ,负数补 1 。(没有无符号左移!)。
 

131、什么是值传递和引用传递?

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

132、是否可以在static环境中访问非static变量?

static变量在Java中是属于类的,它在所有的实例中的值是一样的。当类被Java虚拟机载入的时候,会对static变量进行初始化。如果你的代码尝试不用实例来访问非static的变量,编译器会报错,因为这些变量还没有被创建出来,还没有跟任何实例关联上。

133、解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法。

通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用JVM中的栈空间;而通过new关键字和构造器创建的对象则放在堆空间,堆是垃圾收集器管理的主要区域,由于现在的垃圾收集器都采用分代收集算法,所以堆空间还可以细分为新生代和老生代,再具体一点可以分为Eden、Survivor(又可分为From Survivor和To Survivor)、Tenured;方法区和堆都是各个线程共享的内存区域,用于存储已经被JVM加载的类信息、常量、静态变量、JIT编译器编译后的代码等数据;程序中的字面量(literal)如直接书写的100、”hello”和常量都是放在常量池中,常量池是方法区的一部分,。栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,栈和堆的大小都可以通过JVM的启动参数来进行调整,栈空间用光了会引发StackOverflowError,而堆和常量池空间不足则会引发OutOfMemoryError。

134、接口和抽象类的区别是什么?

从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。 
Java提供和支持创建抽象类和接口。它们的实现有共同点,不同点在于: 
接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。 
类可以实现很多个接口,但是只能继承一个抽象类 
类可以不实现抽象类和接口声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。 
抽象类可以在不提供接口方法实现的情况下实现接口。 
Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。 
Java接口中的成员函数默认是public的。抽象类的成员函数可以是private,protected或者是public。 
接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是,如果它包含main方法的话是可以被调用的。 
也可以参考JDK8中抽象类和接口的区别。

135、用最有效率的方法计算2乘以8?

答: 2 << 3(左移3位相当于乘以2的3次方,右移3位相当于除以2的3次方)。

136、手写单例模式(饿汉和饱汉模式)和工厂模式?

(1)单例饿汉模式://饿汉式单例类.在类初始化时,已经自行实例化 

public class Singleton1 { 
    //私有的默认构造子 
    private Singleton1() {} 
    //已经自行实例化 
    private static final Singleton1 single = new Singleton1(); 
    //静态工厂方法 
    public static Singleton1 getInstance() { 
         return single; 
    } 
}

(2)懒汉模式://懒汉式单例类.在第一次调用的时候实例化 

public class Singleton2 { 
    //私有的默认构造子 
    private Singleton2() {} 
    //注意,这里没有final 
    private static Singleton2 single=null; 
    //静态工厂方法 
    public synchronized static Singleton2 getInstance() { 
        if (single == null) { 
             single = new Singleton2(); 
        } 
        return single; 
    } 
}

(3)工厂模式: 

interface IFactory{ 
    public IProduct createProduct();
} 
Class Factory implements IFactory{ 
    public IProduct createProduct(){
        return new Product();
    }
} 
Public class client{ 
    Public Static void main (String [] args){
        IFactory factory=new Factory(); 
        IProduct product=factory.createProduct(); 
        product.ProductMethod();
    }
}

137、Java集合框架是什么?说出一些集合框架的优点?

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

138、集合框架中的泛型有什么优点?

Java1.5引入了泛型,所有的集合接口和实现都大量地使用它。泛型允许我们为集合提供一个可以容纳的对象类型,因此,如果你添加其它类型的任何元素,它会在编译时报错。这避免了在运行时出现ClassCastException,因为你将会在编译时得到报错信息。泛型也使得代码整洁,我们不需要使用显式转换和instanceOf操作符。它也给运行时带来好处,因为不会产生类型检查的字节码指令。

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

Collection为集合层级的根接口。一个集合代表一组对象,这些对象即为它的元素。Java平台不提供这个接口任何直接的实现。
Set是一个不能包含重复元素的集合。这个接口对数学集合抽象进行建模,被用来代表集合,就如一副牌。 
List是一个有序集合,可以包含重复元素。你可以通过它的索引来访问任何元素。List更像长度动态变换的数组。 
Map是一个将key映射到value的对象.一个Map不能包含重复的key:每个key最多只能映射一个value。 
一些其它的接口有Queue、Dequeue、SortedSet、SortedMap和ListIterator。

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

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

141、为何Map接口不继承Collection接口?

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

142、什么是迭代器(Iterator)?

Iterator接口提供了很多对集合元素进行迭代的方法。每一个集合类都包含了可以返回迭代器实例的迭代方法。迭代器可以在迭代的过程中删除底层集合的元素,但是不可以直接调用集合的remove(Object Obj)删除,可以通过迭代器的remove()方法删除。

143、Iterator和ListIterator的区别是什么?

下面列出了他们的区别: 
Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。 
Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。 
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。

144、快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

快速失败:当你在迭代一个集合的时候,如果有另一个线程正在修改你正在访问的那个集合时,就会抛出一个ConcurrentModification异常。 
在java.util包下的都是快速失败。 
安全失败:你在迭代的时候会去底层集合做一个拷贝,所以你在修改上层集合的时候是不会受影响的,不会抛出ConcurrentModification异常。 
在java.util.concurrent包下的全是安全失败的。

145、Java中的HashMap的工作原理是什么?

我们知道在Java中最常用的两种结构是数组和模拟指针(引用),几乎所有的数据结构都可以利用这两种来组合实现,HashMap也是如此。实际上HashMap是一个“链表散列”,如下是它数据结构:最左侧是一个数组,数组中的每一个元素都是一个链表,链表的每一个元素都是entry。

HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。

146、当两个对象的hashcode相同会发生什么?

因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中。

147、如果两个键的hashcode相同,你如何获取值对象?

当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,然后会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象。

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

HashMap使用Key对象的hashCode()和equals()方法去决定key-value对的索引。当我们试着从HashMap中获取值的时候,这些方法也会被用到。如果这些方法没有被正确地实现,在这种情况下,两个不同Key也许会产生相同的hashCode()和equals()输出,HashMap将会认为它们是相同的,然后覆盖它们,而非把它们存储到不同的地方。同样的,所有不允许存储重复数据的集合类都使用hashCode()和equals()去查找重复,所以正确实现它们非常重要。equals()和hashCode()的实现应该遵循以下规则: 
(1)如果o1.equals(o2),那么o1.hashCode() == o2.hashCode()总是为true的。 
(2)如果o1.hashCode() == o2.hashCode(),并不意味着o1.equals(o2)会为true。 
具体可以参考 http://blog.csdn.net/javazejian/article/details/51348320

149、HashMap和Hashtable有什么区别?

1、HashMap是非线程安全的,HashTable是线程安全的。 
2、HashMap的键和值都允许有null值存在,而HashTable则不行。 
3、因为线程安全的问题,HashMap效率比HashTable的要高。 
4、Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。 
一般现在不建议用HashTable, ①是HashTable是遗留类,内部实现很多没优化和冗余。②即使在多线程环境下,现在也有同步的ConcurrentHashMap替代,没有必要因为是多线程而用HashTable。

150、如何决定选用HashMap还是TreeMap?

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

151、ArrayList和Vector有何异同点?

ArrayList和Vector在很多时候都很类似。 
(1)两者都是基于索引的,内部由一个数组支持。 
(2)两者维护插入的顺序,我们可以根据插入顺序来获取元素。 
(3)ArrayList和Vector的迭代器实现都是fail-fast的。 
(4)ArrayList和Vector两者允许null值,也可以使用索引值对元素进行随机访问。 
以下是ArrayList和Vector的不同点。 
(1)Vector是同步的,而ArrayList不是。然而,如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWriteArrayList。 
(2)ArrayList比Vector快,它因为有同步,不会过载。 
(3)ArrayList更加通用,因为我们可以使用Collections工具类轻易地获取同步列表和只读列表。

152、Array和ArrayList有何区别?什么时候更适合用Array?

Array可以容纳基本类型和对象,而ArrayList只能容纳对象。 
Array是指定大小的,而ArrayList大小是固定的。 
Array没有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。尽管ArrayList明显是更好的选择,但也有些时候Array比较好用。 
(1)如果列表的大小已经指定,大部分情况下是存储和遍历它们。 
(2)对于遍历基本数据类型,尽管Collections使用自动装箱来减轻编码任务,在指定大小的基本类型的列表上工作也会变得很慢。 
(3)如果你要使用多维数组,使用[][]比List

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

修饰符当前类同 包子 类其他包
public
protected×
default××
private×××

类的成员不写访问修饰时默认为default。默认对于同一个包中的其他类相当于公开(public),对于不是同一个包中的其他类相当于私有(private)。受保护(protected)对子类相当于公开,对不是同一包中的没有父子关系的类相当于私有。Java中,外部类的修饰符只能是public或默认,类的成员(包括内部类)的修饰符可以是以上四种。

154、

public class Test03 {

    public static void main(String[] args) {
        Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;

        System.out.println(f1 == f2);
        System.out.println(f3 == f4);
    }
}

如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象,所以上面的面试题中f1==f2的结果是true,而f3==f4的结果是false

155、解释内存中的栈(stack)、堆(heap)和静态区(static area)的用法。

答:通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用内存中的栈空间;而通过new关键字和构造器创建的对象放在堆空间;程序中的字面量(literal)如直接书写的100、"hello"和常量都是放在静态区中。栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,理论上整个内存没有被其他进程使用的空间甚至硬盘上的虚拟内存都可以被当成堆空间来使用。

String str = new String("hello");

上面的语句中变量str放在栈上,用new创建出来的字符串对象放在堆上,而"hello"这个字面量放在静态区。

156、是否可以继承String类?

答:String 类是final类,不可以被继承。
    补充:继承String本身就是一个错误的行为,对String类型最好的重用方式是关联关系(Has-A)和依赖关系(Use-A)而不是继承关系(Is-A)。

158、静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同?


答:Static Nested Class是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化

159、面试题 - 下面的代码哪些地方会产生编译错误?
 

class Outer {

    class Inner {}

    public static void foo() { new Inner(); }

    public void bar() { new Inner(); }

    public static void main(String[] args) {
        new Inner();
    }
}

   注意:Java中非静态内部类对象的创建要依赖其外部类对象,上面的面试题中foo和main方法都是静态方法,静态方法中没有this,也就是说没有所谓的外部类对象,因此无法创建内部类对象,如果要在静态方法中创建内部类对象,可以这样做:

            new Outer().new Inner();

160、Java 中会存在内存泄漏吗,请简单描述。

答:理论上Java因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被广泛使用于服务器端编程的一个重要原因);然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被GC回收,因此也会导致内存泄露的发生。例如Hibernate的Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收这些对象的,然而这些对象中可能存在无用的垃圾对象,如果不及时关闭(close)或清空(flush)一级缓存就可能导致内存泄露。

161、抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被synchronized修饰?

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

162、阐述静态变量和实例变量的区别。

答:静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。

163、如何实现对象克隆?

答:有两种方式:
1). 实现Cloneable接口并重写Object类中的clone()方法;
2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。

164、String s = new String("xyz");创建了几个字符串对象?

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

165、接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concrete class)?

答:接口可以继承接口,而且支持多重继承。抽象类可以实现(implements)接口,抽象类可继承具体类也可以继承抽象类。


166、指出下面程序的运行结果。

class A {

    static {
        System.out.print("1");
    }

    public A() {
        System.out.print("2");
    }
}

class B extends A{

    static {
        System.out.print("a");
    }

    public B() {
        System.out.print("b");
    }
}

public class Hello {

    public static void main(String[] args) {
        A ab = new B();
        ab = new B();
    }

}

答:执行结果:1a2b2b。创建对象时构造器的调用顺序是:先初始化静态成员,然后调用父类构造器,再初始化非静态成员,最后调用自身构造器。

167、如何实现字符串的反转及替换?

答:方法很多,可以自己写实现也可以使用String或StringBuffer/StringBuilder中的方法。有一道很常见的面试题是用递归实现字符串反转,代码如下所示:

 public static String reverse(String originStr) {
        if(originStr == null || originStr.length() <= 1)
            return originStr;
        return reverse(originStr.substring(1)) + originStr.charAt(0);
 }

168、怎样将GB2312编码的字符串转换为ISO-8859-1编码的字符串?

答:代码如下所示:
String s1 = "你好";
String s2 = new String(s1.getBytes("GB2312"), "ISO-8859-1");

169、Collection和Collections的区别?

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

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

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

171、synchronized关键字的用法?

答:synchronized关键字可以将对象或者方法标记为同步,以实现对对象和方法的互斥访问,可以用synchronized(对象) { … }定义同步代码块,或者在声明方法时将synchronized作为方法的修饰符。在第60题的例子中已经展示了synchronized关键字的用法。

172、举例说明同步和异步。

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

173、java中如何实现序列化,有什么意义?

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

174、当多个事务访问同一数据时,可能会存在5类问题,包括3类数据读取问题(脏读、不可重复读和幻读)和2类数据更新问题(第1类丢失更新和第2类丢失更新)。

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

时间转账事务A取款事务B
T1 开始事务
T2开始事务 
T3 查询账户余额为1000元
T4 取出500元余额修改为500元
T5查询账户余额为500元(脏读) 
T6 撤销事务余额恢复为1000元
T7汇入100元把余额修改为600元 
T8提交事务 

不可重复读(Unrepeatable Read):事务A重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务B修改过了。

时间转账事务A取款事务B
T1 开始事务
T2开始事务 
T3 查询账户余额为1000元
T4查询账户余额为1000元 
T5 取出100元修改余额为900元
T6 提交事务
T7查询账户余额为900元(不可重复读) 

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

时间统计金额事务A转账事务B
T1 开始事务
T2开始事务 
T3统计总存款为10000元 
T4 新增一个存款账户存入100元
T5 提交事务
T6再次统计总存款为10100元(幻读) 

第1类丢失更新:事务A撤销时,把已经提交的事务B的更新数据覆盖了。

时间取款事务A转账事务B
T1开始事务 
T2 开始事务
T3查询账户余额为1000元 
T4 查询账户余额为1000元
T5 汇入100元修改余额为1100元
T6 提交事务
T7取出100元将余额修改为900元 
T8撤销事务 
T9余额恢复为1000元(丢失更新) 

第2类丢失更新:事务A覆盖事务B已经提交的数据,造成事务B所做的操作丢失。

时间转账事务A取款事务B
T1 开始事务
T2开始事务 
T3 查询账户余额为1000元
T4查询账户余额为1000元 
T5 取出100元将余额修改为900元
T6 提交事务
T7汇入100元将余额修改为1100元 
T8提交事务 
T9查询账户余额为1100元(丢失更新) 

Read uncommitted(读未提交):最低级别,任何情况都会发生。
Read Committed(读已提交):可避免脏读的发生。
Repeatable read(可重复读):可避免脏读、不可重复读的发生。
Serializable(串行化):避免脏读、不可重复读,幻读的发生。

隔离级别脏读不可重复读幻读第一类丢失更新第二类丢失更新
READ UNCOMMITED允许允许允许不允许允许
READ COMMITTED不允许允许允许不允许允许
REPEATABLE READ不允许不允许允许不允许不允许
SERIALIZABLE不允许不允许不允许不允许不允许

175、简述正则表达式及其用途。

答:在编写处理字符串的程序时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。

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

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

178、面试被问到关于设计模式的知识时,可以拣最常用的作答,例如:

- 工厂模式:工厂类可以根据条件生成不同的子类实例,这些子类有一个公共的抽象父类并且实现了相同的方法,但是这些方法针对不同的数据进行了不同的操作(多态方法)。当得到子类的实例后,开发人员可以调用基类中的方法而不必考虑到底返回的是哪一个子类的实例。
- 代理模式:给一个对象提供一个代理对象,并由代理对象控制原对象的引用。实际开发中,按照使用目的的不同,代理可以分为:远程代理、虚拟代理、保护代理、Cache代理、防火墙代理、同步化代理、智能引用代理。
- 适配器模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起使用的类能够一起工作。
- 模板方法模式:提供一个抽象类,将部分逻辑以具体方法或构造器的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法(多态实现),从而实现不同的业务逻辑。

179、JSP有哪些内置对象?作用分别是什么?

答:JSP有9个内置对象:
- request:封装客户端的请求,其中包含来自GET或POST请求的参数;
- response:封装服务器对客户端的响应;
- pageContext:通过该对象可以获取其他对象;
- session:封装用户会话的对象;
- application:封装服务器运行环境的对象;
- out:输出服务器响应的输出流对象;
- config:Web应用的配置对象;
- page:JSP页面本身(相当于Java程序中的this);
- exception:封装页面抛出异常的对象。

180、get和post请求的区别?

答:
①get请求用来从服务器上获得资源,而post是用来向服务器提交数据;
②get将表单中数据按照name=value的形式,添加到action 所指向的URL 后面,并且两者使用"?"连接,而各个变量之间使用"&"连接;post是将表单中的数据放在HTTP协议的请求头或消息体中,传递到action所指向URL;
③get传输的数据要受到URL长度限制(1024字节);而post可以传输大量的数据,上传文件通常要使用post方式;
④使用get时参数会显示在地址栏上,如果这些数据不是敏感数据,那么可以使用get;对于敏感数据还是应用使用post;
⑤get使用MIME类型application/x-www-form-urlencoded的URL编码(也叫百分号编码)文本的格式传递参数,保证被传送的参数由遵循规范的文本组成,例如一个空格的编码是"%20"。

181、JSP和Servlet是什么关系?

答:其实这个问题在上面已经阐述过了,Servlet是一个特殊的Java程序,它运行于服务器的JVM中,能够依靠服务器的支持向浏览器提供显示内容。JSP本质上是Servlet的一种简易形式,JSP会被服务器处理成一个类似于Servlet的Java程序,可以简化页面内容的生成。Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。有人说,Servlet就是在Java中写HTML,而JSP就是在HTML中写Java代码,当然这个说法是很片面且不够准确的。JSP侧重于视图,Servlet更侧重于控制逻辑,在MVC架构模式中,JSP适合充当视图(view)而Servlet适合充当控制器(controller)。

182、讲解JSP中的四种作用域。

答:JSP中的四种作用域包括page、request、session和application,具体来说:
- page代表与一个页面相关的对象和属性。
- request代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件;需要在页面显示的临时数据可以置于此作用域。
- session代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中。
- application代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。

183、servlet生命周期

实例化

初始化

服务

销毁

 在Servlet产生到消亡的过程中,有三个生命周期函数,初始化方法init(),处理客户请求的方法service(),终止方法destroy()。

init方法
        在一个Servlet的生命周期中,init方法只会被执行一次,之后无论用户执行多少次请求,都不会在调用该方法。 
        关于init方法的执行时机,有两种方式可选,一般的是在服务器启动后第一个用户请求改Servlet是调用,你也可以设置该Servlet在服务器启动后自动执行。 
init方法负责简单的创建或者加载一些数据,这些数据将用于该Servlet的整个生命周期中。

service方法
        当一个客户请求改Servlet时,实际的处理工作全部有service方法来完成,service方法用来处理客户端的请求,并生成格式化数据返回给客户端。 
        每一次请求服务器都会开启一个新的线程并执行一次service方法,service根据客户端的请求类型,调用doGet、doPost等方法。 
        service是由web容器来调用的,我们无需对service具体内容做任何处理,service会自动的根据客户端的请求类型去调用doGet、doPost等方法,所以我们只需要做好doGet、doPost方法的实现就可以了。

destroy方法
        该方法在整个生命周期中,也是只会被调用一次,在Servlet对象被销毁是调用,在servlet中,我们可以做一些资源的释放等操作,执行destory方法之后的servlet对象,会等待jvm虚拟机的垃圾回收机制择时回收。

doGet、doPost方法
        实际的业务处理流程,service根据客户端的请求类型来自动匹配需要执行那个方法。

184、spring框架怎么解决持久层松耦合问题
 

依赖注入

185、java常用集合:List集合、Set集合、Map集合
 

    List集合的主要实现类:ArrayList,LinkedList,Vector(线程安全,但是性能较差)
    Set集合主要实现类:HashSet,TreeSet
    Map集合主要实现类:HashMap,HashTable(hashmap可以存空键值,hashtable线程安全),TreeMap

186、ReentrantLock和synchronizd的区别?

synchronizd:在java中,每一个对象有且仅有一个同步锁。这也意味着,同步锁是依赖于对象而存在。不同线程对同步锁的访问是互斥的。
    也就是说,某时间点,对象的同步锁只能被一个线程获取到!

ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”。
    除了synchronized的功能,多了三个高级功能。 
        等待可中断,公平锁,绑定多个Condition。 
        1. 等待可中断:在持有锁的线程长时间不释放锁的时候,等待的线程可以选择放弃等待,tryLock(long timeout, TimeUnit unit) 
        2. 公平锁:按照申请锁的顺序来一次获得锁称为公平锁,synchronized的是非公平锁,ReentrantLock可以通过构造函数实现公平锁。new RenentrantLock(boolean fair) 
        3. 绑定多个Condition:通过多次newCondition可以获得多个Condition对象,可以简单的实现比较负责的线程同步的功能,通过await(),signal();

187、线程池有几种,区别

(1)fixThreadPool  正规线程

          我的理解这是一个有指定的线程数的线程池,有核心的线程,里面有固定的线程数量,响应的速度快。正规的并发线程,多用于服务器。固定的线程数由系统资源设置。

(2)caCheThreadPool  缓存线程池

         只有非核心线程,最大线程数很大(Int.Max(values)),它会为每一个任务添加一个新的线程,这边有一个超时机制,当空闲的线程超过60s内没有用到的话,就会被回收。缺点就是没有考虑到系统的实际内存大小。

(3)singleThreadPoll   单线程线程池

  看这个名字就知道这个家伙是只有一个核心线程,就是一个孤家寡人,通过指定的顺序将任务一个个丢到线程,都乖乖的排队等待执行,不处理并发的操作,不会被回收。确定就是一个人干活效率慢。

(4)ScheduledThreadPoll   调度线程池

         这个线程池就厉害了,是唯一一个有延迟执行和周期重复执行的线程池。它的核心线程池固定,非核心线程的数量没有限制,但是闲置时会立即会被回收。


188、java IO和NIO的区别

      NIO即New IO,这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,
      NIO的效率要比IO高很多。在Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO。
       (1)IO是面向流的,NIO是面向缓冲区的;
       (2)Java IO的各种流是阻塞的,Java NIO的非阻塞模式,所以一个单独的线程现在可以管理多个输入和输出通道(channel);
       (3) Java NIO的选择器允许一个单独的线程来监视多个输入通道

189、什么是JAVA反射机制

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

 

190、Mybatis中的$和#的区别

  在mybatis中的$与#都是在sql中动态的传入参数
   我觉得#与$的区别最大在于:#{} 传入值时,sql解析时,参数是带引号的,

                                                  而${}穿入值,sql解析时,参数是不带引号的
    

191、mysql三大引擎:myisam、innoDB、memory

     myisam引擎的特点:MyISAM是MySQL默认的引擎,使用一种表格锁定的机制来优化多个并发的读写操作。需要经常运行OPTIMIZE TABLE命令,来恢复被更新机制所浪费的空间,否则碎片也会随之增加,最终影响数据访问性能。 
优点:1、MyISAM表是独立于操作系统的,这说明可以轻松地将其从Windows服务器移植到Linux服务器。2、MyISAM存储引擎在查询大量数据时非常迅速,这是它最突出的优点。3、另外进行大批量插入操作时执行速度也比较快。4、单独保存了表的行数。 
缺点:1、MyISAM表没有提供对数据库事务的支持。2、不支持行级锁和外键。3、不适合用于经常UPDATE(更新)的表,效率低。

   innodb引擎特点:1、提供事物支持。2、提供系统崩溃修复能力。3、支持多版本并发控制(即MVCC Multi-Version Concurrency Control)的行级锁,由于锁粒度小,写操作和更新操作并发高、速度快。适合更新频繁的表。4、支持自增长列。5、支持外键。6、适合于大容量数据库系统,支持自动灾难恢复。

    memory引擎的特点:为了得到最快的响应时间,MEMORY引擎采用的逻辑存储介质是系统内存。 
优点:1、在内存中存储表数据具有很高的性能。2同时支持散列索引和B树索引。 
缺点:1、MySQL守护进程崩溃时,所有memory数据都会丢失。2、不能使用长度可变的数据类型(如BLOB和TEXT),但varchar类型可用(varchar在MySQL内部被当做char类型)
 


192、 JDK1.8和JDK1.7的区别

1.二进制变量的表示,支持将整数类型用二进制来表示,用0b开头。
 2.Switch语句支持string类型 
 3.实现java.lang.AutoCloseable接口的资源都可以放到try中,跟final里面的关闭资源类似; 
    按照声明逆序关闭资源 ;Try块抛出的异常通过Throwable.getSuppressed获取 
 4. Catch多个异常 说明:Catch异常类型为final; 生成Bytecode 会比多个catch小; Rethrow时保持异常类型 
 5. 数字类型的下划线表示 更友好的表示方式,如: long creditCardNumber = 1234_5678_9012_3456L;
 6.泛型实例的创建可以通过类型推断来简化 可以去掉后面new部分的泛型类型,只用<>就可以了。
 (还有些增强就不列举了)

193、描述一下JVM 加载class 文件的原理机制? 

JVM 中类的装载是由ClassLoader 和它的子类来实现的,Java ClassLoader是一个重要的Java 运行时系统组件。它负责在运行时查找和装入类文件的类。

194、Static Nested Class 和Inner Class 的不同?

答:Static Nested Class 是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化。

195、说出ArrayList,Vector, LinkedList 的存储性能和特性?

ArrayList 和Vector 都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector 由于使用了synchronized 方法(线程安全),通常性能上较ArrayList 差,而LinkedList 使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。

196、Collection 和Collections 的区别?

Collection 是java.util 下的接口,它是各种集合的父接口,继承于它的接口主要有Set 和List;Collections 是个java.util 下的类,是针对集合的帮助类,提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。

197、HashMap 和Hashtable 的区别? 

二者都实现了Map 接口,是将惟一键映射到特定的值上;主要区别在于:

1)HashMap 没有排序,允许一个null 键和多个null 值,而Hashtable 不允许;

2)HashMap 把Hashtable 的contains 方法去掉了,改成containsvalue 和containsKey,因为contains 方法容易让人引起误解;

3)Hashtable 继承自Dictionary 类,HashMap 是Java1.2 引进的Map 接口的实现;

4)Hashtable 的方法是Synchronize 的,而HashMap 不是,在多个线程访问Hashtable 时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。Hashtable 和HashMap 采用的hash/rehash 算法大致一样,所以性能不会有很大的差异。

198、线程的基本概念、线程的基本状态以及状态之间的关系?

线程指在程序执行过程中,能够执行程序代码的一个执行单位,每个程序至少都有一个线程,也就是程序本身;Java 中的线程有四种状态分别是:运行、就绪、挂起、结束。

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

主要相同点:Lock 能完成synchronized 所实现的所有功能;主要不同点:Lock 有比synchronized 更精确的线程语义和更好的性能。synchronized 会自动释放锁,而Lock 一定要求程序员手工释放,并且必须在finally 从句中释放。

200、事务处理?

Connection 类中提供了3 个事务处理方法:setAutoCommit(Boolean autoCommit):设置是否自动提交事务,默认为自动提交事务,即为true,通过设置false 禁止自动提交事务;commit():提交事务;rollback():回滚事务。

201、System.out.println(3 | 9);输出什么?

11.

3 | 9=0011(二进制) | 1001(二进制)=1011(二进制)=11(十进制)

&和&&:

共同点:它们都表示运算符的两边都是true时,结果为true;

不同点: & 表示在运算时两边都会计算,然后再判断;&&表示先运算符号左边的东西,然后判断是否为true,是true就继续运算右边的然后判断并输出,是false就停下来直接输出不会再运行后面的东西。

|和||:

共同点:它们都表示运算符的两边任意一边为true,结果为true,两边都不是true,结果就为false;

不同点:| 表示两边都会运算,然后再判断结果;|| 表示先运算符号左边的东西,然后判断是否为true,是true就停下来直接输出不会再运行后面的东西,是false就继续运算右边的然后判断并输出。

 202、说一下转发(Forward)和重定向(Redirect)的区别

转发是服务器行为,重定向是客户端行为。

转发(Forword) 通过RequestDispatcher对象的forward(HttpServletRequest request,HttpServletResponse response)方法实现的。RequestDispatcher 可以通过HttpServletRequestgetRequestDispatcher()方法获得。例如下面的代码就是跳转到 login_success.jsp 页面。

request.getRequestDispatcher("login_success.jsp").forward(request, response);
复制代码

重定向(Redirect) 是利用服务器返回的状态吗来实现的。客户端浏览器请求服务器的时候,服务器会返回一个状态码。服务器通过HttpServletRequestResponse的setStatus(int status)方法设置状态码。如果服务器返回301或者302,则浏览器会到新的网址重新请求该资源。

  1. 从地址栏显示来说: forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址. redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
  2. 从数据共享来说: forward:转发页面和转发到的页面可以共享request里面的数据. redirect:不能共享数据.
  3. 从运用地方来说: forward:一般用于用户登陆的时候,根据角色转发到相应的模块. redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等
  4. 从效率来说: forward:高. redirect:低.

203、 在浏览器中输入url地址 ->> 显示主页的过程,整个过程会使用哪些协议

ç¶æç 

总体来说分为以下几个过程:

  1. DNS解析
  2. TCP连接
  3. 发送HTTP请求
  4. 服务器处理请求并返回HTTP报文
  5. 浏览器解析渲染页面
  6. 连接结束

204、TCP 三次握手和四次挥手

TCPä¸æ¬¡æ¡æ

  • 客户端–发送带有 SYN 标志的数据包–一次握手–服务端
  • 服务端–发送带有 SYN/ACK 标志的数据包–二次握手–客户端
  • 客户端–发送带有带有 ACK 标志的数据包–三次握手–服务端

为什么要三次握手

三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。

第一次握手:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常。

第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己接收正常,对方发送正常

第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送接收正常

所以三次握手就能确认双发收发功能都正常,缺一不可。

为什么要传回 SYN

接收端传回发送端所发送的 SYN 是为了告诉发送端,我接收到的信息确实就是你所发送的信号了。

SYN 是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立正常的 TCP 网络连接时,客户机首先发出一个 SYN 消息,服务器使用 SYN-ACK 应答表示接收到了这个消息,最后客户机再以 ACK(Acknowledgement[汉译:确认字符 ,在数据通信传输中,接收站发给发送站的一种传输控制字符。它表示确认发来的数据已经接受无误。 ])消息响应。这样在客户机和服务器之间才能建立起可靠的TCP连接,数据才可以在客户机和服务器之间传递。

传了 SYN,为啥还要传 ACK

双方通信无误必须是两者互相发送信息都无误。传了 SYN,证明发送方(主动关闭方)到接收方(被动关闭方)的通道没有问题,但是接收方到发送方的通道还需要 ACK 信号来进行验证

TCPå次æ¥æ

为什么要四次挥手

任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了TCP连接。

举个例子:A 和 B 打电话,通话即将结束后,A 说“我没啥要说的了”,B回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,这样通话才算结束。

205、IP地址与MAC地址的区别

IP地址是指互联网协议地址(Internet Protocol Address)IP Address的缩写。IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。

MAC 地址又称为物理地址、硬件地址,用来定义网络设备的位置。网卡的物理地址通常是由网卡生产厂家写入网卡的,具有全球唯一性。MAC地址用于在网络中唯一标示一个网卡,一台电脑会有一或多个网卡,每个网卡都需要有一个唯一的MAC地址。

206、HTTP请求、响应报文格式

HTTP请求报文主要由请求行、请求头部、请求正文3部分组成

HTTP响应报文主要由状态行、响应头部、响应正文3部分组成

207、为什么要使用索引?索引这么多优点,为什么不对表中的每一个列创建一个索引呢?索引是如何提高查询速度的?说一下使用索引的注意事项?Mysql索引主要使用的两种数据结构?什么是覆盖索引?

为什么要使用索引?

  1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
  2. 可以大大加快 数据的检索速度(大大减少的检索的数据量), 这也是创建索引的最主要的原因。
  3. 帮助服务器避免排序和临时表
  4. 将随机IO变为顺序IO
  5. 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。

索引这么多优点,为什么不对表中的每一个列创建一个索引呢?

  1. 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
  2. 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
  3. 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。

索引是如何提高查询速度的?

将无序的数据变成相对有序的数据(就像查目录一样)

说一下使用索引的注意事项

  1. 避免 where 子句中对宇段施加函数,这会造成无法命中索引。
  2. 在使用InnoDB时使用与业务无关的自增主键作为主键,即使用逻辑主键,而不要使用业务主键。
  3. 将打算加索引的列设置为 NOT NULL ,否则将导致引擎放弃使用索引而进行全表扫描
  4. 删除长期未使用的索引,不用的索引的存在会造成不必要的性能损耗 MySQL 5.7 可以通过查询 sys 库的 chema_unused_indexes 视图来查询哪些索引从未被使用
  5. 在使用 limit offset 查询缓慢时,可以借助索引来提高性能

Mysql索引主要使用的哪两种数据结构?

  • 哈希索引:对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。
  • BTree索引:Mysql的BTree索引使用的是B树中的B+Tree。但对于主要的两种存储引擎(MyISAM和InnoDB)的实现方式是不同的。

更多关于索引的内容可以查看我的这篇文章:【思维导图-索引篇】搞定数据库索引就是这么简单

什么是覆盖索引?

如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称 之为“覆盖索引”。我们知道在InnoDB存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次,这样就会比较慢。覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!

208、进程与线程的区别是什么?进程间的几种通信方式说一下?线程间的几种通信方式知道不

进程与线程的区别是什么?

线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。另外,也正是因为共享资源,所以线程中执行时一般都要进行同步和互斥。总的来说,进程和线程的主要差别在于它们是不同的操作系统资源管理方式。

进程间的几种通信方式说一下?

  1. 管道(pipe):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有血缘关系的进程间使用。进程的血缘关系通常指父子进程关系。管道分为pipe(无名管道)和fifo(命名管道)两种,有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间通信。
  2. 信号量(semophore):信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它通常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
  3. 消息队列(message queue):消息队列是由消息组成的链表,存放在内核中 并由消息队列标识符标识。消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。消息队列与管道通信相比,其优势是对每个消息指定特定的消息类型,接收的时候不需要按照队列次序,而是可以根据自定义条件接收特定类型的消息。
  4. 信号(signal):信号是一种比较复杂的通信方式,用于通知接收进程某一事件已经发生。
  5. 共享内存(shared memory):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问,共享内存是最快的IPC方式,它是针对其他进程间的通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量配合使用,来实现进程间的同步和通信。
  6. 套接字(socket):套接口也是一种进程间的通信机制,与其他通信机制不同的是它可以用于不同及其间的进程通信。

线程间的几种通信方式知道不?

1、锁机制

  • 互斥锁:提供了以排它方式阻止数据结构被并发修改的方法。
  • 读写锁:允许多个线程同时读共享数据,而对写操作互斥。
  • 条件变量:可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。

2、信号量机制:包括无名线程信号量与有名线程信号量

3、信号机制:类似于进程间的信号处理。

线程间通信的主要目的是用于线程同步,所以线程没有象进程通信中用于数据交换的通信机制。


 

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值