面经 - Java 基础面试题2

简介: 面经 - Java 基础面试题2

36、为了向文件 hello.txt 尾加数据,下列哪个是正确创建指向 hello.txt 的流?

A、try{ OutputStreamout=newFileOutputStream("hello.txt");}
catch(IOExceptione){}
B、try { OutputStreamout=newFileOutputStream("hello.txt", true);}
catch(IOExceptione){}
C、try{ OutputStreamout=newFileOutputStream("hello.txt", false);}
catch(IOExceptione){}
D、try{ OutputStreamout=newOutputStream("hello.txt", true);}
catch(IOExceptione){}

答案:B

解析:FileOutputStream类是文件字节输出流,它是OutputStream类的子类,所以FileOutputStream的实例方法都是从OutputStream类继承来的,那么在这道题中需要使用上转型对象才能调用重写父类的方法,排除D。ABC在于FileOutpurStream的构造方法的不同,题目要求向文件尾加数据,A选项是具有刷新功能的构造方法,也就是会覆盖掉原来文件的数据,B选项参数为true表示不会刷新所指向的文件,而是从文件的末尾开始向文件写入数据,取值为false,输出流将刷新所指向的文件,与A选项等价,因为A选项源码中默认传false。


37、为什么构造方法里 this 或者 super 函数调用必须放在第一行且无法共存?

解析:super 方法在构造函数的第一行原因是子类有可能访问了父类对象,比如在构造函数中使用父类对象的成员函数和变量,在成员初始化使用了父类,在代码块中使用了父类等,所以放在第一行可以保证在子类可以访问父类对象之前完成对父类对象的初始化。


this 方法在构造函数的第一行原因是为保证父类对象初始化的唯一性,因为假设类 B 是类 A 的子类,如果 this 方法可以在构造函数的任意行使用则首先程序运行到构造函数 B() 的第一行发现没有调用 this() 和 super(),就自动在第一行补齐了 super() 方法(这是 java 默认的机制),以此完成了对父类对象的初始化,然后返回子类的构造函数继续执行,当运行到构造函数 B() 的 this(参数)调用行时,调用 B 类对象的另一个构造方法 B(参数),在 B(参数) 中还会对父类对象再次初始化,这就造成了对资源的浪费,也有可能造成某些意想不到的结果,所以 this 方法不能出现在构造方法除第一行以外的其他行。


这也就解释了为啥在构造方法里面 this 与 super 方法不能同时存在的原因。


38、Java 构造方法能否被重写和重载?

解析:重写是子类方法重写父类的方法,重写的方法名不变,而类的构造方法名必须与类名一致,假设父类的构造方法如果能够被子类重写则子类类名必须与父类类名一致才行,所以 Java 的构造方法是不能被重写的。而重载是针对同一个的,所以构造方法可以被重载。


39、下列哪种说法是正确的()

A、实例方法可直接调用超类的实例方法

B、实例方法可直接调用超类的类方法

C、实例方法可直接调用其他类的实例方法

D、实例方法可直接调用本类的类方法

答案:D

解析:ABC选项:如果方法是 private 则不可以,而且 C 还要考虑需要 new 对象才能调用,而不是所谓的直接调用;D选项虽然类方法可以类名.类方法来调用,但是 new 该类的实例方法去调也不不会报错;记住一句话:实例方法只能直接调用本类的方法,其他类的方法得需要引用调用。


40、提供 Java 存取数据库能力的包是()

A、java.sql

B、java.awt

C、java.lang

D、java.swing

答案:A

解析:

A:存储在数据源(通常是一个关系数据库)中的数据的 API

B:java.awt 是一个软件包,包含用于创建用户界面和绘制图形图像的所有分类

C:是提供利用 Java 编程语言进行程序设计的基础类

D:Swing 是一个用于开发 Java 应用程序用户界面的开发工具包


41、String、StringBuffer、StringBuilder 的区别?

