Java面试(三)
1、java 中实现多态的机制是什么?
2、abstract class 和 interface 有什么区别?
3、abstract 的 method 是否可同时是 static,是否可同时是 native,是否可同时是 synchronized?
4、什么是内部类?Static Nested Class 和 Inner Class 的不同。
5、String 和 StringBuffer 的区别
6、Anonymous Inner Class (匿名内部类) 是否可以 extends(继承)其它类,是否可以 implements(实现)interface(接 口)?
7、String 是最基本的数据类型吗?
8、String s = Hello;s = s + world!;这两行代码执行后,原始的 String 对象中的内容到底变了没有?
9、是否可以继承 String 类?
10、String s = new String(xyz);创建了几个 String Object? 二者之间有什么区别?
1、java 中实现多态的机制是什么?
靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行期才动态 绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量 的类型中定义的方法。
2、abstract class 和 interface 有什么区别?
含有 abstract 修饰符的 class 即为抽象类,abstract 类不能创建的实例对象。含有 abstract 方法的类必须定义为 abstract class,abstract class类中的方法不必是抽象的。abstract class类中定义抽象方法必须在具体(Concrete)子类 中实现,所以,不能有抽象构造方法或抽象静态方法。如果的子类没有实现抽象父类中的所有抽象方法,那么子 类也必须定义为 abstract 类型。
接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的方法定义默认 为 public abstract 类型,接口中的成员变量类型默认为 public static final。
3、abstract 的 method 是否可同时是 static,是否可同时是 native,是否可同时是 synchronized?
abstract 的 method 不可以是 static 的,因为抽象的方法是要被子类实现的,而 static 与子类扯不上关系。
native 方法表示该方法要用另外一种依赖平台的编程语言实现的,不存在着被子类实现的问题,所以,它也不 能是抽象的,不能与 abstract 混用。例如,FileOutputSteam 类要硬件打交道,底层的实现用的是操作系统相关的 api 实现,例如,在 windows 用 c 语言实现的,所以,查看 jdk 的源代码,可以发现 FileOutputStream 的 open 方 法的定义如下:
private native void open(String name) throws FileNotFoundException;
如果我们要用 java 调用别人写的 c 语言函数,我们是无法直接调用的,我们需要按照 java 的要求写一个 c 语言 的函数,又我们的这个 c 语言函数去调用别人的 c 语言函数。由于我们的 c 语言函数是按 java 的要求来写的,我们这个 c 语言函数就可以与 java 对接上,java 那边的对接方式就是定义出与我们这个 c 函数相对应的方法,java 中对应的方法不需要写具体的代码,但需要在前面声明 native。
关于 synchronized 与 abstract 合用的问题,我觉得也不行,因为在我几年的学习和开发中,从来没见到过这种情 况,并且我觉得 synchronized 应该是作用在一个具体的方法上才有意义。而且,方法上的 synchronized 同步所使 用的同步锁对象是 this,而抽象方法上无法确定 this 是什么。
4、什么是内部类?Static Nested Class 和 Inner Class 的不同。
内部类就是在一个类的内部定义的类,内部类中不能定义静态成员(静态成员不是对象的特性,只是为了找一个容身之处)内部类可以直接访问外部类中的成员变量,内部类可以定义在外部类的方法外面,也可以定 义在外部类的方法体中,如下所示:
public class Outer { int out_x = 0;
public void method() {
Inner1 inner1 = new Inner1();<span class=kd>public</span> <span class=kd>class</span> <span class=nc>Inner2</span> <span class=o>{</span> <span class=c1>//在方法内外面定义的内部类
public method() {
out_x = 3;
}
}
Inner2 inner2 = new Inner2();
}
public class Inner1 { //在方法体外面定义的内部类
}
}
在方法体外面定义的内部类的访问类型可以是 public,protecte,默认的,private 等 4 种类型,这就好像类中定义 的成员变量有 4 种访问类型一样,它们决定这个内部类的定义对其他类是否可见;对于这种情况,我们也可以在外面创建内部类的实例对象,创建内部类的实例对象时,一定要先创建外部类的实例对象,然后用这个外部类的实例对象去创建内部类的实例对象,代码如下:
Outer outer = new Outer();
Outer.Inner1 inner1 = outer.new Innner1();
在方法内部定义的内部类前面不能有访问类型修饰符,就好像方法中定义的局部变量一样,但这种内部类的前面可以使用 final 或 abstract 修饰符。这种内部类对其他类是不可见的其他类无法引用这种内部类,但是这种内部类创建的实例对象可以传递给其他类访问。这种内部类必须是先定义,后使用,即内部类的定义代码必须出现在使用该类之前,这与方法中的局部变量必须先定义后使用的道理也是一样的。这种内部类可以访问方法体中的局部变量,但是,该局部变量前必须加 final 修饰符。
对于这些细节,只要在idea写代码试试,根据开发工具提示的各类错误信息就可以马上了解到。
在方法体内部还可以采用如下语法来创建一种匿名内部类,即定义某一接口或类的子类的同时,还创建了该子 类的实例对象,无需为该子类定义名称:
public class Outer {
public void start() {
new Thread(
new Runable(){
public void run(){};
}
).start();
}
}
最后,在方法外部定义的内部类前面可以加上static关键字,从而成为Static Nested Class,它不再具有内部类 的特性,所有,从狭义上讲,它不是内部类。Static Nested Class 与普通类在运行时的行为和功能上没有什么区别, 只是在编程引用时的语法上有一些差别,它可以定义成 public、protected、默认的、private 等多种类型,而普通类 只能定义成 public 和默认的这两种类型。在外面引用 Static Nested Class 类的名称为“外部类名.内部类名”。在外 面不需要创建外部类的实例对象,就可以直接创建Static Nested Class,例如,假设Inner是定义在Outer类中的 Static Nested Class,那么可以使用如下语句创建 Inner 类:
Outer.Inner inner = new Outer.Inner();
由于 static Nested Class 不依赖于外部类的实例对象,所以,static Nested Class 能访问外部类的非 static 成员变 量。当在外部类中访问 Static Nested Class 时,可以直接使用 Static Nested Class 的名字,而不需要加上外部类的名 字了,在 Static Nested Class 中也可以直接引用外部类的 static 的成员变量,不需要加上外部类的名字。
在静态方法中定义的内部类也是 Static Nested Class,这时候不能在类前面加 static 关键字,静态方法中的 Static Nested Class 与普通方法中的内部类的应用方式很相似,它除了可以直接访问外部类中的 static 的成员变量,还可 以访问静态方法中的局部变量,但是,该局部变量前必须加 final 修饰符。
5、String 和 StringBuffer 的区别
JAVA 平台提供了两个类:String 和 StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。
这 个 String 类提供了数值不可改变的字符串。而这个 StringBuffer 类提供的字符串进行修改。当你知道字符数据要改 变的时候你就可以使用 StringBuffer。
典型地,你可以使用 StringBuffers 来动态构造字符数据。另外,String 实现了 equals方法,new String(“abc”).equals(new String(“abc”)的结果为true,而StringBuffer没有实现equals方法,所以, new StringBuffer(“abc”).equals(new StringBuffer(“abc”)的结果为 false。
接着要举一个具体的例子来说明,我们要把 1 到 100 的所有数字拼起来,组成一个串。
StringBuffer sbf = new StringBuffer();
for(int i=0;i<100;i++){
sbf.append(i);
}
上面的代码效率很高,因为只创建了一个 StringBuffer 对象,而下面的代码效率很低,因为创建了 101 个对象。
String str = new String();
for(int i=0;i<100;i++){
str = str + i;
}
在讲两者区别时,应把循环的次数搞成 10000,然后用 endTime-beginTime 来比较两者执行的时间差异,最后还要 讲讲 StringBuilder 与 StringBuffer 的区别。
String 覆盖了 equals 方法和 hashCode 方法,而 StringBuffer 没有覆盖 equals 方法和 hashCode 方法,所以,将 StringBuffer 对象存储进 Java 集合类中时会出现问题。
6、Anonymous Inner Class (匿名内部类) 是否可以 extends(继承)其它类,是否可以 implements(实现)interface(接 口)?
可以继承其他类或实现其他接口。
7、String 是最基本的数据类型吗?
基本数据类型包括 byte、int、char、long、float、double、boolean 和 short。
java.lang.String 类是 final 类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用 StringBuffer 类。
8、String s = Hello;s = s + world!;这两行代码执行后,原始的 String 对象中的内容到底变了没有?
没有。
因为 String 被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。
在这段代码中,s 原先指向一个 String 对象,内容是 Hello,然后我们对 s 进行了+操作,那么 s 所指向的那个对象是否发生了改变呢? 答案是没有。这时,s 不指向原来那个对象了,而指向了另一个 String 对象,内容为Hello world!,原来那个对 象还存在于内存之中,只是 s 这个引用变量不再指向它了。
通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的 修改,那么使用 String 来代表字符串的话会引起很大的内存开销。因为 String 对象建立之后不能再改变,所以对 于每一个不同的字符串,都需要一个 String 对象来表示。这时,应该考虑使用 StringBuffer 类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。 同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都 new 一个 String。例如我们要在构造器中对 一个名叫 s 的 String 引用变量进行初始化,把它设置为初始值,应当这样做:
public class Demo {
private String s;
...
public Demo {
s = Initial Value;
}
...
}
而非
s = new String(Initial Value);
后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为 String 对象不可改变,所以对于内容相同的字符串,只要一个 String 对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象, 他们的 String 类型属性 s 都指向同一个对象。 上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java 认为它们代表同一个 String 对象。而用关键字 new 调用构造器,总是会创建一个新的对象,无论内容是否相同。
至于为什么要把 String 类设计成不可变类,是它的用途决定的。其实不只 String,很多 Java 标准类库中的类都是 不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思 想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也 有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以 Java 标准类库还提供了 一个可变版本,即 StringBuffer。
9、是否可以继承 String 类?
String 类是 final 类故不可以继承。
10、String s = new String(xyz);创建了几个 String Object? 二者之间有什么区别?
两个或一个。
”xyz”对应一个对象,这个对象放在字符串常量缓冲区,常量”xyz”不管出现多少遍,都是缓冲区中的那一个。New String 每写一遍,就创建一个新的对象,它一句那个常量”xyz”对象的内容来创建出一个新 String对象。如果以前就用过’xyz’,这句代表就不会创建”xyz”自己了,直接从缓冲区拿