欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java基础面试题02

程序员文章站 2022-03-15 19:37:32
...

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的区别

  1. ==既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
  2. equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也是==;我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中用的比较多,久而久之,形成了equals是比较值的错误观点。
  3. 具体要看自定义类里有没有重写Object的equals方法来判断。
  4. 通常情况下,重写equals方法,会比较类中的相应属性是否都相等。
  5. 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的数组大小。扩容时扩充为原来的两倍