Java面试高频知识点总结
Java基础
- NIO的实现原理
NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统 IO 基于字节流和字符流进行操作,而 NIO 基于 Channel 和 Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。
IO是面向流的,而NIO是面向缓冲区的。
NIO的缓冲区:
JavaIO面向流意味着每次从流中读取一个或多个字节直到读取完所有字节,数据没有被缓存到任何地方,不能前后移动流中的数据,只有将其混村到一个缓冲区中。而NIO可以,数据读取到缓冲区中随后可以在缓冲区中移动,但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
NIO的非阻塞:
JavaIO各种流是阻塞的,一个线程调用读写方法时,线程会被阻塞直到数据被处理,期间不能干别的事。
NIO的非阻塞,当一个线程从某通道发送读取数据请求,它只能得到当前可用的数据,如果没有他所需要的数据,不会等待也不会阻塞,直到数据变得可用之前它都可以去干任何事。 线程通常将非阻塞 IO 的空闲时间用于在其它通道上执行 IO 操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
- HashMap、HashSet
HashMap实现原理
HashMap允许K-V为空,当其key为null时,调用putForNullKey方法,放入到table[0]的这个位置
HashMap的扩容机制:
扩容必须满足两个条件:
1、 存放新值的时候当前已有元素的个数必须大于等于阈值 (数组大小*loadfactor加载因子大小)
2、 存放新值的时候当前存放数据发生hash碰撞(当前key计算的hash值换算出来的数组下标位置已经存在值)
扩容后放在二倍大小的新数组中
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
//判断是否有超出扩容的最大值,如果达到最大值则不进行扩容操作
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
// transfer()方法把原数组中的值放到新数组中
transfer(newTable, initHashSeedAsNeeded(newCapacity));
//设置hashmap扩容后为新的数组引用
table = newTable;
//设置hashmap扩容新的阈值
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
transfer()在实际扩容时候把原来数组中的元素放入新的数组中
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) {
while(null != e) {
Entry<K,V> next = e.next;
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
//通过key值的hash值和新数组的大小算出在当前数组中的存放位置
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
}
因为上面这两个条件,所以存在下面这些情况
(1)、就是hashmap在存值的时候(默认大小为16,负载因子0.75,阈值12),可能达到最后存满16个值的时候,再存入第17个值才会发生扩容现象,因为前16个值,每个值在底层数组中分别占据一个位置,并没有发生hash碰撞。
(2)、当然也有可能存储更多值(超多16个值,最多可以存26个值)都还没有扩容。原理:前11个值全部hash碰撞,存到数组的同一个位置(这时元素个数小于阈值12,不会扩容),后面所有存入的15个值全部分散到数组剩下的15个位置(这时元素个数大于等于阈值,但是每次存入的元素并没有发生hash碰撞,所以不会扩容),前面11+15=26,所以在存入第27个值的时候才同时满足上面两个条件,这时候才会发生扩容现象。
HashSet实现原理
HashSet的去重原理:
HashMap 的 put() 方法添加 key-value 对时,当新放入 HashMap 的 Entry 中 key 与集合中原有 Entry 的 key 相同(hashCode()返回值相等,通过 equals 比较也返回 true),新添加的 Entry 的 value 会将覆盖原来 Entry 的 value(HashSet 中的 value 都是PRESENT),但 key 不会有任何改变,因此如果向 HashSet 中添加一个已经存在的元素时,新添加的集合元素将不会被放入 HashMap中,原来的元素也不会有任何改变,这也就满足了 Set 中元素不重复的特性。
- LinkedHashMap
LinkedHashMap实现原理
- ConcurrentHashMap
ConcurrentHashMap实现原理
成员是segment数组,而segment数组的数据结构为HashEntry的数组,HashEntry存储键值对可以构成链表。
并发写:put方法要加锁完成,先调用ConcurrentHashMap的put()方法,再调用segment的put()方法,先加锁得到hash对应table中的索引index,再找到hash对应的具体是那个桶,即哪个hashEntry链表,操作完后解锁。
加锁操作针对的hash值对应的是某个segment而不是整个ConcurrentHashMap,因此其他线程也可以对别的segment进行put操作,同时读线程不会因本线程加锁而阻塞。
- hashcode与equals方法
equals只能用于对象间来判断是否相等,hashcode计算对象的哈希码
- equals相等的两个对象,hashcode一定要相等,但hashcode相等的两个对象不一定equals相等
- 重写对象的equals方法必须重写hashCode方法,尽管不写也能通过编译;
- RuntimeException和Exception Error
Throwable 是 Java 语言中所有错误或异常的超类。下一层分为 Error 和 Exception。
Error:程序发生不可控的错误时通常通知用户并终止程序的进行,它代表编译时间和系统错误,用来指示合理的应用程序不应该试图捕获严重问题,由java虚拟机生成并抛出,包括动态连接失败,虚拟机错误等。
Exception:分为CheckedException和RuntimeException
RuntimeException: 如 NullPointerException 、 ClassCastException是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类
CheckedException:一般是外部错误,这种异常都发生在编译阶段,Java 编译器会强制程序去捕获此类异常,即会出现要求你把这段可能出现异常的程序进行 try catch,该类异常一般包括几个方面:
- 试图在文件尾部读取数据
- 试图打开一个错误格式的 URL
- 试图根据给定的字符串查找 class 对象,而这个字符串表示的类并不存在
如 I/O 错误导致的 IOException、SQLException。
- 注解的使用
Java注解
- 基本数据类型
数值型:
整数类型:
类型 | 大小 | 范围 |
---|---|---|
byte | 1字节 | -128~127 |
short | 2字节 | -2^15 ~ 2^15 -1(-32768~32767) |
int | 4字节 | -2^31 ~2^31-1 |
long | 8字节 | -2^63 ~2^63-1 |
浮点类型:
类型 | 大小 |
---|---|
float | 4字节 |
double | 8字节 |
字符型:
char | 2字节 |
---|
布尔型:
boolean | 1字节 |
---|
- 五种常见的网络IO模型
1.阻塞IO模型
2.非阻塞IO模型
3.多路复用IO模型
4.信号驱动IO模型
5.异步IO模型
上一篇: OC开发之——分类的基本应用(31)
下一篇: OC 类工厂及类的本质和启动过程