Java开发八股文之基础篇+spring+集合

目前是准大四生,最近在进行一个求职,于是整理了一些自己背的八股文的题目和参考答案,在求职的小伙伴也可以参考一下我整理的八股文,参考答案仅供参考,要是有什么不合理的地方可以跟我说我再进行一个修改~,后续会慢慢补充

基础篇:

1.JVM、JDK、JRE三者关系?

jdk:包括了java运行环境jre,java工具和Java基础类库

jre:是Java程序运行所需的最小环境

JVM:是Java虚拟机,是Java程序运行的环境。它负责将Java字节码(由Java编译器生成)解释或编译成机器码,=并执行程序。JVM提供了内存管理、垃圾回收、安全性等功能,使得Java程序具备跨平台性。

2.八种基本的数据类型

Java支持数据类型分为两类:基本数据类型和引I用数据类型。

基本数据类型共有8种,可以分为三类:

数值型:整数类型(byte、short、int、long)和浮点类型(float、double)

·字符型:char

·布尔型:boolean

Java八种基本数据类型的字节数:1字节(byte、boolean)、2字节(short、char)、4字节(int、float)、8字节(long、double)

3.数据类型转换方式你知道哪些?

自动类型转换(隐式转换):当目标类型的范围大于原类型时候,会自动转化

强制类型转换(显示转换):小于范围时候 long-》int

字符串转换Interger.parseInt()方法,将字符串转换为整型

数值之间的转换:将整型转换为字符型等,使用包装类实现

4.类型互转会出现什么问题吗?

数据丢失:比如当范围较大的数据类型转换为比较小的数据类型时,long-》int转,会导致高位数据丢失

数据溢出:范围小转换为范围大 int-long

精度丢失:float-double转换

数据转换类型不匹配问题

5.装箱和拆箱是什么?

是将基本数据类型和它所对应的包装类之间的转换

6.Java为什么要有Integer?

①在泛型中的应用:泛型只能使用引用类型,而不是使用基本类型

②在转换中的应用:基本类型和引用类型不能直接转换,必须使用包装类来实现,如int转换为String,必须先转换为Integer

③在集合中的应用:集合只能存储对象,而不能存储基本数据类型。

7.说一下 integer的缓存

Java的Integer类内部实现了一个静态缓存池,用于存储特定范围内的整数值对应的Integer对

象。

默认情况下,这个范围是-128至127。当通过Integer.valueOf(int)方法创建一个在这个范围内

的整数对象时,并不会每次都生成新的对象实例,而是复用缓存中的现有对象,会直接从内

存中取出,不需要新建一个对象

8.怎么理解面向对象?简单说说封装继承多态

面向对象是一种编程范式,它将现实世界中的事物抽象为对象,对象具有属性(称为宇段或属性)和行为

(称为方法).面向对象编程的设计思想是以对象为中心,通过对象之间的交互来完成程序的功能,具有

灵活性和可扩展性,通过封装和继承可以更好地应对需求变化。

java面向对象的三大特性包括:封装,继承,多态:

**封装:**封装是指将对象的属性(数据)和行为(方法)结合在一起,对外隐藏对象的内部细节,仅通过对象提供的接口与外界交互。封装的目的是增强安全性和简化编程,使得对象更加独立。

**继承:**继承是一种可以使得子类自动共享父类数据结构和方法的机制。它是代码复用的重要手段,通过继承可以建立类与类之间的层次关系,使得结构更加清晰。

**多态:**多态是指允许不同类的对象对同一消息作出响应。 即同一个接口,使用不同的实例而执行不同操作。多态性可以分为编译时多态(重载)和运行时多态 (重写).它使得程序具有良好的灵活性和扩展性。

9.多态体现在哪几个方面?

方法重载:

方法重载是指同一类中可以有多个同名方法,它们具有不同的参数列表(参数类型,数量或顺序不同).虽然方法名相同,但根据传入的参数不同,编译器会在编译时确定调用哪个方法。

示例:对于一个 add 方法,可以定义为 add(int a, int b) 和 add( double a,double b)
方法重写:

方法重写是指子类能够提供对父类中同名方法的具体实现。在运行时,vm会根据对象的实际类型确定调用哪个版本的方法。这是实现多态的主要方式。

示例:在一个动物类中,定义一个 punos 方法,子类 dog 可以重写该方法以实现

