java面试题-速记

java面试题

提示:本文记录了本人2022年碰到的面试问题以及答案,如文章中有错误欢迎评论区留言


目录


一、容器

Hashpmap扩容

关键变量:

  • capacity(初始容量,默认16)
  • loadFactor(负载因子,默认是0.75)
  • threshold (阀值,默认12,阈值=容量*负载因子)

扩容:

  • 当元素数量超过阈值时触发扩容;
  • 第一次初始扩容的长度为16
  • 非第一次扩容,新容量=旧容量×2,新阈值=新容量×负载因子;负载因子不变。
Hashpmap实现原理

数据结构:数组+链表-----数组里面存放的是链表。
调用put方法时对计算key的hashcode,hashcode相同放入同一数组,取值的时候根据key的hashcode找到数组下标然后通过equals()方法找到正确的键值对。
在这里插入图片描述

HashSet的实现原理

HashSet是基于HashMap实现的,HashSet的元素都存放在HashMap的key上。

三、多线程

常用线程池有哪几种
名称英文说明
可缓存线程池newCachedThreadPool最大线程数为Integer.MAX_VALUE,线程池长度超过处理需要,灵活回收空闲线程,若无可回收,则新建线程,回收时间默认1分钟
固定长度线程池newFixedThreadPool可控制线程最大并发数,超出的线程会在队列中等待
周期性执行任务线程池newScheduledThreadPool可指定线程数,周期执行比如每隔 10 秒钟执行一次
单一线程池newSingleThreadScheduledExecutor只有一个线程执行任务,之后的线程在队列中等待,所有任务按照指定顺序执行(先进先出、先进后出)
线程池核心参数有哪些,作用是什么
参数英文说明
核心线程数corePoolSize控制核心线程数量,此线程不会被回收
最大线程数maximumPoolSize最大线程数
最大空闲时间keepAliveTime针对非核心线程,到达时间回收
时间单位TimeUnit unit配合最大空闲时间使用
拒绝策略RejectedExecutionHandler handler拒绝策略
阻塞队列BlockingQueue workQueue阻塞队列
线程工厂ThreadFactory threadFactory线程工厂
线程池的拒绝策略
策略
直接丢弃,并抛出异常
丢弃,不抛出异常
被拒绝的任务直接在主线程中运行,不再进入线程池
丢弃任务队列中最旧未执行任务,将被拒绝任务添加到等待队列
进程和线程的区别
  • 进程是正在执行的程序,是资源分配的基本单元,而线程是CPU调度的基本单元。
  • 进程之间相互独立不能共享资源,一个进程至少有一个线程,同一进程的各线程共享整个进程的资源(寄存器、堆栈、上下文)。
  • 线程的创建和切换开销比进程小
创建线程的方式有哪几种?
  • 继承Thread类,并重写run方法。
  • 实现Runnable接口,并重写run方法
  • 线程池
Runnable和Callable的区别?

相同点:都需要调用Thread.start()启动线程
不同点:

  • Callable 能返回执行结果,能向上抛出异常
  • Runnable 不能返回执行结果、不能向上抛出异常
sleep()和wait()有什么区别?

相同点:都是暂停线程的方法
不同点:

  • sleep 不释放锁,到了时间,重新执行任务
  • wait 释放锁,到了时间,需要申请锁,拿到锁才能执行任务。
死锁发生的场景?

多个线程争抢资源 如:两个资源a和b, 一个线程抢到 a的锁,等待b的锁, 一个线程同时抢到b的锁 等待a的锁释放。

怎么防止死锁的发生呢?
  • 对资源加锁顺序一致
  • 使用.wait()时传入等待时间
  • 使用ReentrantLock时调用tryLock传入等待时间
线程池怎么设置合理大小

CPU密集型:核心线程数 = CPU核数 + 1
IO密集型:核心线程数 = CPU核数 * 2

并行和并发有什么区别?

并行是指两个或者多个事件在同一时刻发生;
并发是指两个或多个事件在同一时间间隔发生。

三、java其他

泛型?符号和T/E 的区别

可以接受任意类型,如List<> 可以接受 同时接收string、integer、 T/E这种只能接收一种固定的类型要么是string 要么是其他。

代理实现的两种方式
  • JDK代理-反射机制实现aop的动态代理,在调用具体方法前调用InvokeHandler来处理。
  • CGLIB代理-使用字节码框架asm,通过修改字节码生成子类

使用上的区别:

  • JDK代理只能对实现接口的类生成代理
  • CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。
获取class的三种方式
  • Class aClass = new User().getClass();
  • Class aClass = Class.forName(“com.demo.User”);
  • Class aClass = User.class;
类加载机制
顺序摘要详细
1加载根据类全限定名获取类的二进制字节流,将字节流所代表的静态存储结构转化为方法区运行时数据结构,内存中生成一个代表这个类的 java.lang.CLass 对象
2验证文件格式验、元数据验证、字节码验证、符号引用验证
3准备为类的静态变量分配内存,并设置默认初始值
4解析类或接口解析、字段解析、类和接口方法解析、符号引用、直接引用
5初始化最后一个阶段,程序员可在静态块static{}中自定义赋值变量,或在构造函数中赋值

详情过程参考网友文章,点这里

浅拷贝和深拷贝

为什么需要浅拷贝和深拷贝呢?

@Data
    @AllArgsConstructor
    public static class Score{
        public Integer score;
    }
    @Data
    public static class User {
        public String name;
        public Score score;
    }
    public static void main(String[] args) {
        User user = new User();
        user.setName("小明");
        user.setScore(new Score(10));
        User user1 = user;
        System.out.println((user1==user));
    }

以上代码输出true,你会发现通过User user1 = user;的方式指定的还是同一个对象

如果需要不同对象该怎么做呢?

浅拷贝

这个时候就需要浅拷贝了,在要拷贝的类中实现Cloneable并重写clone方法(只需重写无其他操作)使用时通过对象.clone获取,如: User user1 = (User) user.clone();

@Data
    @AllArgsConstructor
    public static class Score {
        public Integer score;
    }
    @Data
    public static class User implements Cloneable {
        public String name;
        public Score score;
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        User user = new User();
        user.setName("小明");
        user.setScore(new Score(10));
        User user1 = (User) user.clone();
        System.out.println((user1 == user));
         System.out.println((user1.getScore() == user.getScore()));
    }

第一行输出false,证明浅拷贝获取的对象与之前的对象非同一内存地址。
第二行输出true,证明浅拷贝获得的对象,其引用的子类依旧是同一内存地址。
注意:这里的user1变成了 User user1 = (User) user.clone();

注意:第二行输出true说明:通过对象.clone()获取到的子类依旧是同一个对象,需要子类是不同对象时需要用到深拷贝

深拷贝
上面说了由于浅拷贝获得的对象,其引用的子类依旧是同一内存地址,所以需要用到深拷贝。

实现方式有两种:

  1. 在需要拷贝子类中也实现Cloneable并重写clone方法,但子类太多就很麻烦。
  2. 也可以通过序列化的方式,然后再反序列回来。
public static <T extends Serializable> T clone(T obj) {
		T cloneObj = null;
		try {
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			ObjectOutputStream obs = new ObjectOutputStream(out);
			obs.writeObject(obj);
			obs.close();
			ByteArrayInputStream is = new ByteArrayInputStream(out.toByteArray());
			ObjectInputStream os = new ObjectInputStream(is);
			cloneObj = (T) os.readObject();
			os.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return cloneObj;
	}
ThreadLocal 是什么?

ThreadLocal 是线程本地存储
经典实用场景:数据库连接,session管理

ERROR和Excepition的区别

ERROR 不能捕捉和处理 如:内存溢出
Excepition 可以捕捉和处理 如:io、空指针

三、redis相关

redis的基本数据类型
类型详情
String最基本的数据类型,二进制安全的字符串,最大512M。
list按照添加顺序保持顺序的字符串列表。
set无序的字符串集合,不存在重复的元素。
sorted set已排序的字符串集合。
hashkey-value对的一种集合。
redis超出设置内存淘汰策略有哪几种

已设置过期的key:

  • 最近最少使用
  • 将要过期
  • 随机

其他:

  • 淘汰最近最少使用
  • 淘汰任意
  • 禁止淘汰
redis有几种删除过期key的策略呢?
  • 定时删除,为每个key设置一个定时器,不推荐。
  • 惰性删除,每次从键空间中获取键时判断是否过期,过期删除。
  • 定期删除,每隔一段时间删除过期的key。
redis锁了解过吗?

使用setnx来做锁,再用 expire 给锁加过期时间防止锁忘记了释放。
可以同时把 setnx 和expire 合成一条指令来用(防止expire 执行之前宕机或者重启。

redis怎么大批量删除key?

使用sscan scanParams.count(500); 每次删除指定条数。

redis高可用
  • 主从一个master 多个slave ,master提供增删改 slve只提供查询
  • 哨兵,在主从的基础上,如果发生异常会选举
  • 集群,多个mastter 多个slave
redis为什么是线程安全的?

Redis是单进程单线程的,Redis利用队列技术将并发访问变为串行访问。

四、框架相关

spring作用域
类型详细
singleton单例,默认
prototype每个bean请求提供一个实例
request
session
global- session
@SpringBootApplication注解里面有啥
  • springBootConfigureation 配置文件
  • EnableAutoConfiguration 打开自动配置
  • @ComponenetScan spring组件扫描
springBoot启动的时候运行特定的代码

实现接口ApplicationRunner 获取CommandLineRunner

springboot异常统一处理

@ControllerAdvice+@ExceptionHandler处理全局异常

bootstrap.properties和application.properties的区别?

bootstrap加载优先级更高值不会被覆盖

spring事务隔离级别
英文详细
REQUIRED默认值,支持当前事务,如果没有事务会新建事务
REQUIRES_NEW新建新事务并挂起当前事务
SUPPORTS支持当前事务,如果没有事务的话以非事务方式执行
MANDATORY支持当前事务,如果没有事务抛出异常
NOT_SUPPORTED以非事务方式执行,如果当前存在事务则将当前事务挂起
NEVER以非事务方式进行,如果存在事务则抛出异常
NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与REQUIRED类似的操作
@Resouce和@Autowired的区别
  • @Resouce,是java自带的,可以通过name和type来获取,默认是name
  • @Autowired,是spring的,只支持type,如果接口有两个实现类配合Qualifier(userImpl) 来使用

五、springcloud相关

nacos
nacos挂了a服务还能访问b服务吗
服务是怎么注册到nacos上的

服务提供者向nacos发送rest请求,带上ip地址和端口等信息,nacos接收到会保存起来

nacos是怎么知道服务健康状态的

使用心跳方式监控方式:

  1. nacos支持服务端主动监测提供者状态
  2. 服务提供者向nacos发送心跳、默认5秒
nacos怎么判断服务挂了呢?

nacos15秒未收到某个服务心跳,将服务设置未不健康状态,30秒剔除服务

nacos更多详情参考网友文章点这里


Ribbon常用负载均衡策略
  • 轮询(多个服务,123个服务依次调用,默认)
  • 随机
  • 权重策略(相应时长越短权重越高)
  • 重试(指定超时间,按照轮询策略重试)

六、Mysql相关

InnoDB有哪几种索引类型,使用场景
类型场景
Hash等值查询,不支持范围查询
b+树范围查询
hash的索引类型能做范围查询吗?

不行,因为hash类型的索引存放的key的hash值,并且hash是无序的。

什么是回表
  • 普通索引,存放了对主键索引的引用
  • 主键索引,存放的是完整的一条数据

使用普通索引查询时,会先查询到普通索引,再通过普通索引查询主键索引,主键索引里面就有该行的数据。

会导致索引失效的几种写法
写法详细
WHERE user_id=‘A’ or status=‘A’引擎会放弃索引,可使用union all 进入多次查询
WHERE user_name LIKE ‘%A’由于前面是模糊的所以不能使用索引进行查询
联合索引未按最左侧规则匹配如索引a,b,c 只使用了bc,或者b,c进行查询,查询条件中没有a
对索引字段使用函数如 where mothe(modified)=7
隐式转换如:查询条件输入的是数字,字段类型是varchar,可参考点击这里
隐式编码转换如联表查询时一个字段的字符编码是utf8mb4,另一个是utf8
索引类型是Hash,但是使用了范围查询当索引类型为Hash时不支持范围查询,因为hash是无序的
事务隔离级别
写法详细
读取未提交内容
读取已提交内容
可重复读

七、kafka

怎么防止消息丢失
情况方案
生产者丢失代码里面发消息时使用有回调的方法producer.send(mes,callback) ,不使用producer.send(msg)
消费者丢失确保消息消费完提交,设置手动提交,enable.auto.commit 设置为false
怎么保证两条消息都发送成功

可以使用kafka的事务。

八、mybatis

mybatis的缓存
  • 一级缓存-默认开启,作用域sqlsession,一个sqlsession下同一sql执行两次走缓存。
  • 二级缓存-作用域全局的,多个sqlsession共享一个缓存。

新增删除修改会清空缓存。
开启二级缓存后,优先读取二级缓存,找不到在去一级缓存中找
详情过程参考网友文章,点这里

mybatis传参时#和$的区别

#-是占位符,mybatis会预编译防止sql注入。
$-不会预编译,如order by ${name } 可传入数据库的表字段,直接由前端传入会引起sql注入。
如要传入排序字段,name 是 “time”
order by #{name} DESC 是 “time" -排序无效
order by ${name} DESC 是 time

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值