解析:String 字符串常量(final修饰,不可被继承),String 是常量,当创建之后即不能更改。(可以通过 StringBuffer 和 StringBuilder 创建 String 对象(常用的两个字符串操作类)),StringBuffer 字符串变量(线程安全),其也是 final 类别的,不允许被继承,其中的绝大多数方法都进行了同步处理,包括常用的 Append 方法也做了同步处理(synchronized修饰),其自 jdk1.0 起就已经出现。其 toString 方法会进行对象缓存,以减少元素复制开销。


42、Which two demonstrate a "has a" relationship(Choose two)?(

A、public interface Person {}

public class Employee extends Person{}

B、public interface Shape {}

public interface Rectandle extends Shape {}

C、public interface Colorable {}

public class Shape implements Colorable {}

D、public class Species {}

public class Animal {private Species species;}

E、interface Component {}

class Container implements Component {private Component[] children;}

答案:DE

解析:在Java中代码重用有两种可能的方式,即组合(“has a”关系)和继承(“is a”关系)。“has a”关系是通过定义类的属性的方式实现的;而“is a”关系是通过类继承实现的。本例中选项A、B、C体现了“is a”关系;选项D、E体现了“has a”关系。


43、问:简单说说 Array 和 ArrayList 的区别?

解析:这题相当小儿科,Array 可以包含基本类型和对象类型,ArrayList 只能包含对象类型;Array 的大小是固定的,ArrayList 的大小是动态变化的;ArrayList 提供了更多的方法和特性,譬如 addAll()、removeAll()、iterator() 等。


44、问:为什么 HashMap 中 String、Integer 这样的包装类适合作为 key 键,即为什么使用它们可以减少哈希碰撞?

解析:因为 String、Integer 等包装类是 final 类型的,具有不可变性,而且已经重写了 equals() 和 hashCode() 方法。不可变性保证了计算 hashCode() 后键值的唯一性和缓存特性,不会出现放入和获取时哈希码不同的情况且读取哈希值的高效性,此外官方实现的 equals() 和 hashCode() 都是严格遵守相关规范的,不会出现错误。


45、请看如下代码,下面哪些放在 // point x 行是正确的?()

classPerson { 
privateinta;
publicintchange(intm) {
returnm;
    }
}
publicclassTeacherextendsPerson { 
publicintb;
privateintc;
publicstaticvoidmain(Stringarg[]) { 
Personp=newPerson();
Teachert=newTeacher(); 
inti;
// point x    }
}


A、i = m;

B、i = b;

C、i = p.a;

D、i = p.change(3);

E、i = t.c;

答案:DE

解析:本题考查类的声明。选项A中m没有被声明过,不能使用;选项B中虽然b是类Teacher的public 成员变量,但在静态方法中,不能使用类中的非静态成员;选项C中a是类Person的private成员,在类外不能直接引用;选项D中change(int m)方法是pubtic方法,并且返回一个int型值,可以通过类的实例变量p引用并赋值给一个int型变量。最后E选项,虽然c为私有属性,但是因为在本类中访问,所以还是OK的。


46、问:Java 匿名内部类在使用时如何初始化?

解析:匿名内部类无法通过构造方法初始化,所以我们只能通过构造代码块进行初始化。

newAbstractAction() {
    {
// do stuff here    }
publicvoidactionPerformed(ActionEventevent) {
// ...    }
}

47、下面那几个函数是 public void method() {} 的重载函数?()

A、public void method(int m) {}

B、public int method() {}

C、public void method2() {}

D、public int method(int m, float f) {}

答案:AD

解析:重载就是方法名一样(必须的),里面执行的内容不一样,但是呢,又出现一个问题,你写的两个或者多个方法要能让JVM(JAVA 虚拟机)认识是唯一的,所以,这里就和返回的类型无关了,因为如果返回类型不一样,其他都一样的话,那JVM是不知道到底是调用哪个方法的。所以方法名一样,入参的类型,个数,顺序(术语:方法的签名)只要有一个不同就是方法的重载了。


48、问:开发中使用 Java 匿名内部类有哪些注意事项(经验)?

解析:常见的注意事项如下。使用匿名内部类时必须是继承一个类或实现一个接口(二者不可兼得且只能继承一个类或者实现一个接口)。

匿名内部类中是不能定义构造函数的,如需初始化可以通过构造代码块处理。匿名内部类中不能存在任何的静态成员变量和静态方法。匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。匿名内部类不能是抽象类,必须要实现继承的类或者实现接口的所有抽象方法。


49、问:请简单谈谈 TreeMap HashMap 的区别?

解析:TreeMap 实现了 SortMap 接口,其能够根据键排序,默认是按键的升序排序,也可以指定排序的比较器,当用 Iterator 遍历 TreeMap 时得到的记录是排过序的,所以在插入和删除操作上会有些性能损耗,TreeMap 的键和值都不能为空,其为非并发安全 Map,此外 TreeMap 基于红黑树实现。

HashMap 是最常用的 Map,其基于哈希散列表实现,主要根据键的 hashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度,当用 Iterator 遍历 HashMap 时得到的记录顺序是随机的,HashMap 只允键值均为空,其为非并发安全 Map。

所以一般情况下我们选用 HashMap,因为 HashMap 的键值对在取出时是随机的,其依据键的 hashCode 值和键的 equals 方法存取数据,具有很快的访问速度,所以在 Map 中插入、删除及索引元素时其是效率最高的实现。而 TreeMap 取出来的是排序后的键值对,所以效率会低点。


50、问:说说 HashSet HashMap 的区别?

解析:从实质上说 HashSet 的实现实质就是一个 Map 对象的包装,只是 Map 的 value 为 Object 固定对象,Set 只利用了 Map 的 key 而已。

具体区别来说如下:HashMap 实现了 Map 接口,而 HashSet 实现了 Set 接口。HashMap 储存键值对,而 HashSet 仅仅存储对象。

HashMap 使用 put 方法将元素放入 Map 中,而 HashSet 使用 add 方法将元素放入 Set 中。

HashMap 中使用键对象来计算 hashcode 值,而 HashSet 使用成员对象来计算 hashcode 值。


51、问:简单说说什么是二叉树?什么是完全二叉树?什么是满二叉树?什么是排序二叉树?

解析:

二叉树是一种从上往下分叉的一种数据结构,其每个节点最多有两个孩子节点,一左一右,左边的称为左孩子,右边的称为右孩子。

完全二叉树是普通二叉树除最后一层外,在每一层上的结点数均达到最大值,在最后一层上只缺少右边若干结点的二叉树。

满二叉树是普通二叉树中的每个结点恰好有两个孩子结点且所有叶子结点都在同一层的二叉树。

若普通二叉树每个节点满足左子树所有节点值小于它的根节点值且右子树所有节点值大于它的根节点值,则这样的二叉树就是排序二叉树。


52、问:面向对象的三大特征是什么?请详细介绍他们各自的部分原理?

解析:特征可以说有三种,继承、封装、多态,也可以说有四种,继承、封装、多态、抽象。

继承性是类的一种层次模型,其提供了一种明确表述共性的方法,对象的新类可以从现有的类中继承派生,类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。

封装性是把过程和数据包围起来,使得数据的访问只能通过已定义的接口,保证了对象被访问的隐私性和可靠性。

多态性是对象在不同时刻表现出来的多种状态,是一种编译时期状态和运行时期状态不一致的现象,多态性包括参数化多态性和包含多态性。

抽象性是指对一类事物的高度提炼以得到共同的共性部分,抽象不需要了解全部细节,而只是一种通用的描述约束,抽象可以是过程抽象或者数据抽象。


53、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制?

解析:可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致。


54、Java 有没有 goto?

解析:java中的保留字,现在没有在java中使用。


55、在JAVA中如何跳出当前的多重嵌套循环?

解析:在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break语句,即可跳出外层循环。


例如

ok:
for (inti=0; i<10; i++) { 
for (intj=0; j<10; j++){
System.out.println("i="+i+",j="+j); 
if (j==5) 
breakok;
    }
}

另外,我个人通常并不使用标号这种方式,而是让外层的循环条件表达式的结果可以受到里层循环体代码的控制,例如,要在二维数组中查找到某个数字。

intarr[][] = {{1,2,3},{4,5,6,7},{9}};
booleanfound=false;
for (inti=0; i<arr.length&&!found; i++) 
for (intj=0; j<arr[i].length; j++){
System.out.println("i="+i+",j="+j); 
if (arr[i][j] ==5) {
found=true; 
break;
        }
    }
}


56、switch语句能否作用在byte上,能否作用在long上,能否作用在 String上?

解析:在switch(e)中,e只能是一个整数表达式或者枚举常量(更大字体),整数表达式可以是int基本类型或Integer包装类型,由于byte,short,char都可以隐含转换为int,所以这些类型以及这些类型的包装类型也是可以的。显然,long和String类型都不符合switch的语法规定,并且不能被隐式转换成int类型,所以,它们不能作用于swtich语句中。switch语句能否作用在String上说错了,Java1.7之后已经支持这种写法了!


最新提示

Incompatible types. Found: 'long', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum'


57、short s1 = 1; s1 = 1 + 1; 有什么错?short s1 = 1; s1 += 1; 有什么错?

解析:

  • 对于 short s1= 1; s1 = s1 + 1; 由于s1+1运算时会自动提升表达式的类型,所以结果是int型,再赋值给 short 类型s1时,编译器将报告需要强制转换类型的错误。
  • 对于 short s1= 1; s1 += 1; 由于 += 是java语言规定的运算符,java编译器会对它进行特殊处理,因此可以正确编译(原来这个复合赋值是自带了隐式的强制类型转换的)。



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

解析:char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,所以 char 型变量中当然可以存储汉字啦。不过,如果某个特殊的汉字没有被包含在unicode编码字符集中,那么,这个 char 型变量中就不能存储这个特殊汉字。补充说明:unicode编码占用两个字节,所以,char类型的变量也是占用两个字节。


59、使用 final 关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?

解析:使用 final 关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。例如,对于如下语句:


finalStringBuffera=newStringBuffer("immutable");

执行如下语句将报告编译期错误:a = new StringBuffer(""); 但是,执行如下语句则可以通过编译:a.append("broken!");

有人在定义方法的参数时,可能想采用如下形式来阻止方法内部修改传进来的参数对象:

publicvoidmethod(finalStringBufferparam) {
}

实际上,这是办不到的,在该方法内部仍然可以增加如下代码来修改参数对象:param.append("a")。


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

解析:在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。

在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只 要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用 了。总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。

例如,对于下面的程序,无论创建多少个实例对象,永远都只分配了一个staticVar变量,并且每创建一个实例对象,这个staticVar就会加1;但是,每创建一个实例对象,就会分配一个instanceVar,即可能分配多个instanceVar,并且每个instanceVar的值都只自加了1次。

publicclassVariantTest{
publicstaticintstaticVar=0; 
publicintinstanceVar=0; 
publicVariantTest() {
staticVar++; 
instanceVar++;
System.out.println(staticVar+instanceVar);
    }
}


61、是否可以从一个 static 方法内部发出对非 static 方法的调用?

解析:不可以。因为非static方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方法调用,而static方法调用时不需要创建对象,可以直接调用。也就是说,当一个static方法被调用时,可能还没有创建任何实例对象,如果从一个static方法中发出对非static方法的调用,那个非static方法是关联到哪个对象上的呢?这个逻辑无法成立,所以,一个static方法内部发出对非static方法的调用。


62、Integer 与 int 的区别?

解析:int是java提供的8种原始数据类型之一。Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。int的默认值为0,而Integer的默认值为null,即Integer可以区分出未赋值和值为0的区别,int 则无法表达出未赋值的情况。 例如:要想表达出没有参加考试和考试成绩为0的区别,则只能使用Integer。在JSP开发中,Integer的默认为null,所以用el表达式在文本框中显示时,值为空白字符串,而int默认的默认值为0,所以用el表达式在文本框中显示时,结果为0,所以,int不适合作为web层的表单数据的类型。在Hibernate中,如果将OID定义为Integer类型,那么Hibernate就可以根据其值是否为null而判断一个对象是否是临时的,如果将OID定义为了int类型,还需要在hbm映射文件中设置其unsaved-value属性为0。另外,Integer提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数,Integer中还定义了表示整数的最大值和最小值的常量。


63、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.ceil(11.6)的结果为 11,Math.ceil(-11.6)的结果是-12;最难掌握的是round方法,它表示“四舍五入”,算法为 Math.floor(x+0.5),即将原来的数字加上0.5后再向下取整,所以,Math.round(11.5)的结果为12,Math.round(-11.5)的结果为-11。这里有一些笔误,floor的英文意义是地板,该方法就表示向下取整,Math.floor(11.6)的结果为 11,Math.floor(-11.6)的结果是-12。


64、Overload和Override的区别?Overloaded的方法是否可以改变返回值的类型?

解析:Overload是重载的意思,Override是覆盖的意思,也就是重写。

重载Overload表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同)。重写Override表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类定义的那个完全相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现。子类覆盖父类的方法时,只能比父类抛出更少的异常, 或者是抛出父类抛出的异常的子异常,因为子类可以解决父类的一些问题,不能比父类有更多的问题。 子类方法的访问权限只能比父类的更大,不能更小。如果父类的方法是private类型,那么,子类则不存在覆盖的限制,相当于子类中增加了一个全新的方法。 至于Overloaded的方法是否可以改变返回值的类型这个问题,要看你倒底想问什么呢?这个题目很模糊。如果几个Overloaded的方法的参数列表不一样,它们的返回者类型当然也可以不一样。但我估计你想问的问题是:如果两个方法的参数列表完全一样,是否可以让它们的返回值不同来实现重载 Overload。这是不行的,我们可以用反证法来说明这个问题,因为我们有时候调用一个方法时也可以不定义返回结果变量,即不要关心其返回结果,例如,我们调用map.remove(key)方法时,虽然remove方法有返回值,但是我们通常都不会定义接收返回结果的变量,这时候假设该类中有两个名称和参数列表完全相同的方法,仅仅是返回类型不同,java就无法确定编程者倒底是想调用哪个方法了,因为它无法通过返回结果类型来判断。 override可以翻译为覆盖,从字面就可以知道,它是覆盖了一个方法并且对其重写,以求达到不同的作用。对我们来说最熟悉的覆盖就是对接口方法的实现,在接口中一般只是对方法进行了声明,而我们在 实现时,就需要实现接口声明的所有方法。除了这个典型的用法以外,我们在继承中也可能会在子类覆 盖父类中的方法。


在覆盖要注意以下的几点:

1、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果;

2、覆盖的方法的返回值必须和被覆盖的方法的返回一致;

3、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;

4、被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。Overload对我们来说可能比较熟悉,可以翻译为重载,它是指我们可以定义一些名称相同的方法,通过定义不同的输入参数来区分这些方法,然后再调用时,VM就会根据不同的参数样式,来选择合适的方法执行。


在使用重载要注意以下的几点:

1、在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int,float),但是不能为 fun(int,int));