bark,而 cat 可以实现 meow .

10.重载与重写有什么区别?

重载(overloading) 指的是在同一个类中,可以有多个同名方法,它们具有不同的参数类型,参数个数或参数顺序不同),编译器根据调用时的参数类型来决定调用哪个方法。
重写(overriding)指 的是子类可以重新定义父类中的方法名,参数列表和这回类型必须与父类中的方法一致,通过@override注解来明确表示这是对父类方法的重写。

重载是指在同一个类中定义多个同名方法,而重写是指子类重新定义父类中的方法。

11.抽象类能加final修饰吗?

不能,Java中的抽象类是用来被继承的,而final修饰符用于禁止类被继承或方法被重写,因此,抽象类和final修饰符是互斥的,不能同时使用。

12.接口可以包含构造函数吗?

不可以,因为接口不会有自己的实例,不需要有构造函数

13.Java 中 final 作用是什么?

修饰类、方法、变量

类:不能被继承

修饰方法:不能被重写

修饰变量:一旦被赋值不能被改变

14.什么是泛型?

泛型是Java编程语言中的一个重要特性,它允许类、接口和方法在定义时使用一个或多个类

型参数,这些类型参数在使用时可以被指定为具体的类型。

泛型的主要目的是在编译时提供更强的类型检查,并且在编译后能够保留类型信息,避免了

在运行时出现类型转换异常。

15.java创建对象有哪些方式?

(1)new关键字

(2)使用class类的newInstance()方法

(3)使用constructor类的newInstance()方法

(4)使用clone()方法

(5)使用反序列化,通过对象序列化到文件或流中,然后再进行反序列化来创建对象

14.什么是泛型?

泛型是Java编程语言中的一个重要特性,它允许类、接口和方法在定义时使用一个或多个类

型参数,这些类型参数在使用时可以被指定为具体的类型。

泛型的主要目的是在编译时提供更强的类型检查,并且在编译后能够保留类型信息,避免了

在运行时出现类型转换异常。

15.java创建对象有哪些方式?

(1)new关键字

(2)使用class类的newInstance()方法

(3)使用constructor类的newInstance()方法

(4)使用clone()方法

(5)使用反序列化,通过对象序列化到文件或流中,然后再进行反序列化来创建对象

16.如何获取私有对象?

java中,私有的对象通常用private修饰,只能在类内部使用

不过可以通过以下两种方式来简介获取私有对象

第一:使用公共访问器方法(getter方法)

第二:采用反射机制

反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法,对于任意一个对象,都能调用它的任意一个属性和方法。

17.反射在你平时写代码或者框架中的应用场景有哪些?

我们项目使用的数据库是MySQL,使用jdbc连接数据库时使用Class.forName()通过加载数据库驱动程序。

18.能讲一讲Java注解的原理吗?

注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理

类。

我们通过反射获取注解时,返回的是ava运行时生成的动态代理对象。通过代理对象调用自定

义注解的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从

memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。

19.对注解解析的底层实现了解吗?

注解解析的底层实现主要依赖于Java的反射机制和字节码文件的存储。通过@Retention元注解可以控制注解的保留策略,当使用RetentionPolicy.RUNTIME时,可以在运行时通过反射API来解析注解信息。在JVM层面,会从字节码文件中读取注解信息,并创建注解的代理对象来获取注解的属性值。

20.Java注解的作用域呢?

类级别作用域(类的访问级别、继承关系、注释)、

方法级别作用域(访问级别、返回类型、异常、注释)、

字段级别作用域(访问级别、默认值、注释)

21.介绍一下Java异常

异常主要基于两大类,throwable类以及子类,throwable有两个比较重要的子类Error和Exception

Error表示环境运行时错误,这个错误是程序无法处理的严重问题

Exception:可以处理的异常,分为两大类:非运行时异常和运行时异常

非运行异常如文件不存在,运行时异常如空指针

22、Java异常处理有哪些?

try-catch,将觉得有异常的代码写在try里面,catch用于捕获并处理异常

throw语句:手动抛出异常,在代码中使用

复制代码
throw new ExceptionType("Exception message");

throws关键字:在方法声明中声明可能抛出的异常

