协程是一种轻量级线程

优点:

跨平台

跨体系架构

无需线程上下文切换的开销

无需原子操作锁定及同步的开销

方便切换控制流,简化编程模型

高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

缺点:

无法利用多核资源,协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。

进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序,这一点和事件驱动一样,可以使用异步IO操作来解决

15、如何判断一个hash函数好不好?

1、计算性能

2、离散性

hash算法一定要把数据哈希均匀,不能全部挤在一坨,对hash算法不太清楚的必须看看这篇文章,我以前花了整整一天时间写的,举了很多例,真正搞懂hashCode和hash算法

16、http中get和post的区别?

GET在浏览器回退时是无害的,而POST会再次提交请求。

GET产生的URL地址可以被Bookmark,而POST不可以。

GET请求会被浏览器主动cache,而POST不会,除非手动设置。

GET请求只能进行url编码,而POST支持多种编码方式。

GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。

GET请求在URL中传送的参数是有长度限制的,而POST么有。

对参数的数据类型,GET只接受ASCII字符,而POST没有限制。

GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。

GET参数通过URL传递,POST放在Request body中。

(本标准答案参考自w3schools)

然而实际上,它们的本质都是 TCP 连接,并无区别。上面的答案纯粹是为了应付面试官。真正导致产生区别的原因是 HTTP 的规定以及浏览器/服务器的限制,这才导致它们在应用过程中可能会有所不同。

17、解决hash冲突的方式?

拉链法

代表作:hashMap

原理:把所有hash值相同的元素放到链表中

再哈希法

原理:产生冲突时,对结果再进行一次hash,直到没有hash冲突,一般没人用,太鲁莽了,而且治标不治本,理论上永远不能彻底解决hash冲突

开放地址法

1、线性探测法

2、线性补偿探测法

3、伪随机探测

18、简述装饰者模式和适配器模式

装饰者模式

概括:在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能),它属于结构型模式,mybatis-plus的QueryWrapper就是装饰者模式的体现

适配器模式

概况:将一个接口转换成用户需要的另一个接口,使接口不兼容的那些类可以一起工作,也属于结构型模式,SpringMvc中的HandlerAdapter就是适配器模式的体现

19、hashCode和equals方法的联系

java规定:

如果两个对象的hashCode()相等,那么他们的equals()不一定相等。

如果两个对象的equals()相等,那么他们的hashCode()必定相等。

对hash算法不太清楚的同学必须看看这篇文章,我以前花了整整2天时间写的,举了很多例,真正搞懂hashCode和hash算法

20、什么是重写和重载?

1、重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Override)

2、重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overload)

3、重载是一个类的多态性表现,而重写是子类与父类的一种多态性表现

21、Java有几种基本数据类型?分别占用多少字节?

| 数据类型 | 占用字节 |

| — | — |

| byte | 1 |

| short | 2 |

| int | 4 |

| long | 8 |

| char | 2(c语言是1字节,可以存储一个汉字) |

| float | 4 |

| double | 8 |

| boolean | 1/8 |

22、Java异常有哪些类型?

Java所有的异常都继承至Throwable,分为Error和Exception两大类,其中:

Error是系统级错误,在代码层无法处理,常见的有StackOverflowError、OutOfMemoryError等;

Exception是异常,通常可以在代码层处理,常见的有NullpointerException、ClassCaseException、IndexOutOfBoundsException等

23、简述jvm内存模型?

程序计数器:线程私有,各线程之间独立储存,互不影响,若当前执行的是Java方法,则记录的就是当前执行指令的地址,若是native方法,则为空;

java虚拟机栈:线程私有,每个方法在执行时都会创建一个栈帧,方法执行过程就是栈帧在虚拟机栈中从入栈到出栈的过程,入栈表示方法开始被调用,出栈表示方法执行完毕,栈帧用于保存方法内部局部变量、操作数、方法返回值、动态链接;我们平时说的栈其实一般就是指局部变量区:用于存放方法参数、方法内定义的局部变量,还有已知的八大基本数据类型、对象引用、返回值地址;

本地方法栈:线程私有,和虚拟机栈相似,区别在于虚拟机栈的服务对象是java方法,而本地方法栈是本地方法;

