查看“Java面经”的源代码
←
Java面经
跳转至:
导航
,
搜索
因为以下原因,您没有权限编辑本页:
您所请求的操作仅限于该用户组的用户使用:
用户
您可以查看与复制此页面的源代码。
== 基础== === interface, abstract class区别 === https://blog.csdn.net/b271737818/article/details/3950245 === string stringbuffer stringbuilder 区别 === == 集合 == === 说一下数组和链表的区别?它们分别使用于什么场合? === 数组和链表。数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,插入和删除容易。 === HashMap冲突解决 === <b>Java 8 之前:</b>HashMap和其他基于map的类都是通过<font color="green">链地址法</font>解决冲突,它们使用<font color="green">单向链表来存储相同索引值的元素</font>。在最坏的情况下,这种方式会将HashMap的get方法的性能从O(1)降低到O(n)。<br /> <b>Java 8中:</b>为了解决在频繁冲突时hashmap性能降低的问题,Java 8中<font color="green">使用平衡树来替代链表存储冲突的元素。</font>这意味着我们可以将最坏情况下的性能从O(n)提高到O(logn)。 在Java 8中使用常量TREEIFY_THRESHOLD来控制是否切换到平衡树来存储。目前,这个常量值是8,这意味着<font color="green">当有超过8个元素的索引一样时,HashMap会使用树来存储它们</font>。<br /> <b>为什么产生冲突:</b> HashMap中调用hashCode()方法来计算hashCode。 由于在Java中两个不同的对象可能有一样的hashCode,所以不同的键可能有一样hashCode,从而导致冲突的产生。 === hashmap put get实现原理(JDK1.8后) === * put # 对key的hashCode()做hash,然后再计算index; ----! 通过 array大小&hashcode 算出index, get时也是同样==先算hashcode, 有冲突后再equals # 如果没碰撞直接放到bucket里; # 如果碰撞了,以链表的形式存在buckets后; # 如果碰撞导致链表过长(大于等于TREEIFY_THRESHOLD),就把链表转换成红黑树; # 如果节点已经存在就替换old value(保证key的唯一性) # 如果bucket满了(超过load factor*current capacity),就要resize。<br /> * get # bucket里的第一个节点,直接命中; # 如果有冲突,则通过key.equals(k)去查找对应的entry ## 若为树,则在树中通过key.equals(k)查找,O(logn); ## 若为链表,则在链表中通过key.equals(k)查找,O(n)。 * <b>put</b> <source lang="java"> 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; } </source> * <b>get</b> <source lang="java"> 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; } </source> == 线程 == === HashMap是不是线程安全?为什么非线程安全或者说哪里体现了非线程安全?HashMap的读写线程安全吗? === === 那如何实现线程安全呢?Hashtable和ConcurrentHashMap实现方式? === === synchronized锁实现原理? === === 实现线程安全的方式有哪些?并介绍一下实现? === === 了解线程池不?线程池的基本参数有哪些? === === 线程池是解决什么问题的? === == spring框架 == === spring ioc是什么?实现了什么之间的反转? === === spring aop 原理 === === spring, spring-boot, spring-cloud区别 === === spring-boot boot-starter了解吗 === == JVM == === jvm内存模型 === <b>运行时内存模型:</b>分为线程私有和共享数据区两大类 * 线程私有的数据区:程序计数器、虚拟机栈、本地方法区 * 所有线程共享的数据区:Java堆、方法区,在方法区内有一个常量池。 <gallery widths=800px heights=400px> jvm_memory.png|运行时内存模型 </gallery> === gc算法 === == 数据库 == === 三范式 === # 第一范式:要求有主键,并且要求每一个字段原子性不可再分 # 第二范式:要求所有非主键字段完全依赖主键,不能产生部分依赖 # 第三范式:所有非主键字段和主键字段之间不能产生传递依赖 === 左连接,右连接 === === 事务隔离级别 === ✔️表示会发生, ❌表示不会发生 {| class="wikitable" |- ! 隔离级别 !! 脏读(Dirty Read) !! 不可重复读(NonRepeatable Read) !! 幻读(Phantom Read) |- | 未提交读(Read uncommitted) ||✔️||✔️|| ✔️ |- | 已提交读(Read committed)|| ❌ || ✔️ || ✔️ |- | 可重复读(Repeatable read)|| ❌ || ❌ || ✔️ |- | 可串行化(Serializable) ||❌ || ❌ || ❌ |} * <font color="red"><b>未提交读(Read Uncommitted):</b></font> 一个事务可以读取到另一个事务未提交的修改。 * <font color="red"><b>提交读(Read Committed):</b></font> 一个事务只能读取另一个事务已经提交的修改。 * <font color="red"><b>可重复读(Repeated Read):</b></font> 同一个事务中多次读取相同的数据返回的结果是一样的。因为:在事务执行期间会锁定该事务以任何方式引用的所有行。这是MySQL中InnoDB默认的隔离级别。 * <font color="red"><b>可串行化(Serializable):</b></font> 事务串行执行。每次读都需要获得表级共享锁,读写相互都会阻塞 <br /> # <b>不可重复读和幻读的区别</b> <pre> 如果使用锁机制来实现这两种隔离级别,在可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免。需要Serializable隔离级别 ,读用读锁,写用写锁,读锁和写锁互斥,这么做可以有效的避免幻读、不可重复读、脏读等问题,但会极大的降低数据库的并发能力。 所以说不可重复读和幻读最大的区别,就在于如何通过锁机制来解决他们产生的问题。 MySQL、ORACLE、PostgreSQL等成熟的数据库,出于性能考虑,都是使用了以乐观锁为理论基础的MVCC(多版本并发控制)来避免这两种问题。 </pre> === 在(A, B)上创建索引,select B时这个索引有用吗,为什么 === === 最左原则 === === 分布式事务(DTS) === == 算法 == === 快排 === === swap(a, b) 深究 值拷贝,引用传递 === <source lang="java"> 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); } } </source> === 两个数组找出相同的元素,说出4种不同的方法,并分析时间空间复杂度 === === Java collection实现一个cache === === 实现一个分布式锁 === [[category: java]]
返回至
Java面经
。
导航菜单
个人工具
创建账户
登录
命名空间
页面
讨论
不转换
不转换
简体
繁體
大陆简体
香港繁體
澳門繁體
大马简体
新加坡简体
台灣正體
视图
阅读
查看源代码
查看历史
更多
搜索
导航
首页
最近更改
随机页面
帮助
工具
链入页面
相关更改
特殊页面
页面信息