【面试题】Java面试题整理3

1.平衡二叉树和红黑树

  • 左子树的所有节点的值都小于父节点,右子树的节点都大于根节点
  • 平衡因子计算 左子树高度-右子树高度 平衡因子超过[-1, 1]则需要调整
  • 平衡二叉树的中序遍历是结构是有序的

红黑树

  • 每一个节点是红色或者黑色,根节点必须是黑色
  • 每个叶节点(Nil)是黑色的
  • 不能出现两个红色节点相连
  • 对每一个节点,到其所有的后代节点的简单路径上,均包含相同的黑色节点

红黑树
https://blog.csdn.net/python0731/article/details/120683210


2.List和Set的区别

  • 相同点:List和Set都是Collection的子接口,因此他们具有一些通用的方法,比如添加和删除
  • 不同点,List存储元素允许重复,Set存储元素不允许重复。List是有序的,即读取元素的顺序和存储元素的顺序是一致的。Set根据不同的实现类,读取的顺序不同。HashSet是无序的,TreeSet是有序的排序根据指定的比较器排序,LinkedHashSet按照添加顺序
  • Set不允许元素重复,比较通过hashCode和equals方法实现。

关于HashSet

  • HashSet存储元素无序的原因是因为它是根据哈希值来确定元素的存储位置的。
  • HashSet底层实现基于HashMap,将保存的元素存储在key中, value则使用一个obj对象代替。

关于LinkedHashMap

  • 基于HashMap实现,对每个节点增加了before和after指针,用来指向他的前驱和后继节点,这样就保证了数据插入和读取的顺序是一样的
  • 应用场景LRU

关于HashMap
在这里插入图片描述
在这里插入图片描述

初始长度为16,当容量达到扩容因子的0.75倍时候扩容


3.我们能否使用任意类作为Map的key

可以,但是我们要重写这个类的hashcode和equals方法。
Student s1 = new Student('jack', 12);
Student s2 = new Student('jack', 12);
// Map<Student, String> map = new Map<>();

如果什么都不重写,那么因为两个对象哈希值不同,则都直接插入,导致key重复
如果只重写hashcode,那么计算得到哈希值相同,进行下一步比较,调用equals
由于没有重写equals,那么对比的是地址,返回false,导致key重复
因此只有两个方法都重写,才能保证key的唯一性


4.Sleep wait notify notifyAll的用法

  • Sleep是Thread的静态方法,调用sleep方法可以让当前正在运行的线程进入睡眠状态,即暂时停止运行指定的单位时间。并且该线程在睡眠期间不会释放对象锁。
  • Wait是Object类的一个方法,调用wait方法可以让当前线程进入等待状态,并释放锁。该线程会处于等待唤醒状态直到另一个线程调用了object对象的notify方法或者notifyAll方法
  • Nofity:唤醒一个线程,其他线程依然处于wait的等待唤醒状态,如果被唤醒的线程结束时没调用notify,其他线程就永远没人去唤醒,只能等待超时,或者被中断
  • NotifyAll:所有线程退出wait的状态,开始竞争锁,但只有一个线程能抢到,这个线程执行完后,其他线程又会有一个幸运儿脱颖而出得到锁
  • 以上的所有方法只能在sync代码块或者sync方法中才可以调用。并且这些方法的调用者必须是sync中的同步监视器,否则出错IllegalMonitorStateException
  • 这些方法是写在obj类中的,原因是因为同步监视器可以是任何一个类的对象,所以为了保证所有的类的对象都可以调用上述的方法,只能将这些方法写在obj类中,因为obj类是所有类的父类

5.线程创建的方式

  • 继承Thread类,重写类的run方法即可。在调用的时候使用start方法执行。这种方法的缺点是由于Java是单继承,所以无法再继承别的类
  • 实现Runable接口,重写run方法。
  • 实现Callable接口
  • 使用线程池。

6.构造器可以重写么

构造器可以被重载,但不能重写


7.装箱和拆箱

装箱指的是将基本数据类型转换成包装数据类型;
拆箱指的是将包装数据类型转换成基本数据类型

public class Demo {
    public static void main(String[] args) {
        // 手动装箱:通过调用方法将基本数据类型转化为包装类类型
        Integer i = Integer.valueOf(100);
        // 自动装箱 将基本数据类型直接赋值给包装类,由系统自动转换
        Integer ii = 100;
        // 手动拆箱
        // 由于ii是Integer类型 因此需要将ii转化为int类型,然后相加
        // 相加以后的结果再转化为Integer类型
        // ii += 100
        ii = Integer.valueOf(ii.intValue() + 100);
        System.out.println(ii);
        // 自动拆箱
        ii += 100;
    }
}

8.流的分类和区别在这里插入图片描述

区别

  • 处理的数据不同;字节流以字节为单位,传递图片,视频,音乐等文件,采用ASCII编码;字符流以占用2个字节的Unicode为单位,主要* 传输文本文件
  • 字节流默认不使用缓冲区,字符流使用缓冲区

9.Java中的容器

四大容器 List Set Queue Map
在这里插入图片描述


10.List,Set和Map的区别

  • List和Set都实现了Collection接口,存储单列数据;Map存储双列数据
  • List存储的数据可以重复,并且读取顺序和存入数据相同;Set处理LinkedHashSet以外读取顺序是随机的,按照哈希码排序,且存储的数据不可以重复

Map的区别
在这里插入图片描述
HashSet和TreeSet

  • 实现方式不同,HashSet底层是数组+链表;TreeSet底层是红黑树
  • HashSet是无序的,TreeSet是有序的,可以在构造时实现Compartor接口或让类实现comparable接口

https://blog.csdn.net/weixin_45928836/article/details/117266233


11.线程的五种状态

  • 新建:new创建一个线程
  • 就绪:.start()后,该线程处于就绪状态,只是表示可以运行,等待jvm调用
  • 运行:线程获得cpu开始执行run()方法体
  • 阻塞:如果其他线程抢占CPU就会阻塞,或者调用sleep()
  • 死亡:run()、call()执行完毕,线程正常结束,或者是抛出异常、调用stop()但是容易死锁
    在这里插入图片描述

12.线程池

为什么使用线程池

创建线程整个过程的浪费是比较大的,而线程池的意义在于先创建线程需要的数据,当要用的时候直接去线程池中取出线程,这样节省了创建连接和关闭资源的开销。

什么是线程池

线程池是一种池化技术,主要就是基于一直资源服用到思想。比较常见的其他的池化技术就是数据库连接池。线程池中被复用的资源就是线程,主要目的就是减少频繁的去创建或者销毁线程。线程的切换和创建过程中涉及到CPU上下文的切换,内存的分配等问题,消耗资源较大。此外线程池可以通过参数控制创建线程的数量,避免无休止的创建线程带来一些资源利用过高的问题。

线程池的参数

  • 核心线程数:默认长期正在工作的线程
  • 最大线程数:

什么时候使用线程池

创建线程整个过程的浪费是比较大的,而线程池的意义在于先创建线程需要的数据,当要用的时候直接去线程池中取出线程,这样节省了创建连接和关闭资源的开销。
完成一个任务需要 T1创建时间 T2执行时间 T3销毁时间,当T1+T3远大于T2时,就应该考虑使用线程池

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值