interviewQ

Java基础


1.面向对象的特征:封装、继承、多态、抽象。

封装:就是把对象的属性和行为(数据)结合为一个独立的整体,并尽可能隐藏对

象的内部实现细节,就是把不想告诉或者不该告诉别人的东西隐藏起来,把可以告诉别人的

公开,别人只能用我提供的功能实现需求,而不知道是如何实现的。增加安全性。

继承:子类继承父类的数据属性和行为,并能根据自己的需求扩展出新的行为,提

高了代码的复用性。

多态:指允许不同的对象对同一消息做出相应。即同一消息可以根据发送对象的不

同而采用多种不同的行为方式(发送消息就是函数调用)。封装和继承几乎都是为多态而准

备的,在执行期间判断引用对象的实际类型,根据其实际的类型调用其相应的方法。

抽象表示对问题领域进行分析、设计中得出的抽象的概念,是对一系列看上去不同,

但是本质上相同的具体概念的抽象。在 Java 中抽象用 abstract 关键字来修饰,用 abstract

修饰类时,此类就不能被实例化,从这里可以看出,抽象类(接口)就是为了继承而存在的。

2.java八大基本数据类型

整数类型:byte,1字节,8位,最大存储数据量是255,存放的数据范围是-128~127之间。

整数类型:short,2字节,16位,最大数据存储量是65536,数据范围是-32768~32767之间。

整数类型:int,4字节,32位,最大数据存储容量是2的32次方减1,数据范围是负的2的31次方到正的2的31次方减1。

整数类型:long,8字节,64位,最大数据存储容量是2的64次方减1,数据范围为负的2的63次方到正的2的63次方减1。

浮点类型:float,4字节,32位,数据范围在3.4e-45~1.4e38,直接赋值时必须在数字后加上f或F。

浮点类型:double,8字节,64位,数据范围在4.9e-324~1.8e308,赋值时可以加d或D也可以不加。

字符型:char,2字节,16位,存储Unicode码,用单引号赋值。

布尔型:boolean,只有true和false两个取值

3.jdk、jre、jvm区别

jdk包括jre、jre包括jvm

jdk是java开发包,包括javac和jar等编译工具,也包括jre

jre包下有bin和lib目录,bin就是jvm,lib是jvm工作所需的类库

4.重载和重写的区别

重载:

发生在同一个类中,方法名称必须相同,参数类型、个数、顺序,修饰符、返回值可以不同,发生在编译时

重写:

发生在父子类中,方法名称和参数列表必须相同,子类返回值范围小于等于父类。

子类访问修饰符大于等于父类,如果父类是private修饰,则不能被重写

5.==和equals区别

==比较基本类型,比较的是值。

==比较引用类型,比较的是地址值。

equals是Object类的方法,本质上和==一样,有些类重写的equals方法,比如在String中equals被重写后,比较的是字符值,重写equals后,还得重写hashcode方法。

在字符串中==比较的是内存地址(堆内存)

6.String、StringBuffer、StringBuilder 三者之间的区别

string是字符串常量,在源码中是final修饰的字符数组来保存字符串,所以string是不可变的,通常用于对一些不会经常改变的字符串

abstractStringBuilder是StringBuilder和StringBuffer的父类,定义了一些字符串常用的操作,如append、indexof、insert

stringBuffer加了同步锁,所以是线程安全的,多线程下操作以改变的数据可以用这个

stringBuilder是非线程安全的,单线程下操作以改变的数据可以用这个

7.接口和抽象类的区别

接口用implements来实现

抽象类用extends来继承

抽象类可以有构造函数和main方法,并且可以运行

类可以实现多个接口,只能继承一个抽象类

接口中的方法默认使用public修饰,抽象类可以用任意修饰符

8.string常用方法有哪些

indexof():返回指定字符的索引

replace():字符串替换

split():分割字符串

trim():去除两端的字符串

length():返回字符串长度

substring():截取字符串

equals():字符串比较

9.单例模式

有三种

懒汉式:线程安全,调用的时候才会创建

饿汉式:线程安全,初始化的时候创建一次

双检锁:线程安全,延迟初始化

10.反射

获取class对象的三种方式

.class

.forName

.getClass

11.jdk1.8新特性

lambda表达式、stream、LocalDateTime

12.BIO、NIO、AIO的区别

BIO:blockingIO 同步阻塞IO,相关包在java.io下

bio会给每个请求分配一个线程进行处理,是基于stream流的

NIO:newIO或者Non-block IO 同步非阻塞IO

13.ThreadLocal 的原理

并发编程(多线程)


1.java创建线程的几种方式 和运行线程的几种方式

创建线程: 2种方式,推荐Runnable,因为java是单继承多实现的,Runnable是实现,而thread是继承

