当前位置: 首页 > news >正文

Java 基础面试

final、finalize 和 finally 的不同之处?
  • Final:是一个修饰符,可以修饰变量、方法和类。如果 final 修饰变量,意味着该变量的值在初始化后不 能被改变。
  • Finalize:方法是在对象被回收之前调用的方法, 给对象自己最后一个复活的机会,但是什么时候调用 finalize 没有保证。
  • Finally:与 try 和 catch 一起用于异常的处理。finally 块不一定会被执行,try前有return,虚拟机退出这两种情况是不会执行的。
String 是最基本的数据类型吗?

String 并不是最基本的数据类型,而是引用数据类型。

基本数据类型:

  • 整数类型byte(1 字节)short(2 字节)int(4 字节)long(8 字节)
  • 浮点类型float(4 字节)double(8 字节)
  • 字符类型char(2 字节)
  • 布尔类型boolean(理论上占 1 位,实际实现中通常占 1 字节)。
Java引用类型包括哪些?
  • 强引用(StrongReference)
  • 软引用(SoftRefernce)
  • 弱引用(WeakReference)
  • 虚引用(PhantomReference)
Http和Https的区别
  • https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
  • http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
  • http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  • http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
cookie和session的区别?

Cookie主要存放在客户端,session主要存放在服务端,cookiec存储的数据一般不能超过 4KB,Session 没有数据大小的限制。

new String () 一共创建了几个对象?

如果字符串常量池没有该字符串,会创建 2 个对象,一个在堆中,一个在字符串常量池;如果字符串常量池已经存在该字符串,则只在堆中创建 1 个对象 。

序列化和反序列化的底层实现原理是什么?
  • 序列化:将对象的状态信息转换为字节流,以便在网络传输或存储到文件中。Java 的 ObjectOutputStream 类通过反射获取对象的属性和值,并按照一定的格式将其写入字节流。

  • 反序列化:将字节流重新转换为对象。Java 的 ObjectInputStream 类从字节流中读取数据,并根据保存的对象信息重建对象。

hashCode 和 equals 方法的区别和联系是什么?
  • 区别:hashCode 方法返回一个哈希值,用于在哈希表中快速定位对象;equals 方法用于比较两个对象的内容是否相等。

  • 联系:如果两个对象 equals 方法返回 true,那么它们的 hashCode 值必须相等;但如果两个对象 hashCode 值相等,equals 方法不一定返回 true 。

若 hashCode 方法永远返回 1 或者一个常量会产生什么结果?

会导致所有对象的哈希值相同,在哈希表中会产生大量的哈希冲突,所有元素都会存储在同一个链表中,查找、插入和删除操作的时间复杂度会退化为 O (n),严重影响哈希表的性能。

讲讲 String、StringBuilder、StringBuffer 的区别和使用场景?
  • 不可变性:String 是不可变的,每次修改都会生成新的 String 对象;StringBuilder 和 StringBuffer 是可变的。

  • 线程安全性:StringBuffer 是线程安全的,内部方法使用 synchronized 关键字修饰;StringBuilder 是非线程安全的 。

  • 使用场景:如果字符串操作较少,使用 String;如果在单线程环境下进行大量字符串拼接操作,使用 StringBuilder;如果在多线程环境下进行大量字符串拼接操作,使用 StringBuffer。

Object 类中常见的方法有哪些?为什么 wait 和 notify 会放在 Object 里边?
  • 常见方法:equals、hashCode、toString、clone、finalize、wait、notify、notifyAll 等。

  • 原因:因为任意对象都可以作为锁对象,而线程等待和唤醒操作是基于锁的,所以将 wait 和 notify 方法放在 Object 类中,这样所有对象都可以使用这些方法来实现线程间的通信。

浅拷贝和深拷贝的区别是什么?
  • 浅拷贝:只复制对象的引用,不复制对象本身。修改新对象的引用类型属性会影响原对象。

  • 深拷贝:不仅复制对象的引用,还复制对象本身。修改新对象的引用类型属性不会影响原对象。

