欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java面试高频知识点总结

程序员文章站 2024-03-23 20:17:58
...

Java基础

- NIO的实现原理
NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统 IO 基于字节流和字符流进行操作,而 NIO 基于 Channel 和 Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。
Java面试高频知识点总结
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计算对象的哈希码

  1. equals相等的两个对象,hashcode一定要相等,但hashcode相等的两个对象不一定equals相等
  2. 重写对象的equals方法必须重写hashCode方法,尽管不写也能通过编译;

- RuntimeException和Exception Error
Throwable 是 Java 语言中所有错误或异常的超类。下一层分为 Error 和 Exception。
Error:程序发生不可控的错误时通常通知用户并终止程序的进行,它代表编译时间和系统错误,用来指示合理的应用程序不应该试图捕获严重问题,由java虚拟机生成并抛出,包括动态连接失败,虚拟机错误等。
Exception:分为CheckedException和RuntimeException
RuntimeException: 如 NullPointerException 、 ClassCastException是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类
CheckedException:一般是外部错误,这种异常都发生在编译阶段,Java 编译器会强制程序去捕获此类异常,即会出现要求你把这段可能出现异常的程序进行 try catch,该类异常一般包括几个方面:

  1. 试图在文件尾部读取数据
  2. 试图打开一个错误格式的 URL
  3. 试图根据给定的字符串查找 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模型

相关标签: java