**运行线程:**线程池、callable、thread.start

2.线程的几种状态(五种)

新建(NEW)

就绪(RUNNABLE)

运行(RUNNING)

阻塞(BLOCKED)

死亡(DEAD)

3.sleep和wait的区别

1)sleep是thread的方法,wait是object的方法

2)sleep不会让出锁,会让出cpu,此时是加锁的,即使让出cpu,其他线程也无法执行,因为代码是加锁的;

wait会释放锁,并加入等待队列,等待其他线程调用notify唤醒

3)调用sleep进入阻塞状态;调用wait进入等待状态,调用notify进入就绪状态

4.线程池执行流程

判断核心线程数->判断等待队列->判断最大线程数

5.线程池参数有哪些(7个)
corePoolSize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:线程超过corePoolSize等待时间
unit:单位
workQueue:等待队列
threadFactory:创建线程工厂类
RejectedExecutionHandler:线程满了,并且等待队列满了,执行拒绝策略

附:自我总结

线程池常用ThreadpoolExecutor

Executors是用来创建线程的工具类

线程池源码

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
      	int c = ctl.get();
    //workerCountOf(c):获取当前线程数
    //addWorker 相当于new Thread()启动一个线程
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

执行流程:

1.判断当前线程数是否小于核心线程数

2.判断当前线程池是否正在运行isRunning©,并且 workQueue.offer(command)把任务放到队列中,成功为true,否则为false

3.执行!addWorker(command, false),执行非核心线程,最大线程-核心线程,参数false为非核心线程数,true为核心线程数

4.执行拒绝策略reject(command)

集合


1.HashMap 视频链接
(1)介绍下 HashMap 的底层数据结构

Jdk1.8之前是数组+链表

jdk1.8之后是数组+链表或红黑树

(2)1.8为什么要变成数组+链表或红黑树

主要是为了提升在哈希冲突时链表过长的查找性能,使用链表的查找性能是 O(n),而使用红黑树是 O(logn)。

(3)那在什么时候用链表?什么时候用红黑树?

**插入:**默认情况下是使用链表节点。当同一个索引位置的节点在新增后达到9个(阈值8):如果此时数组长度大于等于 64,则会触发链表节点转红黑树节点;而如果数组长度小于64,则不会触发链表转红黑树,而是会进行扩容,因为此时的数据量还比较小。

移除:当同一个索引位置的节点在移除后达到 6 个,并且该索引位置的节点为红黑树节点,会触发红黑树节点转链表节点

(4)为什么链表转红黑树的阈值是8?

阈值为8是在时间和空间上权衡的结果,红黑树节点大小约为链表节点的2倍,在节点太少时,红黑树的查找性能优势并不明显,付出2倍空间的代价不值得,到8个节点时,红黑树的性能优势也会开始展现出来,因此8是一个较合理的数字

(5)为什么转回链表节点是用的6而不是复用8?

如果我们设置节点多于8个转红黑树,少于8个就马上转链表,当节点个数在8徘徊时,就会频繁进行红黑树和链表的转换,造成性能的损耗。

(6)HashMap 的容量必须是 2的N次方,这是为什么?

长度为 2 的次幂,那么(length-1)的二进制最后一位一定是 1,在对 hash 值做“与”运算时,最后一位就可能为 1,也可能为 0,换句话说,取模的结果既有偶数,又有奇数。设想若(length-1)为偶数,那么“与”运算后的值只能是 0,奇数下标的 bucket 就永远散列不到,会浪费一半的空间。

(7)负载因子为什么默认值是0.75

在时间和空间上权衡的结果。如果值较高,例如1,此时会减少空间开销,但是 hash 冲突的概率会增大,增加查找成本;而如果值较低,例如 0.5 ,此时 hash 冲突会降低,但是有一半的空间会被浪费,所以折中考虑 0.75 似乎是一个合理的值负载因子为什么默认值是0.75

(8)HashMap的插入流程是怎么样的?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(9)图里刚开始有个计算 key hash 值,是怎么设计的?

答:拿到 key 的 hashCode,并将 hashCode 的高16位和 hashCode 进行异或(XOR)运算,得到最终

的 hash 值

static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
(10)为什么要将 hashCode 的高16位参与运算?

减少哈希碰撞

(11)扩容(resize)流程介绍下?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(12)对比JDK1.7 JDK 1.8主要进行了哪些优化?

1)底层数据结构从“数组+链表”改成“数组+链表+红黑树”,主要是优化了 hash 冲突较严重时,链表过长的查找性能:O(n) -> O(logn)。

2)优化了 hash 值的计算方式,新的只是简单的让高16位参与了运算。