复制代码
public void methodName() throws ExceptionType {//方法体}

finally块:无论异常是否发生都会执行这边,通常用于释放资源

23.== 与 equals 有什么区别?

==比较的是两个字符串内存地址(堆内存)的数值是否相等,属于数值比较;

equals(:比较的是两个字符串的内容,属于内容比较。

24.hashcode和equals方法有什么关系?

假如重写了equals方法则必须重写hashcode,以保证在使用哈希表等数据结构时,对象的相等性判断和存储查找操作能够正常工作。

23.String、StringBuffer、StringBuilder的区别和联系

都是Java中处理字符串常用的类

  • String:不可变的字符串,线程安全,适合固定内容的场景。
  • StringBuffer:可变的字符串,线程安全,适合多线程下的频繁修改。
  • StringBuilder:可变的字符串,非线程安全,适合单线程下的高性能操作。

24.怎么把一个对象从一个jvm转移到另一个jvm?

这个可以使用序列化和反序列化来实现,将对象序列化为字节流,并将其发送到另一个jvm,然后在另一个jvm中反序列化字节流回复对象,这个可以通过java的objectOutStream和objectInputStream来实现

还有一种方法就是使用消息中间件,将对象序列化为消息,通过消息队列进行传输,步骤主要是先将对象转换为通用格式,然后发送消息到中间件,最后目标JVM订阅消息并反序列化

25.将对象转为二进制字节流具体怎么实现?

在Java中通过序列化对象流来完成序列化和反序列化:

首先让类实现serializable接口

创建输出流并写入对象,通过ObjectOutputStream里的writeObject()方法做序列化操作

创建输入流并读取对象,通过ObjectInputStrean里的readObject()方法做反序列化操作。

spring篇:

28.说一下你对 Spring 的理解

spring框架的核心特性包括:

**ioc容器:**spring通过控制反转实现了对象的创建和对象间的依赖关系管理,开发者定义好bean以及依赖关系,spring容器负责创建和组装这些对象

**aop:**面向对象编程,允许开发者定义横切关注点,通过aop,可以将这些关注点模块化,提高代码的可维护性和可重用性

**事务管理:**spring提供了一致的事务管理接口,支持声明式和编程式事务。开发者可以轻松管理事务,而无需关心具体的api

**MVC框架:**SpringMVC是一个基于ServletAPI构建的Web框架,采用了模型-视图-控制器(MVC)架构。它支持灵活的URL到页面控制器的映射。

29.spring的核心思想说说你的理解?

ioc:控制反转

di:抵赖注入

aop:面向切面编程

30.Spring的aop介绍一下

aop是spring框架中的一个重要模块,用于实现面向切面编程。

在面向对象编程中,把功能分为核心业务功能(增删改查登录注册)和周边功能(日志、事务管理),两者分别独立开发的,不是耦合的,然后把切面功能和核心功能编织在一起,叫做aop

在aop有以下几个概念:

切面aspectj:只是一个概念,没有具体的接口或类与之对应。

连接点join point:指程序执行过程中的一个点,例如方法调用、异常处理

通知advice:即我们定义的一个切面中的横切逻辑,有"around","before"和"after"三种类型。在很多的 AOP 实现框架中,Advice 通常作为一个拦截器,也可以包含许多个拦截器作为一条链路围绕着 Join point 进行处理。

切点(pointcut):切点,用于匹配连接点

31.怎么理解SpringIoc?

IOC:InversionOfControl,即控制反转,是一种设计思想。在传统的JavaSE程序设计中,我们直接在对象内部通过new的方式来创建对象,是程序主动创建依赖对象;而在spring程序设计中,ioc是有专门的容器去控制对象,所谓的控制就是对象的创建、初始化、销毁

创建:原来是new一个,现在是由spring容器创建

初始化:原来是由构造器或者是setter方法给对象赋值,现在是由spring容器自动注入

销毁:原来是为对象赋值为null或者是做一些销毁动作,现在是由spring容器管理生命周期负责销毁对象

反转就是将对象的控制权交给了spring,我们由对象的控制在变成了ioc的被控制者,这样可以解决繁琐的对象生命周期,解偶我们的代码。

32.依赖倒置,依赖注入,控制反转分别是什么?

33.依赖注入了解吗?怎么实现依赖注入的?

在传统编程中,当一个类需要使用另一个类的对象时,通常会在该类内部通过new关键字来创建依赖对象,这使得类与类之间的耦合度较高。

而依赖注入则是将对象的创建和依赖关系的管理交给Spring容器来完成,类只需要声明自己所依赖的对象,容器会在运行时将这些依赖对象注入到类中,从而降低了类与类之间的耦合度,提高了代码的可维护性和可测试性。

具体到Spring中,常见的依赖注入的实现方式,比如构造器注入、Setter方法注入,还有字段注入(@Autowired)。

34.Bean的生命周期说一下?

35.Bean是否单例?

Spring中的Bean默认都是单例的。

就是说,每个Bean的实例只会被创建一次,并且会被存储在Spring容器的缓存中,以便在后续的请求中重复使用。这种单例模式可以提高应用程序的性能和内存效率。

36.了解SpringMVC的处理流程吗?

37.为什么使用springboot

·简化开发:SpringBoot通过提供一系列的开箱即用的组件和自动配置,简化了项目的配置和开发过程,开发人员可以更专注于业务逻辑的实现,而不需要花费过多时间在繁琐的配置上。

·快速启动:SpringBoot提供了快速的应用程序启动方式,可通过内嵌的Tomcat、Jetty或Undertow等容器快速启动应用程序,无需额外的部署步骤,方便快捷。

·自动化配置:SpringBoot通过自动配置功能,根据项目中的依赖关系和约定俗成的规则来配置应用程序,减少了配置的复杂性,使开发者更容易实现应用的最佳实践。

38.怎么理解SpringBoot中的约定大于配置

39.springboot怎么开启事务?

使用@Transactional注解

40.说几个启动器?

spring-boot-starter-web 最常用的起步依赖,包含了springmvc和tomcat

mybatis-spring-boot-starter 这个starter是由mybatis提供的,用于简化在springboot应用中集成mybatis的过程,它自动配置类mybatis的相关组件,包括SqlSessionFactory等,使得开发者能有快速的开始使用mybtais进行数据库操作

spring-boot-starter-jdbc:提供了基本的jdbc支持

spring-boot-starter-data-redis:用于集成redis缓存和数据存储服务

spring-boot-starter-test:包含了单元测试和集成测试所需的库

41.SpringBoot自动装配原理是什么?

在springboot项目中引导类上有个**@springbootApplication**注解,这个注解封装了三个注解,分别是springbootConfiguration、EnaleAutoConfiguration、componetScan注解,其中EnaleAutoConfiguration是自动配置的核心注解,该注解通过import注解导入对应的配置选择器,其内容就是读取项目和项目引入jar的classpath文件下的META-INF/spring.factories文件中所配置的全类名,在这些配置中导入的bean会根据条件注解所指定的条件来进行判断是否要注入到spring容器中

条件注解如conditionalonclass,判断是否有对应的class文件,有就将该配置类文件的所有bean放入spring容器中所使用。

42.SpringBoot和spring里面有哪些重要的注解?

springboot:

@SpringBootApplication:用于标注主应用程序类,标识一个Spring Boot应用程序的入口点,同时启用自动配置和组件扫描。

@Controller:标识控制器类,处理HTTP请求。

@RestController:结合@Controller和@ResponseBody

@Service:标识服务类,通常用于标记业务逻辑层。

@Repository:标识数据访问组件,通常用于标记数据访问层。

@Component:通用的Spring组件注解,表示一个受Spring管理的组件。

@Autowired:用于自动装配Spring Bean。

@Value:用于注入配置属性值。

@RequestMapping:用于映射HTTP请求路径到Controller的处理方法。

@GetMapping、@PostMapping、@PutMapping、@DeleteMapping

@Configuration:用于指定一个类为配置类,其中定义的bean会被Spring容器管理。

spring:

@Autowired、@Component、@Configuration、@Bean、@Service、@Repository、@Controller

43.SpringBoot 过滤器和拦截器说一下?

过滤器是java servlet规范中的一部分,它可以对进入servlet请求和响应进行一个预处理和后处理,过滤器通过实现filter接口,并重写init,dofilter,destory方法来完成相应的逻辑,当请求进入servlet,会按照规定好的配置依次顺序执行这些过滤器,响应返回时以相反的顺序执行这些过滤器。

拦截器是spring框架中的一种机制,它可以对控制器方法的执行进行拦截,通过实现处理拦截器接口,实现perhandle、posthandle、aftercompition方法来完成相应的逻辑,当请求达到控制器时,会先进入拦截器prehandle方法,如果返回结果为true,则继续后面的控制器方法和拦截器,在控制方法执行完成之后,则会调用posthandle方法,最后请求处理完成之后,在调用aftercompition方法

区别:

所属规范不同:过滤器是java servle规范的一部分,拦截器是spring框架的一个机制

执行顺序不同:过滤器是在servlet请求之后,控制器执行之前执行,拦截器是在控制器之

后执行的

使用范围不同:过滤器可以对所有类型的请求进行过滤,包括静态资源请求

拦截器是只能对springmvc控制器的请求进行拦截

44.与传统的JDBC相比,MyBatis的优点?

①基于sql语句实现的,比较灵活,将sql语句写在xml文件里,对代码和sql语句进行了一个解耦,便于统一管理。提供xml标签,支持动态SQL,并且可以重用。

②可以很好的与各种数据库相连,只要是jdbc能支持的数据库,mybatis也可以支持。

③能与spring很好的集成,开发效率高

④支持对象与数据库的字段的orm关系映射

45.还记得JDBC连接数据库的步骤吗?

连接jdbc有七大步骤

①加载数据库驱动程序,通过class.forname加载

②获取数据库url路径

③建立与数据库的连接:通过drivemanage.getconntion方法创建conntion对象

④创建statement对象

⑤执行sql语句

⑥返回结果集

⑦释放资源:一般会先释放resultset,再释放statement,最后释放conntion,一般会在finally语句块进行资源的释放

46.Mybatis里的 # 和 $ 的区别?

mybtais在处理#时,会创建预编译sql语句,将#代替为?,在执行SQL语句时,会给?进行一个赋值,采用preparedStatement的set方法进行赋值,执行效率高,可以防止sql注入,安全,适合传参使用

在处理$时,只是普通的创建sql语句,在执行sql语句时,直接将参数拼入sql语句中,不能防止sql注入,由于是直接拼接进去,没有对参与进行一个验证和过滤等操作,安全性不高

47.MybatisPlus和Mybatis的区别?

mybatisplus是基于mybatis得一个增强,旨在提高开发效率

在增删查改操作:mybatisplus继承了basemapper,里面有很多一系列得内置方法

mybatisplus提供代码生成器得功能,根据数据库表结构可以快速生成实体类,xml文件和mapper接口

还有很多通用方法得封装,入分页插件,分页查询等,而在传统mybtais就需要手动写分页逻辑

还有注解的支持,可以通过注解来配置实体类和数据库表之间的映射关系

48.了解SpringCloud吗,说一下他和SpringBoot的区别

这个我是有了解过一些的

springboot是构建单体spring框架结构,springcloud是构建微服务分布式架构,提供了一些服务注册与发现、负载均衡、网关等功能

简单的比喻一下,springboot假设是构建一辆汽车,则微服务就是构建交通系统,两者结合使用才能构建高效的分布式系统。

49.用过哪些微服务组件?

结合项目使用

在之前我写过一个仿京东得一个项目,里面就有用过一些微服务组件

如nacos注册中心,做动态配置管理,将一些application得配置文件配置到nacos

nginx负载均衡和反向代理,采用轮询的策略让用户可以访问不同的端口,解决高并发问题

gateway统一网关,提供统一的api接口,结合redis+令牌桶,对热门商品进行一个限流,还有一个网关的过滤器实现登录鉴权

sentinel服务熔断降级,设置熔断规则等

50.网关过滤器怎么实现登录鉴权

通过网关的过滤器实现登录鉴权

1.登陆成功,生成token,存入redis并设置有效时间

2.把token返回给前端,并存入cookie

3.每次请求,请求头都携带token

4.过滤器(白名单就正常放行)

5.被过滤器拦截获取请求头的token判断有没有携带token, 判断有没有过期

6.token没有过期更新token时间

7.放行

51.负载均衡有哪些算法?

轮询、权重、ip_hash、url_hash、fair、第三方服务

集合篇:

52.数组与集合区别,用过哪些?

数组是固定长度的数据结构,,一旦创建长度就无法改变,而集合是动态长度的数据结构,可以根据需要动态增加或减少元素。

数组可以包含基本数据类型和对象 而集合只能包含对象。

数组可以直接访问元素,而集合需要通过迭代器或其他方法访问元素。

  • ArrayList:适合频繁随机访问、较少插入删除的场景。
  • LinkedList:适合频繁在头部或中间插入删除、较少随机访问的场景。

hashmap是基于哈希表实现的map,根据键的哈希值来存储和获取键值对,数组+链表+红黑树来实现的

53.集合遍历的方法有哪些?

普通for遍历

for-each循环

interator迭代器

54.Collections和Collection的区别

collection是集合的接口,用于定义集合的基本操作,比如添加删除元素

collections是工具类,提供对集合的排序查找等静态方法

55.HashMap实现原理介绍一下?

在jdk1.7版本之前,hashmap数据结构是数组和链表,hashmap通过哈希算法将元素的key映射到数组中的槽位,如果多个键同时映射到同一个槽位,他们就会以链表的形式存储在同一个槽位上,使得链表非常长,冲突很严重,效率就很低

所以在 JDK 1.8 版本的时候做了优化,当一个链表的长度超过8的时候就转换数据结构,不再使用链表存储,而是使用**红黑树,**当数量较少时,会转回链表

额外了解:

为什么是8=》泊松分布统计,哈希冲突达到 8 的概率仅为 0.00000006%(六百万亿分之一),此时树化收益远高于成本。

哈希函数是怎么计算==》哈希计算(一算二扰三取模):首先调用key.hashCode()得到初始值哈希值,然后再用过干扰函数混合高低位信息,最后哈希值与数组长度减一按位与运算,确定数组索引。

哈希冲突怎么处理 :哈希冲突处理:冲突场景就是不同键的哈希值映射到同一索引上,当链表长度大于等于8则转为红黑树(这个需要满足数据长度大于等于64,否则优先扩容)

扩容机制:容量扩大为原来的2倍,重新哈希遍历旧数组,数据迁移

链表查询时间复杂度O(n),红黑树O(lgn)

HashMap 是非线程安全的,多线程环境可以使用Collections.synchronizedMap同步加锁的方式

56.HashMap一般用什么做Key?为啥String适合做Key呢?

用string做key,因为string对象是不可变的,一旦创建就不能被修改,保证了key的稳定性如果Key是可变的,可能会导致hashCode和equals方法的不一致,进而影响hashmap正确性。

57.为什么HashMap要用红黑树而不是平衡二叉树?

平衡二叉树追求的是一种完全平衡的状态,任何节点的左右子树高度差不能超过1,这个要求太严,hashmap会频繁的插入/删除元素,会导致多次旋转来维持平衡。

而红黑树不追求这种完全平衡的状态,而是追求一种"弱平衡"状态,整个树的最长路径不会超过最短路径的2倍,插入/删除最多 2 次旋转 即可恢复平衡,插入/删除效率更高,不需要像平衡二叉树那样频繁的调整。这也是我们大多数情况下使用红黑树的原因。

58.hashmap key可以为null吗?

可以为null,haspmap采用hash方法来计算key的哈希值,当可以为空的时候,直接让key的hash值为0,不走key.hashCode方法

但是虽然支持key和value为null,但是key的null只能有一个,值可以有多个null,因为在hashmap中,如果key值一样,那么会进行一个覆盖

59.重写HashMap的equal和hashcode方法需要注意什么?

相等对象哈希必相等,哈希相等对象不一定相等

原因:HashMap在比较元素时,会先通过hashCode进行比较,相同的情况下再通过equals进行比较

必须同时重写equals和hashcode,必须用不可变字段计算哈希值

原因:hashmap通过hashcode定位桶,通过equals比较键

如果只重写equals,则hashmap可能将逻辑上相等的对象存入不同桶

如果用可变的对象,存入之后修改字段会导致get找不到原对象

60.列举HashMap在多线程下可能会出现的问题?

jdk1.7时,头插法可能会导致链表形成环,get死循环

jdk1.8时,尾插法修复了这个问题,但仍存在导致数据覆盖和丢失问题。

多线程同时执行put操作,如果计算出来的索引位置是相同的,那会造成前一个key

被后一个key覆盖,从而导致元素的丢失,所以HashMap 不是线程安全的

61.HashMap的扩容机制介绍一下

hashmap默认的负载因子是0.75,即如果hashmap中的元素个数超过了总容量75%,则

会触发扩容,扩容分为两个步骤:

第1步是对哈希表长度的扩展(2倍)

第2步是重新哈希所有元素。旧数组的每个槽位中的链表或红黑树会被拆分到新数组的两个位置(原位置或原位置+旧容量)。"

扩容完之后,新索引的计算可以通过hash&oldCapacity进行一个&运算,如果为0则新索引=旧索引,如果非零,则新索引=旧索引+旧容量

62.HashMap的大小为什么是2的n次方大小呢?

是为了用位运算替代取模运算,在扩容时就可以通过位运算高效迁移数据

63.说说hashmap的负载因子

负载因子的默认值是0.75,当hashmap中的元素个数超过了容量75%就会进行扩容

负载因子太低会导致大量的空桶浪费空间,复杂因子太高会导致大量的碰撞,降低性能,所以0.75的负载因子在这两个元素之间取得了良好的平衡。

64.ConcurrentHashMap怎么实现的?

在jdk1.7 的concurrentHashMap底层实现是数组+链表的形式,所以在数据比较多的情况下访问很慢,要遍历整个链表,所以在jdk1.8的时候使用数组+链表/红黑树的方式优化了concurrentHashMap的实现

通过volatile+CAS或者synchronized来实现线程安全,添加元素时会判断容器是否为空

如果为空,则使用volatile+cas初始化

如果不为空,则根据存储元素计算该位置是否为空

空-》利用cas设置该节点

不空-》使用synchronized,然后遍历桶中数据,并替换或新增节点到桶中,最后在判断是否要转为红黑树,从而保证访问时线程安全

65.已经用了synchronized,为什么还要用CAS呢?

synchronized像一把大锁,每次只有一个线程进入同步代码块,其他线程必须等待,适合长时间操作

cas像换牌,直接比较内存值是否被修改过,如果没改就偷偷换成新值,适合用户超短操作

所以如果代码很复杂,用synchronized更安全,如果只是简单的修改,用cas跟高效

他们两个是属于互补的,cas可能失败(比如其他线程先修改了值),此时可以降级用synchronized重试

66.ConcurrentHashMap用了悲观锁还是乐观锁?

悲观锁和乐观都有用到,

回答实现原理

cas乐观锁,synchronized悲观锁

67.说一下HashMap和ConcurrentMap的区别

①线程安全:hashmap线程不安全,ConcurrentMap线程安全

②性能:hashmap单线程快,多线程需要加锁,ConcurrentMap适合高并发

③null支持:hashmap允许键/值为null,ConcurrentMap不允许

④原子操作:hashmap没有原子操作,ConcurrentMap提供putIfAbsent等原子方法

⑤适合场景:hashmap适用于单线程、局部变量,ConcurrentMap多线程、高并发

68.Set集合有什么特点?如何实现key无重复的?

set集合特点:元素唯一

set实现原理:set集合通过内部的逻辑结构来实现key无重复。当向set集合中插入元素时,会先根据元素的hashcode值来确定元素的存储位置,然后再通过equals方法来判断是否已经存在相同的元素,如果存在则不会再次插入,保证了元素的唯一性。

相关推荐
军军君014 分钟前
基于Springboot+UniApp+Ai实现模拟面试小工具三:后端项目基础框架搭建上
前端·vue.js·spring boot·面试·elementui·微信小程序·uni-app
Pi_Qiu_12 分钟前
Python初学者笔记第十三期 -- (常用内置函数)
java·笔记·python
hsx66618 分钟前
Android 基础筑基(一)
java
hy.z_77724 分钟前
【数据结构】反射、枚举 和 lambda表达式
android·java·数据结构
從南走到北34 分钟前
JAVA青企码协会模式系统源码支持微信公众号+微信小程序+H5+APP
java·微信·微信小程序·小程序·uni-app·微信公众平台
草履虫建模1 小时前
Ajax原理、用法与经典代码实例
java·前端·javascript·ajax·intellij-idea
程序员二黑1 小时前
零基础10分钟配好自动化环境!保姆级教程(Win/Mac双版)附避坑工具包
面试·程序员·测试
时寒的笔记1 小时前
js入门01
开发语言·前端·javascript
强哥叨逼叨1 小时前
别被假象迷惑!揭秘 Java 线程池中“线程空着但任务卡着”的真相
java
_extraordinary_1 小时前
Java 栈和队列
java·开发语言