2、不能通过访问权限、返回类型、抛出的异常进行重载;

3、方法的异常类型和数目不会对重载造成影响;

4、对于继承来说,如果某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。


65、接口是否可继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concreteclass)?抽象类中是否可以有静态的 main 方法?

解析:接口可以继承接口。抽象类可以实现(implements)接口,抽象类可以继承具体类。抽象类中可以有静态的main方法。备注:只要明白了接口和抽象类的本质和作用,这些问题都很好回答,你想想,如果你是java语言的设计者,你是否会提供这样的支持,如果不提供的话,有什么理由吗?如果你没有道理不提供,那答案就是肯定的了。只要记住抽象类与普通类的唯一区别就是不能创建实例对象和允许有abstract方法。


66、Java 中实现多态的机制是什么?

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


67、abstractclass 和 interface 语法上有什么区别?

解析:

  1. 抽象类可以有构造方法,接口中不能有构造方法。
  2. 抽象类中可以有普通成员变量,接口中没有普通成员变量。
  3. 抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
  4. 抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然 eclipse 下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为 public abstract 类型。
  5. 抽象类中可以包含静态方法,接口中不能包含静态方法。
  6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
  7. 一个类可以实现多个接口,但只能继承一个抽象类。


68、abstract 的 method 是否可同时是 static,是否可同时是 native,是否可同时是 synchronized?