// JDK 1.7.0
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
// JDK 1.8.0_191
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

3)扩容时插入方式从头插法”改成“尾插法”,避免了并发下的死循环。

4)扩容时计算节点在新表的索引位置方式从“h & (length-1)”改成“hash & oldCap”,性能可能提升不大,但设计更巧妙、更优雅。

附:自我总结

Hashmap中默认变量:
DEFAULT_INITIAL_CAPACITY:默认初始化容量 16
DEFAULT_LOAD_FACTOR:加载因子(负载因子)0.75f
threshold:扩容阈值 = DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR
什么是哈希冲突?

哈希冲突是指在使用哈希函数进行键值对映射时,不同的键计算得到的哈希码(hashcode)值相同的情况。

put的时候有一个参数onlyIfAbsent

if (e != null) { // existing mapping for key
                V oldValue = e.value;
    			// 如果onlyIfAbsent 为fales 则会进行value覆盖操作
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
Hashmap什么时候会进行数组扩容

1.元素个数大于阈值会进行扩容

2.当链表个数大于8并且数组小于64会进行扩容

什么时候会进行树化

当链表个数大于8,并且数组容量大于64的时候会进行树化

HashMap是如何解决Hash冲突的

首先,hashmap底层采用的数组+链表/红黑树,当执行put的时候,会计算出来key的hash值,如果两个不同的key计算出来的hash值一样就会产生hash冲突,在hashmap中如果产生hash冲突时,会采用链式寻址法,就是转换成链表,当链表长度大于8且数组长度大于65时,会转换成红黑树。

Hashmap和HashTable的区别

1.ht采用数组+链表 hm采用数组+链表+红黑树

2.ht采用全局安全锁,性能差,hm线程不安全

3.ht初始容量11,hm初始容量16

Spring


SpringBoot


数据库


redis


SpringCloud


MQ


Linux


RPC


1.简述rpc流程
  1. 客户端调用:客户端发起远程调用,调用远程服务器上的某个方法。
  2. 参数封装:客户端将调用所需的参数封装成一个请求对象。
  3. 序列化:将请求对象进行序列化,将对象转换成可以在网络中传输的格式,如二进制流或JSON。
  4. 网络传输:客户端通过网络将序列化后的请求对象发送给远程服务器。
  5. 远程服务器接收请求:远程服务器接收到请求后,进行解析。
  6. 反序列化:远程服务器将接收到的请求对象进行反序列化,将其转换成对应的对象。
  7. 调用方法:远程服务器根据请求对象中的方法名和参数,调用对应的方法。
  8. 处理业务逻辑:远程服务器执行该方法的业务逻辑,并获取到结果。
  9. 封装结果:远程服务器将执行结果封装成一个响应对象。
  10. 序列化:将响应对象进行序列化,转换成可以在网络中传输的格式。
  11. 网络传输:远程服务器通过网络将序列化后的响应对象发送给客户端。
  12. 客户端接收响应:客户端接收到响应后进行解析。
  13. 反序列化:客户端将接收到的响应对象进行反序列化,还原成对应的对象。
  14. 获取结果:客户端从响应对象中获取方法调用的结果。
  15. 处理结果:客户端根据需求处理方法调用的结果,进行错误处理、日志记录或其他操作。
2.序列化选型要素

性能、可扩展性、兼容性、安全性、可读性

3.NIO三大组件

. 序列化:将请求对象进行序列化,将对象转换成可以在网络中传输的格式,如二进制流或JSON。
4. 网络传输:客户端通过网络将序列化后的请求对象发送给远程服务器。
5. 远程服务器接收请求:远程服务器接收到请求后,进行解析。
6. 反序列化:远程服务器将接收到的请求对象进行反序列化,将其转换成对应的对象。
7. 调用方法:远程服务器根据请求对象中的方法名和参数,调用对应的方法。
8. 处理业务逻辑:远程服务器执行该方法的业务逻辑,并获取到结果。
9. 封装结果:远程服务器将执行结果封装成一个响应对象。
10. 序列化:将响应对象进行序列化,转换成可以在网络中传输的格式。
11. 网络传输:远程服务器通过网络将序列化后的响应对象发送给客户端。
12. 客户端接收响应:客户端接收到响应后进行解析。
13. 反序列化:客户端将接收到的响应对象进行反序列化,还原成对应的对象。
14. 获取结果:客户端从响应对象中获取方法调用的结果。
15. 处理结果:客户端根据需求处理方法调用的结果,进行错误处理、日志记录或其他操作。

2.序列化选型要素

性能、可扩展性、兼容性、安全性、可读性

3.NIO三大组件
4.reactor三种线程模型
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sky21cnwww

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值