面试被问SpringCloud Alibaba Nacos,一问三不知?看这里!
随着业务的发展,用户量和业务复杂度逐渐增加,系统为了支撑更大的流量需要做很多优化,比如升级服务器配置提升性能。在软件方面,我们会采用微服务架构,对业务服务进行微服务化拆分,水平扩容等来提升系统性能,以及解决业务的复杂性问题。
在微服务架构下,一个业务服务会拆分多个微服务,各个服务之间相互通信完成整体的功能。另外,为了避免单点故障,微服务都会采取集群方式的高可用部署,集群规模越大,性能也会越高
服务消费者要去调用多个服务提供者组成的集群。首先,服务消费者需要在本地配置文件中维护一个服务提供者集群的每个节点的请求地址。其次,服务提供者集群中如果某个节点下线或者宕机,服务消费者本地配置中需要同步删除这个节点的请求地址,防止请求发送到已经宕机的节点上造成请求失败,为了解决这类问题,就需要引入服务注册中心,它主要有以下功能:
- 服务地址的管理
- 服务注册
- 服务动态感知
Nacos是阿里巴巴开源的一个对微服务架构中服务发现,配置管理和服务管理平台,由于第一代SpringCloud也就是Springcloud Netflix很多组件已经进入停更维护模式,所以迫使我们必须要找到一个可以代替Netflix的第二代产品,这时候SpringCloud Alibaba出现了
Nacos就是注册中心+配置中心的结合体,在SpringCloud第一代中=Eureka+Config+Bus
- 服务发现与健康监测
- 动态配置管理
- 动态DNS服务
- 服务和元数据管理(管理平台的角度,nacos也有一个ui页面,可以看到注册的服务以及实例信息(元数据信息等),动态的服务权重调整,动态服务优雅下线,都可以去做)
先来点开Nacos官网:
https://nacos.io/zh-cn/docs/quick-start.html
快速开始看一下官网是如何教大家安装的
从官网看到,官网上说了两种编译方式
您可以从 github.com/alibaba/nac… 下载 nacos-server-$version.zip 包。
启动命令(standalone代表着单机模式运行,非集群模式):
sh startup.sh -m standalone
如果您使用的是ubuntu系统,或者运行脚本报错提示[[符号找不到,可尝试如下运行:
bash startup.sh -m standalone
启动命令(standalone代表着单机模式运行,非集群模式):
cmd startup.cmd -m standalone
这里我们刚刚下好了一个最新版的nacos-server!
解压,进入nacos目录下,进入bin目录
因为我电脑是mac所以需要执行sh startup.sh -m standalone命令
如果没有报错,访问http://127.0.0.1:8848/nacos/#/login
账号密码都是nacos
img
进入到首页
Namespace命名空间,Group分组,集群这些都是为了进行归类管理,把服务和配置文件进行归类,归类之后就可以实现一定的效果,比如隔离
比如,对于服务来说,不同命名空间中的服务不能够相互访问调用
img
Namespace:命名空间,对不同的环境进行隔离,比如隔离开发环境,测试环境和生产环境
Group:分组,将若干个服务或者若干个配置集归为一组,通常习惯一个系统归为一个组
Service:某一个服务
DataId:配置集或者可以认为是一个配置文件
Namespace + Group + Service 如同 Maven 中的GAV坐标,GAV坐标是为了锁定 Jar,二这里是为了锁定服务
Namespace + Group + DataId 如同 Maven 中的GAV坐标,GAV坐标是为了锁定 Jar,二这里是为了锁定配置文件
pom.xml:
NacosDubboProviderApplication.java
启动项目,访问nacos控制台
img
git clone https://github.com/alibaba/nacos.git
下载nacos源码,使用idea打开
Nacos主要源码主要分三个部分:
- 服务注册
- 服务地址的获取
- 服务地址变化的感知
首先,搞过SpringBoot的都知道,自动装配
那么,上面实战中,我们在maven中导入的spring-cloud-starer-alibaba-nacos这个包,我们在idea中项目目录中External Libraries下找到这个包
在META-INF目录下找到spring.factories文件
这里在Spring Boot启动的时候,就会自动装配以下几个类,这里直接找com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration
这里跟大家说一下,自动装配了这么多的类,我们如果一个一个点进去看,真的,累死,废时间,那么,我们可以根据类名猜一下。NacosDiscoveryAutoConfiguration(Nacos自动发现配置)看翻译, 像这么点意思,后面的就是RibbonNacosAutoConfiguration这个一看就是跟ribbon相关的,所以先不用管之后就是NacosDiscoveryEndpointAutoConfiguration,这个endpoint一看就不是主线代码,所以也先不用管,在之后NacosDiscoveryClientAutoConfiguration看着也有点那么意思,所以暂时先保留,再看最后一个NacosConfigServerAutoConfiguration,带Config的就肯定是就是把properties文件或者yml文件中的配置信息加载的类,所以,我们找NacosDiscoveryAutoConfiguration类来看一下
看到@Configuration这个注解,就说明这个类是一个配置类,所以我们就需要看里面的三个@Bean就行了。首先第一个是NacosServiceRegistry,也就是nacos服务注册的意思,后面看到服务注册回看这里,先跳过。第二个是NacosRegistration,nacos注册,好像也跟注册有关。这里我们看到第三个@Bean下面的类是NacosAutoServiceRegistration,Auto是自动的意思,自动装配,这里我们点进去看一下
到这里我们看一下这个类有没有继承什么父类,如果有,那么,我们还需要先看一下它的父类
这里我们点进父类看一下
这里我们看到它实现了ApplicationListener这个接口。对Spring了解的人应该知道这个接口是做事件监听的。这里会调用事件处理方法onApplicationEvent方法
这个onApplicationEvent方法调用了bind方法,我们点进bind方法看一下
在看源码的时候,为了可以快速的了解整体流程,这种if return是可以过掉的,因为实际上这种代码并没有太大的作用 后面是一个cas,cas之后调用了this.start方法,这个start方法,看名字就知道应该是个非常重要的方法,我们点进去看一下
这个start方法里面的逻辑大概看一下,if里面就不用看了,直接看else,else里面,我们第一眼就看到有个方法,叫register,看到这个方法,我们大概猜一下就知道,这个方法应该是用来注册的,我们点进去看一下
发现它就调用了register方法,我们继续点进去看
发现这个实际上是一个接口方法,这里我们向下找它具体的实现
这里if也是不用看的,首先就是得到一个serviceId,之后调用getNacosInstanceFromRegistration方法,得到一个Instance实体类,我们点进getNacosInstanceFromRegistration方法看一下
这里就是简单的拼装了一下信息,而这些信息实际上就是你在yml文件中写的端口号,ip等信息,然后返回
我们继续回到register方法,后面调用了registerInstance方法,看名字,可以猜到注册实例,所以我们继续点进去看一下
这里还是一个接口,我们点击找到这个接口的实现方法
进入registerInstance方法
看到这里,也算是真正的找到最下面的具体是如何注册实例的了
registerInstance方法的实现,主要逻辑如下
- 通过beatReactor.addBeatInfo创建心跳信息实现健康检测,Nacos Server必须要保证注册的服务实例是健康的,而心跳检测就是服务健康检测的手段
- serverProxy.registerService实现服务注册
我们点进addBeatInfo方法看一下
从上述代码看,所谓心跳机制就是客户端通过schedule定时向服务端发送一个数据包,然后启动一个线程不断检测服务端的回应,如果在设定时间内没有收到服务端的回应,则认为服务器出现了故障,Nacos服务端不会根据客户端的心跳包不断更新服务状态了
这里我们点进schedule方法看一下
我们往上找,看了一下类
这里学过并发编程的朋友都知道,其实这个就是一个线程池
那既然这个类是线程池,说明new BeatReactor.BeatTask(beatInfo)这个应该是一个线程,那我们点进BeatTask方法看一下
这里我们发现,BeatTask这个类其实是实现了Runnable的类
那这里我们看一下它的run方法
这里可以看到,其中方法叫sendBeat(发送心跳)。所以这里我们点进这个方法看一下
这里if也不用看,params是在组装数据,看看中间,我们看到一个this.reqAPI方法,reqAPI,看名字应该是请求api,再看一眼后面参数,应该是在拼装路径
在官网https://nacos.io/zh-cn/docs/open-api.html 的open api中,有一个是发送心跳
我们点击NACOS_URL_BASE发现NACOS_URL_BASE属性为空,所以我们把整个类看一下,看是在哪赋值的
原来是在static代码块中赋值的,这样就跟官网的发送实例心跳的请求路径是一样的了,所以这里我们更确定,是发送心跳方法了
这里我们点进去看一下
这里我们再回到run方法中
最后还有一个调用schedule方法,说明这里是周期性的发送心跳,时间为5s,关于秒数前面参数传了,感兴趣的可以自己点回去看一下
这里我们在回到run方法之前的registerInstance方法中看最后一个方法this.serverProxy.registerService,这个方法应该是最核心的方法了,我们点进去看一下
前面又是拼装了一堆参数,最后又看到了这个熟悉的方法,reqAPI。这里我们点开NACOS_URL_INSTANCE看一下
发现还是没赋值的 我们又找到static代码块
发现NACOS_URL_INSTANCE=NACOS_URL_BASE+/insetance
在官网上,我们找到了这个api,注册一个实例到服务
ok,看到这里大家就知道了,这句代码实际上就是给服务端发送注册实例的请求,那这时候,我们就应该看一下服务端这个代码具体是如何实现的了
这里我们在官网上下载好nacos源码,直接用idea打开,等待包下载完毕就可以了
我们大概看一下目录,发现了naming这个文件夹,点进去找到controllers文件夹看到了InstanceController,点开看一下代码
看到这个,就说明找对了。我们往下看
register正是我们服务端要调的接口,我们看到里面有一个registerInstance方法,我们点进去看一下
这里第一句代码是createEmptyService,创建一个空的service,我们前面说Nacos数据模型的时候也说过一个词叫Service是包含在Namespace和Group之中的。我们在写springboot客户端的时候,都会去yml或者properties文件中,写一个spring.application.name,这个就对应着一个service
这里我们点进createEmptyService方法看一下
这里调用了createServiceIfAbsent(大概意思就是如果是没有的,那么就创建)。所以我们点进这个方法看一下
这里首先是调用getService判断service是否为空,我们看一下getService方法
这里看出来有一个serviceMap
这个map是由namespace和group+serviceName进行分离的
如果不是空,就直接返回map里面具体的值,这里我们点开Service源码看一下
可以看到Service里面有一个clusterMap,这里就跟一开始的图关联起来了,这里Cluster是用来真正存放实例的
点开Cluster看一下
Cluster中包含了两个,一个是持久的实例一个临时实例
回到createServiceIfAbsent方法中
因为第一次一定是空,所以我们看if中的逻辑。最后有一个putServiceAndInit,看名字大概是添加Service和初始化,我们点进去看一下
这里调用了putService,我们继续点进去看一下
这里首先是判断了一下是否存在,如果不存在就加锁,紧接着又进行了一次判断,这里进行两次判断就很nice。有点单例模式双重锁的感觉,这里可能有小伙伴不太清楚为啥要判断两次,我简单说明一下 这里第一次判断,是为了减少锁等待时间,假如,有很多个线程同时请求进来了,假如serviceMap是有返回值的,那么,这里还是上锁了,还是需要串行的执行,消耗时间。
synchronized里面的判断是,假如有两个线程第一个if判断是空了,走到synchronized了,线程A加锁了,线程B等待,线程A put了,线程B就需要判断再判断一下,要不然就重复put了,也消耗时间
这里把外层的put进去了之后,紧接着就获取外层的,在put里面的map
这里我们再回到putServiceAndInit方法中,putService方法看完了,下面有一个init方法,我们点进去看一下
我们看到有个类叫HealthCheckReactor,可以看出这个类是用来健康检查的,我们点进scheduleCheck方法看一下
这里有几个参数,第一个5000是过5s执行,第二个5000是每隔5s执行一次,这个task,跟之前客户端一样,肯定是一个线程,所以我们点开ClientBeatCheckTask类来看一下
实现了Runnable接口,所以我们找到run方法
这里我们回到registerInstance方法中,我们之前看完了createEmptyService方法内容,它只是new了一个空的service并没有往service中放具体的实例所以我们往下看addInstance方法
剩下的下一篇来讲,这一篇写的有点多了,哈哈哈!关注一下我,期待下一篇。
程序员去大公司面试,Java岗大厂面试官常问的那些问题,进阶学习
这段时间一直在学习Netty相关知识,因为涉及知识点比较多,也走了不少弯路。目前网上关于Netty学习资料琳琅满目,不知如何下手,其实大家都是一样的,学习方法和技巧都是总结出来的,我们在没有找到很好的方法之前不如按部就班先从基础开始,一般从总分总的渐进方式,既观森林,又见草木。
Netty是一款提供异步的、事件驱动的网络应用程序框架和工具,是基于NIO客户端、服务器端的编程框架。所以这里我们先以NIO和依赖相关的基础铺垫来进行剖析讲解,从而作为Netty学习之旅的一个开端。
文末有资料获取方式
和 equals 的区别是什么?
两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?
final 在 java 中有什么作用?
java 中的 Math.round(-1.5) 等于多少?
String 属于基础的数据类型吗?
java 中操作字符串都有哪些类?它们之间有什么区别?
String str=\”i\”与 String str=new String(“i”)一样吗?
如何将字符串反转?
String 类的常用方法都有那些?
抽象类必须要有抽象方法吗?
普通类和抽象类有哪些区别?
抽象类能使用 final 修饰吗?
接口和抽象类有什么区别?
java 中 IO 流分为几种?
BIO、NIO、AIO 有什么区别?
Files的常用方法都有哪些?
java 容器都有哪些?
Collection 和 Collections 有什么区别?
List、Set、Map 之间的区别是什么?
HashMap 和 Hashtable 有什么区别?
如何决定使用 HashMap 还是 TreeMap?
说一下 HashMap 的实现原理?
说一下 HashSet 的实现原理?
ArrayList 和 LinkedList 的区别是什么?
如何实现数组和 List 之间的转换?
ArrayList 和 Vector 的区别是什么?
Array 和 ArrayList 有何区别?
在 Queue 中 poll()和 remove()有什么区别?
哪些集合类是线程安全的?
迭代器 Iterator 是什么?
Iterator 怎么使用?有什么特点?
Iterator 和 ListIterator 有什么区别?
怎么确保一个集合不能被修改?
并行和并发有什么区别?
线程和进程的区别?
守护线程是什么?
创建线程有哪几种方式?
说一下 runnable 和 callable 有什么区别?
线程有哪些状态?
sleep() 和 wait() 有什么区别?
notify()和 notifyAll()有什么区别?
线程的 run()和 start()有什么区别?
创建线程池有哪几种方式?
线程池都有哪些状态?
线程池中 submit()和 execute()方法有什么区别?
在 java 程序中怎么保证多线程的运行安全?
多线程锁的升级原理是什么?
什么是死锁?
怎么防止死锁?
ThreadLocal 是什么?有哪些使用场景?
说一下 synchronized 底层实现原理?
synchronized 和 volatile 的区别是什么?
synchronized 和 Lock 有什么区别?
synchronized 和 ReentrantLock 区别是什么?
说一下 atomic 的原理?
什么是反射?
什么是 java 序列化?什么情况下需要序列化?
动态代理是什么?有哪些应用?
怎么实现动态代理?
为什么要使用克隆?
如何实现对象克隆?
深拷贝和浅拷贝区别是什么?
jsp 和 servlet 有什么区别?
jsp 有哪些内置对象?作用分别是什么?
说一下 jsp 的 4 种作用域?
session 和 cookie 有什么区别?
说一下 session 的工作原理?
如果客户端禁止 cookie 能实现 session 还能用吗?
spring mvc 和 struts 的区别是什么?
如何避免 sql 注入?
什么是 XSS 攻击,如何避免?
什么是 CSRF 攻击,如何避免?
hrow 和 throws 的区别?
final、finally、finalize 有什么区别?
try-catch-finally 中哪个部分可以省略?
try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
常见的异常类有哪些?
http 响应码 301 和 302 代表的是什么?有什么区别?
forward 和 redirect 的区别?
简述 tcp 和 udp的区别?
tcp 为什么要三次握手,两次不行吗?为什么?
说一下 tcp 粘包是怎么产生的?
OSI 的七层模型都有哪些?
get 和 post 请求有哪些区别?
如何实现跨域?
说一下 JSONP 实现原理?
说一下你熟悉的设计模式?
简单工厂和抽象工厂有什么区别?
什么是spring?
Spring的俩大核心概念
Spring框架的设计目标,设计理念,和核心是什么
Spring由哪些模块组成?
Spring 框架中都用到了哪些设计模式?
使用 Spring 有哪些方式?
spring 支持几种 bean 的作用域?
spring 自动装配 bean 有哪些方式?
spring 事务实现方式有哪些?
什么是Spring MVC?简单介绍下你对Spring MVC的理解?
Spring MVC的主要组件?
什么是Spring MVC框架的控制器?
MVC是什么?MVC设计模式的好处有哪些
Spring MVC常用的注解有哪些?
Spring MVC与Struts2区别
Spring MVC怎么样设定重定向和转发的?
Spring MVC的异常处理?
什么是Spring MVC框架的控制器?
说一下 spring mvc 运行流程?
spring mvc 有哪些组件?
@RequestMapping 的作用是什么
@Autowired 的作用是什么?
什么是 spring boot?
为什么要用 spring boot?
spring boot 核心配置文件是什么?
spring boot 配置文件有哪几种类型?它们有什么区别?
spring boot 有哪些方式可以实现热部署?
jpa 和 hibernate 有什么区别?
什么是 spring cloud?
spring cloud 断路器的作用是什么?
spring cloud 的核心组件有哪些?
为什么要使用 hibernate?
什么是 ORM 框架?
hibernate 中如何在控制台查看打印的 sql 语句?
hibernate 有几种查询方式?
hibernate 实体类可以被定义为 final 吗?
在 hibernate 中使用 Integer 和 int 做映射有什么区别?
hibernate 是如何工作的?
get()和 load()的区别?
说一下 hibernate 的缓存机制?
hibernate 对象有哪些状态?
在 hibernate 中 getCurrentSession 和 openSession 的区别是什么?
hibernate 实体类必须要有无参构造函数吗?为什么?
MyBatis是什么?
Mybatis优缺点
Hibernate 和 MyBatis 的区别
为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?
传统JDBC开发存在什么问题?
JDBC编程有哪些不足之处,MyBatis是如何解决的?
MyBatis和Hibernate的适用场景?
MyBatis编程步骤是什么样的?
请说说MyBatis的工作原理
MyBatis的功能架构是怎样的
MyBatis的框架架构设计是怎么样的
什么是DBMS
Mybatis都有哪些Executor执行器?它们之间的区别是什么?
Mybatis中如何指定使用哪一种Executor执行器?
Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
Mybatis如何执行批量操作
当实体类中的属性名和表中的字段名不一样 ,怎么办
Mapper 编写有哪几种方式?
什么是MyBatis的接口绑定?有哪些实现方式?
使用MyBatis的mapper接口调用时有哪些要求?
Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
简述Mybatis的Xml映射文件和Mybatis内部数据结构之间的映射关系?
Xml映射文件中,除了常见的select|insert|updae|delete标签之外,还有哪些标签?
Mybatis是否可以映射Enum枚举类?
简述Mybatis的插件运行原理,以及如何编写一个插件。
Mybatis的一级、二级缓存
什么是MQ
MQ的优点
解耦、异步、削峰是什么?。
消息队列有什么缺点
你们公司生产环境用的是什么消息中间件?
Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?
MQ 有哪些常见问题?如何解决这些问题?
什么是RabbitMQ?
rabbitmq 的使用场景
RabbitMQ的工作模式
如何保证RabbitMQ消息的顺序性?
消息如何分发?
消息基于什么传输?
如何保证消息不被重复消费?或者说,如何保证消息消费时的幂等性?
如何确保消息正确地发送至 RabbitMQ? 如何确保消息接收方消费了消息?
如何保证RabbitMQ消息的可靠传输?
为什么不应该对所有的 message 都使用持久化机制?
如何保证高可用的?RabbitMQ 的集群
如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,怎么办?
设计MQ思路?
kafka 可以脱离 zookeeper 单独使用吗?为什么?
kafka 有几种数据保留的策略?
kafka 同时设置了 7 天和 10G 清除数据,到第五天的时候消息达到了 10G,这个时候 kafka 将如何处理?
什么情况会导致 kafka 运行变慢?
使用 kafka 集群需要注意什么?
zookeeper 是什么?
ZooKeeper 提供了什么?
Zookeeper 怎么保证主从节点的状态同步?
zookeeper 是如何保证事务的顺序一致性的?
Zookeeper Watcher 机制 – 数据变更通知
集群中为什么要有主节点?
集群中有 3 台服务器,其中一个节点宕机,这个时候 zookeeper 还可以使用吗?
说一下 zookeeper 的通知机制?
Watcher 特性总结
客户端注册 Watcher 实现
服务端处理 Watcher 实现
Zookeeper 下 Server 工作状态
zookeeper 是如何保证事务的顺序一致性的?
分布式集群中为什么会有 Master主节点?
zookeeper 负载均衡和 nginx 负载均衡区别
Zookeeper 有哪几种几种部署模式?
集群支持动态添加机器吗?
Zookeeper 对节点的 watch 监听通知是永久的吗?为什么不是永久的?
Zookeeper 的 java 客户端都有哪些?
chubby 是什么,和 zookeeper 比你怎么看?
Zookeeper 都有哪些功能?
说一下 Zookeeper 的通知机制?
Zookeeper 和 Dubbo 的关系?
为什么要使用数据库?
什么是MySQL?
MySql, Oracle,Sql Service的区别
mysql有关权限的表都有哪几个
MySQL的binlog有有几种录入格式?分别有什么区别?
数据库经常使用的函数?
mysql有哪些数据类型?
MySQL存储引擎MyISAM与InnoDB区别
MyISAM索引与InnoDB索引的区别?
什么是索引?索引有哪些优缺点?
怎么创建索引的,有什么好处,有哪些分类
索引有哪几种类型?
索引的数据结构(b树,hash)
索引算法有哪些?
创建索引的三种方式
百万级别或以上的数据如何删除
B树和B+树的区别
事物的四大特性(ACID)介绍一下?
什么是事务的隔离级别?MySQL的默认隔离级别是什么?
从锁的类别上分MySQL都有哪些锁呢?
MySQL中InnoDB引擎的行锁是怎么实现的?
什么是存储过程?有哪些优缺点?
什么是触发器?触发器的使用场景有哪些?
SQL语句主要分为哪几类
怎么优化SQL查询语句吗
如何定位及优化SQL语句的性能问题?创建的索引有没有被使用到?或者说怎么才可以知道这条语句运行很慢的原因?
SQL的生命周期?
MySQL数据库cpu飙升到500%的话他怎么处理?
备份计划,mysqldump以及xtranbackup的实现原理
什么是Redis?
Redis有哪些优缺点?
使用redis有哪些好处?
为什么要用 Redis / 为什么要用缓存
为什么要用 Redis 而不用 map/guava 做缓存?
Redis为什么这么快
Redis有哪些数据类型
Redis的应用场景
Redis 的持久化机制是什么?各自的优缺点?
如何选择合适的持久化方式
Redis持久化数据和缓存怎么做扩容?
Redis的过期键的删除策略
Redis key的过期时间和永久有效分别怎么设置?
我们知道通过expire来设置key 的过期时间,那么对过期的数据怎么处理呢?
MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据
Redis主要消耗什么物理资源?
Redis的内存用完了会发生什么?
Redis如何做内存优化?
Redis事务的概念
Redis事务支持隔离性吗
redis 集群模式的工作原理能说一下么?
在集群模式下,redis 的 key 是如何寻址的?
分布式寻址都有哪些算法?
了解一致性 hash 算法吗?
Redis集群的主从复制模型是怎样的?
生产环境中的 redis 是怎么部署的?
Redis集群会有写操作丢失吗?为什么?
Redis是单线程的,如何提高多核CPU的利用率?
你知道有哪些Redis分区实现方案?
如何解决 Redis 的并发竞争 Key 问题
分布式Redis是前期做还是后期规模上来了再做好?为什么?
Redis和Redisson有什么关系?
我们开发人员编写的Java代码是怎么让电脑认识的
Jdk和Jre和JVM的区别
说一下 JVM由那些部分组成,运行流程是什么?
说一下 JVM 运行时数据区
详细的介绍下程序计数器?(重点理解)
详细介绍下Java虚拟机栈?(重点理解
你能给我详细的介绍Java堆吗?(重点理解)
能不能解释一下本地方法栈?
能不能解释一下方法区(重点理解)
什么是JVM字节码执行引擎
你听过直接内存吗?
知道垃圾收集系统吗?
堆栈的区别是什么?
深拷贝和浅拷贝
Java会存在内存泄漏吗?请说明为什么?
JVM 中都有哪些引用类型?
怎么判断对象是否可以被回收?
对象什么时候可以被垃圾器回收
JVM 垃圾回收算法有哪些?
JVM中的永久代中会发生垃圾回收吗
说一下 JVM 有哪些垃圾回收器?
说一下 JVM 调优的工具?
俗话说,好学者临池学书,不过网络时代,对于大多数的我们来说,我倒是觉得学习意识的觉醒很重要,这是开始学习的转折点,比如看到对自己方向发展有用的信息,先收藏一波是一波,比如如果你觉得我这篇文章ok,先点赞收藏一波。这样,等真的沉下心来学习,不至于被找资料分散了心神。慢慢来,先从点赞收藏做起,加油吧!
阿里伤透我心,疯狂复习刷题,终于喜提offer 哈哈~好啦,不闲扯了,文章开头说要免费给大家分享我的复习资料,下面就给大家展示一下
1、JAVA面试核心知识整理(PDF):包含JVM,JAVA集合,JAVA多线程并发,JAVA基础,Spring原理,微服务,Netty与RPC,网络,日志,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存,Hadoop,Spark,Storm,YARN,机器学习,云计算共30个章节。
2、Redis学习笔记及学习思维脑图
3、数据面试必备20题+数据库性能优化的21个最佳实践
文章到这里就结束了,感谢你的观看,需要这些面试题 资料的小伙伴 可以私信我 “资料”免费获取
挑战10个最难的Java面试题(附答案)【上】
这是收集的10个最棘手的Java面试问题列表。这些问题主要来自 Java 核心部分 ,不涉及 Java EE 相关问题。你可能知道这些棘手的 Java 问题的答案,或者觉得这些不足以挑战你的 Java 知识,但这些问题都是容易在各种 Java 面试中被问到的,而且包括我的朋友和同事在内的许多程序员都觉得很难回答。
一个棘手的 Java 问题,如果 Java编程语言不是你设计的,你怎么能回答这个问题呢。Java编程的常识和深入了解有助于回答这种棘手的 Java 核心方面的面试问题。
为什么 wait,notify 和 notifyAll 是在 Object 类中定义的而不是在 Thread 类中定义
这是有名的 Java 面试问题,招2~4年经验的到高级 Java 开发人员面试都可能碰到。这个问题的好在它能反映了面试者对等待通知机制的了解, 以及他对此主题的理解是否明确。就像为什么 Java 中不支持多继承或者为什么 String 在 Java 中是 final 的问题一样,这个问题也可能有多个答案。
为什么在 Object 类中定义 wait 和 notify 方法,每个人都能说出一些理由。从我的面试经验来看, wait 和 nofity 仍然是大多数Java 程序员最困惑的,特别是2到3年的开发人员,如果他们要求使用 wait 和 notify, 他们会很困惑。因此,如果你去参加 Java 面试,请确保对 wait 和 notify 机制有充分的了解,并且可以轻松地使用 wait 来编写代码,并通过生产者-消费者问题或实现阻塞队列等了解通知的机制。
为什么等待和通知需要从同步块或方法中调用, 以及 Java 中的 wait,sleep 和 yield 方法之间的差异,如果你还没有读过,你会觉得有趣。为何 wait,notify 和 notifyAll 属于 Object 类? 为什么它们不应该在 Thread 类中? 以下是我认为有意义的一些想法:
1) wait 和 notify 不仅仅是普通方法或同步工具,更重要的是它们是 Java 中两个线程之间的通信机制。对语言设计者而言, 如果不能通过 Java 关键字(例如 synchronized)实现通信此机制,同时又要确保这个机制对每个对象可用, 那么 Object 类则是的正确声明位置。记住同步和等待通知是两个不同的领域,不要把它们看成是相同的或相关的。同步是提供互斥并确保 Java 类的线程安全,而 wait 和 notify 是两个线程之间的通信机制。
2) 每个对象都可上锁,这是在 Object 类而不是 Thread 类中声明 wait 和 notify 的另一个原因。
3) 在 Java 中为了进入代码的临界区,线程需要锁定并等待锁定,他们不知道哪些线程持有锁,而只是知道锁被某个线程持有, 并且他们应该等待取得锁, 而不是去了解哪个线程在同步块内,并请求它们释放锁定。
4) Java 是基于 Hoare 的监视器的思想。在Java中,所有对象都有一个监视器。线程在监视器上等待,为执行等待,我们需要2个参数:一个线程、一个监视器(任何对象)
在 Java 设计中,线程不能被指定,它总是运行当前代码的线程。但是,我们可以指定监视器(这是我们称之为等待的对象)。这是一个很好的设计,因为如果我们可以让任何其他线程在所需的监视器上等待,这将导致“入侵”,导致在设计并发程序时会遇到困难。请记住,在 Java 中,所有在另一个线程的执行中侵入的操作都被弃用了(例如 stop 方法)。
我发现这个 Java 核心问题很难回答,因为你的答案可能不会让面试官满意,在大多数情况下,面试官正在寻找答案中的关键点,如果你提到这些关键点,面试官会很高兴。在 Java 中回答这种棘手问题的关键是准备好相关主题, 以应对后续的各种可能的问题。
这是非常经典的问题,与为什么 String 在 Java 中是不可变的很类似; 这两个问题之间的相似之处在于它们主要是由 Java 创作者的设计决策使然。
为什么Java不支持多重继承, 可以考虑以下两点:
1)第一个原因是围绕钻石形继承问题产生的歧义,考虑一个类 A 有 foo() 方法, 然后 B 和 C 派生自 A, 并且有自己的 foo() 实现,现在 D 类使用多个继承派生自 B 和C,如果我们只引用 foo(), 编译器将无法决定它应该调用哪个 foo()。这也称为 Diamond 问题,因为这个继承方案的结构类似于菱形,见下图:
即使我们删除钻石的顶部 A 类并允许多重继承,我们也将看到这个问题含糊性的一面。如果你把这个理由告诉面试官,他会问为什么 C++ 可以支持多重继承而 Java不行。嗯,在这种情况下,我会试着向他解释我下面给出的第二个原因,它不是因为技术难度, 而是更多的可维护和更清晰的设计是驱动因素, 虽然这只能由 Java 言语设计师确认,我们只是推测。维基百科链接有一些很好的解释,说明在使用多重继承时,由于钻石问题,不同的语言地址问题是如何产生的。
2)对我来说第二个也是更有说服力的理由是,多重继承确实使设计复杂化并在转换、构造函数链接等过程中产生问题。假设你需要多重继承的情况并不多,简单起见,明智的决定是省略它。此外,Java 可以通过使用接口支持单继承来避免这种歧义。由于接口只有方法声明而且没有提供任何实现,因此只有一个特定方法的实现,因此不会有任何歧义。
另一个类似棘手的Java问题。为什么 C++ 支持运算符重载而 Java 不支持? 有人可能会说+运算符在 Java 中已被重载用于字符串连接,不要被这些论据所欺骗。
与 C++ 不同,Java 不支持运算符重载。Java 不能为程序员提供自由的标准算术运算符重载,例如+, – ,*和/等。如果你以前用过 C++,那么 Java 与 C++ 相比少了很多功能,例如 Java 不支持多重继承,Java中没有指针,Java中没有引用传递。另一个类似的问题是关于 Java 通过引用传递,这主要表现为 Java 是通过值还是引用传参。虽然我不知道背后的真正原因,但我认为以下说法有些道理,为什么 Java 不支持运算符重载。
1)简单性和清晰性。清晰性是Java设计者的目标之一。设计者不是只想复制语言,而是希望拥有一种清晰,真正面向对象的语言。添加运算符重载比没有它肯定会使设计更复杂,并且它可能导致更复杂的编译器, 或减慢 JVM,因为它需要做额外的工作来识别运算符的实际含义,并减少优化的机会, 以保证 Java 中运算符的行为。
2)避免编程错误。Java 不允许用户定义的运算符重载,因为如果允许程序员进行运算符重载,将为同一运算符赋予多种含义,这将使任何开发人员的学习曲线变得陡峭,事情变得更加混乱。据观察,当语言支持运算符重载时,编程错误会增加,从而增加了开发和交付时间。由于 Java 和 JVM 已经承担了大多数开发人员的责任,如在通过提供垃圾收集器进行内存管理时,因为这个功能增加污染代码的机会, 成为编程错误之源, 因此没有多大意义。
3)JVM复杂性。从JVM的角度来看,支持运算符重载使问题变得更加困难。通过更直观,更干净的方式使用方法重载也能实现同样的事情,因此不支持 Java 中的运算符重载是有意义的。与相对简单的 JVM 相比,复杂的 JVM 可能导致 JVM 更慢,并为保证在 Java 中运算符行为的确定性从而减少了优化代码的机会。
4)让开发工具处理更容易。这是在 Java 中不支持运算符重载的另一个好处。省略运算符重载使语言更容易处理,这反过来又更容易开发处理语言的工具,例如 IDE 或重构工具。Java 中的重构工具远胜于 C++。
我最喜欢的 Java 面试问题,很棘手,但同时也非常有用。一些面试者也常问这个问题,为什么 String 在 Java 中是 final 的。
字符串在 Java 中是不可变的,因为 String 对象缓存在 String 池中。由于缓存的字符串在多个客户之间共享,因此始终存在风险,其中一个客户的操作会影响所有其他客户。例如,如果一段代码将 String “Test” 的值更改为 “TEST”,则所有其他客户也将看到该值。由于 String 对象的缓存性能是很重要的一方面,因此通过使 String 类不可变来避免这种风险。
同时,String 是 final 的,因此没有人可以通过扩展和覆盖行为来破坏 String 类的不变性、缓存、散列值的计算等。String 类不可变的另一个原因可能是由于 HashMap。
由于把字符串作为 HashMap 键很受欢迎。对于键值来说,重要的是它们是不可变的,以便用它们检索存储在 HashMap 中的值对象。
由于 HashMap 的工作原理是散列,因此需要具有相同的值才能正常运行。如果在插入后修改了 String 的内容,可变的 String将在插入和检索时生成两个不同的哈希码,可能会丢失 Map 中的值对象。
如果你是印度板球迷,你可能能够与我的下一句话联系起来。字符串是Java的 VVS Laxman,即非常特殊的类。我还没有看到一个没有使用 String 编写的 Java 程序。这就是为什么对 String 的充分理解对于 Java 开发人员来说非常重要。
String 作为数据类型,传输对象和中间人角色的重要性和流行性也使这个问题在 Java 面试中很常见。
为什么 String 在 Java 中是不可变的是 Java 中最常被问到的字符串访问问题之一,它首先讨论了什么是 String,Java 中的 String 如何与 C 和 C++ 中的 String 不同,然后转向在Java中什么是不可变对象,不可变对象有什么好处,为什么要使用它们以及应该使用哪些场景。
这个问题有时也会问:“为什么 String 在 Java 中是 final 的”。在类似的说明中,如果你正在准备Java 面试,我建议你看看《Java程序员面试宝典(第4版) 》,这是高级和中级Java程序员的优秀资源。它包含来自所有重要 Java 主题的问题,包括多线程,集合,GC,JVM内部以及 Spring和 Hibernate 框架等。
正如我所说,这个问题可能有很多可能的答案,而 String 类的唯一设计者可以放心地回答它。我在 Joshua Bloch 的 Effective Java 书中期待一些线索,但他也没有提到它。我认为以下几点解释了为什么 String 类在 Java 中是不可变的或 final 的:
1)想象字符串池没有使字符串不可变,它根本不可能,因为在字符串池的情况下,一个字符串对象/文字,例如 “Test” 已被许多参考变量引用,因此如果其中任何一个更改了值,其他参数将自动受到影响,即假设
现在字符串 B 调用 \”Test\”.toUpperCase(), 将同一个对象改为“TEST”,所以 A 也是 “TEST”,这不是期望的结果。
下图显示了如何在堆内存和字符串池中创建字符串。
2)字符串已被广泛用作许多 Java 类的参数,例如,为了打开网络连接,你可以将主机名和端口号作为字符串传递,你可以将数据库 URL 作为字符串传递, 以打开数据库连接,你可以通过将文件名作为参数传递给 File I/O 类来打开 Java 中的任何文件。如果 String 不是不可变的,这将导致严重的安全威胁,我的意思是有人可以访问他有权授权的任何文件,然后可以故意或意外地更改文件名并获得对该文件的访问权限。由于不变性,你无需担心这种威胁。这个原因也说明了,为什么 String 在 Java 中是最终的,通过使 java.lang.String final,Java设计者确保没有人覆盖 String 类的任何行为。
3)由于 String 是不可变的,它可以安全地共享许多线程,这对于多线程编程非常重要. 并且避免了 Java 中的同步问题,不变性也使得String 实例在 Java 中是线程安全的,这意味着你不需要从外部同步 String 操作。关于 String 的另一个要点是由截取字符串 SubString 引起的内存泄漏,这不是与线程相关的问题,但也是需要注意的。
4)为什么 String 在 Java 中是不可变的另一个原因是允许 String 缓存其哈希码,Java 中的不可变 String 缓存其哈希码,并且不会在每次调用 String 的 hashcode 方法时重新计算,这使得它在 Java 中的 HashMap 中使用的 HashMap 键非常快。简而言之,因为 String 是不可变的,所以没有人可以在创建后更改其内容,这保证了 String 的 hashCode 在多次调用时是相同的。
5)String 不可变的绝对最重要的原因是它被类加载机制使用,因此具有深刻和基本的安全考虑。如果 String 是可变的,加载“java.io.Writer” 的请求可能已被更改为加载 “mil.vogoon.DiskErasingWriter”. 安全性和字符串池是使字符串不可变的主要原因。顺便说一句,上面的理由很好回答另一个Java面试问题: “为什么String在Java中是最终的”。要想是不可变的,你必须是最终的,这样你的子类不会破坏不变性。你怎么看?
另一个基于 String 的棘手 Java 问题,相信我只有很少的 Java 程序员可以正确回答这个问题。这是一个真正艰难的核心Java面试问题,并且需要对 String 的扎实知识才能回答这个问题。
这是最近在 Java 面试中向我的一位朋友询问的问题。他正在接受技术主管职位的面试,并且有超过6年的经验。如果你还没有遇到过这种情况,那么字符数组和字符串可以用来存储文本数据,但是选择一个而不是另一个很难。但正如我的朋友所说,任何与 String 相关的问题都必须对字符串的特殊属性有一些线索,比如不变性,他用它来说服访提问的人。在这里,我们将探讨为什么你应该使用char[]存储密码而不是String的一些原因。
字符串:
1)由于字符串在 Java 中是不可变的,如果你将密码存储为纯文本,它将在内存中可用,直到垃圾收集器清除它. 并且为了可重用性,会存在 String 在字符串池中, 它很可能会保留在内存中持续很长时间,从而构成安全威胁。
由于任何有权访问内存转储的人都可以以明文形式找到密码,这是另一个原因,你应该始终使用加密密码而不是纯文本。由于字符串是不可变的,所以不能更改字符串的内容,因为任何更改都会产生新的字符串,而如果你使用char[],你就可以将所有元素设置为空白或零。因此,在字符数组中存储密码可以明显降低窃取密码的安全风险。
2)Java 本身建议使用 JPasswordField 的 getPassword() 方法,该方法返回一个 char[] 和不推荐使用的getTex() 方法,该方法以明文形式返回密码,由于安全原因。应遵循 Java 团队的建议, 坚持标准而不是反对它。
3)使用 String 时,总是存在在日志文件或控制台中打印纯文本的风险,但如果使用 Array,则不会打印数组的内容而是打印其内存位置。虽然不是一个真正的原因,但仍然有道理。
输出
我还建议使用散列或加密的密码而不是纯文本,并在验证完成后立即从内存中清除它。因此,在Java中,用字符数组用存储密码比字符串是更好的选择。虽然仅使用char[]还不够,还你需要擦除内容才能更安全。
这个 Java 问题也常被问: 什么是线程安全的单例,你怎么创建它。好吧,在Java 5之前的版本, 使用双重检查锁定创建单例 Singleton 时,如果多个线程试图同时创建 Singleton 实例,则可能有多个 Singleton 实例被创建。从 Java 5 开始,使用 Enum 创建线程安全的Singleton很容易。但如果面试官坚持双重检查锁定,那么你必须为他们编写代码。记得使用volatile变量。
为什么枚举单例在 Java 中更好
枚举单例是使用一个实例在 Java 中实现单例模式的新方法。虽然Java中的单例模式存在很长时间,但枚举单例是相对较新的概念,在引入Enum作为关键字和功能之后,从Java5开始在实践中。本文与之前关于 Singleton 的内容有些相关, 其中讨论了有关 Singleton 模式的面试中的常见问题, 以及 10 个 Java 枚举示例, 其中我们看到了如何通用枚举可以。这篇文章是关于为什么我们应该使用Eeame作为Java中的单例,它比传统的单例方法相比有什么好处等等。
Java 枚举和单例模式
Java 中的枚举单例模式是使用枚举在 Java 中实现单例模式。单例模式在 Java 中早有应用, 但使用枚举类型创建单例模式时间却不长. 如果感兴趣, 你可以了解下构建者设计模式和装饰器设计模式。
1) 枚举单例易于书写
这是迄今为止最大的优势,如果你在Java 5之前一直在编写单例, 你知道, 即使双检查锁定, 你仍可以有多个实例。虽然这个问题通过 Java 内存模型的改进已经解决了, 从 Java 5 开始的 volatile 类型变量提供了保证, 但是对于许多初学者来说, 编写起来仍然很棘手。与同步双检查锁定相比,枚举单例实在是太简单了。如果你不相信, 那就比较一下下面的传统双检查锁定单例和枚举单例的代码:
在 Java 中使用枚举的单例
这是我们通常声明枚举的单例的方式,它可能包含实例变量和实例方法,但为了简单起见,我没有使用任何实例方法,只是要注意,如果你使用的实例方法且该方法能改变对象的状态的话, 则需要确保该方法的线程安全。默认情况下,创建枚举实例是线程安全的,但 Enum 上的任何其他方法是否线程安全都是程序员的责任
你可以通过EasySingleton.INSTANCE来处理它,这比在单例上调用getInstance()方法容易得多。
具有双检查锁定的单例示例
下面的代码是单例模式中双重检查锁定的示例,此处的 getInstance() 方法检查两次,以查看 INSTANCE 是否为空,这就是为什么它被称为双检查锁定模式,请记住,双检查锁定是代理之前Java 5,但Java5内存模型中易失变量的干扰,它应该工作完美。
你可以调用DoubleCheckedLockingSingleton.getInstance() 来获取此单例类的访问权限。
现在,只需查看创建延迟加载的线程安全的 Singleton 所需的代码量。使用枚举单例模式, 你可以在一行中具有该模式, 因为创建枚举实例是线程安全的, 并且由 JVM 进行。
人们可能会争辩说,有更好的方法来编写 Singleton 而不是双检查锁定方法, 但每种方法都有自己的优点和缺点, 就像我最喜欢在类加载时创建的静态字段 Singleton, 如下面所示, 但请记住, 这不是一个延迟加载单例:
单例模式用静态工厂方法
这是我最喜欢的在 Java 中影响 Singleton 模式的方法之一,因为 Singleton 实例是静态的,并且最后一个变量在类首次加载到内存时初始化,因此实例的创建本质上是线程安全的。
你可以调用 Singleton.getSingleton() 来获取此类的访问权限。
2) 枚举单例自行处理序列化
传统单例的另一个问题是,一旦实现可序列化接口,它们就不再是 Singleton, 因为 readObject() 方法总是返回一个新实例, 就像 Java 中的构造函数一样。通过使用 readResolve() 方法, 通过在以下示例中替换 Singeton 来避免这种情况:
如果 Singleton 类保持内部状态, 这将变得更加复杂, 因为你需要标记为 transient(不被序列化),但使用枚举单例, 序列化由 JVM 进行。
3) 创建枚举实例是线程安全的
如第 1 点所述,因为 Enum 实例的创建在默认情况下是线程安全的, 你无需担心是否要做双重检查锁定。
总之, 在保证序列化和线程安全的情况下,使用两行代码枚举单例模式是在 Java 5 以后的世界中创建 Singleton 的最佳方式。你仍然可以使用其他流行的方法, 如你觉得更好, 欢迎讨论。
经典但核心Java面试问题之一。
如果你没有参与过多线程并发 Java 应用程序的编码,你可能会失败。
如何避免 Java 线程死锁?
如何避免 Java 中的死锁?是 Java 面试的热门问题之一, 也是多线程的编程中的重口味之一, 主要在招高级程序员时容易被问到, 且有很多后续问题。尽管问题看起来非常基本, 但大多数 Java 开发人员一旦你开始深入, 就会陷入困境。
面试问题总是以“什么是死锁?”开始
当两个或多个线程在等待彼此释放所需的资源(锁定)并陷入无限等待即是死锁。它仅在多任务或多线程的情况下发生。
如何检测 Java 中的死锁?
虽然这可以有很多答案, 但我的版本是首先我会看看代码, 如果我看到一个嵌套的同步块,或从一个同步的方法调用其他同步方法, 或试图在不同的对象上获取锁, 如果开发人员不是非常小心,就很容易造成死锁。
另一种方法是在运行应用程序时实际锁定时找到它, 尝试采取线程转储,在 Linux 中,你可以通过kill -3命令执行此操作, 这将打印应用程序日志文件中所有线程的状态, 并且你可以看到哪个线程被锁定在哪个线程对象上。
你可以使用 fastthread.io 网站等工具分析该线程转储, 这些工具允许你上载线程转储并对其进行分析。
另一种方法是使用 jConsole 或 VisualVM, 它将显示哪些线程被锁定以及哪些对象被锁定。
如果你有兴趣了解故障排除工具和分析线程转储的过程, 我建议你看看 Uriah Levy 在多元视觉(PluraIsight)上《分析 Java 线程转储》课程。旨在详细了解 Java 线程转储, 并熟悉其他流行的高级故障排除工具。
编写一个将导致死锁的Java程序?
一旦你回答了前面的问题,他们可能会要求你编写代码,这将导致Java死锁。
这是我的版本之一
如果 method1() 和 method2() 都由两个或多个线程调用,则存在死锁的可能性, 因为如果线程 1 在执行 method1() 时在 Sting 对象上获取锁, 线程 2 在执行 method2() 时在 Integer 对象上获取锁, 等待彼此释放 Integer 和 String 上的锁以继续进行一步, 但这永远不会发生。
此图精确演示了我们的程序, 其中一个线程在一个对象上持有锁, 并等待其他线程持有的其他对象锁。
你可以看到, Thread1 需要 Thread2 持有的 Object2 上的锁,而 Thread2 希望获得 Thread1 持有的 Object1 上的锁。由于没有线程愿意放弃, 因此存在死锁, Java 程序被卡住。
其理念是, 你应该知道使用常见并发模式的正确方法, 如果你不熟悉这些模式,那么 Jose Paumard 《应用于并发和多线程的常见 Java 模式》是学习的好起点。
如何避免Java中的死锁?
现在面试官来到最后一部分, 在我看来, 最重要的部分之一; 如何修复代码中的死锁?或如何避免Java中的死锁?
如果你仔细查看了上面的代码,那么你可能已经发现死锁的真正原因不是多个线程, 而是它们请求锁的方式, 如果你提供有序访问, 则问题将得到解决。
下面是我的修复版本,它通过避免循环等待,而避免死锁, 而不需要抢占, 这是需要死锁的四个条件之一。
现在没有任何死锁,因为两种方法都按相同的顺序访问 Integer 和 String 类文本上的锁。因此,如果线程 A 在 Integer 对象上获取锁, 则线程 B 不会继续, 直到线程 A 释放 Integer 锁, 即使线程 B 持有 String 锁, 线程 A 也不会被阻止, 因为现在线程 B 不会期望线程 A 释放 Integer 锁以继续。
任何序列化该类的尝试都会因NotSerializableException而失败,但这可以通过在 Java中 为 static 设置瞬态(trancient)变量来轻松解决。
Java 序列化相关的常见问题
Java 序列化是一个重要概念, 但它很少用作持久性解决方案, 开发人员大多忽略了 Java 序列化 API。根据我的经验, Java 序列化在任何 Java核心内容面试中都是一个相当重要的话题, 在几乎所有的网面试中, 我都遇到过一两个 Java 序列化问题, 我看过一次面试, 在问几个关于序列化的问题之后候选人开始感到不自在, 因为缺乏这方面的经验。
他们不知道如何在 Java 中序列化对象, 或者他们不熟悉任何 Java 示例来解释序列化, 忘记了诸如序列化在 Java 中如何工作, 什么是标记接口, 标记接口的目的是什么, 瞬态变量和可变变量之间的差异, 可序列化接口具有多少种方法, 在 Java 中,Serializable 和 Externalizable 有什么区别, 或者在引入注解之后, 为什么不用 @Serializable 注解或替换 Serializalbe 接口。
关注 点击下方,第一时间了解华为云新鲜技术~
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。