解析:abstract的method不可以是static的,因为抽象的方法是要被子类实现的,而static与子类扯不上关系!native方法表示该方法要用另外一种依赖平台的编程语言实现的,不存在着被子类实现的问题,所以,它也不能是抽象的,不能与abstract混用。例如,FileOutputSteam类要硬件打交道,底层的实现用的是操作系统相关的api实现;例如,在windows用c语言实现的,所以,查看jdk的源代码,可以发现 FileOutputStream 的open方法的定义如下:

private native void open(Stringname) throws FileNotFoundException; 如果我们要用java调用别人写的C语言函数,我们是无法直接调用的,我们需要按照java的要求写一个C语言的函数,又我们的这个C语言函数去调用别人的C语言函数。由于我们的C语言函数是按java的要求来写的,我们这个c语言函数就可以与java对接上,java那边的对接方式就是定义出与我们这个C函数相对应的方法,java中对应的方法不需要写具体的代码,但需要在前面声明native。关于synchronized与abstract合用的问题,我觉得也不行,因为在我几年的学习和开发中,从来没见到过这种情况,并且我觉得synchronized应该是作用在一个具体的方法上才有意义。而且方法上的 synchronized 同步所使用的同步锁对象是this,而抽象方法上无法确定this是什么。


69、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引用变量进行初始化,把它设置为初始值,应当这样做:

publicclassDemo { 
privateStrings;
    ...
publicDemo {
s="Initial Value";
    }
    ...
}

而非

s=newString("Initial Value");


后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为String对象不可改变,所以对于内容相同的字符串,只要一个String对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象,他们的 String 类型属性s都指向同一个对象。上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java认为它们代表同一个String对象。而用关键字new调用构造器,总是会创建一个新的对象,无论内容是否相同。至于为什么要把String类设计成不可变类,是它的用途决定的。其实不只String,很多Java标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以Java标准类库还提供了一个可变版本,即StringBuffer。


70、ArrayList 和 Vector 的区别?

解析:这两个类都实现了List接口(List接口继承了Collection接口),他们都是有序集合,即存储在这两个集合中的元素的位置都是有顺序的,相当于一种动态的数组,我们以后可以按位置索引号取出某个元素,并且其中的数据是允许重复的,这是与HashSet之类的集合的最大不同处,HashSet之类的集合不可以按索引号去检索其中的元素,也不允许有重复的元素。

ArrayList与Vector的区别主要包括两个方面:


(1)同步性

Vector是线程安全的,也就是说是它的方法之间是线程同步的,而ArrayList是线程序不安全的,它的方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用ArrayList,因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用Vector,因为不需要我们自己再去考虑和编写线程安全的代码。


(2)数据增长

ArrayList与Vector都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就需要增加ArrayList与Vector的存储空间,每次要增加存储空间时,不是只增加一个存储单元,而是增加多个存储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。Vector默认增长为原来两倍,而ArrayList的增长策略在文档中没有明确规定(从源代码看到的是增长为原来的1.5倍)。ArrayList与Vector都可以设置初始的空间大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法。


总结:即Vector增长原来的一倍,ArrayList增加原来的0.5倍。


71、HashMap 和 Hashtable 的区别?

解析:HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,在只有一个线程访问的情况下,效率要高于Hashtable。HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。

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

Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Mapinterface的一个实现。

最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap就必须为之提供同步。


就HashMap与HashTable主要从三方面来说:

  1. 历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java1.2引进的Map接口的一个实现
  2. 同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的
  3. 值:只有HashMap可以让你将空值作为一个表的条目的key或value


72、ListMapSet 三个接口,存取元素时,各有什么特点?

解析:(这样的题比较考水平,两个方面的水平:一是要真正明白这些内容,二是要有较强的总结和表述能力)


首先,List与Set具有相似性,它们都是单列元素的集合,所以,它们有一个共同的父接口,叫Collection。


Set里面不允许有重复的元素,即不能有两个相等(注意,不是仅仅是相同)的对象,即假设Set集合中有了一个A对象,现在我要向Set集合再存入一个B对象,但B对象与A对象equals相等,则B对象存储不进去,所以,Set集合的add方法有一个boolean的返回值,当集合中没有某个元素,此时add方法可成功加入该元素时,则返回true,当集合含有与某个元素equals相等的元素时,此时add方法无法加入该元素,返回结果为false。


Set取元素时,不能细说要取第几个,只能以Iterator接口取得所有的元素,再逐一遍历各个元素。


List表示有先后顺序的集合,注意,不是那种按年龄、按大小、按价格之类的排序。当我们多次调用add(Obje)方法时,每次加入的对象就像火车站买票有排队顺序一样,按先来后到的顺序排序。有时候,也可以插队,即调用add(intindex,Obje)方法,就可以指定当前对象在集合中的存放位置。

一个对象可以被反复存储进List中,每调用一次add方法,这个对象就被插入进集合中一次,其实,并不是把这个对象本身存储进了集合中,而是在集合中用一个索引变量指向这个对象,当这个对象被add多次时,即相当于集合中有多个索引指向了这个对象,如图x所示。List除了可以用Iterator接口取得所有的元素,再逐一遍历各个元素之外,还可以调用get(indexi)来明确说明取第几个。


Map与List和Set不同,它是双列的集合,其中有put方法,定义如下:put(objkey,objvalue),每次存储时,要存储一对key/value,不能存储重复的key,这个重复的规则也是按equals比较相等。取则可以根据key获得相应的value,即get(Objectkey)返回值为key所对应的value。

另外,也可以获得所有的key的结合,还可以获得所有的value的结合,还可以获得key和value组合成的Map.Entry对象的集合。List以特定次序来持有元素,可有重复元素。Set无法拥有重复元素,内部排序。Map保存key-value值,value可多值。


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

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


74、去掉一个 Vector 集合中重复的元素

解析:代码如下

VectornewVector=newVector(); 
for (inti=0; i<newVector.size(); i++) {
Objectobj=newVector.get(i); 
if(!newVector.contains(obj))
newVector.add(obj);
}
// 还有一种简单的方式, 利用了Set不允许重复元素HashSetset=newHashSet(newVector);


75、Collection 和 Collections 的区别?

解析:Collection是集合类的上级接口,继承他的接口主要有Set和List.Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。


76、Set 里的元素是不能重复的,那么用什么方法来区分重 复与否呢?是用 == 还是 equals()?它们有何区别?

解析:Set里的元素是不能重复的,元素重复与否是使用equals()方法进行判断的。


==和equal区别也是考烂了的题,这里说一下:


==操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用==操作符。


equals方法是用于比较两个独立对象的内容是否相同,就好比去比较两个人的长相是否相同,它比较的两个对象是独立的。比如:两条new语句创建了两个对象,然后用a/b这两个变量分别指向了其中一个对象,这是两个不同的对象,它们的首地址是不同的,即a和b中存储的数值是不相同的,所以,表达式a==b将返回false,而这两个对象中的内容是相同的,所以,表达式a.equals(b)将返回true。


77、String s = new String("xyz"); 创建了几个 StringObject?是否可以继承 String 类?

解析:两个或一个都有可能,”xyz”对应一个对象,这个对象放在字符串常量缓冲区,常量”xyz”不管出现多少遍,都是缓冲区中的那一个。New String 每写一遍,就创建一个新的对象,它使用常量“xyz”对象的内容来创建出一个新String对象。


如果以前就用过’xyz’,那么这里就不会创建”xyz”了,直接从缓冲区拿,这时创建了一个StringObject;但如果以前没有用过"xyz",那么此时就会创建一个对象并放入缓冲区,这种情况它创建两个对象。至于String类是否继承,答案是否定的,因为String默认final修饰,是不可继承的。


78、下面这条语句一共创建了多少个对象:String s = "a"+"b"+"c"+"d" ?

解析:对于如下代码:

Strings1="a"; 
Strings2=s1+"b";
Strings3="a"+"b";
System.out.println(s2=="ab"); 
System.out.println(s3=="ab");

第一条语句打印的结果为false,第二条语句打印的结果为true,这说明javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期再去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。题目中的第一行代码被编译器在编译时优化后,相当于直接定义了一个”abcd”的字符串,所以,上面的代码应该只创建了一个String对象。写如下两行代码:

Strings="a"+"b"+"c"+"d";
System.out.println(s=="abcd");

最终打印的结果应该为true


79、try{} 里有一个return语句,那么紧跟在这个try后的nally{} 里的code会不会被执行,什么时候被执行,在 return 前还是后?

解析:我们知道finally{}中的语句是一定会执行的,那么这个可能正常脱口而出就是return之前,return之后可能就出了这个方法了,鬼知道跑哪里去了,但更准确的应该是在return中间执行,请看下面程序代码的运行结果:

publicclassTest {
publicstaticvoidmain(String[] args) { 
System.out.println(newTest().test());
    }
staticinttest() {
intx=1; 
try {
returnx;
        } finally {
++x;
        }
    }
}

运行结果是1,为什么呢?主函数调用子函数并得到结果的过程,好比主函数准备一个空罐子,当子函数要返回结果时,先把结果放在罐子里,然后再将程序逻辑返回到主函数。所谓返回,就是子函数说,我不运行了,你主函数继续运行吧,这没什么结果可言,结果是在说这话之前放进罐子里的。


80、error 和 exception 有什么区别?

解析:error表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。exception表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。

目录
相关文章
|
1天前
|
Java
【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
10 0
|
2天前
|
安全 Java 程序员
【Java多线程】面试常考——锁策略、synchronized的锁升级优化过程以及CAS(Compare and swap)
【Java多线程】面试常考——锁策略、synchronized的锁升级优化过程以及CAS(Compare and swap)
5 0
|
4天前
|
Java
三个可能的Java面试题
Java垃圾回收机制自动管理内存,回收无引用对象的内存,确保内存有效利用。多态性允许父类引用操作不同子类对象,如Animal引用可调用Dog的方法。异常处理机制通过try-catch块捕获和处理程序异常,例如尝试执行可能导致ArithmeticException的代码,catch块则负责处理异常。
25 9
|
14天前
|
Java
【JAVA面试题】static的作用是什么?详细介绍
【JAVA面试题】static的作用是什么?详细介绍
|
14天前
|
Java
【JAVA面试题】final关键字的作用有哪些
【JAVA面试题】final关键字的作用有哪些
|
14天前
|
JavaScript 前端开发 Java
【JAVA面试题】什么是引用传递?什么是值传递?
【JAVA面试题】什么是引用传递?什么是值传递?
|
14天前
|
安全 Java
【JAVA面试题】什么是对象锁?什么是类锁?
【JAVA面试题】什么是对象锁?什么是类锁?
|
14天前
|
存储 自然语言处理 Java
【JAVA面试题】什么是代码单元?什么是码点?
【JAVA面试题】什么是代码单元?什么是码点?
|
14天前
|
Java 程序员
【JAVA面试题】基本类型的强制类型转换是否会丢失精度?引用类型的强制类型转换需要注意什么?
【JAVA面试题】基本类型的强制类型转换是否会丢失精度?引用类型的强制类型转换需要注意什么?
|
14天前
|
Java
【JAVA面试题】什么是深拷贝?什么是浅拷贝?
【JAVA面试题】什么是深拷贝?什么是浅拷贝?