堆:线程共享,在虚拟机启动的时候创建,用于存放对象实例,堆是GC管理的主要区域;

方法区:线程共享,其实方法区也是堆的物理组成部分,用于存放常量、静态变量 、 类信息(构造方法/接口定义) 、运行时常量池;注意, 实例变量在堆内存中,和方法区无关。

(jdk1.8之前,方法区的实现是永久代,从1.8开始,用元空间代替了永久代,注意一点,方法区还是那个方法区)

这一坨内容请酌情观看,我怕把你绕晕~

再细分的话还有运行时常量池和字符串常量池:jdk1.6的时候,两者都是属于方法区,1.7开始,字符串常量池被移到了堆内存;运行时常量池用于存放编译期生成的各种常量(“abc”,123等)和符号引用;而字符串常量池是为了提高jvm效率单独用来存放字符串的,因为字符串不同于其他数据类型,它可以很长很长很长很长很长很长很长很长很长很长很长很长~;

24、简述GC机制,新生代和老年代的区别?

垃圾回收是java管理内存的一种机制,作用是清理无用对象避免内存泄漏,gc主要发生在java堆上,而堆可以细分为新生代和老年代(分代是为了提高gc效率,其实不分代也可以完成gc,只不过gc机制会对堆的所有区域进行扫描,浪费资源),新生代还可以细分为三个虚拟的区,Eden区、FromSurvivor区、ToSurvivor区,一开始对象都在Eden区,Eden区的对象经过一次新生代gc(复制算法)后若还能存活,就会移动到survivor区(ToSurvivor区),在此次新生代gc时,在survivor发生的改变就是,From区中的对象会根据年龄来决定去留,达到阈值,会移动到老年代,没达到就移动到To区,经过此次新生代gc,Eden和From区都已被清空,From区和To区会互换;

新生代gc(Minor GC)触发时机:当Eden区没有足够空间进行分配时;

老年代gc(Major GC/Full GC)触发时机:当老年代中没有足够的内存空间来存放对象时,虚拟机会发起一次Major GC/Full GC。只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,否则将进行Full GC。

25、如何优化JVM频繁Minor GC?

最简单的就是适当扩大堆内存太小

26、简述类加载机制,什么是双亲委派?

1、加载:将源文件(代码)编译成.class文件,传递给类加载器

2、验证:类加载器拿到这些字节码文件后开始执行检查(通过本地类库),确保加载进来的字节流格式符合java虚拟机要求

3、准备:对类变量(不是实例变量)分配内存,并且给一个默认初始化的值(0或者null)

4、解析:将常量池中的符号引用加载成直接引用(如string str = new string(“123”),str就是符号引用,123是直接引用)

5、初始化:对类的静态变量初始化为指定的值,执行静态代码块

双亲委派机制:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此。

因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

27、synchronized底层实现原理?它与lock相比有什么优缺点?

首先那些说看过synchronized源码的基本都是大聪明,synchronized根本点不进去,想弄懂它的实现原理,我们只能通过看编译好的字节码文件

原理:

基于对象的监视器(ObjectMonitor),我们在字节码文件里面可以看到,在同步方法执行前后,有两个指令,方法前monitorenter,方法后monitorexit;

关于字节码一两句话说不清楚,我单独写了篇synchronized底层实现原理,保证你五分钟看懂

与lock对比:

1、synchronized不需要手动释放锁,lock需要在锁用完后进行unlock;

2、synchronized只能是默认的非公平锁,lock可以指定使用公平锁或者非公平锁;

3、lock提供的Condition(条件)可以指定唤醒哪些线程,而synchronized只能随机唤醒一个或者全部唤醒;

28、jvm指令重排原因?怎么避免?

原因:计算机内存操作速度远慢于CPU运行速度,所以就造成CPU空置,为了将提高CPU利用率,虚拟机会按照自己的一些规则会跳过执行慢的代码,去执行快的代码(即对代码重新排序),从而提升jvm的整体性能。

怎么避免:给关键的代码加上volatile关键字,所谓关键,就是会被执行顺序影响结果。

volatile关键字的三个特征是:线程可见、不具备原子性、禁止指令重排,volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

29、ReentrantLock是什么?有什么用?怎么用?和synchronized的区别?

