Java面试一

基础一

知识点一

  • 集合类:List和Set比较,各自的子类比较(ArrayList,Vector,LinkedList;HashSet,TreeSet)。
    ArrayList : 列表,可以说是动态数组;有序的,可以重复的;非线程安全。
    LinkedList : 双向列表,可以根据下个节点访问上一个节点;有序的,可以重复的;线程安全。
    Vector : 有序的,可以重复的;线程安全。
    HashSet: 不可重复,无序的。
    TreeSet : 有序的,不可重复。
  • HashMap的底层实现,之后会问ConcurrentHashMap的底层实现。
    HashMap:本质是数组链表,Key.hashCode()做主键。Key-Value的对象。HashMap是非线程安全的。
    ConcurrentHashMap在线程安全的基础上提供了更好的写并发能力,但同时降低了对读一致性的要求。
    LinkedHashMap 与 LRUcache:
    LRU 缓存利用了这样的一种思想。LRU 是 Least Recently Used 的缩写,翻译过来就是“最近最少使用”,也就是说,LRU 缓存把最近最少使用的数据移除,让给最新读取的数据。而往往最常读取的,也是读取次数最多的,所以,利用 LRU 缓存,我们能够提高系统的 performance。
import java.util.LinkedHashMap;
import java.util.Collection;
import java.util.Map;
import java.util.ArrayList;

/**
 * An LRU cache, based on <code>LinkedHashMap</code>.
 *
 * <p>
 * This cache has a fixed maximum number of elements (<code>cacheSize</code>).
 * If the cache is full and another entry is added, the LRU (least recently
 * used) entry is dropped.
 *
 * <p>
 * This class is thread-safe. All methods of this class are synchronized.
 *
 * <p>
 * Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland<br>
 * Multi-licensed: EPL / LGPL / GPL / AL / BSD.
 */
public class LRUCache<K, V> {
    private static final float hashTableLoadFactor = 0.75f;
    private LinkedHashMap<K, V> map;
    private int cacheSize;

    /**
     * Creates a new LRU cache. 在该方法中,new LinkedHashMap<K,V>(hashTableCapacity,
     * hashTableLoadFactor, true)中,true代表使用访问顺序
     *
     * @param cacheSize
     *            the maximum number of entries that will be kept in this cache.
     */
    public LRUCache(int cacheSize) {
        this.cacheSize = cacheSize;
        int hashTableCapacity = (int) Math
                .ceil(cacheSize / hashTableLoadFactor) + 1;
        map = new LinkedHashMap<K, V>(hashTableCapacity, hashTableLoadFactor,
                true) {
            // (an anonymous inner class)
            private static final long serialVersionUID = 1;

            @Override
            protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
                return size() > LRUCache.this.cacheSize;
            }
        };
    }

    /**
     * Retrieves an entry from the cache.<br>
     * The retrieved entry becomes the MRU (most recently used) entry.
     *
     * @param key
     *            the key whose associated value is to be returned.
     * @return the value associated to this key, or null if no value with this
     *         key exists in the cache.
     */
    public synchronized V get(K key) {
        return map.get(key);
    }

    /**
     * Adds an entry to this cache. The new entry becomes the MRU (most recently
     * used) entry. If an entry with the specified key already exists in the
     * cache, it is replaced by the new entry. If the cache is full, the LRU
     * (least recently used) entry is removed from the cache.
     *
     * @param key
     *            the key with which the specified value is to be associated.
     * @param value
     *            a value to be associated with the specified key.
     */
    public synchronized void put(K key, V value) {
        map.put(key, value);
    }

    /**
     * Clears the cache.
     */
    public synchronized void clear() {
        map.clear();
    }

    /**
     * Returns the number of used entries in the cache.
     *
     * @return the number of entries currently in the cache.
     */
    public synchronized int usedEntries() {
        return map.size();
    }

    /**
     * Returns a <code>Collection</code> that contains a copy of all cache
     * entries.
     *
     * @return a <code>Collection</code> with a copy of the cache content.
     */
    public synchronized Collection<Map.Entry<K, V>> getAll() {
        return new ArrayList<Map.Entry<K, V>>(map.entrySet());
    }

    // Test routine for the LRUCache class.
    public static void main(String[] args) {
        LRUCache<String, String> c = new LRUCache<String, String>(3);
        c.put("1", "one"); // 1
        c.put("2", "two"); // 2 1
        c.put("3", "three"); // 3 2 1
        c.put("4", "four"); // 4 3 2
        if (c.get("2") == null)
            throw new Error(); // 2 4 3
        c.put("5", "five"); // 5 2 4
        c.put("4", "second four"); // 4 5 2
        // Verify cache content.
        if (c.usedEntries() != 3)
            throw new Error();
        if (!c.get("4").equals("second four"))
            throw new Error();
        if (!c.get("5").equals("five"))
            throw new Error();
        if (!c.get("2").equals("two"))
            throw new Error();
        // List cache content.
        for (Map.Entry<String, String> e : c.getAll())
            System.out.println(e.getKey() + " : " + e.getValue());
    }
}
  • 如何实现HashMap顺序存储:可以参考LinkedHashMap的底层实现。
    双向链表,详细参考http://wiki.jikexueyuan.com/project/java-collection/linkedhashmap.html
  • HashTable和ConcurrentHashMap的区别。
    HashTable不能包含null,HashMap可以包含null。虽然HashTable和ConcurrentHashMap都是线程安全的。但是HashTable是使用synchronized;ConcurrentHashMap使用分段锁实现,称为Segment;ConcurrentHashMap内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。ConcurrentHashMap中的HashEntry相对于HashMap中的Entry有一定的差异性:HashEntry中的value以及next都被volatile修饰,这样在多线程读写过程中能够保持它们的可见性。
