JAVA面试题整理(1)-基础

1、List 和 Set 的区别 

共同点:它们都是Collection的子接口

区别:

List:这个接口能够精准的记录每一个元素的插入位置(换句话说就是这个接口内容所有元素是按照顺序去保存的),使用者可以通过索引的方式去取得某个元素的值,这个跟java中数组有点类似,List中保存的所有数据允许重复
Set:这个接口无法记录每一个元素的具体位置,整个集合中所有元素是无序排列的,并且Set存储数据集合是不允许有重复的元素的。

既然List和Set均为接口,那么就不能直接实例化,需要借助实现他们接口的子类进行实例化,由此来使用接口中提供的各种方法。
实现List接口的子类中包括:ArrayList,LinkedList和Vector
其中ArrayList这个类是类似数组形式的集合实例化,而LinkedList类则是链表形式的实例化,具体的差别在于数据结构上面的不同。
Vector 类非常类似ArrayList,两者的不同之处在于Vector是同步的方法,在多线程操作的时候可能会抛出ConcurrentModificationException。
实现Set接口的子类中包括:HasSet

2、HashSet 是如何保证不重复的 

根据HashSet.add(E e)的JDK源码,发现HashSet竟然是借助HashMap来实现的,利用HashMap中Key的唯一性,来保证HashSet中不出现重复值。

当调用add(E e)方法时候,首先会调用Object的hashCode方法判hashCode是否已经存在,如不存在则直接插入元素;
如果已存在则调用Object对象的equals方法判断是否返回true,如果为true则说明元素已经存在,如为false则插入元素。

public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable
{
    static final long serialVersionUID = -5024744406713321676L;

    private transient HashMap<E,Object> map;

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }

    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }
    
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    } 
    public Object clone() {
        try {
            HashSet<E> newSet = (HashSet<E>) super.clone();
            newSet.map = (HashMap<E, Object>) map.clone();
            return newSet;
        } catch (CloneNotSupportedException e) {
            throw new InternalError();
        }
    }
}
    /**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     *
     * @param key key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

3、HashMap 是线程安全的吗,为什么不是线程安全的(最好画图说明多线程环境下不安全)? 

参考:http://www.importnew.com/22011.html

4、HashMap 的扩容过程


5、HashMap 1.7 与 1.8 的 区别,说明 1.8 做了哪些优化,如何优化的?


6、final finally finalize 

final修饰符(关键字)
被final修饰的类,就意味着不能再派生出新的子类,不能作为父类而被子类继承。因此一个类不能既被abstract声明,又被final声明。将变量或方法声明为final,可以保证他们在使用的过程中不被修改。被声明为final的变量必须在声明时给出变量的初始值,而在以后的引用中只能读取。被final声明的方法也同样只能使用,不能重载。

finally(关键字)
finally是在异常处理时提供finally块来执行任何清除操作。不管有没有异常被抛出、捕获,finally块都会被执行。
try块中的内容是在无异常时执行到结束。
catch块中的内容,是在try块内容发生catch所声明的异常时,跳转到catch块中执行。
finally块则是无论异常是否发生,都会执行finally块的内容,所以在代码逻辑中有需要无论发生什么都必须执行的代码,就可以放在finally块中。

finalize是方法名
java技术允许使用finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。
它是在object类中定义的,因此所有的类都继承了它。子类覆盖finalize() 方法以整理系统资源或者被执行其他清理工作。
finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。

7、强引用 、软引用、 弱引用、虚引用

不同的引用,根据被GC(垃圾处理器)回收的可能性分为 强、弱、软、虚引用。

强引用(Strong Reference):是最难被GC回收的,宁可虚拟机抛出异常,中断程序,也不回收强引用指向的实例对象
软引用 (SoftReference):在内存不足时,GC会回收软引用指向的对象
弱引用(WeakReference):不管内存足不足,只要我GC,我都可能回收弱引用指向的对象
虚引用(PhantomReference ):该回收就回收,无所谓了,虚引用,我随便回收你,也叫幽灵引用,其实就是相当于没有指向任何实例对象

8、Java反射-What,Why,How

What is java reflection?
Java Reflaction in Action有这么一句话可以解释:反射是运行中的程序检查自己和软件运行环境的能力,它可以根据它发现的进行改变。
通俗的讲就是反射可以在运行时根据指定的类名获得类的信。

Why?
我们为什么要使用反射,它的作用是什么,它在实际的编程中有什么应用。
首先我们先明确两个概念,静态编译和动态编译。

静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
我们可以明确的看出动态编译的好处,而反射就是运用了动态编译创建对象。
那么我们再来看看实际中反射又有什么好处(往往对比能更加直观的向我们展示两者的不同)?

package com.demo.reflection;

public interface Car {

    public void run();
}
package com.demo.reflection;

public class Benz implements Car {

    @Override
    public void run() {
        System.out.println("Benz running...");
    }
}
package com.demo.reflection;

public class BMW implements Car {
    @Override
    public void run() {
        System.out.println("BMW running...");
    }
}
package com.demo.reflection;

public class ReflectionDemo {
    public static void main(String [] args) {
        Benz benz = (Benz) Factory.getInstance("com.demo.reflaction.Benz");
        benz.run();
    }
}

class Factory{
//    反射前编码
//    public static Car getInstance(String brand) {
//        Car car = null;
//
//        if ("Benz".equalsIgnoreCase(brand)) {
//            car = new Benz();
//        }
//        if ("BMW".equalsIgnoreCase(brand)) {
//            car = new BMW();
//        }
//
//        return  car;
//    }
    
    //反射后编码
    public static Object getInstance(String className) {
        Object obj = null;

        try {
            obj = Class.forName(className).newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }

        return  obj;
    }
}

在出现新的品牌汽车的时候,可以完全不用去修改原有代码。

为什么不全部都用反射呢?因为它的开销是什么昂贵的,尽量在最需要的地方使用反射。

How?
Class c=Class.forName("className");
注明:className必须为全名,也就是得包含包名。

创建对象的实例
Object obj=c.newInstance();

获得构造函数的方法
Constructor getConstructor(Class[] params) //根据指定参数获得public构造器
Constructor[] getConstructors() //获得public的所有构造器
Constructor getDeclaredConstructor(Class[] params) //根据指定参数获得public和非public的构造器
Constructor[] getDeclaredConstructors() //获得public的所有构造器

获得类方法的方法
Method getMethod(String name, Class[] params) //根据方法名,参数类型获得方法
Method[] getMethods() //获得所有的public方法
Method getDeclaredMethod(String name, Class[] params) //根据方法名和参数类型,获得public和非public的方法
Method[] getDeclaredMethods() //获得所以的public和非public方法

获得类中属性的方法
Field getField(String name) //根据变量名得到相应的public变量
Field[] getFields() //获得类中所以public的方法
Field getDeclaredField(String name) //根据方法名获得public和非public变量
Field[] getDeclaredFields() //获得类中所有的public和非public方法

9、Arrays.sort 实现原理和 Collection 实现原理


10、LinkedHashMap的应用


11、cloneable接口实现原理


12、异常分类以及处理机制


13、wait和sleep的区别


14、数组在内存中如何分配

 

 

 

答案待补充... ...

posted @ 2018-07-29 14:31  阿東哥©  阅读(214)  评论(0编辑  收藏  举报