ReentrantLock是Lock的一个子类

作用:

用来给资源加锁,避免高并发造成的数据异常问题;

使用:

public void lockTest() {

int i = 0;

//创建lock实例,可传参数true或者false,表示是否是公平锁,默认是非公平锁

ReentrantLock lock = new ReentrantLock();

//在需要保证同步的代码前lock

lock.lock();

i++;

//代码后unlock

lock.unlock();

}

与synchronized主要区别,lock需要手动释放锁,可以指定公平锁或者非公平锁

这里只是为了便于理解,实际使用时我们会把代码块try起来,把unlock丢在finally里面

30、volatile关键字解决了什么问题?实现原理是什么?

解决了什么问题:

1、保证了变量的可见性

2、禁止指令重排

比如i=i+1,单线程操作没问题,如果使用多线程,比如两个线程,执行这段代码后(i初始值为0),i应该等于2,但是如果不用volatile修饰变量i,结果会等于1,初始时,两个线程分别读取i的值存入各自所在的CPU的高速缓存当中,然后线程1进行加1操作,然后把i的最新值1写入到内存。此时线程2的高速缓存当中i的值还是0,进行加1操作之后,i的值为1,然后线程2把i的值写入内存。

实现原理

基于内存屏障,关于内存屏障,搞java开发的同学在开发中不可能接触到,所以不用关心太多,知道内存屏障有什么作用,面试官问到你能唬住他就行了,因为面试官自己也不懂

(1)它确保指令重排序时不会把其后面的指令排到内存屏障前面,也不会把前面的指令排到内存屏障后面,总之一句话,他能保证指令按照我们希望的顺序执行;

(2)它会强制将对缓存的修改操作立即写入主存,使得其它线程能立马发现;

31、ThreadLocal实现原理?

ThreadLocal中文名叫线程变量,它底层维护了一个map,key就是当前的ThreadLocal对象(可以理解为当前执行该段代码的线程),value就是你set的值,这个map保证了各个线程的数据互不干扰;

想进一步了解ThreadLocal的同学请移步我凭ThreadLocal唬住了京东面试官

32、线程池实现原理?

要说实现原理,必须得把线程池的几个参数彻底搞懂,不要死记硬背

1、corePoolSize(必填):核心线程数。

2、maximumPoolSize(必填):最大线程数。

3、keepAliveTime(必填):线程空闲时长。如果超过该时长,非核心线程就会被回收。

4、unit(必填):指定keepAliveTime的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。

5、workQueue(必填):任务队列。通过线程池的execute()方法提交的Runnable对象将存储在该队列中。

6、threadFactory(可选):线程工厂。一般就用默认的。

7、handler(可选):拒绝策略。当线程数达到最大线程数时就要执行饱和策略。

贴心的博主单独给你们写了篇讲线程池的,五分钟就能看懂的那种,请移步线程池实现原理吐血总结

33、java是如何实现线程安全的?哪些数据结构是线程安全的?

1、锁机制:用synchronize、lock给共享资源加锁;

2、使用java提供的安全类:java.util.concurrent包下的类自身就是线程安全的,在保证安全的同时还能保证性能;

线程安全的数据结构:

常见的有Atomicinteger、ConcurrentHashMap,太多了,截个图吧,大家可以自己去java.util.concurrent包下看

34、java线程间通信方式

1、基于synchronized+wait() 和 notify()

2、基于reentrantLock的Condition

3、基于volatile

4、基于CountDownLatch

一两句话说不清楚,单独写了篇,每个方式我都给了demo,复制就能运行,请移步java线程间通讯的几种方式

35、springMvc的controller是单例还是多例?

默认是单例,可以通过@Scope(“prototype”)注解指定成多例,但一般没人这么干

36、什么是redis缓存穿透、缓存击穿、缓存雪崩?如何解决?

从事态严重性来讲:穿透 > 雪崩 > 击穿

缓存穿透:请求数据库中根本就不存在的数据,既然数据库中都没有,缓存中更没有,导致每次请求直接怼到数据库;

缓存雪崩:缓存大面积失效;

缓存击穿:请求了很多缓存中没有但是数据库中真实存在的数据,一般是缓存过期导致,也导致请求直接怼到数据库;

解决办法:

缓存穿透:最简单的就是利用布隆过滤器过滤非法key,我写了个 demo来分析具体原理,请移步布隆过滤器原理

缓存雪崩:设置key过期时间的时候加上一个随机数,关键点就在于让key错开时间失效;

缓存击穿:延长热点数据过期时间,或者直接设置永远不过期;

37、说说数据库设计三范式?

第一范式

属性(字段)的原子性约束,要求属性具有原子性,不可再分割; 比如个人信息,个人信息不能作为一个字段,它可以再分为姓名、name、age等;

第二范式

记录的惟一性约束,要求记录有惟一标识,每条记录需要有一个属性来做为实体的唯一标识;

第三范式

字段冗余性的约束,即任何字段不能由其他字段派生出来;主键没有直接关系的数据列必须消除,消除的办法就是再创建一个表来存放他们,当然外键除外;

误区:

并不是非得严格按照三范式来设计,好的数据库设计一定不是这样的,而是根据实际情况柔性处理;

38、简单阐述Java中的io、nio、bio

i/o即input/output,就是指读写操作

面试官经常问io和nio的区别,如果把io和nio放一起比较的话,那这里的io其实可以理解为bio,即blocking-io:

bio:同步阻塞

bio是java传统的io模型,他是同步阻塞io,一个线程触发io操作后,必须等待这个io操作执行完成,期间不能去做其他事情;

nio:同步非阻塞

nio(non-blocking-io)是同步非阻塞io,一个线程触发io操作后它可以立即返回,但是他需要不断地轮询去获取返回结果;

aio:异步非阻塞

aio(Asynchronous-io)是异步非阻塞io,一个线程触发io操作后她可以立马返回去做其他事情,内核系统将io操作执行完成后会通知线程;

多路复用io:异步阻塞

io多路复用:可以理解为异步阻塞io,但官方没这么叫,一个线程可以管理多个连接,不用来回切换;

39、什么是内存泄漏,怎么确定内存泄漏?

概念:内存泄漏就是指jvm内存没有及时释放,用人话说就是使用完的对象没有被及时回收

怎么确认:linux有个工具叫valgrind,一两句话说不清楚,单独拎出来讲,移步使用valgrind来检查内存泄漏

40、实现单例设计模式(懒汉、饿汉)

//懒汉,顾名思义比较懒,在用的时候才实例化

public class Singleton {

//创建实例,注意,此时没有new

private static volatile Singleton instance = null;

//构造方法私有化,无法在外部获取实例,只能通过下方的公有静态方法

private Singleton() {}

//公有的静态方法,返回实例对象

public static synchronized Singleton getInstance() {

//先看下是否存在实例,有的话就不再new了

if (instance == null) {

//这里才new

instance = new Singleton();

}

return instance;

}

}

//饿汉,顾名思义很饥饿,创建对象的时候就直接new

public class Singleton {

//创建实例的时候就new

private static Singleton instance = new Singleton();

// 私有化构造方法,外部不能new

private Singleton() {}

//公有的静态方法,返回实例对象

public static Singleton getInstance() {

//直接将事先new好的实例返回

return instance;

}

}

41、手写生产者消费者模型

生产者消费者是并发编程很经典的一个模型

牵涉三个对象:仓库、生产者、消费者

仓库:代表共享变量

生产者:表示在仓库生产货物

消费者:表示从仓库拿出货物

实现思路:线程间通信

具体demo请见一看就懂的生产者消费者模型

42、int和Integer有什么区别?

int是基本数据类型,默认值为0,integer是其包装类型,默认值为null

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)

最后

由于篇幅限制,小编在此截出几张知识讲解的图解

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

、源码讲义、实战项目、讲解视频,并且后续会持续更新**

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java) [外链图片转存中…(img-cORfjAVM-1712492108640)]

最后

由于篇幅限制,小编在此截出几张知识讲解的图解

[外链图片转存中…(img-zL6HJQmh-1712492108640)]

[外链图片转存中…(img-9Y0axMuX-1712492108640)]

[外链图片转存中…(img-A4PakBSH-1712492108641)]

[外链图片转存中…(img-07WZSZku-1712492108641)]

[外链图片转存中…(img-bCCVRGT2-1712492108641)]

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

好文阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: