Java基础面试题02
1. 为什么super(…)和this(…)调用语句不能同时在一个构造器内出现?为什么super(…)和this(…)调用语句只能作为构造器的第一句?
不能同时出现是因为他们只能作为第一句。因为无论哪个构造器创建子类对象,需要先保证父类的初始化。目的:当子类继承父类后,“继承”父类中所有的属性和方法。因此子类有必要知道父类如何为对象进行初始化。
2. finalize()方法
垃圾回收机制只回收JVM堆内存里的对象空间。
对其他物理连接,比如数据库连接、输入流输出流、Socket连接无能为力
现在的JVM有多种垃圾回收实现算法,表现各异。
垃圾回收发生具有不可预知性,程序无法精确控制垃圾回收机制执行。
可以将对象的引用变量设置为null,暗示垃圾回收机制可以回收该对象。
程序员可以通过System.gc()或者Runtime.getRuntime().gc()来通知系统进行垃圾回收,会有
一些效果,但是系统是否进行垃圾回收依然不确定。
垃圾回收机制回收任何对象之前,总会先调用它的finalize方法(如果覆盖该方法,让一
个新的引用变量重新引用该对象,则会重新**对象)。
永远不要主动调用某个对象的finalize方法,应该交给垃圾回收机制调用。
3. 读程序,看输出
可变参数,确定参数
public class tets {
public static void main(String[] args) {
Base base = new Sub();
base.add(1,2,3);// sub
Sub sub = (Sub) base;
sub.add(1,2,3);//add
}
}
class Base{
public void add(int a,int ... arr){
System.out.println("base");
}
}
class Sub extends Base{
public void add(int a,int[] arr){
System.out.println("sub");
}
public void add(int a,int b,int c){
System.out.println("add");
}
}
可变形参相当于数组。因为有多态,是去寻找父类中的add方法,又发现子类有重写方法,所以调用的是子类重写的方法。
在可变参数和确定参数,优先考虑确定参数
4. 读程序,看输出
Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1);// 输出 1.0 因为编译的时候会进行自动类型提升
Object o2;
if (true)
o2 = new Integer(1);
else
o2 = new Double(2.0);
System.out.println(o2);//1 没有进行类型提升
5. Integer源码剖析
读程序,看输出
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);
Integer m = 1;
Integer n = 1;
System.out.println(m == n);
Integer x = 128;
Integer y = 128;
System.out.println(x == y);
第一个输出为false,因为new的是两个对象
第二个输出true,第三个输出false
分析Integer源码:Integer内部定义了一个内部类:IntegerCache,这个内部类中定义了一个数组。
数组保存了从-128~127范围的整数,如果我们使用自动装箱的方式,给Integer赋值这个范围的数,那么就会直接使用数组中的元素,没有再去new一个对象,如果超过这个范围,则会去new一个对象
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.misc.VM.getSavedProperty("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() {}
}
6. == 和equals的区别
- ==既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
- equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也是==;我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中用的比较多,久而久之,形成了equals是比较值的错误观点。
- 具体要看自定义类里有没有重写Object的equals方法来判断。
- 通常情况下,重写equals方法,会比较类中的相应属性是否都相等。
- String、Date、File、包装类都重写了equals方法
7. System.out.println()是怎么输出一个对象的
源码:
public void println(Object x) {
String s = String.valueOf(x);// 调用的是该方法
synchronized (this) {
print(s);
newLine();
}
}
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
可以看到实际上调用的是对象的toString()方法
String、Date、File、包装类都重写了toString()方法
8. ArrayList、LinkedList和Vector三者的异同
相同点:三个都是实现了List接口,存储数据相同的特点:存储有序的、可重复的数据
不同点:
①ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[]数组
②LinkedList:底层使用双向链表存储;所以对频繁插入、删除的操作效率比ArrayList高
③Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[]数组
1. ArrayList
JDK1.7情况下:
ArrayList list = new ArrayList(); 底层创建了长度是10的Object[]数组elementData
list.add(123);// elementData[0] = new Integer(123)
....
list.add(11);// 如果此时的添加导致底层的elementData数组容量不够,则扩容。默认情况下,扩容为原来的1.5倍,同时将原有数组的内容复制到新的数组
JDK1.8的变化:
ArrayList list = new ArrayList(); 底层Object[]数组elementData为{},没有创建长度是10的Object[]数组elementData
// 第一次调用add()方法时,才创建了大小为10的数组
小结:JDK1.7中ArrayList的对象的创建方式类似于单例模式中的饿汉式。而JDK1.8中的类似于懒汉式
2. LinkedList
内部节点的定义:体现了双向链表
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
3. Vector
new Vector()底层创建了默认为10的数组大小。扩容时扩充为原来的两倍
上一篇: python3-迭代器