这是一份Java基础知识的面试题。在网上的关于Java的面试题数不胜数,但认真看过感觉大多数都没有实用性,有很多是面试官根本就不会问到的,那些已经脱离了实际开发的技术问题。而这份资料来源自一份个人觉得最贴近真实面试的Java面试题,网上流传较少。
最近又对这份Java基础面试题重新整理了一遍,本人重新编辑后结合了自己许多次的面试经历和经验,做出了标记,划分了重点,以及对某些知识做出一些简单明白的注释,相信可以对需要回顾Java知识,以及准备着面试的Java开发工程师会有一定帮助。
1. 面向对象的特征有哪些方面?
答:
1. 继承
通过继承允许复用已有的类。继承关系是一种一般到特殊的关系,比如苹果类继承水果类,这个过程称为类继承。
子类可以从父类那里继承得到方法和成员变量,而且子类可以修改或增加新的方法使之适合子类的需要。
2. 封装
封装是把对象的状态数据隐藏起来,再通过暴露合适的方法来允许外部程序修改对象的状态数据。Java的封装主要通过private,protected,public等访问控制符来实现。
3. 多态
多态指的是同一个类型的引用类型的变量在执行相同的方法时,实际上会呈现出多种不同的行为特征。
多态增加了编程的灵活性,实际上大量设计模式都是基于多态类实现的。
4. 抽象
抽象就是忽略与当前目标无关的方面,以便更充分的突出与当前目标有关的方面。
抽象包括两个方面,一是过程抽象,二是数据抽象。
2. Java中实现多态的机制是什么?
答:Java允许父类或接口定义的引用变量指向子类或具体实现类的实例对象,
(导致编译时引用变量指向的方法和程序在内存里运行时实例对象调用的方法不一致。)
而程序调用的方法在运行时才动态绑定,就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。
3. 一个.java源文件中是否可以包括多个类(不是内部类)?有什么限制?
答:可以有多个类,但是只能有一个有public修饰的类,并且public类的类名必须与文件名相同。包含多个类的.java源文件编译之后会产生多个.class文件。
4. String是最基本的数据类型吗?
答:不是。
最基本的数据类型有:byte,short,int,long,char,float,double,Boolean
String是java.lang.String 类,属于引用类型。
(所谓的引用类型包括:类,接口,数组。
因为String是final修饰的类,是不可继承的类。并且是一个不可变类。因此如果程序需要使用的字符串所包含的字符序列需要经常改变,建议使用StringBuffer(线程安全,性能略差)类或者StringBuilder(线程不安全,性能好)类。)
5. Java有没有goto (转到)?
答:没有。因为goto是Java的保留字,暂时不是Java的关键字。
6. int和Integer有什么区别?
答:int是Java的基本数据类型,Integer是Java为int提供的包装类(封装类)。Integer包装类具有面向对象的特征。
从Java5开始,Java提供了自动装箱,自动拆箱功能。因此包装类也可以直接参与表达式运算,因此使用起来十分方便。
(Java为每个原始类型提供了包装类。
在JSP开发中,Integer的默认值为null,所以用EL输出null的Integer时,会显示空白字符串,而int的默认值为0,所以int不适合作为Web层的表单数据的类型。
另外,Integer提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数,Integer中还定义了表示整数的最大值和最小值的常量。)
7.String和StringBuffer,StringBuilder的区别?
答:
StringBuffer是线程安全的,性能较差,而StringBuilder是线程不安全的,适合单线程环境使用,性能较好。
String代表了字符序列不可变的字符串,而StringBuffer,StringBuilder代表了字符序列可变的字符串。
(它们都是CharSequence(字符序列)的实现类,都可以作为字符串使用。
)
8.Collection和Collections的区别?
答:Collection是集合类(List集合,Set集合,Queue队列)的根接口。
Collections是针对集合类的一个工具类,它提供一系列静态方法实现对各种集合的搜索,排序,线程安全化等操作。
9.说说&和&&的区别?
答:
&和&&都可以用作逻辑与的运算符,&当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。
双与有短路的功能,也称短路与。如果第一个表达式为false,则不再计算第二个表达式了。实际开发中可以用来把要比较的表达式放后面,排除报空指针异常的情况。
而单与在第一个表达式为false时,还会计算第二个表达式,所以性能略慢。
除此之外,&还可以用作位运算符,当&操作符两边不是boolean类型时,&表示按位与操作。
10.Overload和Override的区别。
Overloaded的方法是否可以改变返回值的类型?
答:Overload是方法的重载。(load 加载)
Override是方法的重写,也叫覆盖。
Overload要求两个方法具有方法名相同,形参列表不同的要求,返回值类型不能作为重载的条件。
Override要求子类方法与父类方法具有“两同两小一大”的要求。
两同:父类方法,子类方法的方法名,形参列表相同。
两小:子类方法的返回值类型要么是父类方法返回值类型的子类,要么返回值类型相同。
子类方法的抛出异常类型要么是父类方法抛出异常类型的之类,要么异常类型相同。
一大:子类的访问权限要么比父类的大,要么相同。
Overloaded方法可以改变返回值的类型。
11.Java如何跳出当前的多重嵌套循环?
答:可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break语句,即可跳出外层循环。
例如:
Outer:
for(int i=0;i<10;i++)
{
for(int j=0;j<10;j++)
{
System.out.println(“i=” + i + “,j=” + j);
if(j == 5) break Outer;
}
}
12.switch语句(判断选择)能否作用在byte上,能否作用在long上,能否作用在String上?
答:在Java1.7以前,可以作用在char,byte,short,int以及对应的包装类上。
在Java1.7以后,switch中可以是字符串类型,所以可以作用在String上。
也可以是枚举类型,在Java1.7以前以及以后都不能作用在long,double,float,boolean以及对应的包装类上。
(long,double,float,long比较特殊)
13.String s = new String(“xyz”);创建了几个String Object?
答:两个。一个是直接量的xyz对象,另一个是通过new String()构造器创建出来的String对象。通常来说,应该尽量使用直接量的String对象,这样具有更好的性能。
直接量就是程序中直接使用的数据值。
14.数组有没有length()这个方法?String有没有length()这个方法?
答:数组没有length()这个方法,只有length这个属性。String有length()方法。
Length长度。
15.short s1 = 1;s1 = s1 + 1;有什么错?short s1 = 1;s1+=s1有什么错?
答:s1 + 1 在运算时会自动提升表达式的类型,s1 + 1结果是int型,但复制给short类型的s1时,编译器将报告需要强制转换类型的错误。
第二个,由于+=运算符里已经包括了一个隐式的强制类型转换,因此它不会有任何错误。
16.char型变量中能不能存储一个中文字符?为什么?
答:char型变量是用来存储Unicode编码的字符的,Unicode编码字符集中包含了汉字,因此char型变量中可以存储汉字。
不过,如果某个特殊汉字没有被包含在Unicode编码字符集中,那么,char型变量中不能存储这个特殊汉字。
此外,char类型的变量占两个字节,而Unicode编码中每个字符也占两个字节。因此char型变量可以存储任何一个Unicode字符。
Unicode又称统一码。
17.用最有效率的方法算出2乘以8等于几?
答:2<<3 ,<<,箭头向左,是左移运算符,>>,箭头向右,是右移运算符
的n次方;那么,一个数乘以8只要将其左移3位即可,2的3次方,而CPU直接支持位运算,效率最高。
但是需要注意的是,如果这个数字本身已经很大,比如本身已经是2的30次方了,此时再使用这种位移运算就可能导致“溢出”,这样就得不到正确的结果了。
18.使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
答:使用final关键字修饰一个变量时,是指被修饰的引用变量不能变,引用变量所指向的对象的内容是可以改变的。
例如,对于如下语句:
final StringBuilder a=new StringBuilder ("immutable");
执行如下语句将报告编译错误:
a = new StringBuilder ("");
但如下语句则是完全正确的
a.append("fkjava.org");
有人希望在定义方法的形参时,通过final修饰符来阻止方法内部修改传进来的实参:
public void method(final StringBuilder param)
{
}
实际上这没有用,在该方法内部仍然可以增加如下代码来修改实参对象:
param.append("fkjava.org");
19.”==”和equals方法究竟有什么区别?
答:通俗地讲,”==”比较左右是不是一个东西,equals是比较左右是不是长得一样。
==操作符的功能有两个:
A.如果==的两边是基本类型的变量,包装类对象所组成的表达式,==用于比较两边的表达式的值是否相等,如果值相等,即使数据类不同,也会返回true。
B.如果==的两边是引用类型的变量,==用于判断这两个引用类型的变量是否引用同一块内存,只有它们引用同一块内存时,==才会返回true。
C.而equals()则是java.lang.Object类的一个方法,因此任何Java对象都可以调用此方法与其它对象进行比较。
D.开发者重写equals()方法就可以根据业务要求来决定这两个对象是否“相等”。
20.静态变量和实例变量的区别?
答:A.在语法定义上的区别,静态变量前要加static关键字,而实例变量前则不加。
B.在程序运行时的区别,实例变量属于一个对象,必须先创建实例对象,它的实例变量才会被分配空间,才能使用这个实例变量。静态变量则属于类,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。
总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
C.在内存中,static修饰的类变量只能有一个,而实例变量每创建一个实例对象就可以分配一个。
21.是否可以从一个static方法内部调用非static方法?
答:不可以,静态成员不可以调用非静态成员。
非static方法属于对象,必须创建一个对象后,才可以通过该对象来调用实例方法。
而static方法调用时不需要创建对象,通过类就可以调用该方法。
也就是说,当一个static方法被调用时,可能还没有创建任何实例对象。
因此Java不允许static方法内部调用非static方法。
22.Math.round(11.5)等于多少?Math.round(-11.5)等于多少?
答:Math类中提供了三个与取整有关的方法:ceil,floor,round,这些方法的作用与它们的英文名称的含义相对应。Ceil:天花板,向上取整Math.ceil(11.3)=12,Math.ceil(-11.3)=-11
Floor:地板,向下取整Math.floor(11.6)的结果为11,Math.floor(-11.6)=-12
Round:表示“四舍五入”,Math.round(11.5)=12,Math.round(-11.5)=-11。
23.请说出作用域public,private,protected,以及不写时的区别。
答:这四个作用域的可见范围如下表所示。
作用域 当前类 同一package 子类 全局
public √ √ √ √
protected √ √ √ ×
default √ √ × ×
private √ √ √ √
如果在修饰的元素上面没有写任何访问修饰符,则表示default。
访问权限由大到小是:public,protected,default,private
Java存在的4个访问范围:全局,子类,同一个package,当前类
24.外部类能用private,protected修饰吗?内部类可以用private,protected修饰吗?
答:外部类不能用private,protected修饰。内部类能用private,protected修饰。
外部类的上一级程序单位是包package,因此它只能有两个使用范围:包内和包外,因此它只能使用public(表示可以在全局位置访问),和默认修饰符default(表示只能被同一个包内的其它类使用)修饰。
25.一个类定义多个重载方法,参数分别是int,char,和double,然后将double x=2传递进去,会选择哪个方法?
答:选择参数类型为double的方法。
26.说说has a(有)与is a(是)的区别。
答:is a是典型的“一般到特殊“的关系,也就是典型的继承关系。例如Apple is a Fruit.。苹果是水果。Apple是一种特殊的Fruit,也就是说Apple继承了Fruit。
has a是典型的“组合“关系。比如Wolf has a Leg.狼有腿。也就是Leg组合成了Wolf。
需要指出的是:由于继承会造成对父类的破坏,因此有时候可以通过组合来代替继承。
使用继承的好处:程序语义更好理解。坏处是:子类可能重写父类方法,不利于父类封装。
使用组合则造成语义的混淆,但组合类不会重写被组合类的方法,因此更利于被复合类封装。
27.ClassLoader如何加载Class?
答:JVM(Java虚拟机)里有多个类加载,每个类加载可以负责加载特定位置的类。
除了bootstrap之外,其它的类加载器本身也是java类,它们的父类是ClassLoader。
28.Java抽象类的作用。
答:抽象类更利于代码的维护和重用。
抽象类的特点:1.拥有抽象方法。
2.抽象方法和抽象类都必须被abstract关键字修饰。
3.抽象类不可以创建实例和实例化对象,因为抽象类本身是不完整的。
4.抽象类的抽象方法要被使用,必须由子类重写所有父类抽象类的抽象方法,建立子类对象调用。(如果子类只重写(覆盖)了部分抽象方法,那么该子类还是一个抽象类)
抽象类比一般类多了抽象方法。
28.GC是什么?为什么要有GC?
答:GC是垃圾收集的意思,是Java的垃圾回收机制。
内存处理是编程人员容易出现问题的地方,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的。
Java的System和Runtime类都提供了“通知“程序进行垃圾回收的方法。
JVM何时来进行垃圾回收,还是由JVM自己来决定。
种回收机制。
答:
1.Java语言最显著的特点就是引入了垃圾回收机制,它使java程序员在编写程序时不再考虑内存管理的问题。
2.由于有垃圾回收机制,Java中的对象不再有“作用域“的概念,只有引用的对象才有作用域。
3.垃圾回收机制有效的防止了内存泄漏,可以有效的使用可使用的内存。
4.垃圾回收器通常作为一个单独的低级别的线程运行,在不可预知的情况下对内存堆中已经死亡的或很长的时间没有用过的对象进行清除和回收。
5.程序员不能实时的对某个对象或所有对象调用垃圾回收器进行垃圾回收。
垃圾回收机制有分代复制垃圾回收,标记垃圾回收,增量垃圾回收。
30.垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
答:对于Java程序中对象而言,如果这个对象没有任何引用变量引用它,那么这个对象不能被程序访问,因此可认为它是垃圾。
1. 只要有1个以上的引用变量引用该对象,该对象就不会被垃圾回收。
2. 对于Java的垃圾回收器来说,它使用“有向图“来记录和管理堆内存中的所有对象,通过这个有向图就可以识别哪些对象是”可达的“(有1个引用变量引用该对象),哪些对象是”不可达的“,所有不可达的对象就会被垃圾回收。
3. 程序员可以手动执行System.gc(),通知GC运行,但这只是一个通知,而JVM依然有权决定何时进行垃圾回收。
31.什么时候用assert
答:assert是JDK1.4新增的关键字。
assertion断言在软件开发中是一种常见的调试方式,很多开发语言中都支持这种机制。在实现中,assert就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为true,如果该值为false,说明程序已经处于不正确的状态下,assert将给出警告或退出。
Java的assert是关键字。
Java命令默认不启动断言,
为了启动用户断言,应该在运行java命令时增加-ea(Enable Assert)选项。
为了启动系统断言,应该在运行java命令时增加-esa(Enable System Assert)选项。
32.Java中会存在内存泄漏吗?请简单描述。
答:什么是内存泄漏:程序运行过程中会不断地分配空间;那些不再使用的内存空间应该即时回收它们,从而保证系统可以再次使用这些内存。如果存在无用的内存没有被回收回来,那就是内存泄漏。
33.能不能自己写个类,也叫java.lang.String?
答:可以,但在应用的时候,需要用自己的类加载器去加载,否则,系统的类加载器永远只是去加载jre.jar包中的那个java.lang.String。
但在Tomcat的web应用程序中,都是由webapp自己的类加载器先自己加载WEB-INF/classes目录中的类,然后才委托上级的类加载器加载,如果我们在Tomcat的web应用程序中写一个java.lang.String,这时候Servlet程序加载的就是我们自己写的java.lang.String。但这样做就会出很多潜在的问题,原来所有用了java.lang.String类都将出现问题。
34.ArrayList如何实现插入的数据按自定义的方式有序存放?
答:编程思路:实现一个类对ArrayList进行包装,当程序试图向ArrayList中放入数据时,程序将先检查该元素与ArrayList集合中其他元素的大小,然后将该元素插入到指定位置。
35.序列化接口的id有什么用?
答:(对象的序列化:把对象转换为字节序列的过程.
对象的反序列化:把字节序列恢复为对象的过程。
对象的序列化主要有两种用途:
1. 把对象的字节序列永久地保存到硬盘上,通常放在一个文件中。
2. 在网络上传送对象的字节序列。
在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。)
反序列化Java对象时必须提供该对象的class文件,现在的问题是随着项目的升级,系统的class文件也会升级,Java如何保证两个class文件的兼容性?
Java序列化机制允许为序列化类提供一个UID,也就是说如果一个类升级后,只要它的id值保持不变,序列化机制也会把它们当成同一个序列化版本。
(序列化类的id是为了在进行项目升级时,保证系统的两个class文件的兼容性,不会导致重复和冲突。)
36.hashCode()方法的作用?
答:hashCode()方法与equals()方法类似。都允许用户定义子类重写这两个方法。
equals()主要判断两个对象表面上或者内容上看是否相同。
而hashCode方法的判断就要严格的多。
如果两个对象的hashCode()相等,即可认为这两个对象是相等的。
因此当我们重写一个类的equals()方法时,也应该重写它的hashCode()方法。而且这两个方法判断对象的标准也应该是一样的。
37.编写一个函数将十六进制的字符串参数转换成整数返回。
String str = “13abf”;
Int len = str.length;
Int sum = 0;
Integer.parseInt(str,16);
parseInt方法提供了把16进制字符串参数转换成整数返回的方法。
38.银行还款问题
银行贷款的还款方式中最常用的是一种叫“等额本息”,还款法,即借款人在约定还款期限内的每一期(月)归还的金额(产生的利息+部分本金)都是相等的,现有一笔总额为T元的N年期住房贷款,年利率为R,要求算出每一期的还款的本金和利息总额,请写出解决思路和任意一种编程语言实现的主要代码。
思路:既然是按月还款,那我就要将N年按月来计算,即要还N*12个月,这样就可以求出每月要还的本金。由于每月要还的那部分本金所欠的时间不同,所以,它们所产生的利息是不同的,该部分本金的利息为:部分本金额*所欠月数*月利率。应该是这么个算法,如果利息还计利息,如果月还款不按年利率来算,老百姓算不明白的。
”之类,输出它们所有的排列组合
答:String str = "fkjav";
char[] arr1 = str.toCharArray();
char[] arr2 = java.util.Arrays.copyOf(arr1,arr1.length);
for(int i=0;i<arr1.length-1;i++)
{
for(int j = i+1;j<arr2.length;j++){
System.out.println(arr1[i] + "," + arr2[j]);
}
}
40.构造器Constructor是否可被override?
答:构造器Constructor不能被继承,因此不能重写Override,但可以被重载Overrload(形参列表不同)。
41.接口是否可以继承接口?抽象类是否可以实现(implements)接口?抽象类是否可以继承具体类(concrete class)?抽象类中是否可以有静态的main方法?
答:接口可以继承接口。
抽象类可以实现接口。
抽象类可以继承具体类。
抽象类中可以有静态的main方法。
归法:抽象类的特征是有得有失,得到的功能是抽象类可以拥有抽象方法(当然也可以没有);失去的功能是抽象类不能创建实例了。至于其他的,抽象类与普通类在语法上大致是一样的。
(抽象类除了不能创建实例,只要普通类可以的它都可以。)
42.写clone()方法时,通常都有一行代码,是什么?
答:clone故名思意就是复制,当对象调用此方法,就会在内存中分配一个和源对象同样大小的空间,在这个空间中创建一个新的复制对象。
clone有缺省(默认)行为,super.clone();
因为首先要把父类中的成员复制到位,然后才是复制自己的成员。
43.abstract class 和interface有什么区别?
答:抽象类和接口的语法区别:
接口可以说成是抽象类的一种特例,接口中所有方法都必须是抽象的。
1.一个类可以实现多个接口,但只能继承一个抽象类。
1.接口是彻底的抽象类。抽象类可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的。
2.抽象类可以有构造器,接口中不能有构造器。
3.抽象类中可以有实例成员变量,接口中没有实例成员变量。
4.抽象类中的抽象方法的访问类型可以是public , protected和默认访问权限。但接口中的抽象方法只能是public类型的。
5.抽象类中可以包含static静态方法,接口中不能包含static静态方法。
6.抽象类和接口都可以包含静态成员变量,但抽象类的静态成员变量的访问类型可以是任意,而接口的静态成员变量的访问类型只能是public static final
两者在应用上的区别:
接口体现的是一种规范,而抽象类在代码实现方面发挥作用,可以实现代码的重用。
例如,模板设计模式是抽象类的一个典型应用。假设项目中需要大量的DAO组件,这些组件通常都有增,删,改,查等基本方法。因此我们就可以定义一个抽象的DAO基类,然后让其它DAO组件来继承这个抽象的DAO基类,把这个DAO基类当成模板使用。
44.abstract抽象的method方法是否可同时是static,是否可同时是native(原生态方法,方法对应的实现不在当前文件,而是在其他语言(c,c++)实现的文件中),是否可同时是synchronized同步的?
答:抽象的方法不可以是static的,因为抽象方法要被子类实现。
native方法表示该方法要用另一种依赖平台的编程语言实现,不存在着被子类实现的问题,所以,它也不能是抽象的。
Synchronized同步锁与abstract不能同时使用,它修饰一个方法时,表示将会使用该方法的调用者作为同步监视器。但abstract方法所在的类是抽象类,不能创建实例,因此无法确定synchronized修饰方法时的同步监视器。
45.什么是内部类?Static Nested Class(静态嵌入类型)和 Inner Class(插入类型)的不同。
答:内部类就是一个在类的内部定义的类,内部类中不能定义静态成员。
静态成员不能访问非静态成员, 因此静态内部类不能访问外部类的非静态成员。
如果内部类使用了static修饰,该内部类就是静态内部类,也就是所谓的static nested class。
如果内部类没有使用inner修饰,它就是Inner class。
除此之外,还有一种局部内部类,在方法中定义的内部类就是局部内部类,局部内部类只在方法中有效。
对于Inner class而言,它属于实例成员,因此Inner class的实例必须寄生在外部类的实例中,因此程序在创建Inner Class实例之前,必须先获得它所寄生的外部类的实例。否则程序无法创建Inner Class的实例。
46.内部类可以引用它的外部类的成员吗?有没有什么限制?
答:内部类可以访问所在外部类的成员。
但是由于Java中,静态成员不能访问非静态成员,因此静态内部类(属于静态成员)就不能访问外部类的非静态成员。
47.Anonymous Inner Class(匿名内部类)是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)?
答:匿名内部类可以继承其他类或实现其它接口。
鉴于匿名内部类的特殊语法
new 父类、接口(){
类体实现部分
}
所以匿名内部类由于语法原因必须要实现父类或接口。
48.super.getClass()方法调用
下面程序的输出结果是多少?
import java.util.Date;
public class Test extends Date{
public static void main(String[] args) {
new Test().test();
}
public void test(){
System.out.println(super.getClass().getName());
}
}
输出结果:Test。
Super是一个限定词,当用super引用时,它也是引用当前对象本身,只是super限定了访问当前对象从父类那里得到成员变量和方法。
如果要访问父类的类名,应该使用如下语法:
Super.getClass().getSupserClass().getName();
49.JDK中哪些类是不可继承的?
答:不能继承的类是用final关键字修饰的类。
使用final关键字修饰的类,可以阻止被继承,不会有子类来重写该类的方法,完全封闭起来,因此更安全。
50.String s = “Hello”;
s = s + “world!”;这两行代码执行后,原始String对象中的内容到底变了没有?
答:没有。String被设计成是一个不可变类。当第2行代码执行以后,s引用变量指向了一个新的对象,原来的内容为”Hello”直接量对象被存放在内存中。
建议使用StringBuffer或StringBuilder类。
51.是否可以继承String类?
答:String类是final类,被final关键字修饰,所以不能被继承。
52.如何把一段逗号分割的字符串转换成一个数组?
答:可以使用String.split(“,”)方法使用String[]数组接收。
如:String [] result = "this,is,a,test".split(",");
其中result数组中存放了this、is、a、test等字符串元素。
53.下面这条语句一共创建了多少个对象:String s=”a”+”b”+”c”+”d”;
答:一个String对象。Java会在编译时对字符串相加进行优化处理,在编译时发现所有参与运算的都是字符串直接量,Java会把这个表达式的值计算出来。直接将该表达式的结果赋值给字符串引用变量。
对于如下代码:
String s1=”a”;
String s2=s1 + “b”;//对象+字符串=新的对象,和字符串常量池中的直接量没有关系
String s3=”a” + “b”;
System.out.println(s2 == “ab”);//false
System.out.println(s3 == “ab”);//true
字符串直接量会被放入字符串缓冲池中。
如下两行代码:
String s = “a” + “b” + “c” + “d”;
System.out.println(s == “abcd”);//true
由于s引用了字符串缓冲池中的”abcd”字符串,因此上面的输出结果为true。
54.Collection集合框架中实现比较要实现什么接口。
答:Java集合框架中需要比较大小的集合包括TreeMap、TreeSet,其中TreeMap会根据key-value对中key的大小进行排序,而TreeSet则会对集合元素进行排序。
集合框架中比较大小有2种方式:
A. 自然排序
要求TreeMap中的所有key都实现Comparable接口。JDK很多类都已经实现了Comparable接口,如String,Date,BigDecimal等。
B. 定制排序
需要在创建TreeMap或TreeSet时传入一个Comparator对象,Comparator只是一个接口,因此创建Comparator对象只能是创建它的实现类的对象,所以需要实现Comparator接口。
55.ArrayList和Vector的区别?
答:
Collection是接口,Collections是工具类。
相同点:这两个类都实现了List接口,List接口继承了Collection接口,它们都是有序集合。
(我们可以按位置索引号取出某个元素,并且其中的数据是允许重复的,这是List集合规范制定的)
而且ArrayList与Vector底层都是基于数组的,因此它们的实现代码也大致相似。
区别:在于Vector是一个古老的集合,从JDK1.0开始就有了,因此它包含了大量方法名很长的方法。JDK1.2开始引入集合框架,引入List接口,才让Vector实现了List接口,因此又增加了一些List接口中定义的方法。
总体来说,ArrayList可以完全替代Vector,除了在一些很古老的API中强制要求使用Vector之外。
Vector还有一个特征,它是线程安全的,因此性能较差,而ArrayList并不是线程安全的,因此性能较好。
实际上即使我们要在多线程环境下使用List集合,也应该选择ArrayList而不是Vector,因为Java还提供了一个Collections工具类,它可以把ArrayList包装成线程安全的集合类,例如如下代码:
List list = Collections.synchronizedList(new ArrayList());
56.HashMap和HashTable的区别
答:HashMap和HashTable的区别类似于ArrayList与Vector的区别。
HashTable与Vector都是JDK1.0就有一个古老的集合,HashTable是一个继承自Dictionary(字典)类的古老集合。
从JDK1.2引入集合框架的Map接口之后,Java让HashTable也实现了Map接口,因此HashTable也新增实现了一些Map接口中定义的方法。
实际上HashMap与HashTable底层的实现很相似,它们都是基于Hash表的实现。
HashMap与HashTable的区别主要有2点:
A. HashMap允许null作为key或value,而HashTable不允许。
B. HashMap是线程不安全的,因此性能较好。HashTable是线程安全的,因此性能较差。
实际上在多线程环境下,Java提供了Collection工具类把HashMap包装成线程安全的类,因此依然应该使用HashMap,如下代码所示:
Map map = Collections. synchronizedMap(new HashMap());
简单的说,编程时应该尽量避免使用HashTable,它与HashMap的底层实现很相似,但除非在一个古老的API中强制要求HashTable。
57.List和Map的区别?
答:List集合的特点:可重复,有序,有索引,程序可以通过元素的索引来读取元素,因此List相当于一个动态数组。
Map集合的特点:有Key-Value对,不允许重复,无序,无索引,程序可以根据key来取出对应的Value。
深入阐述:如果换个角度来看,我们可以把List当成Map来看,List相当于key都是数值的Map,程序通过元素的索引来读取List集合时,完全可以当成Map根据key来读取value。从另一个角度看,Map也可以当成元素索引可以是任意类型的List集合。
58.List,Set,Map是否继承自Collection接口?
答:List,Set集合继承自Collection接口,Map集合不少继承自Collection接口。
59.List,Map,Set三个接口,存取元素时,各有什么特点?
答:List集合:可重复,有序,有索引,实现类,ArrayList
Set集合:不允许重复,无序,无索引
对于Map集合而言,其底层存,取性能与Set集合完全一样。其实Set集合本身就是基于Map实现的,除了有键值对以外。
60.说出ArrayList,Vector,LinkedList的存储性能和特性。
答:ArrayList和Vector都是使用数组方式存储数据,Vector由于使用了synchrnized(线程安全)的方法,因此性能较ArrayList差,而LinkedList使用了双向链表实现存储,所以插入速度较快,而且线程也是不安全的。LinkedList提供了一些方法,使得LinkedList可以被当作堆栈和队列来使用。
实际上Java提供了Collection集合的工具类,它可以把ArrayList,vector包装成线程安全的集合,因此在实际编程中应该避免使用Vector集合,它是一个古老的,在JDK1.0时代就出现的集合。
61.去掉一个Vector集合中重复的元素。
最简单的方式:HashSet set = new HashSet(vector);
基于HashSet不允许重复的特性。
62.Set里的元素是不能重复的,那么用什么方法来区分重复与否呢?是用==还是equals()?它们有何区别?
答:Set只是一个接口,它的不同实现类判断元素是否相等的标准是不同的。笼统地说,Set里的元素是不能重复的,判断元素重复应该使用equals方法而不是==。
对于HashSet而言,判断两个对象是否相等是通过equals()方法和hashCode()方法,只要两个对象通过equals()比较返回false,或两个对象的hashCode值不一样,那么HashSet就会把它们当成不相同的元素。
对于TreeSet而言,判断两个对象是否相等的唯一标准是:两个对象通过compareTo(Object obj)比较是否返回0,与equals()方法无关。
63.你所知道的集合类都有哪些?主要方法?
答:最常用的集合接口是Set,List,Queue,它们都是Collection的子接口,除此之外还有Map接口。
对于Set集合而言,它的常用实现类包括HashSet与TreeSet,HashSet还有一个子类,LinkedHashSet。
对于List集合而言,它的常用实现类包括ArrayList,Vector与LinkedList。
对于Queue集合而言,它有一个子接口,代表双端队列,Deque,它的常用实现类包括ArrayDeque,LinkedList。
对于Map集合而言,它的常用实现类是HashMap,TreeMap,HashMap还有一个子类,LinkedHashMap。
至于这些集合的方法,由于集合类也就是“容器类”,因此它的方法无非就是向容器中添加,删除,取出,遍历元素的方法。
List集合的元素都是有序,有索引的,因此它包括了大量根据索引来添加,删除,取出集合元素的方法。
对于Deque集合而言,由于它是双端队列,既可以当成队列使用,也可以当成栈使用,因此它增加栈,队列的方法,压栈和弹栈,如push,pop,offer,peek等。
对于Map集合而言,它的方法无非就是根据key来添加,删除,取出value的方法。
64.两个对象值相同(x.equals(y)==true),但却可以有不同的hash code,这句话对不对?
答:对。
因为equals方法和hashcode方法都可以由开发者来重写,因此它们是否相等并没有必然的关系。
如果对象要保存在HashSet或者HashMap中,它们的equals相等,那么hashcode返回值也应该相等。
65.TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用子类的compareTo方法,还是抛异常?
答:当前正在添加父类对象就多次调用父类对象的CompareTo方法,当前正在添加子类对象就多次调用子类对象的CompareTo方法。
至于程序是否抛出异常,则取决于CompareTo方法的实现,如果子类在实现CompareTo方法时,试图把被比较对象转换为子类对象之后再比较,如果TreeSet集合中已经包括了父类对象,这就会引起ClassCastException类型转换异常。
66.说出一些常用的类,包,接口,请各举5个。
答:常用的包有:
java.lang包下包括Math,System,StringBuilder,StringBuffer,Runtime,Thread,Runnable等
(Math数学,System系统,StringBuilder/StringBuffer,运行时类,线程类)
java.util包下包括List,Set,Map,以及这些接口的常用实现类,ArrayList,LinkedList,HashSet,TreeSet,HashMap,TreeMap等
java.io包下包括
(字节流,字符流,缓冲流)
InputStream,OutputStream,Reader,Writer,FileInputStream,FileOutputStream,FileReader,FileWriter,BufferInputStream,BufferOutputStream,BufferReader,BuffWriter等
Java.sql包下包括Connection,Statement,PrepareStatement,ResultSet等(创建连接对象,编译,预编译语句,结果集)
Java.net包下包括
Socket,ServerSocket,URL,URLConnection,DatagramPacker,DatagramSocket等
(Socket套接字)
67.java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以继承,请说出它们分别是那些类?
答:字节流和字符流,字节流由InputStream,OutputStream派生出来,字符流由Reader,Writer派生出来。在java.io包中还有许多其它的流,主要是为了提高性能和使用方便。
68.字符流和字节流的区别。
答:字节流和字符流的区别是,它们的用法几乎完全一样,区别在于字节流和字符流所操作的位的字符。
节流主要由InputStream,OutputStream作为基类,字符流主要由Reader,Writer作为基类。
字节流是基于字节输入,输出的,因此它的适用性更广。字符流则在处理文本内容时的输入,输出更加方便——不会出现只读取半个字符的情况。
Java提供了将字节流转换为字符串的InputStreamReader , OutputStreamWriter,但没有提供将字符流转化为字节流的方法。因为,字节流比字符流的使用范围更广,但字符流在处理字符的时候比字节流操作方便。如果有一个字节流,我们知道它的内容是文本内容,因此把它转换成字符流来处理就会更方便一些。各有所需。
69.什么是Java序列化,如何实现Java序列化?或者请解释Serializable接口的作用。
答:序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象,对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,持久保存到磁盘上,通过网络将这种二进制流传输到另一个网络节点。其它程序一旦获得了这种二进制流,无论是从磁盘中获取还是通过网络获取,都可以将这种二进制流恢复成原来的Java对象。
Serializable接口只是一个标记接口,实现该接口无需实现任何方法,实现了该接口的类就是可序列化的类。
序列化在Web开发中十分重要,Tomcat在某些时候需要把保存在Session中的对象序列化到硬盘,因此放入Session中的对象必须是可序列化的,这就可以通过Serializable接口来实现。还有,如果一个对象要经过网络传输(比如RMI远程方法调用的形参或返回值),这个对象也应该是可序列化的。
70.描述一下JVM加载class文件的原理机制?
答:
当程序主动使用某个类,如果该类还未必加载到内存中,系统会通过3个步骤来对该类进行初始化。加载,连接,初始化。
类加载指的当程序中使用任何类时,系统都会为之建立一个java.lang.Class类对象。
类的加载由加载器完成,类加载器通常由JVM提供,除此之外,开发者可以通过继承ClassLoader基类来创建自己的类加载器。
71.heap和stack有什么区别?
答:stack指的是栈区,当程序进入一个方法时,系统会专门为这个方法分配一个内存空间,这块内存空间也被称为该方法栈区,该方法的栈区专门用于存储该方法中定义的局部变量,(括基本类型变量和引用变量)。当这个方法结束时,该方法栈区将会自动被销毁,栈区中的所有局部变量也会被销毁。
heap指的是堆,堆内存是Java虚拟机拥有的内存区。所有的Java对象都被放在堆内存中,位于堆内存中的Java对象由系统垃圾回收器负责跟踪管理——垃圾回收(GC),当堆内存中的对象没有引用变量引用它时,这个Java对象就变成了垃圾,垃圾回收器会在合适的时候回收它
72.try{}里有一个return语句,那么紧跟在这个try后的finally{}里的code会不会被执行,什么时候被执行,在return前还是return后?
答:肯定会执行,在return后执行。
Finally{}块的代码只有在try{}块中包含遇到System.exit(0;之类的导致Java虚拟机直接退出的语句才会不执行。
当程序执行try{}遇到return时,程序会先执行return语句,但并不会立即返回,而是把执行流程转到finally块,完成finally块的执行后就直接返回刚才return语句已经准备好的结果。
73.下面程序代码输出的结果是多少?
public class smallT
{
public static void main(String args[])
{
smallT t = new smallT();
int b = t.get();
System.out.println(b);
}
public int get()
{
try
{
return 1 ;
}
finally
{
return 2 ;
}
}
}
输出结果是:2
Java会把return语句先执行完,把所有需要处理的东西都先处理完成,但是还未返回之前,程序流程会转去finally块。但如果在执行finally块时遇到了return语句,那么程序会直接使用finally块中的return语句来返回,因此上面程序会输出2。
74.final,finally,finalize的区别。
答:final最终的,finally最后,finalize完成。
Final是一个修饰符,它可以修饰类,方法,变量。
(final修饰类表明这个类不可以被继承,final修饰方法时表明这个方法不可以被其子类重写(重写就是覆盖Override.final,final修饰变量可分为局部变量,实例变量,静态变量。)
当final修饰局部变量时,该局部变量可以被赋值一次。当final修饰实例变量时,实例变量必须由程序在构造器,初始化块,”定义时” 这3个位置的其中之一指定初始值。当final修饰静态变量时,静态变量必须由程序在静态初始化块,定义时这2个位置的其中之一指定初始值。
Finally是异常处理语句结构的一部分,表示总会执行的代码块。
Finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法。但实际上重写此方法进行资源回收并不安全,因为JVM并不保证该方法总被调用。
75.运行时异常与一般异常有何异同?
答:Checked检查异常就是一般异常,体现了Java的设计哲学,没有完善错误处理的代码根本不会被执行。
一般异常的处理方式有2种,
A. 当前方法知道如何处理异常,程序应使用try…catch块来捕获异常,然后在对应的catch块中修补该异常。
B. 当前方法不知道如何处理异常,应该在定义方法时声明抛出该异常。Throw Exception
Runtime异常对比处理更加灵活,Runtime异常无需显式声明抛出,如果程序需要捕捉Runtime异常,也可以使用try…catch块来捕捉runtime异常。
76.error和exception有什么区别?
答:Error错误,一般是指虚拟机相关的问题,如系统崩溃,虚拟机出错,动态连接失败等。这种错误无法恢复或不能被捕获,将导致应用程序中断。
Exception异常,表示一种设计或实现问题。也就是说,程序员应该对这些情况进行考虑,并提供相应的处理。
77.Java中的异常处理机制的简单原理和应用。
答:程序运行过程中可能出现各种“非预期“情况,这种非预期情况可能导致程序非正常结束。为了提高程序的健壮性,Java提供了异常处理机制。
Java对异常进行了分类,不同类型的异常分别用不同的Java类表示,所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error错误和Exception异常,Error错误一般是指虚拟机相关的问题,如系统崩溃,虚拟机出现错误,动态连接失败等。这种错误无法恢复或不能捕获,将导致应用程序中断。通常应用程序无法处理这种错误,因此不能使用try…catch{}块来捕获Error对象。
Exception表示一种设计或实现问题,也就是说,程序员应该对这些情况进行考虑,并提供相应的处理。
异常分为运行时异常和一般检查异常,一般检查异常体现了Java的设计哲学,没有完善错误的代码根本就不会被执行。
对于一般检查异常的处理方式有两种:
A.当前方法知道如何处理异常,程序应使用try…catch块来捕获异常,然后在对应的catch块中修补该异常。
B.当前方法不知道如何处理异常,应该在定义方法时声明抛出该异常。
实际上Java的一般异常后来争议不断,因为一般异常要求程序员要么显式声明抛出,要么进行捕捉,不能对一般异常不闻不问,这就给编程带来了一定的复杂度。
比如Spring,Hibernate框架的一大特点就是把一般异常包装成了运行时异常。
运行时异常则比较灵活,开发者既可以选择捕获运行时异常,也可以不捕获。
78.请写出你最常见到的5个runtime exception.
答:对于一个有1—2年左右编程经验的人来说,总会遇到一些常见的异常。
其中有些就是
Runtime Exception运行时异常。
NullPointerException空指针异常:当调用一个未初始化的引用变量的实例Field,实例方法时都会引发该异常。
ArithmeticException算术异常:比如5/0将引发异常。
ArrayIndexOutOfBoundsException数组索引越界异常。
ClassCastException类型转换异常。
IllegalArgumentException参数非法的异常。
79.Java语言如何进行异常处理,关键字:throws ,throw ,try ,catch ,finally分别代表什么意义?在try块中可以抛出异常吗?
答:try块表示程序正常的业务执行代码。如果程序在执行try块的代码时出现了“非预期”情况,JVM将会生成一个异常对象,这个异常对象将会被后面相应的catch块捕获。
Throw用于手动抛出异常对象。Throw后面需要一个异常对象。
Throws用于在方法签名中声明抛出一个或多个异常类,throws关键字后可以紧跟一个或多个异常类。
Finally块代表异常处理流程中总会执行的代码块。
对于一个完整的异常处理流程,try块是必须的,try块后可以紧跟一个或多个catch块,最后还可以带一个finally块。
Try块中可以抛出异常。
80.Java中有几种方法可以实现一个线程?用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用?
答:在java5以前,有如下两种
A.继承Thread类,重写它的run()方法。
代码如下:
new Thread()
{
public void run()
{
//线程执行体
}
}.start();
C. 实现Runnable接口,并重写它的run()方法。
new Thread(new Runnable()
{
public void run()
{
//线程执行体
}
}).start();
从上面代码不难看出,线程的执行体是一个run()方法,然后程序通过start()方法启动一条线程。
从Java5开始,Java提供了第三种方式来创建多线程:实现Callable接口,并实现Call()方法。Callable接口相当于Runnable接口的增强版,因为Callable接口中定义的call()方法既拥有返回值,也可以声明抛出异常。
代码如下:
new Thread(new FutureTask<Object >(new Callable<Object>()
{
public Object call() throws Exception
{
//线程执行体
}
})).start();
不仅如此,Java5还提供了线程支持,ExecutorService对象就代表了线程池,如果开发者利用ExecutorService来启动线程,ExecutorService底层会负责管理线程池。此时,开发者只要把Runnable对象传给ExecutorService即可。如下代码:
ExecutorService pool = Executors.newFixedThreadPool(3)
pool.execute(new Runnable()
{
public void run()
{
//线程执行体
}
});
如果执行通过Callable方式实现的线程,则可按如下代码:
ExecutorService pool = Executors.newFixedThreadPool(3)
pool.execute(new FutureTask<Object >(new Callable<Object>()
{
public Object call() throws Exception
{
//线程执行体
}
}));
用synchronized关键字修饰同步方法。需要指出的是,非静态的同步方法的同步监视器是this,也就是调用该方法的对象,而静态的同步方法的同步监视器是该类本身。因此使用synchronized修饰的静态方法,非静态方法的同步监视器并不相同,只有基于同一个同步监视器的同步方法,同步代码块才能实现同步。
使用stop()停止方法不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。
Suspend()延迟方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被“挂起”的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
82.sleep()和wait()又什么区别?
答:sleep()是Thread类的静态方法,它的作用是让当前线程从运行状态转入,阻塞状态,线程执行暂停下来,当一个线程通过sleep()方法暂停之后,该线程并不会释放它对同步监视器的加锁。
Wait()是Object对象的方法,但实际上只有同步监视器才能调用该方法。当程序在同步代码块,或同步方法内通过同步监视器调用该方法时,将会导致当前线程释放对该同步监视器的加锁,而该线程则会进入该同步监视器的等待池中,直到该同步监视器调用notify()(通知)或notifyAll()来通知该线程。
83.同步和异步有何异同,在什么情况下分别使用它们?举例说明。
答:块,但多个线程都试图取款800块时,这些线程同时判断余额之后,都会显示余额足够,从而导致每个线程都取款成功。这显然不是我们希望看到的结果。
当程序试图执行一个耗时操作时,程序不希望阻塞当前执行流,因此程序也不应该试图立即获取该耗时操作返回的结果,此时就使用异步编程了,典型的应用场景就是Ajax。当浏览器通过JavaScript发出一个异步请求之后,JavaScript执行流并不会停下来,而是继续向下执行,这就是异步。程序会通过监听器来监听远程服务器响应的到来。
84.多线程有几种实现方法?同步有几种实现方法?
答:
多线程的实现方法:
在Java5以前,有如下两种:
第一种,继承Thread类,重写它的run()方法。
第二种,实现Runnable接口,并重写它的run()方法。
线程的执行体是一个run()方法,然后程序通过start()方法启动一条线程。
从Java5开始,Java提供了实现Callable接口来创建多线程,并实现call()方法。Callable接口相当于Runnable接口的增强版,因为Callable接口中定义的call()方法既拥有返回值,也可以声明抛出异常。
不仅如此,Java5还提供了线程支持,ExecutorService对象就代表了线程池。开发者只要把Runnable对象,传给ExecutorService即可。
同步的实现方法:
同步方法:使用synchronized关键字修饰方法,由于java的每一个对象都有一个内置锁,在调用该方法前需要获得内置锁,否则就处于阻塞状态。
同步代码块:有synchronized关键字修饰的语句块。
使用特殊域变量volatile实现线程同步,需要同步的变量加上volatile
使用重入锁实现线程同步,创建一个ReentrantLock的实例,获得锁lock(),释放锁unlock()。
使用局部变量实现线程同步,如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
注:ThreadLocal与同步机制
A.ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题。
B.前者采用以“空间换时间”的方法,后者采用以“时间换空间”的方式。
85.启动一个线程是用run()还是start()?
答:启动一个线程是调用start()方法,使线程就绪状态,以后可以被调度为运行状态。run()方法是线程的线程执行体——也就是线程将要完成的事情。
86.当线程进入一个对象的一个synchronized方法后,其他线程是否可以进入此对象的其他方法?
答:当线程进入一个对象的synchronized方法之后,其他线程完全有可能再次进入该对象的其他方法。
不过要分几种情况来看:
1. 如果其他方法没有使用synchronized关键字修饰,则可以进入。
2. 如果当前线程进入的synchronized方法是static方法,其他线程可以进入其他synchronized修饰的非静态方法,如果当前线程进入的synchronized方法是非static方法,其他线程可以进入其他synchronized方法。
3. 如果两个方法都是静态方法,或者都是非静态方法,并且都使用了synchronized修饰,但只要在该方法内部调用了同步监视器的wait(),则其他线程依然可以进入其他使用synchronized方法修饰的方法。
4. 如果没有调用wait(),其他线程不能进入其他使用synchronized方法修饰的方法。
87.线程的基本概念,线程的基本状态以及状态之间的关系。
答:线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程可以拥有自己的堆栈,自己的程序计数器和自己的局部变量,但不再拥有系统资源,它与父进程的其他线程共享该进程所拥有的全部资源。因为多个线程共享父进程里的全部资源,因此编程更加方便;但必须更加小心,我们必须确保线程不会妨碍同一进程里的其他线程。
线程的执行需要经过如下状态:
新建线程new
就绪start()
运行
阻塞sleep()
死亡stop()
各状态的转换关系如下图所示:
88.简述synchronized和java . util .concurrent .locks .Lock的异同
答:主要相同点:Lock能完成synchronized所实现的所有功能。
主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。Synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。Lock还有更强大的功能,它的tryLock方法可以非阻塞方式去拿锁。
Java代码查错部分
1.
abstract class Name {
private String name;
public abstract boolean isStupidName(String name) {}
}
答:错。abstract方法必须以分号;结尾,而且不带花括号{}
2.
public class Something {
void doSomething () {
privateString s = "";
int l = s.length();
}
}
有错吗?
答:错。局部变量前不能放置任何修饰符(private,public,和protected),final可以用来修饰局部变量。
1. 面向对象的特征有哪些方面?
答:
1. 继承
通过继承允许复用已有的类。继承关系是一种一般到特殊的关系,比如苹果类继承水果类,这个过程称为类继承。
子类可以从父类那里继承得到方法和成员变量,而且子类可以修改或增加新的方法使之适合子类的需要。
2. 封装
封装是把对象的状态数据隐藏起来,再通过暴露合适的方法来允许外部程序修改对象的状态数据。Java的封装主要通过private,protected,public等访问控制符来实现。
3. 多态
多态指的是同一个类型的引用类型的变量在执行相同的方法时,实际上会呈现出多种不同的行为特征。
多态增加了编程的灵活性,实际上大量设计模式都是基于多态类实现的。
4. 抽象
抽象就是忽略与当前目标无关的方面,以便更充分的突出与当前目标有关的方面。
抽象包括两个方面,一是过程抽象,二是数据抽象。
2. Java中实现多态的机制是什么?
答:Java允许父类或接口定义的引用变量指向子类或具体实现类的实例对象,
(导致编译时引用变量指向的方法和程序在内存里运行时实例对象调用的方法不一致。)
而程序调用的方法在运行时才动态绑定,就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。
3. 一个.java源文件中是否可以包括多个类(不是内部类)?有什么限制?
答:可以有多个类,但是只能有一个有public修饰的类,并且public类的类名必须与文件名相同。包含多个类的.java源文件编译之后会产生多个.class文件。
4. String是最基本的数据类型吗?
答:不是。
最基本的数据类型有:byte,short,int,long,char,float,double,Boolean
String是java.lang.String 类,属于引用类型。
(所谓的引用类型包括:类,接口,数组。
因为String是final修饰的类,是不可继承的类。并且是一个不可变类。因此如果程序需要使用的字符串所包含的字符序列需要经常改变,建议使用StringBuffer(线程安全,性能略差)类或者StringBuilder(线程不安全,性能好)类。)
5. Java有没有goto (转到)?
答:没有。因为goto是Java的保留字,暂时不是Java的关键字。
6. int和Integer有什么区别?
答:int是Java的基本数据类型,Integer是Java为int提供的包装类(封装类)。Integer包装类具有面向对象的特征。
从Java5开始,Java提供了自动装箱,自动拆箱功能。因此包装类也可以直接参与表达式运算,因此使用起来十分方便。
(Java为每个原始类型提供了包装类。
在JSP开发中,Integer的默认值为null,所以用EL输出null的Integer时,会显示空白字符串,而int的默认值为0,所以int不适合作为Web层的表单数据的类型。
另外,Integer提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数,Integer中还定义了表示整数的最大值和最小值的常量。)
7.String和StringBuffer,StringBuilder的区别?
答:
StringBuffer是线程安全的,性能较差,而StringBuilder是线程不安全的,适合单线程环境使用,性能较好。
String代表了字符序列不可变的字符串,而StringBuffer,StringBuilder代表了字符序列可变的字符串。
(它们都是CharSequence(字符序列)的实现类,都可以作为字符串使用。
)
8.Collection和Collections的区别?
答:Collection是集合类(List集合,Set集合,Queue队列)的根接口。
Collections是针对集合类的一个工具类,它提供一系列静态方法实现对各种集合的搜索,排序,线程安全化等操作。
9.说说&和&&的区别?
答:
&和&&都可以用作逻辑与的运算符,&当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。
双与有短路的功能,也称短路与。如果第一个表达式为false,则不再计算第二个表达式了。实际开发中可以用来把要比较的表达式放后面,排除报空指针异常的情况。
而单与在第一个表达式为false时,还会计算第二个表达式,所以性能略慢。
除此之外,&还可以用作位运算符,当&操作符两边不是boolean类型时,&表示按位与操作。
10.Overload和Override的区别。
Overloaded的方法是否可以改变返回值的类型?
答:Overload是方法的重载。(load 加载)
Override是方法的重写,也叫覆盖。
Overload要求两个方法具有方法名相同,形参列表不同的要求,返回值类型不能作为重载的条件。
Override要求子类方法与父类方法具有“两同两小一大”的要求。
两同:父类方法,子类方法的方法名,形参列表相同。
两小:子类方法的返回值类型要么是父类方法返回值类型的子类,要么返回值类型相同。
子类方法的抛出异常类型要么是父类方法抛出异常类型的之类,要么异常类型相同。
一大:子类的访问权限要么比父类的大,要么相同。
Overloaded方法可以改变返回值的类型。
11.Java如何跳出当前的多重嵌套循环?
答:可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break语句,即可跳出外层循环。
例如:
Outer:
for(int i=0;i<10;i++)
{
for(int j=0;j<10;j++)
{
System.out.println(“i=” + i + “,j=” + j);
if(j == 5) break Outer;
}
}
12.switch语句(判断选择)能否作用在byte上,能否作用在long上,能否作用在String上?
答:在Java1.7以前,可以作用在char,byte,short,int以及对应的包装类上。
在Java1.7以后,switch中可以是字符串类型,所以可以作用在String上。
也可以是枚举类型,在Java1.7以前以及以后都不能作用在long,double,float,boolean以及对应的包装类上。
(long,double,float,long比较特殊)
13.String s = new String(“xyz”);创建了几个String Object?
答:两个。一个是直接量的xyz对象,另一个是通过new String()构造器创建出来的String对象。通常来说,应该尽量使用直接量的String对象,这样具有更好的性能。
直接量就是程序中直接使用的数据值。
14.数组有没有length()这个方法?String有没有length()这个方法?
答:数组没有length()这个方法,只有length这个属性。String有length()方法。
Length长度。
15.short s1 = 1;s1 = s1 + 1;有什么错?short s1 = 1;s1+=s1有什么错?
答:s1 + 1 在运算时会自动提升表达式的类型,s1 + 1结果是int型,但复制给short类型的s1时,编译器将报告需要强制转换类型的错误。
第二个,由于+=运算符里已经包括了一个隐式的强制类型转换,因此它不会有任何错误。
16.char型变量中能不能存储一个中文字符?为什么?
答:char型变量是用来存储Unicode编码的字符的,Unicode编码字符集中包含了汉字,因此char型变量中可以存储汉字。
不过,如果某个特殊汉字没有被包含在Unicode编码字符集中,那么,char型变量中不能存储这个特殊汉字。
此外,char类型的变量占两个字节,而Unicode编码中每个字符也占两个字节。因此char型变量可以存储任何一个Unicode字符。
Unicode又称统一码。
17.用最有效率的方法算出2乘以8等于几?
答:2<<3 ,<<,箭头向左,是左移运算符,>>,箭头向右,是右移运算符
的n次方;那么,一个数乘以8只要将其左移3位即可,2的3次方,而CPU直接支持位运算,效率最高。
但是需要注意的是,如果这个数字本身已经很大,比如本身已经是2的30次方了,此时再使用这种位移运算就可能导致“溢出”,这样就得不到正确的结果了。
18.使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
答:使用final关键字修饰一个变量时,是指被修饰的引用变量不能变,引用变量所指向的对象的内容是可以改变的。
例如,对于如下语句:
final StringBuilder a=new StringBuilder ("immutable");
执行如下语句将报告编译错误:
a = new StringBuilder ("");
但如下语句则是完全正确的
a.append("fkjava.org");
有人希望在定义方法的形参时,通过final修饰符来阻止方法内部修改传进来的实参:
public void method(final StringBuilder param)
{
}
实际上这没有用,在该方法内部仍然可以增加如下代码来修改实参对象:
param.append("fkjava.org");
19.”==”和equals方法究竟有什么区别?
答:通俗地讲,”==”比较左右是不是一个东西,equals是比较左右是不是长得一样。
==操作符的功能有两个:
A.如果==的两边是基本类型的变量,包装类对象所组成的表达式,==用于比较两边的表达式的值是否相等,如果值相等,即使数据类不同,也会返回true。
B.如果==的两边是引用类型的变量,==用于判断这两个引用类型的变量是否引用同一块内存,只有它们引用同一块内存时,==才会返回true。
C.而equals()则是java.lang.Object类的一个方法,因此任何Java对象都可以调用此方法与其它对象进行比较。
D.开发者重写equals()方法就可以根据业务要求来决定这两个对象是否“相等”。
20.静态变量和实例变量的区别?
答:A.在语法定义上的区别,静态变量前要加static关键字,而实例变量前则不加。
B.在程序运行时的区别,实例变量属于一个对象,必须先创建实例对象,它的实例变量才会被分配空间,才能使用这个实例变量。静态变量则属于类,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。
总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
C.在内存中,static修饰的类变量只能有一个,而实例变量每创建一个实例对象就可以分配一个。
21.是否可以从一个static方法内部调用非static方法?
答:不可以,静态成员不可以调用非静态成员。
非static方法属于对象,必须创建一个对象后,才可以通过该对象来调用实例方法。
而static方法调用时不需要创建对象,通过类就可以调用该方法。
也就是说,当一个static方法被调用时,可能还没有创建任何实例对象。
因此Java不允许static方法内部调用非static方法。
22.Math.round(11.5)等于多少?Math.round(-11.5)等于多少?
答:Math类中提供了三个与取整有关的方法:ceil,floor,round,这些方法的作用与它们的英文名称的含义相对应。Ceil:天花板,向上取整Math.ceil(11.3)=12,Math.ceil(-11.3)=-11
Floor:地板,向下取整Math.floor(11.6)的结果为11,Math.floor(-11.6)=-12
Round:表示“四舍五入”,Math.round(11.5)=12,Math.round(-11.5)=-11。
23.请说出作用域public,private,protected,以及不写时的区别。
答:这四个作用域的可见范围如下表所示。
作用域 当前类 同一package 子类 全局
public √ √ √ √
protected √ √ √ ×
default √ √ × ×
private √ √ √ √
如果在修饰的元素上面没有写任何访问修饰符,则表示default。
访问权限由大到小是:public,protected,default,private
Java存在的4个访问范围:全局,子类,同一个package,当前类
24.外部类能用private,protected修饰吗?内部类可以用private,protected修饰吗?
答:外部类不能用private,protected修饰。内部类能用private,protected修饰。
外部类的上一级程序单位是包package,因此它只能有两个使用范围:包内和包外,因此它只能使用public(表示可以在全局位置访问),和默认修饰符default(表示只能被同一个包内的其它类使用)修饰。
25.一个类定义多个重载方法,参数分别是int,char,和double,然后将double x=2传递进去,会选择哪个方法?
答:选择参数类型为double的方法。
26.说说has a(有)与is a(是)的区别。
答:is a是典型的“一般到特殊“的关系,也就是典型的继承关系。例如Apple is a Fruit.。苹果是水果。Apple是一种特殊的Fruit,也就是说Apple继承了Fruit。
has a是典型的“组合“关系。比如Wolf has a Leg.狼有腿。也就是Leg组合成了Wolf。
需要指出的是:由于继承会造成对父类的破坏,因此有时候可以通过组合来代替继承。
使用继承的好处:程序语义更好理解。坏处是:子类可能重写父类方法,不利于父类封装。
使用组合则造成语义的混淆,但组合类不会重写被组合类的方法,因此更利于被复合类封装。
27.ClassLoader如何加载Class?
答:JVM(Java虚拟机)里有多个类加载,每个类加载可以负责加载特定位置的类。
除了bootstrap之外,其它的类加载器本身也是java类,它们的父类是ClassLoader。
28.Java抽象类的作用。
答:抽象类更利于代码的维护和重用。
抽象类的特点:1.拥有抽象方法。
2.抽象方法和抽象类都必须被abstract关键字修饰。
3.抽象类不可以创建实例和实例化对象,因为抽象类本身是不完整的。
4.抽象类的抽象方法要被使用,必须由子类重写所有父类抽象类的抽象方法,建立子类对象调用。(如果子类只重写(覆盖)了部分抽象方法,那么该子类还是一个抽象类)
抽象类比一般类多了抽象方法。
28.GC是什么?为什么要有GC?
答:GC是垃圾收集的意思,是Java的垃圾回收机制。
内存处理是编程人员容易出现问题的地方,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的。
Java的System和Runtime类都提供了“通知“程序进行垃圾回收的方法。
JVM何时来进行垃圾回收,还是由JVM自己来决定。
种回收机制。
答:
1.Java语言最显著的特点就是引入了垃圾回收机制,它使java程序员在编写程序时不再考虑内存管理的问题。
2.由于有垃圾回收机制,Java中的对象不再有“作用域“的概念,只有引用的对象才有作用域。
3.垃圾回收机制有效的防止了内存泄漏,可以有效的使用可使用的内存。
4.垃圾回收器通常作为一个单独的低级别的线程运行,在不可预知的情况下对内存堆中已经死亡的或很长的时间没有用过的对象进行清除和回收。
5.程序员不能实时的对某个对象或所有对象调用垃圾回收器进行垃圾回收。
垃圾回收机制有分代复制垃圾回收,标记垃圾回收,增量垃圾回收。
30.垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
答:对于Java程序中对象而言,如果这个对象没有任何引用变量引用它,那么这个对象不能被程序访问,因此可认为它是垃圾。
1. 个以上的引用变量引用该对象,该对象就不会被垃圾回收。
2. 个引用变量引用该对象),哪些对象是”不可达的“,所有不可达的对象就会被垃圾回收。
3. 程序员可以手动执行System.gc(),通知GC运行,但这只是一个通知,而JVM依然有权决定何时进行垃圾回收。
31.什么时候用assert
答:assert是JDK1.4新增的关键字。
assertion断言在软件开发中是一种常见的调试方式,很多开发语言中都支持这种机制。在实现中,assert就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为true,如果该值为false,说明程序已经处于不正确的状态下,assert将给出警告或退出。
Java的assert是关键字。
Java命令默认不启动断言,
为了启动用户断言,应该在运行java命令时增加-ea(Enable Assert)选项。
为了启动系统断言,应该在运行java命令时增加-esa(Enable System Assert)选项。
32.Java中会存在内存泄漏吗?请简单描述。
答:什么是内存泄漏:程序运行过程中会不断地分配空间;那些不再使用的内存空间应该即时回收它们,从而保证系统可以再次使用这些内存。如果存在无用的内存没有被回收回来,那就是内存泄漏。
33.能不能自己写个类,也叫java.lang.String?
答:可以,但在应用的时候,需要用自己的类加载器去加载,否则,系统的类加载器永远只是去加载jre.jar包中的那个java.lang.String。
但在Tomcat的web应用程序中,都是由webapp自己的类加载器先自己加载WEB-INF/classes目录中的类,然后才委托上级的类加载器加载,如果我们在Tomcat的web应用程序中写一个java.lang.String,这时候Servlet程序加载的就是我们自己写的java.lang.String。但这样做就会出很多潜在的问题,原来所有用了java.lang.String类都将出现问题。
34.ArrayList如何实现插入的数据按自定义的方式有序存放?
答:编程思路:实现一个类对ArrayList进行包装,当程序试图向ArrayList中放入数据时,程序将先检查该元素与ArrayList集合中其他元素的大小,然后将该元素插入到指定位置。
35.序列化接口的id有什么用?
答:(对象的序列化:把对象转换为字节序列的过程.
对象的反序列化:把字节序列恢复为对象的过程。
对象的序列化主要有两种用途:
1. 把对象的字节序列永久地保存到硬盘上,通常放在一个文件中。
2. 在网络上传送对象的字节序列。
在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。)
反序列化Java对象时必须提供该对象的class文件,现在的问题是随着项目的升级,系统的class文件也会升级,Java如何保证两个class文件的兼容性?
Java序列化机制允许为序列化类提供一个UID,也就是说如果一个类升级后,只要它的id值保持不变,序列化机制也会把它们当成同一个序列化版本。
(序列化类的id是为了在进行项目升级时,保证系统的两个class文件的兼容性,不会导致重复和冲突。)
36.hashCode()方法的作用?
答:hashCode()方法与equals()方法类似。都允许用户定义子类重写这两个方法。
equals()主要判断两个对象表面上或者内容上看是否相同。
而hashCode方法的判断就要严格的多。
如果两个对象的hashCode()相等,即可认为这两个对象是相等的。
因此当我们重写一个类的equals()方法时,也应该重写它的hashCode()方法。而且这两个方法判断对象的标准也应该是一样的。
37.编写一个函数将十六进制的字符串参数转换成整数返回。
String str = “13abf”;
Int len = str.length;
Int sum = 0;
Integer.parseInt(str,16);
parseInt方法提供了把16进制字符串参数转换成整数返回的方法。
38.银行还款问题
银行贷款的还款方式中最常用的是一种叫“等额本息”,还款法,即借款人在约定还款期限内的每一期(月)归还的金额(产生的利息+部分本金)都是相等的,现有一笔总额为T元的N年期住房贷款,年利率为R,要求算出每一期的还款的本金和利息总额,请写出解决思路和任意一种编程语言实现的主要代码。
思路:既然是按月还款,那我就要将N年按月来计算,即要还N*12个月,这样就可以求出每月要还的本金。由于每月要还的那部分本金所欠的时间不同,所以,它们所产生的利息是不同的,该部分本金的利息为:部分本金额*所欠月数*月利率。应该是这么个算法,如果利息还计利息,如果月还款不按年利率来算,老百姓算不明白的。
”之类,输出它们所有的排列组合
答:String str = "fkjav";
char[] arr1 = str.toCharArray();
char[] arr2 = java.util.Arrays.copyOf(arr1,arr1.length);
for(int i=0;i<arr1.length-1;i++)
{
for(int j = i+1;j<arr2.length;j++){
System.out.println(arr1[i] + "," + arr2[j]);
}
}
40.构造器Constructor是否可被override?
答:构造器Constructor不能被继承,因此不能重写Override,但可以被重载Overrload(形参列表不同)。
41.接口是否可以继承接口?抽象类是否可以实现(implements)接口?抽象类是否可以继承具体类(concrete class)?抽象类中是否可以有静态的main方法?
答:接口可以继承接口。
抽象类可以实现接口。
抽象类可以继承具体类。
抽象类中可以有静态的main方法。
归法:抽象类的特征是有得有失,得到的功能是抽象类可以拥有抽象方法(当然也可以没有);失去的功能是抽象类不能创建实例了。至于其他的,抽象类与普通类在语法上大致是一样的。
(抽象类除了不能创建实例,只要普通类可以的它都可以。)
42.写clone()方法时,通常都有一行代码,是什么?
答:clone故名思意就是复制,当对象调用此方法,就会在内存中分配一个和源对象同样大小的空间,在这个空间中创建一个新的复制对象。
clone有缺省(默认)行为,super.clone();
因为首先要把父类中的成员复制到位,然后才是复制自己的成员。
43.abstract class 和interface有什么区别?
答:抽象类和接口的语法区别:
接口可以说成是抽象类的一种特例,接口中所有方法都必须是抽象的。
1.一个类可以实现多个接口,但只能继承一个抽象类。
1.接口是彻底的抽象类。抽象类可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的。
2.抽象类可以有构造器,接口中不能有构造器。
3.抽象类中可以有实例成员变量,接口中没有实例成员变量。
4.抽象类中的抽象方法的访问类型可以是public , protected和默认访问权限。但接口中的抽象方法只能是public类型的。
5.抽象类中可以包含static静态方法,接口中不能包含static静态方法。
6.抽象类和接口都可以包含静态成员变量,但抽象类的静态成员变量的访问类型可以是任意,而接口的静态成员变量的访问类型只能是public static final
两者在应用上的区别:
接口体现的是一种规范,而抽象类在代码实现方面发挥作用,可以实现代码的重用。
例如,模板设计模式是抽象类的一个典型应用。假设项目中需要大量的DAO组件,这些组件通常都有增,删,改,查等基本方法。因此我们就可以定义一个抽象的DAO基类,然后让其它DAO组件来继承这个抽象的DAO基类,把这个DAO基类当成模板使用。
44.abstract抽象的method方法是否可同时是static,是否可同时是native(原生态方法,方法对应的实现不在当前文件,而是在其他语言(c,c++)实现的文件中),是否可同时是synchronized同步的?
答:抽象的方法不可以是static的,因为抽象方法要被子类实现。
native方法表示该方法要用另一种依赖平台的编程语言实现,不存在着被子类实现的问题,所以,它也不能是抽象的。
Synchronized同步锁与abstract不能同时使用,它修饰一个方法时,表示将会使用该方法的调用者作为同步监视器。但abstract方法所在的类是抽象类,不能创建实例,因此无法确定synchronized修饰方法时的同步监视器。
45.什么是内部类?Static Nested Class(静态嵌入类型)和 Inner Class(插入类型)的不同。
答:内部类就是一个在类的内部定义的类,内部类中不能定义静态成员。
静态成员不能访问非静态成员, 因此静态内部类不能访问外部类的非静态成员。
如果内部类使用了static修饰,该内部类就是静态内部类,也就是所谓的static nested class。
如果内部类没有使用inner修饰,它就是Inner class。
除此之外,还有一种局部内部类,在方法中定义的内部类就是局部内部类,局部内部类只在方法中有效。
对于Inner class而言,它属于实例成员,因此Inner class的实例必须寄生在外部类的实例中,因此程序在创建Inner Class实例之前,必须先获得它所寄生的外部类的实例。否则程序无法创建Inner Class的实例。
46.内部类可以引用它的外部类的成员吗?有没有什么限制?
答:内部类可以访问所在外部类的成员。
但是由于Java中,静态成员不能访问非静态成员,因此静态内部类(属于静态成员)就不能访问外部类的非静态成员。
47.Anonymous Inner Class(匿名内部类)是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)?
答:匿名内部类可以继承其他类或实现其它接口。
鉴于匿名内部类的特殊语法
new 父类、接口(){
类体实现部分
}
所以匿名内部类由于语法原因必须要实现父类或接口。
48.super.getClass()方法调用
下面程序的输出结果是多少?
import java.util.Date;
public class Test extends Date{
public static void main(String[] args) {
new Test().test();
}
public void test(){
System.out.println(super.getClass().getName());
}
}
输出结果:Test。
Super是一个限定词,当用super引用时,它也是引用当前对象本身,只是super限定了访问当前对象从父类那里得到成员变量和方法。
如果要访问父类的类名,应该使用如下语法:
Super.getClass().getSupserClass().getName();
49.JDK中哪些类是不可继承的?
答:不能继承的类是用final关键字修饰的类。
使用final关键字修饰的类,可以阻止被继承,不会有子类来重写该类的方法,完全封闭起来,因此更安全。
50.String s = “Hello”;
s = s + “world!”;这两行代码执行后,原始String对象中的内容到底变了没有?
答:没有。String被设计成是一个不可变类。当第2行代码执行以后,s引用变量指向了一个新的对象,原来的内容为”Hello”直接量对象被存放在内存中。
建议使用StringBuffer或StringBuilder类。
51.是否可以继承String类?
答:String类是final类,被final关键字修饰,所以不能被继承。
52.如何把一段逗号分割的字符串转换成一个数组?
答:可以使用String.split(“,”)方法使用String[]数组接收。
如:String [] result = "this,is,a,test".split(",");
其中result数组中存放了this、is、a、test等字符串元素。
53.下面这条语句一共创建了多少个对象:String s=”a”+”b”+”c”+”d”;
答:一个String对象。Java会在编译时对字符串相加进行优化处理,在编译时发现所有参与运算的都是字符串直接量,Java会把这个表达式的值计算出来。直接将该表达式的结果赋值给字符串引用变量。
对于如下代码:
String s1=”a”;
String s2=s1 + “b”;//对象+字符串=新的对象,和字符串常量池中的直接量没有关系
String s3=”a” + “b”;
System.out.println(s2 == “ab”);//false
System.out.println(s3 == “ab”);//true
字符串直接量会被放入字符串缓冲池中。
如下两行代码:
String s = “a” + “b” + “c” + “d”;
System.out.println(s == “abcd”);//true
由于s引用了字符串缓冲池中的”abcd”字符串,因此上面的输出结果为true。
54.Collection集合框架中实现比较要实现什么接口。
答:Java集合框架中需要比较大小的集合包括TreeMap、TreeSet,其中TreeMap会根据key-value对中key的大小进行排序,而TreeSet则会对集合元素进行排序。
集合框架中比较大小有2种方式:
A. 自然排序
要求TreeMap中的所有key都实现Comparable接口。JDK很多类都已经实现了Comparable接口,如String,Date,BigDecimal等。
B. 定制排序
需要在创建TreeMap或TreeSet时传入一个Comparator对象,Comparator只是一个接口,因此创建Comparator对象只能是创建它的实现类的对象,所以需要实现Comparator接口。
55.ArrayList和Vector的区别?
答:
Collection是接口,Collections是工具类。
相同点:这两个类都实现了List接口,List接口继承了Collection接口,它们都是有序集合。
(我们可以按位置索引号取出某个元素,并且其中的数据是允许重复的,这是List集合规范制定的)
而且ArrayList与Vector底层都是基于数组的,因此它们的实现代码也大致相似。
区别:在于Vector是一个古老的集合,从JDK1.0开始就有了,因此它包含了大量方法名很长的方法。JDK1.2开始引入集合框架,引入List接口,才让Vector实现了List接口,因此又增加了一些List接口中定义的方法。
总体来说,ArrayList可以完全替代Vector,除了在一些很古老的API中强制要求使用Vector之外。
Vector还有一个特征,它是线程安全的,因此性能较差,而ArrayList并不是线程安全的,因此性能较好。
实际上即使我们要在多线程环境下使用List集合,也应该选择ArrayList而不是Vector,因为Java还提供了一个Collections工具类,它可以把ArrayList包装成线程安全的集合类,例如如下代码:
List list = Collections.synchronizedList(new ArrayList());
56.HashMap和HashTable的区别
答:HashMap和HashTable的区别类似于ArrayList与Vector的区别。
HashTable与Vector都是JDK1.0就有一个古老的集合,HashTable是一个继承自Dictionary(字典)类的古老集合。
从JDK1.2引入集合框架的Map接口之后,Java让HashTable也实现了Map接口,因此HashTable也新增实现了一些Map接口中定义的方法。
实际上HashMap与HashTable底层的实现很相似,它们都是基于Hash表的实现。
HashMap与HashTable的区别主要有2点:
A. HashMap允许null作为key或value,而HashTable不允许。
B. HashMap是线程不安全的,因此性能较好。HashTable是线程安全的,因此性能较差。
实际上在多线程环境下,Java提供了Collection工具类把HashMap包装成线程安全的类,因此依然应该使用HashMap,如下代码所示:
Map map = Collections. synchronizedMap(new HashMap());
简单的说,编程时应该尽量避免使用HashTable,它与HashMap的底层实现很相似,但除非在一个古老的API中强制要求HashTable。
57.List和Map的区别?
答:List集合的特点:可重复,有序,有索引,程序可以通过元素的索引来读取元素,因此List相当于一个动态数组。
Map集合的特点:有Key-Value对,不允许重复,无序,无索引,程序可以根据key来取出对应的Value。
深入阐述:如果换个角度来看,我们可以把List当成Map来看,List相当于key都是数值的Map,程序通过元素的索引来读取List集合时,完全可以当成Map根据key来读取value。从另一个角度看,Map也可以当成元素索引可以是任意类型的List集合。
58.List,Set,Map是否继承自Collection接口?
答:List,Set集合继承自Collection接口,Map集合不少继承自Collection接口。
59.List,Map,Set三个接口,存取元素时,各有什么特点?
答:List集合:可重复,有序,有索引,实现类,ArrayList
Set集合:不允许重复,无序,无索引
对于Map集合而言,其底层存,取性能与Set集合完全一样。其实Set集合本身就是基于Map实现的,除了有键值对以外。
60.说出ArrayList,Vector,LinkedList的存储性能和特性。
答:ArrayList和Vector都是使用数组方式存储数据,Vector由于使用了synchrnized(线程安全)的方法,因此性能较ArrayList差,而LinkedList使用了双向链表实现存储,所以插入速度较快,而且线程也是不安全的。LinkedList提供了一些方法,使得LinkedList可以被当作堆栈和队列来使用。
实际上Java提供了Collection集合的工具类,它可以把ArrayList,vector包装成线程安全的集合,因此在实际编程中应该避免使用Vector集合,它是一个古老的,在JDK1.0时代就出现的集合。
61.去掉一个Vector集合中重复的元素。
最简单的方式:HashSet set = new HashSet(vector);
基于HashSet不允许重复的特性。
62.Set里的元素是不能重复的,那么用什么方法来区分重复与否呢?是用==还是equals()?它们有何区别?
答:Set只是一个接口,它的不同实现类判断元素是否相等的标准是不同的。笼统地说,Set里的元素是不能重复的,判断元素重复应该使用equals方法而不是==。
对于HashSet而言,判断两个对象是否相等是通过equals()方法和hashCode()方法,只要两个对象通过equals()比较返回false,或两个对象的hashCode值不一样,那么HashSet就会把它们当成不相同的元素。
对于TreeSet而言,判断两个对象是否相等的唯一标准是:两个对象通过compareTo(Object obj)比较是否返回0,与equals()方法无关。
63.你所知道的集合类都有哪些?主要方法?
答:最常用的集合接口是Set,List,Queue,它们都是Collection的子接口,除此之外还有Map接口。
对于Set集合而言,它的常用实现类包括HashSet与TreeSet,HashSet还有一个子类,LinkedHashSet。
对于List集合而言,它的常用实现类包括ArrayList,Vector与LinkedList。
对于Queue集合而言,它有一个子接口,代表双端队列,Deque,它的常用实现类包括ArrayDeque,LinkedList。
对于Map集合而言,它的常用实现类是HashMap,TreeMap,HashMap还有一个子类,LinkedHashMap。
至于这些集合的方法,由于集合类也就是“容器类”,因此它的方法无非就是向容器中添加,删除,取出,遍历元素的方法。
List集合的元素都是有序,有索引的,因此它包括了大量根据索引来添加,删除,取出集合元素的方法。
对于Deque集合而言,由于它是双端队列,既可以当成队列使用,也可以当成栈使用,因此它增加栈,队列的方法,压栈和弹栈,如push,pop,offer,peek等。
对于Map集合而言,它的方法无非就是根据key来添加,删除,取出value的方法。
64.两个对象值相同(x.equals(y)==true),但却可以有不同的hash code,这句话对不对?
答:对。
因为equals方法和hashcode方法都可以由开发者来重写,因此它们是否相等并没有必然的关系。
如果对象要保存在HashSet或者HashMap中,它们的equals相等,那么hashcode返回值也应该相等。
65.TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用子类的compareTo方法,还是抛异常?
答:当前正在添加父类对象就多次调用父类对象的CompareTo方法,当前正在添加子类对象就多次调用子类对象的CompareTo方法。
至于程序是否抛出异常,则取决于CompareTo方法的实现,如果子类在实现CompareTo方法时,试图把被比较对象转换为子类对象之后再比较,如果TreeSet集合中已经包括了父类对象,这就会引起ClassCastException类型转换异常。
66.说出一些常用的类,包,接口,请各举5个。
答:常用的包有:
java.lang包下包括Math,System,StringBuilder,StringBuffer,Runtime,Thread,Runnable等
(Math数学,System系统,StringBuilder/StringBuffer,运行时类,线程类)
java.util包下包括List,Set,Map,以及这些接口的常用实现类,ArrayList,LinkedList,HashSet,TreeSet,HashMap,TreeMap等
java.io包下包括
(字节流,字符流,缓冲流)
InputStream,OutputStream,Reader,Writer,FileInputStream,FileOutputStream,FileReader,FileWriter,BufferInputStream,BufferOutputStream,BufferReader,BuffWriter等
Java.sql包下包括Connection,Statement,PrepareStatement,ResultSet等(创建连接对象,编译,预编译语句,结果集)
Java.net包下包括
Socket,ServerSocket,URL,URLConnection,DatagramPacker,DatagramSocket等
(Socket套接字)
67.java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以继承,请说出它们分别是那些类?
答:字节流和字符流,字节流由InputStream,OutputStream派生出来,字符流由Reader,Writer派生出来。在java.io包中还有许多其它的流,主要是为了提高性能和使用方便。
68.字符流和字节流的区别。
答:字节流和字符流的区别是,它们的用法几乎完全一样,区别在于字节流和字符流所操作的位的字符。
节流主要由InputStream,OutputStream作为基类,字符流主要由Reader,Writer作为基类。
字节流是基于字节输入,输出的,因此它的适用性更广。字符流则在处理文本内容时的输入,输出更加方便——不会出现只读取半个字符的情况。
Java提供了将字节流转换为字符串的InputStreamReader , OutputStreamWriter,但没有提供将字符流转化为字节流的方法。因为,字节流比字符流的使用范围更广,但字符流在处理字符的时候比字节流操作方便。如果有一个字节流,我们知道它的内容是文本内容,因此把它转换成字符流来处理就会更方便一些。各有所需。
69.什么是Java序列化,如何实现Java序列化?或者请解释Serializable接口的作用。
答:序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象,对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,持久保存到磁盘上,通过网络将这种二进制流传输到另一个网络节点。其它程序一旦获得了这种二进制流,无论是从磁盘中获取还是通过网络获取,都可以将这种二进制流恢复成原来的Java对象。
Serializable接口只是一个标记接口,实现该接口无需实现任何方法,实现了该接口的类就是可序列化的类。
序列化在Web开发中十分重要,Tomcat在某些时候需要把保存在Session中的对象序列化到硬盘,因此放入Session中的对象必须是可序列化的,这就可以通过Serializable接口来实现。还有,如果一个对象要经过网络传输(比如RMI远程方法调用的形参或返回值),这个对象也应该是可序列化的。
70.描述一下JVM加载class文件的原理机制?
答:
当程序主动使用某个类,如果该类还未必加载到内存中,系统会通过3个步骤来对该类进行初始化。加载,连接,初始化。
类加载指的当程序中使用任何类时,系统都会为之建立一个java.lang.Class类对象。
类的加载由加载器完成,类加载器通常由JVM提供,除此之外,开发者可以通过继承ClassLoader基类来创建自己的类加载器。
71.heap和stack有什么区别?
答:stack指的是栈区,当程序进入一个方法时,系统会专门为这个方法分配一个内存空间,这块内存空间也被称为该方法栈区,该方法的栈区专门用于存储该方法中定义的局部变量,(括基本类型变量和引用变量)。当这个方法结束时,该方法栈区将会自动被销毁,栈区中的所有局部变量也会被销毁。
heap指的是堆,堆内存是Java虚拟机拥有的内存区。所有的Java对象都被放在堆内存中,位于堆内存中的Java对象由系统垃圾回收器负责跟踪管理——垃圾回收(GC),当堆内存中的对象没有引用变量引用它时,这个Java对象就变成了垃圾,垃圾回收器会在合适的时候回收它
72.try{}里有一个return语句,那么紧跟在这个try后的finally{}里的code会不会被执行,什么时候被执行,在return前还是return后?
答:肯定会执行,在return后执行。
Finally{}块的代码只有在try{}块中包含遇到System.exit(0;之类的导致Java虚拟机直接退出的语句才会不执行。
当程序执行try{}遇到return时,程序会先执行return语句,但并不会立即返回,而是把执行流程转到finally块,完成finally块的执行后就直接返回刚才return语句已经准备好的结果。
73.下面程序代码输出的结果是多少?
public class smallT
{
public static void main(String args[])
{
smallT t = new smallT();
int b = t.get();
System.out.println(b);
}
public int get()
{
try
{
return 1 ;
}
finally
{
return 2 ;
}
}
}
输出结果是:2
Java会把return语句先执行完,把所有需要处理的东西都先处理完成,但是还未返回之前,程序流程会转去finally块。但如果在执行finally块时遇到了return语句,那么程序会直接使用finally块中的return语句来返回,因此上面程序会输出2。
74.final,finally,finalize的区别。
答:final最终的,finally最后,finalize完成。
Final是一个修饰符,它可以修饰类,方法,变量。
(final修饰类表明这个类不可以被继承,final修饰方法时表明这个方法不可以被其子类重写(重写就是覆盖Override.final,final修饰变量可分为局部变量,实例变量,静态变量。)
当final修饰局部变量时,该局部变量可以被赋值一次。当final修饰实例变量时,实例变量必须由程序在构造器,初始化块,”定义时” 这3个位置的其中之一指定初始值。当final修饰静态变量时,静态变量必须由程序在静态初始化块,定义时这2个位置的其中之一指定初始值。
Finally是异常处理语句结构的一部分,表示总会执行的代码块。
Finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法。但实际上重写此方法进行资源回收并不安全,因为JVM并不保证该方法总被调用。
75.运行时异常与一般异常有何异同?
答:Checked检查异常就是一般异常,体现了Java的设计哲学,没有完善错误处理的代码根本不会被执行。
一般异常的处理方式有2种,
A. 当前方法知道如何处理异常,程序应使用try…catch块来捕获异常,然后在对应的catch块中修补该异常。
B. 当前方法不知道如何处理异常,应该在定义方法时声明抛出该异常。Throw Exception
Runtime异常对比处理更加灵活,Runtime异常无需显式声明抛出,如果程序需要捕捉Runtime异常,也可以使用try…catch块来捕捉runtime异常。
76.error和exception有什么区别?
答:Error错误,一般是指虚拟机相关的问题,如系统崩溃,虚拟机出错,动态连接失败等。这种错误无法恢复或不能被捕获,将导致应用程序中断。
Exception异常,表示一种设计或实现问题。也就是说,程序员应该对这些情况进行考虑,并提供相应的处理。
77.Java中的异常处理机制的简单原理和应用。
答:程序运行过程中可能出现各种“非预期“情况,这种非预期情况可能导致程序非正常结束。为了提高程序的健壮性,Java提供了异常处理机制。
Java对异常进行了分类,不同类型的异常分别用不同的Java类表示,所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error错误和Exception异常,Error错误一般是指虚拟机相关的问题,如系统崩溃,虚拟机出现错误,动态连接失败等。这种错误无法恢复或不能捕获,将导致应用程序中断。通常应用程序无法处理这种错误,因此不能使用try…catch{}块来捕获Error对象。
Exception表示一种设计或实现问题,也就是说,程序员应该对这些情况进行考虑,并提供相应的处理。
异常分为运行时异常和一般检查异常,一般检查异常体现了Java的设计哲学,没有完善错误的代码根本就不会被执行。
对于一般检查异常的处理方式有两种:
A.当前方法知道如何处理异常,程序应使用try…catch块来捕获异常,然后在对应的catch块中修补该异常。
B.当前方法不知道如何处理异常,应该在定义方法时声明抛出该异常。
实际上Java的一般异常后来争议不断,因为一般异常要求程序员要么显式声明抛出,要么进行捕捉,不能对一般异常不闻不问,这就给编程带来了一定的复杂度。
比如Spring,Hibernate框架的一大特点就是把一般异常包装成了运行时异常。
运行时异常则比较灵活,开发者既可以选择捕获运行时异常,也可以不捕获。
78.请写出你最常见到的5个runtime exception.
答:对于一个有1—2年左右编程经验的人来说,总会遇到一些常见的异常。
其中有些就是
Runtime Exception运行时异常。
NullPointerException空指针异常:当调用一个未初始化的引用变量的实例Field,实例方法时都会引发该异常。
ArithmeticException算术异常:比如5/0将引发异常。
ArrayIndexOutOfBoundsException数组索引越界异常。
ClassCastException类型转换异常。
IllegalArgumentException参数非法的异常。
79.Java语言如何进行异常处理,关键字:throws ,throw ,try ,catch ,finally分别代表什么意义?在try块中可以抛出异常吗?
答:try块表示程序正常的业务执行代码。如果程序在执行try块的代码时出现了“非预期”情况,JVM将会生成一个异常对象,这个异常对象将会被后面相应的catch块捕获。
Throw用于手动抛出异常对象。Throw后面需要一个异常对象。
Throws用于在方法签名中声明抛出一个或多个异常类,throws关键字后可以紧跟一个或多个异常类。
Finally块代表异常处理流程中总会执行的代码块。
对于一个完整的异常处理流程,try块是必须的,try块后可以紧跟一个或多个catch块,最后还可以带一个finally块。
Try块中可以抛出异常。
80.Java中有几种方法可以实现一个线程?用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用?
答:在java5以前,有如下两种
A.继承Thread类,重写它的run()方法。
代码如下:
new Thread()
{
public void run()
{
//线程执行体
}
}.start();
C. 实现Runnable接口,并重写它的run()方法。
new Thread(new Runnable()
{
public void run()
{
//线程执行体
}
}).start();
从上面代码不难看出,线程的执行体是一个run()方法,然后程序通过start()方法启动一条线程。
从Java5开始,Java提供了第三种方式来创建多线程:实现Callable接口,并实现Call()方法。Callable接口相当于Runnable接口的增强版,因为Callable接口中定义的call()方法既拥有返回值,也可以声明抛出异常。
代码如下:
new Thread(new FutureTask<Object >(new Callable<Object>()
{
public Object call() throws Exception
{
//线程执行体
}
})).start();
不仅如此,Java5还提供了线程支持,ExecutorService对象就代表了线程池,如果开发者利用ExecutorService来启动线程,ExecutorService底层会负责管理线程池。此时,开发者只要把Runnable对象传给ExecutorService即可。如下代码:
ExecutorService pool = Executors.newFixedThreadPool(3)
pool.execute(new Runnable()
{
public void run()
{
//线程执行体
}
});
如果执行通过Callable方式实现的线程,则可按如下代码:
ExecutorService pool = Executors.newFixedThreadPool(3)
pool.execute(new FutureTask<Object >(new Callable<Object>()
{
public Object call() throws Exception
{
//线程执行体
}
}));
用synchronized关键字修饰同步方法。需要指出的是,非静态的同步方法的同步监视器是this,也就是调用该方法的对象,而静态的同步方法的同步监视器是该类本身。因此使用synchronized修饰的静态方法,非静态方法的同步监视器并不相同,只有基于同一个同步监视器的同步方法,同步代码块才能实现同步。
使用stop()停止方法不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。
Suspend()延迟方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被“挂起”的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
82.sleep()和wait()又什么区别?
答:sleep()是Thread类的静态方法,它的作用是让当前线程从运行状态转入,阻塞状态,线程执行暂停下来,当一个线程通过sleep()方法暂停之后,该线程并不会释放它对同步监视器的加锁。
Wait()是Object对象的方法,但实际上只有同步监视器才能调用该方法。当程序在同步代码块,或同步方法内通过同步监视器调用该方法时,将会导致当前线程释放对该同步监视器的加锁,而该线程则会进入该同步监视器的等待池中,直到该同步监视器调用notify()(通知)或notifyAll()来通知该线程。
83.同步和异步有何异同,在什么情况下分别使用它们?举例说明。
答:块,但多个线程都试图取款800块时,这些线程同时判断余额之后,都会显示余额足够,从而导致每个线程都取款成功。这显然不是我们希望看到的结果。
当程序试图执行一个耗时操作时,程序不希望阻塞当前执行流,因此程序也不应该试图立即获取该耗时操作返回的结果,此时就使用异步编程了,典型的应用场景就是Ajax。当浏览器通过JavaScript发出一个异步请求之后,JavaScript执行流并不会停下来,而是继续向下执行,这就是异步。程序会通过监听器来监听远程服务器响应的到来。
84.多线程有几种实现方法?同步有几种实现方法?
答:
多线程的实现方法:
在Java5以前,有如下两种:
第一种,继承Thread类,重写它的run()方法。
第二种,实现Runnable接口,并重写它的run()方法。
线程的执行体是一个run()方法,然后程序通过start()方法启动一条线程。
从Java5开始,Java提供了实现Callable接口来创建多线程,并实现call()方法。Callable接口相当于Runnable接口的增强版,因为Callable接口中定义的call()方法既拥有返回值,也可以声明抛出异常。
不仅如此,Java5还提供了线程支持,ExecutorService对象就代表了线程池。开发者只要把Runnable对象,传给ExecutorService即可。
同步的实现方法:
同步方法:使用synchronized关键字修饰方法,由于java的每一个对象都有一个内置锁,在调用该方法前需要获得内置锁,否则就处于阻塞状态。
同步代码块:有synchronized关键字修饰的语句块。
使用特殊域变量volatile实现线程同步,需要同步的变量加上volatile
使用重入锁实现线程同步,创建一个ReentrantLock的实例,获得锁lock(),释放锁unlock()。
使用局部变量实现线程同步,如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
注:ThreadLocal与同步机制
A.ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题。
B.前者采用以“空间换时间”的方法,后者采用以“时间换空间”的方式。
85.启动一个线程是用run()还是start()?
答:启动一个线程是调用start()方法,使线程就绪状态,以后可以被调度为运行状态。run()方法是线程的线程执行体——也就是线程将要完成的事情。
86.当线程进入一个对象的一个synchronized方法后,其他线程是否可以进入此对象的其他方法?
答:当线程进入一个对象的synchronized方法之后,其他线程完全有可能再次进入该对象的其他方法。
不过要分几种情况来看:
1. 如果其他方法没有使用synchronized关键字修饰,则可以进入。
2. 如果当前线程进入的synchronized方法是static方法,其他线程可以进入其他synchronized修饰的非静态方法,如果当前线程进入的synchronized方法是非static方法,其他线程可以进入其他synchronized方法。
3. 如果两个方法都是静态方法,或者都是非静态方法,并且都使用了synchronized修饰,但只要在该方法内部调用了同步监视器的wait(),则其他线程依然可以进入其他使用synchronized方法修饰的方法。
4. 如果没有调用wait(),其他线程不能进入其他使用synchronized方法修饰的方法。
87.线程的基本概念,线程的基本状态以及状态之间的关系。
答:线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程可以拥有自己的堆栈,自己的程序计数器和自己的局部变量,但不再拥有系统资源,它与父进程的其他线程共享该进程所拥有的全部资源。因为多个线程共享父进程里的全部资源,因此编程更加方便;但必须更加小心,我们必须确保线程不会妨碍同一进程里的其他线程。
线程的执行需要经过如下状态:
新建线程new
就绪start()
运行
阻塞sleep()
死亡stop()
各状态的转换关系如下图所示:
88.简述synchronized和java . util .concurrent .locks .Lock的异同
答:主要相同点:Lock能完成synchronized所实现的所有功能。
主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。Synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。Lock还有更强大的功能,它的tryLock方法可以非阻塞方式去拿锁。
Java代码查错部分
1.
abstract class Name {
private String name;
public abstract boolean isStupidName(String name) {}
}
答:错。abstract方法必须以分号;结尾,而且不带花括号{}
2.
public class Something {
void doSomething () {
privateString s = "";
int l = s.length();
}
}
有错吗?
答:错。局部变量前不能放置任何修饰符(private,public,和protected),final可以用来修饰局部变量。