# Dive Into Java
1. **java中的几种基本数据类型是什么,各自占了多少字节。**
- 逻辑型
boolean
- 文本型
char
- 整数型
byte short int long
- 浮点型
float double
| 类型 | 字节数 |
| :-----: | :-------------------------: |
| boolean | 待定(java虚拟机规范写的是4个字节) |
| char | 2(java是unicode编码,一个字符占2个字节) |
| byte | 1 |
| short | 2 |
| int | 4 |
| long | 8 |
| float | 4 |
| double | 8 |
2. **Java中是否可以继承String类,为什么?**
不可以。String类有final修饰符。
3. **String,Stringbuffer,StringBuilder的区别**
String:字符串常量,不可变的对象
StringBuffer:字符串变量(线程安全)
StringBuilder:字符串变量(非线程安全)
一般情况下执性速度:StringBuilder>StringBuffer>String
4. **ArrayList 和 LinkedList 有什么区别?**
- ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
- 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
- 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
5. **讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当 new 的时候,他们的执行顺序。**
- 虚拟机在首次加载Java类时,会对静态初始化块、静态成员变量、静态方法(这三种统称为静态代码块)进行一次初始化 。
- 类实例创建过程:按照父子继承关系进行初始化,首先执行父类的初始化块部分,然后是父类的构造方法;再执行本类继承的子类的初始化块,最后是子类的构造方法 。
- 类实例销毁时候,首先销毁子类部分,再销毁父类部分。
new一个对象时,执行过程如下:
默认初始化(对成员赋0或NULL)—>进构造函数—>super()【父类也是该过程】—>显式初始化—>构造代码块—>构造函数中其它代码
6. **用过哪些 Map 类,都有什么区别。HashMap 是线程安全的吗?并发下使用的 Map 是什么,他们内部原理分别是什么,比如存储方式,hashcode,扩容,默认容量等。**
- HashMap
- 数据结构
HashMap底层实现还是数组,只是数组的每一项都是一条链。每次新建一个HashMap时,都会初始化一个table数组。table数组的元素为Entry节点。Entry为HashMap的内部类,它包含了键key、值value、下一个节点next,以及hash值。
- 默认容量
初始容量16,默认加载因子0.75。容量表示哈希表中桶的数量,初始容量是创建哈希表时的容量,加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度,它衡量的是一个散列表的空间的使用程度。
- 存储过程
HashMap保存数据的过程为:首先判断key是否为null,若为null,则直接调用putForNullKey方法。若不为空则先计算key的hash值,然后根据hash值搜索在table数组中的索引位置,如果table数组在该位置处有元素,则通过比较是否存在相同的key,若存在则覆盖原来key的value,否则将该元素保存在链头(最先保存的元素放在链尾)。若table在该处没有元素,则直接保存。
- 扩容
该临界点在当HashMap中元素的数量等于table数组长度*加载因子。但是扩容是一个非常耗时的过程,因为它需要重新计算这些数据在新table数组中的位置并进行复制处理。
- HashTable
类似HashMap。还是有一些不同的:
- HashTable基于Dictionary类,而HashMap是基于AbstractMap。Dictionary是什么?它是任何可将键映射到相应值的类的抽象父类,而AbstractMap是基于Map接口的骨干实现,它以最大限度地减少实现此接口所需的工作。
- HashMap可以允许存在一个为null的key和任意个为null的value,但是HashTable中的key和value都不允许为null。
- TreeMap
TreeMap的实现是红黑树算法的实现。
红黑树顾名思义就是节点是红色或者黑色的平衡二叉树,它通过颜色的约束来维持着二叉树的平衡。对于一棵有效的红黑树二叉树而言我们必须增加如下规则:
- 每个节点都只能是红色或者黑色
- 根节点是黑色
- 每个叶节点(NIL节点,空节点)是黑色的。
- 如果一个结点是红的,则它两个子节点都是黑的。也就是说在一条路径上不能出现相邻的两个红色结点。
- 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
- hashcode
- hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;
- 如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;
- 如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;
- 两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。
HashMap在并发执行put操作时会引起死循环,是因为多线程会导致HashMap的Entry链表形成环,一旦成环,Entry的next节点永远不为空,就会产生死循环。
Hashtable是Java中最老的Map类,它是一个线程安全的Map类,其公有方法均使用synchronize关键字修饰,这表示在多线程操作时,每个线程在操作之前都会锁住整个map,待操作完成后才释放,如线程1使用put进行元素添加,线程2不但不能使用put方法进行添加元素,也不能使用get方法来获取元素,所以竞争越激烈效率越低,这必然导致多线程时性能不佳。另外,Hashtable不能使用null作为key或者value。
ConcurrentHashMap的锁分段技术可有效提升并发访问率 。首先将数据分成一段一段地存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
7. **JAVA8 的 ConcurrentHashMap 为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何设计。**
取消segments字段,直接采用transient volatile HashEntry<K,V>[] table保存数据,采用table数组元素作为锁,从而实现了对每一行数据进行加锁,进一步减少并发冲突的概率。
将原先table数组+单向链表的数据结构,变更为table数组+单向链表+红黑树的结构。对于hash表来说,最核心的能力在于将key hash之后能均匀的分布在数组中。如果hash之后散列的很均匀,那么table数组中的每个队列长度主要为0或者1。但实际情况并非总是如此理想,虽然ConcurrentHashMap类默认的加载因子为0.75,但是在数据量过大或者运气不佳的情况下,还是会存在一些队列长度过长的情况,如果还是采用单向列表方式,那么查询某个节点的时间复杂度为O(n);因此,对于个数超过8(默认值)的列表,jdk1.8中采用了红黑树的结构,那么查询的时间复杂度可以降低到O(logN),可以改进性能。
8. **有没有有顺序的