反射的作用与实现原理是什么?
  • 作用:在运行时获取类的信息,包括类的属性、方法、构造函数等,并可以动态创建对象、调用方法、访问属性。常用于框架开发、测试框架、依赖注入等场景。

  • 实现原理:Java 的反射机制是通过 java.lang.reflect 包下的类来实现的。通过 Class 类获取类的信息,通过 Constructor 类创建对象,通过 Method 类调用方法,通过 Field 类访问属性。

Java 提供的排序算法是怎么实现的?
  • Arrays.sort():对于基本数据类型,使用双轴快速排序(Dual-Pivot Quicksort);对于对象类型,使用 TimSort,它是归并排序和插入排序的结合,具有稳定排序的特点。

  • Collections.sort():底层调用 Arrays.sort () 对 List 进行排序。

HashMap 1.7 和 1.8 的实现区别是什么?
  • 数据结构:1.7 采用数组 + 链表,1.8 引入了红黑树。当链表长度超过阈值(8)时,链表会转换为红黑树,以提高查找效率。

  • hash 算法:1.7 的 hash 算法相对复杂,1.8 简化了 hash 算法,提高了计算效率。

  • 扩容机制:1.7 在扩容时,需要重新计算每个元素的 hash 值并重新插入;1.8 在扩容时,部分元素可以直接迁移到新的数组位置,减少了重新计算 hash 值的开销。

HashMap 中插入、添加、删除元素的时间复杂度是多少?

理想情况下,时间复杂度为 O (1),因为 HashMap 基于哈希表,通过计算哈希值直接定位元素位置。但在哈希冲突严重时,链表会变长,时间复杂度会退化为 O (n);当链表转换为红黑树后,时间复杂度为 O (log n) 。

HashMap 的默认空间、扩容因子等是怎样的?
  • 默认空间:16。

  • 扩容因子:0.75。当 HashMap 中的元素个数达到容量的 0.75 倍时,会进行扩容,扩容后的容量是原来的 2 倍。

为什么在HashMap会使用到红黑树?

如果在Index冲突过多的情况下,在链表上的查询的效率会很慢【时间复杂度是O(n)】,所以在链表长度大于8并且数组长度大于64是就会转为红黑树

HashMap扩容

HashMap扩容是先以原数组长度乘以0.75进行提前扩容,以2倍进行扩容,如果默认长度是16的话,那么会在12的时候就会提前扩容

HashMap加载因子为什么是0.75?

① 如果加载因子太小,key冲突的概率就比较小,但是非常浪费内存空间
② 如果加载因子太大,key冲突的概率就比较大,但是可利用空间就非常好
③ 加载因子为0.75也是官方测试出来的数据,在空间和内存上处于最佳值

HashMap,LinkedHashMap,TreeMap 有什么区别?
  1. 底层数据结构

    • HashMap:HashMap 底层基于哈希表实现,它使用数组和链表(或红黑树)结合的方式来存储键值对。数组中的每个位置被称为一个桶(bucket),当发生哈希冲突时(即不同的键计算出相同的哈希值),会在对应的桶位置以链表或红黑树的形式存储多个元素。当链表长度超过一定阈值(默认为 8)且数组长度达到 64 时,链表会转换为红黑树,以提高查找效率。
    • LinkedHashMap:LinkedHashMap 继承自 HashMap,它在 HashMap 的基础上维护了一个双向链表,用于记录元素的插入顺序或访问顺序。这个双向链表使得 LinkedHashMap 可以保持元素的插入顺序或者在访问元素时将其移动到链表尾部,从而实现按访问顺序排序。
    • TreeMap:TreeMap 底层基于红黑树(一种自平衡的二叉搜索树)实现。红黑树的每个节点都存储一个键值对,并且按照键的自然顺序或者指定的比较器顺序对元素进行排序。这意味着 TreeMap 中的元素始终是有序的。
  2. 元素顺序

    • HashMap:不保证元素的顺序,元素的存储和遍历顺序是无序的,这是因为元素的位置是根据键的哈希值决定的,每次插入或扩容时元素的位置可能会发生变化。
    • LinkedHashMap:可以保持元素的插入顺序或者访问顺序。默认情况下,它按照插入顺序维护元素;如果在构造函数中指定 accessOrder 为 true,则会按照访问顺序维护元素,即每次访问一个元素后,该元素会被移动到链表的尾部。
    • TreeMap:按照键的自然顺序或者指定的比较器顺序对元素进行排序。如果键实现了 Comparable 接口,TreeMap 会使用键的自然顺序;如果在构造函数中传入了一个比较器,TreeMap 会使用该比较器来确定元素的顺序。
HashMap 和 HashTable 有什么区别?
  • 线程安全性:HashMap 线程不安全,HashTable 线程安全。
  • 效率:HashTable 因线程安全,效率低于 HashMap。
  • null 值处理:HashMap 最多允许一条记录的 key 为 null(存于第 0 个位置),允许多条记录的值为 null;HashTable 不允许 key 或值为 null。
  • 初始容量与扩容:HashMap 默认初始化数组大小为 16,扩容时扩大两倍;HashTable 默认初始大小为 11,扩容时扩大两倍 + 1。
  • 哈希值计算:HashMap 需重新计算 hash 值,HashTable 直接使用对象的 hashCode。
HashMap & ConcurrentHashMap 的区别?

HashMap 和 ConcurrentHashMap 都是用于存储键值对的哈希表结构,不过它们在多线程安全、性能、锁机制、对 null 的支持等方面存在显著差异

  • HashMap:没有锁机制,因为它不考虑多线程并发访问的情况,所以在多线程环境下操作时不会对资源进行加锁。
  • ConcurrentHashMap:在不同的 Java 版本中采用了不同的锁机制:
    • Java 7 及以前:采用分段锁(Segment)机制。ConcurrentHashMap 内部被分成多个 Segment,每个 Segment 类似于一个小的 HashMap,并且每个 Segment 都有自己独立的锁。不同的线程可以同时访问不同的 Segment,从而提高并发性能。只有在访问同一个 Segment 时才需要竞争锁。
    • Java 8 及以后:摒弃了分段锁机制,采用 CAS(Compare - And - Swap,比较并交换)和 synchronized 来实现并发控制。当进行插入、删除等操作时,首先会使用 CAS 尝试更新,如果失败则使用 synchronized 对节点进行加锁,锁的粒度更小,仅对需要操作的节点进行加锁,进一步提高了并发性能。
为什么 ConcurrentHashMap 比 HashTable 效率要高?
  • HashTable:采用一把锁锁住整个链表结构来处理并发问题。由于多个线程竞争同一把锁,容易出现阻塞情况。
  • ConcurrentHashMap
    • JDK 1.7:使用分段锁(由ReentrantLock、Segment和HashEntry构成)。将HashMap划分为多个段,每段分配一把锁,支持多线程访问,锁粒度基于Segment,每个Segment包含多个HashEntry。
    • JDK 1.8:采用CAS + synchronized + Node + 红黑树的方式。锁粒度为Node(首结点,实现Map.Entry<K,V>),相较于 JDK 1.7,锁粒度降低了。
HashMap中Put方法的底层实现
  1. 计算键的哈希值。
  2. 根据哈希值找到对应的桶位置。
  3. 检查桶是否为空,如果为空则直接插入新节点。
  4. 如果桶不为空,检查是链表还是红黑树结构。
    • 若是链表,遍历链表查找是否已存在相同键,若存在则更新值,不存在则插入新节点。
    • 若是红黑树,调用红黑树的插入方法插入或更新节点。
  5. 插入节点后,检查是否需要进行扩容操作。
public V put(K key, V value) {
    // 调用 putVal 方法完成实际的插入操作
    return putVal(hash(key), key, value, false, true);
}

// 计算键的哈希值
static final int hash(Object key) {
    int h;
    // 如果 key 为 null,哈希值为 0;否则,将 key 的哈希码与高 16 位进行异或操作
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    // 如果哈希表为空或者长度为 0,进行扩容操作
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    // 根据哈希值计算桶的索引位置,如果该位置为空,直接插入新节点
    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);
                    // 如果链表长度达到树化阈值(默认为 8),将链表转换为红黑树
                    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;
            }
        }
        // 如果找到了相同键的节点,根据 onlyIfAbsent 参数决定是否更新值
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    // 增加修改次数
    ++modCount;
    // 如果元素数量超过阈值,进行扩容操作
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}
ConcurrentHashMap 的实现原理是什么?
  • 数据结构:在 JDK 1.7 中,采用分段锁(Segment)机制,每个 Segment 是一个独立的哈希表,不同 Segment 之间可以并发操作。在 JDK 1.8 中,抛弃了 Segment,采用 Node 数组 + 链表 + 红黑树的数据结构,并且使用 CAS 和 synchronized 关键字来保证并发安全。

  • 并发控制:读操作基本无锁,写操作通过 CAS 和 synchronized 来保证原子性和可见性。

ArrayList和Vector的区别?

Array线程不安全,效率高,Vector线程安全,效率低

ArrayList和LinkList的底层实现原理?
  • ArrayList:采用数组实现,基于下标查询,时间复杂度是O(1),所以查询块,增删慢
  • LinkList:采用链表实现的,每一个节点有三个参数,指向下一节点、指向上一节点,值基于下标查询,时间复杂度是O(n),所以查询慢,适合增删操作
ArrayList 与 LinkedList 初始空间是多少?
  • ArrayList:初始容量为 10。

  • LinkedList:没有初始容量的概念,它是基于链表实现的,节点按需创建。

相关文章:

  • Vue_vue2/vue3无缝滚动效果
  • Discuz! X3.5 根目录权限设置
  • java每日精进 2.13 MySql迁移人大金仓
  • 【Java】泛型与集合篇 —— Set 接口
  • RT-Thread+STM32L475VET6实现红外遥控实验
  • 开题报告——基于Spring Boot的垃圾分类预约回收系统
  • 深度学习pytorch之19种优化算法(optimizer)解析
  • 【算法】动态规划专题⑦ —— 多重背包问题 + 二进制分解优化 python
  • umi react+antd 判断渲染消息提示、input搜索、多选按钮组
  • 【核心算法篇十二】《深入解剖DeepSeek多任务学习:共享表示层的24个设计细节与实战密码 》
  • 数组和指针常见笔试题(深度剖析)
  • Ubuntu:20.04更新cmake到更高版本
  • WebGPU 中的缓冲区输入速率:逐顶点与逐实例模式详解
  • FreeSwitch中mod_dptools和mod_easyroute两个模块及应用场景
  • DeepSeek VS ChatGPT-速度、准确性和成本
  • 使用GDI+、文件和目录和打印API,批量将图片按文件名分组打包成PDF
  • mysql兼容模式下smallint类型修改成boolean类型失败的处理办法
  • 使用 GPTQ 进行 4 位 LLM 量化
  • 校园网架构设计与部署实战
  • 一个网址,详细请求流程
  • 国防部:奉劝有关国家不要引狼入室,甘当棋子
  • 司法部:加快研究制定行政执法监督条例,建立完善涉企行政执法监督长效机制
  • 综合治理食品添加剂滥用问题,国务院食安办等六部门联合出手
  • 丁薛祥在学习《习近平经济文选》第一卷专题研讨班上强调,深入学习贯彻习近平经济思想,加强党中央对经济工作的集中统一领导
  • 科普|肩周炎的自限性,意味着不治也能自己好?
  • 罗马尼亚总理乔拉库宣布辞职