static final class HashEntry<K,V> {
        final int hash;
        final K key;
        volatile V value;
        volatile HashEntry<K,V> next;
  • String,StringBuffer和StringBuilder的区别。
    StringBuffer、StringBuilder和String一样,也用来代表字符串。String类是不可变类,任何对String的改变都 会引发新的String对象的生成;StringBuffer则是可变类,任何对它所指代的字符串的改变都不会产生新的对象。既然可变和不可变都有了,为何还有一个StringBuilder呢?相信初期的你,在进行append时,一般都会选择StringBuffer吧!
    先说一下集合的故事,HashTable是线程安全的,很多方法都是synchronized方法,而HashMap不是线程安全的,但其在单线程程序中的性能比HashTable要高。StringBuffer和StringBuilder类的区别也是如此,他们的原理和操作基本相同,区别在于StringBufferd支持并发操作,线性安全的,适 合多线程中使用。StringBuilder不支持并发操作,线性不安全的,不适合多线程中使用。新引入的StringBuilder类不是线程安全的,但其在单线程中的性能比StringBuffer高。
  • Object的方法有哪些:比如有wait方法,为什么会有?wait和sleep的区别?
    有getClass(),equals(),hashCode(),toString(),notify(),notifyAll()。
    在java.lang.Thread类中,提供了sleep(),而java.lang.Object类中提供了wait(), notify()和notifyAll()方法来操作线程
    sleep()可以将一个线程睡眠,参数可以指定一个时间。而wait()可以将一个线程挂起,直到超时或者该线程被唤醒。wait有两种形式wait()和wait(milliseconds)。
  • JVM的内存结构,JVM的算法。
    JVM基本结构图:

这里写图片描述

具体划分为如下5个内存空间:(非常重要)

栈:存放局部变量
堆:存放所有new出来的东西
方法区:被虚拟机加载的类信息、常量、静态常量等。
程序计数器(和系统相关)
本地方法栈


知识点二

强引用,软引用和弱引用的区别。

1) 强引用:就是正常的引用。
GC是不会清理一个强引用引用的对象的,即使面临内存溢出的情况。

String str = "abc";
List<String> list = new Arraylist<String>();
list.add(str);
//在list集合里的数据不会释放,即使内存不足也不会

2) 软引用:SoftReference。
GC会在内存不足的时候清理引用的对象:

SoftReference reference = new SoftReference(object); 
object = null;

3) 弱引用:GC线程会直接清理弱引用对象,不管内存是否够用。

WeakReference reference = new WeakReference(object); 
object = null;

4) 虚引用:和弱引用一样,会直接被GC清理,而且通过虚引用的get方法不会得到对象的引用,形同虚设,这里弱引用是可以的。

PhantomReference refernce = new PhantomReference(object); 
object = null;
数组在内存中如何分配。

比如int[] arr=new int[3];
栈中为arr分配一个存储单位,存储的是指向一个新创建的数组的引用,该地址指向数组对象。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值