“Java面经”的版本间的差异

来自qingwei personal wiki
跳转至: 导航搜索
(创建页面,内容为“== 基础== === interface, abstract class区别 === https://blog.csdn.net/b271737818/article/details/3950245 === string stringbuffer stringbuilder 区别 === ==…”)
 
(没有差异)

2018年9月19日 (三) 06:55的最新版本

基础

interface, abstract class区别

https://blog.csdn.net/b271737818/article/details/3950245

string stringbuffer stringbuilder 区别

集合

说一下数组和链表的区别?它们分别使用于什么场合?

数组和链表。数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,插入和删除容易。

HashMap冲突解决

Java 8 之前:HashMap和其他基于map的类都是通过链地址法解决冲突,它们使用单向链表来存储相同索引值的元素。在最坏的情况下,这种方式会将HashMap的get方法的性能从O(1)降低到O(n)。

Java 8中:为了解决在频繁冲突时hashmap性能降低的问题,Java 8中使用平衡树来替代链表存储冲突的元素。这意味着我们可以将最坏情况下的性能从O(n)提高到O(logn)。 在Java 8中使用常量TREEIFY_THRESHOLD来控制是否切换到平衡树来存储。目前,这个常量值是8,这意味着当有超过8个元素的索引一样时,HashMap会使用树来存储它们

为什么产生冲突: HashMap中调用hashCode()方法来计算hashCode。 由于在Java中两个不同的对象可能有一样的hashCode,所以不同的键可能有一样hashCode,从而导致冲突的产生。

hashmap put get实现原理(JDK1.8后)

  • put
  1. 对key的hashCode()做hash,然后再计算index; ----! 通过 array大小&hashcode 算出index, get时也是同样==先算hashcode, 有冲突后再equals
  2. 如果没碰撞直接放到bucket里;
  3. 如果碰撞了,以链表的形式存在buckets后;
  4. 如果碰撞导致链表过长(大于等于TREEIFY_THRESHOLD),就把链表转换成红黑树;
  5. 如果节点已经存在就替换old value(保证key的唯一性)
  6. 如果bucket满了(超过load factor*current capacity),就要resize。
  • get
  1. bucket里的第一个节点,直接命中;
  2. 如果有冲突,则通过key.equals(k)去查找对应的entry
    1. 若为树,则在树中通过key.equals(k)查找,O(logn);
    2. 若为链表,则在链表中通过key.equals(k)查找,O(n)。
  • put
public V put(K key, V value) {
    // 对key的hashCode()做hash
    return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    // tab为空则创建
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    // 计算index,并对null做处理
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        // 节点存在
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        // 该链为树
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        // 该链为链表
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        // 写入
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    // 超过load factor*current capacity,resize
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}
  • get
public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}
final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        // 直接命中
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        // 未命中
        if ((e = first.next) != null) {
            // 在树中get
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            // 在链表中get
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}

线程

HashMap是不是线程安全?为什么非线程安全或者说哪里体现了非线程安全?HashMap的读写线程安全吗?

那如何实现线程安全呢?Hashtable和ConcurrentHashMap实现方式?

synchronized锁实现原理?

实现线程安全的方式有哪些?并介绍一下实现?

了解线程池不?线程池的基本参数有哪些?

线程池是解决什么问题的?

spring框架

spring ioc是什么?实现了什么之间的反转?

spring aop 原理

spring, spring-boot, spring-cloud区别

spring-boot boot-starter了解吗

JVM

jvm内存模型

运行时内存模型:分为线程私有和共享数据区两大类

  • 线程私有的数据区:程序计数器、虚拟机栈、本地方法区
  • 所有线程共享的数据区:Java堆、方法区,在方法区内有一个常量池。

gc算法

数据库

三范式

  1. 第一范式:要求有主键,并且要求每一个字段原子性不可再分
  2. 第二范式:要求所有非主键字段完全依赖主键,不能产生部分依赖
  3. 第三范式:所有非主键字段和主键字段之间不能产生传递依赖

左连接,右连接

事务隔离级别

✔️表示会发生, ❌表示不会发生

隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read)
未提交读(Read uncommitted) ✔️ ✔️ ✔️
已提交读(Read committed) ✔️ ✔️
可重复读(Repeatable read) ✔️
可串行化(Serializable)
  • 未提交读(Read Uncommitted): 一个事务可以读取到另一个事务未提交的修改。
  • 提交读(Read Committed): 一个事务只能读取另一个事务已经提交的修改。
  • 可重复读(Repeated Read): 同一个事务中多次读取相同的数据返回的结果是一样的。因为:在事务执行期间会锁定该事务以任何方式引用的所有行。这是MySQL中InnoDB默认的隔离级别。
  • 可串行化(Serializable): 事务串行执行。每次读都需要获得表级共享锁,读写相互都会阻塞


  1. 不可重复读和幻读的区别
如果使用锁机制来实现这两种隔离级别,在可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免。需要Serializable隔离级别 ,读用读锁,写用写锁,读锁和写锁互斥,这么做可以有效的避免幻读、不可重复读、脏读等问题,但会极大的降低数据库的并发能力。

所以说不可重复读和幻读最大的区别,就在于如何通过锁机制来解决他们产生的问题。

MySQL、ORACLE、PostgreSQL等成熟的数据库,出于性能考虑,都是使用了以乐观锁为理论基础的MVCC(多版本并发控制)来避免这两种问题。

在(A, B)上创建索引,select B时这个索引有用吗,为什么

最左原则

分布式事务(DTS)

算法

快排

swap(a, b) 深究 值拷贝,引用传递

public class Main {

    // 反射
    public void swap(Integer a, Integer b) {
        int temp = a;

        try {
            Class clazz = a.getClass();
            Field value = clazz.getDeclaredField("value");
            value.setAccessible(true);
            value.setInt(a, b);
            value.setInt(b, temp);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Main main = new Main();
        /* Integer 换成 int a = 1, 不行!!!*/
        Integer a = 1;
        Integer b = 2;
        main.swap(a, b);
        System.out.println("a = " + a + ", b= " + b);
    }
}

两个数组找出相同的元素,说出4种不同的方法,并分析时间空间复杂度

Java collection实现一个cache

实现一个分布式锁