⾃动化中Java⾯试题
1、⾯向对象的特征有哪些⽅⾯?
答:⾯向对象的特征主要有以下⼏个⽅⾯:
- 抽象:抽象是将⼀类对象的共同特征总结出来构造类的过程,包括数据抽象和⾏为抽象两⽅⾯。抽象只关注对象有哪些属性和⾏为,并不关注这些⾏为的细节是什么。
- 继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为⽗类(超类、基类);得到继承信息的类被称为⼦类(派⽣类)。继承让变化中的软件系统有了⼀定的延续性,同时继承也是封装程序中可变因素的重要⼿段(如果不能理解请阅读阎宏博⼠的《Java与模式》或《设计模式精解》中关于桥梁模式的部分)。
- 封装:通常认为封装是把数据和操作数据的⽅法绑定起来,对数据的访问只能通过已定义的接⼝。⾯向对象的本质就是将现实世界描绘成⼀系列完全⾃治、封闭的对象。我们在类中编写的⽅法就是对实现细节的⼀种封装;我们编写⼀个类就是对数据和数据操作的封装。可以说,封装就是隐藏⼀切可隐藏的东西,只向外界提供最简单的编程接⼝(可以想想普通洗⾐机和全⾃动洗⾐机的差别,明显全⾃动洗⾐机封装更好因此操作起来更简单;我们现在使⽤的智能⼿机也是封装得⾜够好的,因为⼏个按键就搞定了所有的事情)。
- 多态性:多态性是指允许不同⼦类型的对象对同⼀消息作出不同的响应。简单的说就是⽤同样的对象引⽤调⽤同样的⽅法但是做了不同的事情。多态性分为编译时的多态性和运⾏时的多态性。如果将对象的⽅法视为对象向外界提供的服务,那么运⾏时的多态性可以解释为:当A系统访问B系统提供的服务时,B系统有多种提供服务的⽅式,但⼀切对A系统来说都是透明的(就像电动剃须⼑是A系统,它的供电系统是B系统,B系统可以使⽤电池供电或者⽤交流电,甚⾄还有可能是太阳能,A系统只会通过B类对象调⽤供电的⽅法,但并不知道供电系统的底层实现是什么,究竟通过何种⽅式获得了动⼒)。⽅法重载(overload)实现的是编译时的多态性(也称为前绑定),⽽⽅法重写(override)实现的是运⾏时的多态性(也称为后绑定)。运⾏时的多态是⾯向对象最精髓的东西,要实现多态需要做两件事:1). ⽅法重写(⼦类继承⽗类并重写⽗类中已有的或抽象的⽅法);2). 对象造型(⽤⽗类型引⽤引⽤⼦类型对象,这样同样的引⽤调⽤同样的⽅法就会根据⼦类对象的不同⽽表现出不同的⾏为)。
2、访问修饰符public,private,protected,以及不写(默认)时的区别?
答:
修饰符当前类同包⼦类其他包
public√√√√
protected√√√×
default√√××
private√×××
类的成员不写访问修饰时默认为default。默认对于同⼀个包中的其他类相当于公开(public),对于不是同⼀个包中的其他类相当于私有(private)。受保护(protected)对⼦类相当于公开,对不是同⼀包中的没有⽗⼦关系的类相当于私有。Java中,外部类的修饰符只能是public或默认,类的成员(包括内部类)的修饰符可以是以上四种。
3、String 是最基本的数据类型吗?
答:不是。Java中的基本数据类型只有8个:byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type)和枚举类型(enumeration type),剩下的都是引⽤类型(reference type)。
4、float f=3.4;是否正确?
答:不正确。3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-castin
g,也称为窄化)会造成精度损失,因此需要强制类型转换float f =(float)3.4;或者写成float f =3.4F;。
5、short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?
答:对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型。⽽short s1 = 1; s1 += 1;可以正确编译,因为s1+= 1;相当于s1 = (short)(s1 + 1);其中有隐含的强制类型转换。
6、Java有没有goto?
答:goto 是Java中的保留字,在⽬前版本的Java中没有使⽤。(根据James Gosling(Java之⽗)编写的《The Java Programming Language》⼀书的附录中给出了⼀个Java关键字列表,其中有goto和const,但是这两个是⽬前⽆法使⽤的关键字,因此有些地⽅将其称之为保留字,其实保留字这个词应该有更⼴泛的意义,因为熟悉C语⾔的程序员都知道,在系统类库中使⽤过的有特殊意义的单词或单词的组合都被视为保留字)
7、int和Integer有什么区别?
答:Java是⼀个近乎纯洁的⾯向对象编程语⾔,但是为了编程的⽅便还是引⼊了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java为每⼀个基本数据类型都引⼊了对应的包装类型(
wrapper class),int的包装类就是Integer,从Java 5开始引⼊了⾃动装箱/拆箱机制,使得⼆者可以相互转换。
Java 为每个原始类型提供了包装类型:
- 原始类型: boolean,char,byte,short,int,long,float,double
- 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
1 2 3 4 5 6 7 8 9 10class AutoUnboxingTest {java面向对象的特征有哪些方面
public static void main(String[] args) {
Integer a = new Integer(3);
Integer b = 3; // 将3⾃动装箱成Integer类型
int c = 3;
System.out.println(a == b); // false 两个引⽤没有引⽤同⼀对象System.out.println(a == c); // true a⾃动拆箱成int类型再和c⽐较}
}
最近还遇到⼀个⾯试题,也是和⾃动装箱和拆箱有点关系的,代码如下所⽰:
1 2 3 4 5 6 7 8 9public 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);
}
}
如果不明就⾥很容易认为两个输出要么都是true要么都是false。⾸先需要注意的是f1、f2、f3、f4四个变量都是Integer对象引⽤,所以下⾯的==运算⽐较的不是值⽽是引⽤。装箱的本质是什么呢?当我们给⼀个Integer对象赋⼀个int值的时候,会调⽤Integer类的静态⽅法valueOf,如果看看valueOf的源代
码就知道发⽣了什么。
1 2 3 4 5public static Integer valueOf(int i) {
if(i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
IntegerCache是Integer的内部类,其代码如下所⽰:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44/**
* Cache to support the object identity semantics of autoboxing for values between * -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static{
/
/ high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.SavedProperty("java.lang.Integer.IntegerCache.high");
if(integerCacheHighPropValue != null) {
try{
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
/
/ If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
简单的说,如果整型字⾯量的值在-128到127之间,那么不会new新的Integer对象,⽽是直接引⽤常量池中的Integer对象,所以上⾯的⾯试题中f1==f2的结果是true,⽽f3==f4的结果是false。
提醒:越是貌似简单的⾯试题其中的⽞机就越多,需要⾯试者有相当深厚的功⼒。
8、&和&&的区别?
答:&运算符有两种⽤法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。逻辑与跟短路与的差别是⾮常巨⼤的,虽然⼆者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进⾏运算。很多时候我们可能都需要⽤&&⽽不是&,例如在验证⽤户登录时判定⽤户名不是null⽽且不是空字符串,应当写为:username != null &&!username.equals(“”),⼆者的顺序不能交换,更不能⽤&运算符,因为第⼀个条件如果不成⽴,根本不能进⾏字符串的equals⽐较,否则会产⽣NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。
补充:如果你熟悉JavaScript,那你可能更能感受到短路运算的强⼤,想成为JavaScript的⾼⼿就先从
玩转短路运算开始吧。
9、解释内存中的栈(stack)、堆(heap)和静态区(static area)的⽤法。
答:通常我们定义⼀个基本数据类型的变量,⼀个对象的引⽤,还有就是函数调⽤的现场保存都使⽤内存中的栈空间;⽽通过new关键字和构造器创建的对象放在堆空间;程序中的字⾯量(literal)如直接书写的100、”hello”和常量都是放在静态区中。栈空间操作起来最快但是栈很⼩,通常⼤量的对象都是放在堆空间,理论上整个内存没有被其他进程使⽤的空间甚⾄硬盘上的虚拟内存都可以被当成堆空间来使⽤。
1String str = new String("hello");
上⾯的语句中变量str放在栈上,⽤new创建出来的字符串对象放在堆上,⽽”hello”这个字⾯量放在静态区。
补充:较新版本的Java(从Java 6的某个更新开始)中使⽤了⼀项叫”逃逸分析”的技术,可以将⼀些局部对象放在栈上以提升对象的操作性能。
10、und(11.5) 等于多少?und(-11.5)等于多少?
答:und(11.5)的返回值是12,und(-11.5)的返回值是-11。四舍五⼊的原理是在参数上加0.5然后进⾏下取整。
11、switch 是否能作⽤在byte 上,是否能作⽤在long 上,是否能作⽤在String上?
答:在Java 5以前,switch(expr)中,expr只能是byte、short、char、int。从Java 5开始,Java中引⼊了枚举类型,expr也可以是enum类型,从Java 7开始,expr还可以是字符串(String),但是长整型(long)在⽬前所有的版本中都是不可以的。
12、⽤最有效率的⽅法计算2乘以8?
答: 2 << 3(左移3位相当于乘以2的3次⽅,右移3位相当于除以2的3次⽅)。
补充:我们为编写的类重写hashCode⽅法时,可能会看到如下所⽰的代码,其实我们不太理解为什么要使⽤这样的乘法运算来产⽣哈希码(散列码),⽽且为
什么这个数是个素数,为什么通常选择31这个数?前两个问题的答案你可以⾃⼰百度⼀下,选择31是因为可以⽤移位和减法运算来代替乘法,从⽽得到更好的
性能。说到这⾥你可能已经想到了:31 * num 等价于(num << 5) – num,左移5位相当于乘以2的5次⽅再减去⾃⾝就相当于乘以31,现在的VM都能⾃动完成这
个优化。
1 2 3 4 5 6 7public class PhoneNumber { private int areaCode;
private String prefix;
private String lineNumber;
@Override
public int hashCode() {
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
final int prime = 31;
int result = 1;
result = prime * result + areaCode;
result = prime * result
+ ((lineNumber == null) ? 0: lineNumber.hashCode());
result = prime * result + ((prefix == null) ? 0: prefix.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if(this== obj)
return true;
if(obj == null)
return false;
if(getClass() != Class())
return false;
PhoneNumber other = (PhoneNumber) obj;
if(areaCode != other.areaCode)
return false;
if(lineNumber == null) {
if(other.lineNumber != null)
return false;
} else if(!lineNumber.equals(other.lineNumber))
return false;
if(prefix == null) {
if(other.prefix != null)
return false;
} else if(!prefix.equals(other.prefix))
return false;
return true;
}
}
13、数组有没有length()⽅法?String有没有length()⽅法?
答:数组没有length()⽅法,有length 的属性。String 有length()⽅法。JavaScript中,获得字符串的长度是通过length属性得到的,这⼀点容易和Java混淆。
14、在Java中,如何跳出当前的多重嵌套循环?
答:在最外层循环前加⼀个标记如A,然后⽤break A;可以跳出多重循环。(Java中⽀持带标签的break和continue语句,作⽤有点类似于C和C++中的goto语句,但是就像要避免使⽤goto⼀样,应该
避免使⽤带标签的break和continue,因为它不会让你的程序变得更优雅,很多时候甚⾄有相反的作⽤,所以这种语法其实不知道更好)
15、构造器(constructor)是否可被重写(override)?
答:构造器不能被继承,因此不能被重写,但可以被重载。
16、两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?
答:不对,如果两个对象x和y满⾜x.equals(y) == true,它们的哈希码(hash code)应当相同。Java对于eqauls⽅法和hashCode⽅法是这样规定的:(1)如果两个对象相同(equals⽅法返回true),那么它们的hashCode值⼀定要相同;(2)如果两个对象的hashCode相同,它们并不⼀定相同。当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使⽤容器时,相同的对象可以出现在Set集合中,同时增加新元素的效率会⼤⼤下降(对于使⽤哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。
补充:关于equals和hashCode⽅法,很多Java程序都知道,但很多⼈也就是仅仅知道⽽已,在Joshua Bloch的⼤作《》(很多软件公司,《Effective
Java》、《》以及《:改善既有代码质量》是Java程序员必看书籍,如果你还没看过,那就赶紧去亚
马逊买⼀本吧)中是这样介绍equals⽅法的:⾸先equals
⽅法必须满⾜⾃反性(x.equals(x)必须返回true)、对称性(x.equals(y)返回true时,y.equals(x)也必须返回true)、传递性(x.equals(y)和y.equals(z)都返回
true时,x.equals(z)也必须返回true)和⼀致性(当x和y引⽤的对象信息没有被修改时,多次调⽤x.equals(y)应该得到同样的返回值),⽽且对于任何⾮null值的
引⽤x,x.equals(null)必须返回false。实现⾼质量的equals⽅法的诀窍包括:1. 使⽤==操作符检查”参数是否为这个对象的引⽤”;2. 使⽤instanceof操作符检
查”参数是否为正确的类型”;3. 对于类中的关键属性,检查参数传⼊对象的属性是否与之相匹配;4. 编写完equals⽅法后,问⾃⼰它是否满⾜对称性、传递性、
⼀致性;5. 重写equals时总是要重写hashCode;6. 不要将equals⽅法参数中的Object对象替换为其他的类型,在重写时不要忘掉@Override注解。
17、是否可以继承String类?
答:String 类是final类,不可以被继承。
补充:继承String本⾝就是⼀个错误的⾏为,对String类型最好的重⽤⽅式是关联关系(Has-A)和依赖关系(Use-A)⽽不是继承关系(Is-A)。
18、当⼀个对象被当作参数传递到⼀个⽅法后,此⽅法可改变这个对象的属性,并可返回变化后的结果,那么这⾥到底是值传递还是引⽤传递?
答:是值传递。Java语⾔的⽅法调⽤只⽀持参数的值传递。当⼀个对象实例作为⼀个参数被传递到⽅法中时,参数的值就是对该对象的引⽤。对象的属性可以在被调⽤过程中被改变,但对对象引⽤的改变是不会影响到调⽤者的。C++和C#中可以通过传引⽤或传输出参数来改变传⼊的参数的值。在C#中可以编写如下所⽰的代码,但是在Java中却做不到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19using System;
namespace CS01 {
class Program {
public static void swap(ref int x, ref int y) {
int temp = x;
x = y;
y = temp;
}
public static void Main (string[] args) {
int a = 5, b = 10;
swap (ref a, ref b);
// a = 10, b = 5;
Console.WriteLine ("a = {0}, b = {1}", a, b);
}
}
}
说明:Java中没有传引⽤实在是⾮常的不⽅便,这⼀点在Java 8中仍然没有得到改进,正是如此在Java编写的代码中才会出现⼤量的Wrapper类(将需要通过⽅法调⽤修改的引⽤置于⼀个Wrapper类中,再将Wrapper对象传⼊⽅法),这样的做法只会让代码变得臃肿,尤其是让从C和C++转型为Java程序员的开发者⽆法容忍。
19、String和StringBuilder、StringBuffer的区别?
答:Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引⽤的字符串内容是不能被改变的。⽽StringBuffer/StringBuilder类表⽰的字符串对象可以直接进⾏修改。StringBuilder是Java 5中引⼊的,它和StringBuffer的⽅法完全相同,区别在于它是在单线程环境下使⽤的,因为它的所有⽅⾯都没有被synchronized修饰,因此它的效率也⽐StringBuffer要⾼。
⾯试题1 - 什么情况下⽤+运算符进⾏字符串连接⽐调⽤StringBuffer/StringBuilder对象的append⽅法连接字符串性能更好?
⾯试题2 - 请说出下⾯程序的输出。
1 2 3 4 5 6 7 8 9 10 11class StringEqualTest {
public static void main(String[] args) {
String s1 = "Programming";
String s2 = new String("Programming");
String s3 = "Program"+ "ming";
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s1 == s1.intern());
}
}
补充:String对象的intern⽅法会得到字符串对象在常量池中对应的版本的引⽤(如果常量池中有⼀个字符串与String对象的equals结果是true),如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引⽤。
20、重载(Overload)和重写(Override)的区别。重载的⽅法能否根据返回类型进⾏区分?
答:⽅法的重载和重写都是实现多态的⽅式,区别在于前者实现的是编译时的多态性,⽽后者实现的是运⾏时的多态性。重载发⽣在⼀个类中,同名的⽅法如果有不同的参数列表(参数类型不同、参数个数不同或者⼆者都不同)则视为重载;重写发⽣在⼦类与⽗类之间,重写要求⼦类被重写⽅法与⽗类被重写⽅法有相同的返回类型,⽐⽗类被重写⽅法更好访问,不能⽐⽗类被重写⽅法声明更多的异常(⾥⽒代换原则)。重载对返回类型没有特殊的要求。
⾯试题:华为的⾯试题中曾经问过这样⼀个问题 – “为什么不能根据返回类型来区分重载”,快说出你的答案吧!
31、String s = new String(“xyz”);创建了⼏个字符串对象?
答:两个对象,⼀个是静态区的”xyz”,⼀个是⽤new创建在堆上的对象。
32、接⼝是否可继承(extends)接⼝?抽象类是否可实现(implements)接⼝?抽象类是否可继承具体类(concrete class)?
答:接⼝可以继承接⼝,⽽且⽀持多重继承。抽象类可以实现(implements)接⼝,抽象类可继承具体类也可以继承抽象类。
33、⼀个”.java”源⽂件中是否可以包含多个类(不是内部类)?有什么限制?
答:可以,但⼀个源⽂件中最多只能有⼀个公开类(public class)⽽且⽂件名必须和公开类的类名完全保持⼀致。
34、Anonymous Inner Class(匿名内部类)是否可以继承其它类?是否可以实现接⼝?
答:可以继承其他类或实现其他接⼝,在Swing编程和Android开发中常⽤此⽅式来实现事件监听和回调。
35、内部类可以引⽤它的包含类(外部类)的成员吗?有没有什么限制?
答:⼀个内部类对象可以访问创建它的外部类对象的成员,包括私有成员。
36、Java 中的final关键字有哪些⽤法?
答:(1)修饰类:表⽰该类不能被继承;(2)修饰⽅法:表⽰⽅法不能被重写;(3)修饰变量:表⽰变量只能⼀次赋值以后值不能被修改(常量)。
52、List、Set、Map是否继承⾃Collection接⼝?
答:List、Set 是,Map 不是。Map是键值对映射容器,与List和Set有明显的区别,⽽Set存储的零散的元素且不允许有重复元素(数学中的集合也是如此),List是线性结构的容器,适⽤于按数值索引访问元素的情形
74、你在项⽬中哪些地⽅⽤到了XML?
答:XML的主要作⽤有两个⽅⾯:数据交换和信息配置。在做数据交换时,XML将数据⽤标签组装成起来,然后压缩打包加密后通过⽹络传送给接收者,接收解密与解压缩后再从XML⽂件中还原相关信息进⾏处理,XML曾经是异构系统间交换数据的事实标准,但此项功能⼏乎已经被JSON(JavaScript Object Notation)取⽽代之。当然,⽬前很多软件仍然使⽤XML来存储配置信息,我们在很多项⽬中通常也会将作为配置信息的硬代码写在XML⽂件中,Java的很多框架也是这么做的,⽽且这些框架都选择了dom4j作为处理XML的⼯具,因为Sun公司的官⽅API实在不怎么好⽤。
补充:现在有很多时髦的软件(如Sublime)已经开始将配置⽂件书写成JSON格式,我们已经强烈的感受到XML的另⼀项功能也将逐渐被业界抛弃。
91、⽤Java写⼀个单例类。
答:
-
饿汉式单例
1 2 3 4 5 6 7
public class Singleton {
private Singleton(){}
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}
懒汉式单例
1 2 3 4 5 6 7 8public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static synchronized Singleton getInstance(){
if(instance == null) instance =new Singleton();
return instance;
}
}
注意:实现⼀个单例有两点注意事项,①将构造器私有,不允许外界通过构造器创建对象;②通过公开的静态⽅法向外界返回类的唯⼀实例。这⾥有⼀个问题可以思考:Spring的IoC容器可以为普通的类创建单例,它是怎么做到的呢?
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论