Java面试题(2023.3.7版)

基础篇

1.JRE,JDK,JVM

JRE:是Java程序的运行时环境,包含JVM和运行时所需要的核心类库;

JDK:是Java程序开发工具包,包含JRE和开发人员使用的工具;

我们想要运行一个已有的Java程序,只需要安装JRE即可;

我们想要开发一个全新的Java程序,那么必须安装JDK;

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),

就可以在多种平台上不加修改地运行。

2.面向对象和面向过程的区别:

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,

而是为了描叙某个事物在整个解决问题的步骤中的行为。 其实就是两句话,面向对象就是高度实物抽象化、面向过程就是自顶向下的编程

3.面向对象的三个基本特征:封装,继承,多态

封装:需要将对象属性私有化;需要提供get和set方法;验证参数合法性。

继承:我们可以将相同的成员属性和方法抽取放到一个单独的类。子类中可以重新定义,追加属性和方法。

父类(基类,派生类) 子类

优点:提高代码复用性

提高了代码的维护性

缺点:继承让类与类之间产生了关系,类的耦合性也增加了,当父类发生变化时,子类也不得不跟着变化,削弱了子类的独立性。

多态:同一个对象,在不同的时刻表现出来的不同形态

4.继承中变量的访问特点:

子类局部范围找

子类成员范围找

父类成员范围找

如果都没有则报错

即遵循就近原则。

5.super

访问父类的成员属性,成员方法和构造方法;

this

访问本类的成员属性,成员方法和构造方法。

6.继承中构造方法的访问特点。

①.子类中所有的构造方法默认都会访问父类中的无参的构造方法

②.因为子类会继承父类中的数据,可能还会使用父类中的数据,所以子类初始化之前,需要对父类进行初始化。

③.每个子类构造方法的第一句默认都是:super()

7.继承中访问父类中的成员方法的特点。

子类成员范围找

父类成员范围找

如果还找不到就报错(不考虑父类的父类…)

8.方法重写:子类中出现了和父类一模一样的方法,当子类需要父类的功能,而功能主体中,子类有自己独特的内容,就可以通过重写父类中的方法,这样即延续了父类的功能,又定义了自己的独特内容。方法的名称(参数列表)必须与父类 保持一致。

方法重载:发生在同一个类中,方法名必须相同,参数类型不同,个数不同,顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。对返回值不做要求。

9.final(最终态) 修饰我们的成员方法、成员变量、类 的特点。

修饰变量:该变量的值,不能被修改,必须初始化(手动赋值)

修饰方法:该方法,不能被重写

修饰类:该类,不能被继承

10.instanceof 关键字。

instanceof 通过返回一个布尔值来指出,某个对象是否是某个特定类或者是该特定类的子类的一个实例。

在equals源码中被用到。

11.为什么重写equals一定要重写hashcode()方法?

hashcode()方法在散列集合,如hashmap,hashtable,当添加元素的时候需要判断是否存在,直接使用equals效率太低,一般直接使用对象的hashcode值进行取模运算;如果table中不存在这个对象的hashcode值,则直接存进去,如果存在,再调用equals方法,与新元素比较,如果相同则覆盖,不同则散列到其他地址,调用equals次数降低,提高效率。

12.==和equals

==,java中只有值传递,二者比较的是值(基本数据类型比较值,引用数据类型比较堆中内存的地址值)

equals不能作用于基本数据类型,如果重写了则比较的是所指向对象的内容,如果没有重写则与==等价

13.String,stringbuilder和stringbuffer

string是一个不可变字符串,更改就会成为一个新的对象,线程安全,性能最低,存储在字符串常量池

stringbuilder线程不安全但效率高,存在堆内存,可变

stringBuffer线程安全加了同步锁,存在堆内存,效率较低,可变

14.list和set区别

list有序可重复 可以通过下标访问,而set不可以

set无序不重复 ,其中的元素是根据hashcode()来进行数据的存储,位置是固定的但是位置用户不可控制,所以对于用户是无序的。

15.arraylist和linkedlist的区别

都实现了list接口

arraylist底层数据结构是基于数组实现;更适用于读,随机查找;

linkedlist底层数据结构是链表(双向);更适合删除和添加;实现了Deque,可以当作队列使用

16.hashmap和hashtable的区别。

hashmap有四个字段:loadFactor:负载因子,默认为0.75

threshold:表示 所能容纳的键值对的临界值 threshold=数组长度*负载因子

modCount:内部结构变化的次数

size:表示存在的键值对数量

默认容量为:16

数据结构:数组+链表+红黑树.

数组部分称为hash桶,当链表长度大于等于8 时,链表数据将以红黑树的形式进行存储,当长度小于的等于6时,转成链表。

hashmap1.7之前是数组加链表,1.8之后是数组+链表+红黑树;引入红黑树是因为链表的查询效率低,

1.8:当数组容量大于等于64且链表长度大于8的时候,链表转换为红黑树,存放数据的是无序的,底层是散列集合,尾插法;hashmap key为null的时候index=0

1.7:采用头插法,多线程扩容可能引起安全问题;当计算多个key的index发生冲突的时候,会存放在同一个链表,他要从头到尾进行一次查询,效率很低

hashtable:线程安全,key和value不允许为null

18.concurrenthashmap:

整体架构:数组+单项链表+红黑树

1.7之前数组又分为Segent和hashEntry,即大数组和小数组,大数组像一个数据库,存放了许多个hashentry,而hashEntry中的数组采用的是链表的方式存储的。

采用链式寻址来解决Hash表的冲突

基本功能:在hashmap的基础上提供了并发安全的实现,主要通过对node节点加锁保证数据更新的安全性

性能方面:1.8锁的粒度更低,提供了一个size()方法。

19.copyOnWriteArrayList:

copyOnWrute只会复制数据的引用,所以在获取迭代器时速度很快,此时迭代器和数组都持有同一数组引用,

为了避免增删元素时影响到迭代器,cow集合增删元素会创建一个新的数组来复制元素,在新数组上增删。

优点:适用于读多写少,且数据量不大的情况,线程安全,保障迭代器的独立性和隔离性

缺点:内存占用较大,容易引起GC;数据一致性问题

20.Exception

所有异常都继承自顶级父类Throwable

throwable又分为exception和error。

error是无法处理的错误,发生error程序会终止

Execption又分为RuntimeException和CheckException,RunouttrimeException是指

程序运行时发生的异常,会导致程序当前线程执行失败;CheckedException是检查 异常,会导致程序

编译不通过。

Redis篇

1.Redis为啥快?

①.有c语言编写

②.基于IO多路复用

③.单线程

④.纯内存操作

2.redis有哪些基本的数据结构?

String,Hash,set,Zset,List

3.redis持久化策略,内存淘汰策略

持久化策略有RDB和AOF。

RDB也叫做redis数据快照,简单来说就是把内存中的所有数据都记录到磁盘。当Redis实例故障重启以后,

从磁盘读取快照文件,恢复数据。快照文件成为RDB文件,默认是保存在当前运行目录。

使用场景:可以容忍数分钟的数据丢失,追求更快的启动速度

AOF就是Redis处理的每一个写命令都会记录在AOF文件,可以看作是命令日志文件。

使用场景:对数据安全性要求较高

内存淘汰策略:内存淘汰:就是当内存使用达到设定的阈值时,redis主动挑选部分key删除以释放更多内存的流程。redis支持 8种不同的策略来删除key。

4.redis事务有哪些特性

单独的隔离操作

事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

没有隔离级别的概念

队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行

不保证原子性

事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚

5.redis set集合中的intersect功能来求交集,比如共同关注功能。

6.redis和mysql如何保证数据一致性?

1. 先更新Mysql,再更新Redis,如果更新Redis失败,可能仍然不⼀致

2. 先删除Redis缓存数据,再更新Mysql,再次查询的时候在将数据添加到缓存中,这种⽅案能解决1

⽅案的问题,但是在⾼并发下性能较低,⽽且仍然会出现数据不⼀致的问题,⽐如线程1删除了

Redis缓存数据,正在更新Mysql,此时另外⼀个查询再查询,那么就会把Mysql中⽼数据⼜查到Redis中.

3. 延时双删,步骤是:先删除Redis缓存数据,再更新Mysql,延迟⼏百毫秒再删除Redis缓存数据,

这样就算在更新Mysql时,有其他线程读了Mysql,把⽼数据读到了Redis中,那么也会被删除掉,

从⽽把数据保持⼀致。

MySQL篇

1.SQL语言共分为四大类:数据查询语言DQL,数据操纵语言DML,数据定义语言DDL,数据控制语言DCL。

2.B树和 B+树的区别?MySQL为什么使用B+树而不是B树?

B树的每个节点都存储数据,而B+树只在叶子节点存储数据;

B+树的叶子节点加了链表指针,提高了区间访问性能。

随着数据的增加,B树的深度的深度增加,效率变低。

3.你知道MySQL中redolog、binlog、undolog区别与作用?

redolog是重做日志,当数据库 宕机时,可以通过它来恢复数据 ,用来保证事务的持久性

binlog是二进制日志文件,通过它我们可以再现数据更新的全过程

undolog是回滚日志,用来 保证事务原子性 和一致性。

4.SQL语句的执行过程?

1.应用服务器与数据库服务器建立一个连接

2.数据库进程拿到请求SQL

3.解析并生成执行计划,执行

4.读取数据到内存并进行逻辑处理

5.通过步骤一的连接,发送结果到客户端

6.关闭连接,释放资源

5.Mysql主从复制:

1.master将改变记录到 二进制日志(binlog)中

2.slave通过I/O thread读取binlog日志 并拷贝到中继日志(relay log)

3.slave重做中继日志中的时间,将改变应用到自己的数据库中。

6.索引使用

最左前缀法则

如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,索引将会部分失效(后面的字段索引失效)。如果索引最左列不存在,则索引全部失效。

注意:最左前缀法则中指的最左边的列,是指在查询时,联合索引的最左边的字段(即是 第一个字段)必须存在,与我们编写SQL时,条件编写的先后顺序无关。

范围查询

联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效。

在业务允许的情况下,尽可能的使用类似于 >= 或 <= 这类的范围查询,而避免使用 > 或 <。

索引列运算

不要在索引列上进行运算操作,索引将失效。

字符串不加引号

字符串类型字段使用时,不加引号,索引将失效。

模糊查询

如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。

or连接的条件

用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。or连接的条件,左右两侧字段都有索引时,索引才会生效。

数据分布影响

如果MySQL评估使用索引比全表更慢,则不使用索引。

7.SQL优化:

1、查询语句中不要使用select *

2、尽量减少子查询,使用关联查询(left join,right join,inner join)替代

3、减少使用IN或者NOT IN ,使用exists,not exists或者关联查询语句替代

4、or 的查询尽量用 union或者union all 代替(在确认没有重复数据或者不用剔除重复数据时,

union all会更好)

5、应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。

6、应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num is null 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from t where num=0

8.innodb的隔离级别,分别解决了什么问题,RR级别下可以解决幻读吗

9.mvcc是什么?

10.MySQL中的锁有哪些?临建锁和间隙锁有什么区别?

11.了解ICP(索引下推吗)?

12.视图,存储过程,触发器是什么?优缺点?

Spring系列

1.如何理解springboot中的starters?

Starters可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,你可以一站式集成

Spring及其他技术,而不需要到处找示例代码和依赖包。如你想使用Spring JPA访问数据库,只要

加入spring-boot-starter-data-jpa启动器依赖就能使用了。Starters包含了许多项目中需要用到的

依赖,它们能快速持续的运行,都是一系列得到支持的管理传递性依赖。

2.springmvc的执行过程?

1)⽤户发送请求⾄前端控制器 DispatcherServlet。

2)DispatcherServlet 收到请求调⽤ HandlerMapping 处理器映射器。

3)处理器映射器找到具体的处理器(可以根据 xml 配置、注解进⾏查找),⽣成处理器及处理器拦截器(如果有则⽣成)⼀并返回给 DispatcherServlet。

4)DispatcherServlet 调⽤ HandlerAdapter 处理器适配器。

5)HandlerAdapter 经过适配调⽤具体的处理器(Controller,也叫后端控制器)

6)Controller 执⾏完成返回 ModelAndView。

7)HandlerAdapter 将 controller 执⾏结果 ModelAndView 返回给 DispatcherServlet。

8)DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器。

9)ViewReslover 解析后返回具体 View。

10)DispatcherServlet 根据 View 进⾏渲染视图(即将模型数据填充⾄视图中)。

11)DispatcherServlet 响应⽤户。

3.spring bean的生命周期

1.实例化bean

2.给对象注入属性(依赖注入)

3.处理Aware接口

4。如果bean需要实现其他自定义处理,则通过BeanPostProcessor接口的方法完成

5.如果bean在Spring中配置了init-method属性,则会自动调用其配置初始化的方法

6.如果bean实现了BeanPostProcessor接口,由于是在bean初始或以后调用的,所以可以被应用于缓存或内存

7.当bean不再需要时,会进入 清理阶段,如果Bean实现了DisposableBean接口,会调用其实现的destory()方法。

8.如果bean的Spring配置中配置了的destory-method属性,则通过其配置来自动调用销毁方法。

spring支持的几种bean的作用域?

4.Spring容器中的bean可以分为5个范围:

(1)singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维

护。

(2)prototype:为每一个bean请求提供一个实例。

(3)request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回

收。

(4)session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,

bean会随之失效。

(5)global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet

容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那

么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。

5.spring如何解决循环依赖?

1. 首先 A 完成初始化第一步并将自己提前曝光出来(通过 ObjectFactory 将自己提前曝光),在

初始化的时候,发现自己依赖对象 B,此时就会去尝试 get(B),这个时候发现 B 还没有被创建

出来;

2. 然后 B 就走创建流程,在 B 初始化的时候,同样发现自己依赖 C,C 也没有被创建出来;

3. 这个时候 C 又开始初始化进程,但是在初始化的过程中发现自己依赖 A,于是尝试 get(A)。这

个时候由于 A 已经添加至缓存中(一般

都是添加至三级缓存 singletonFactories),通过

ObjectFactory 提前曝光,所以可以通过 ObjectFactory#getObject() 方法来拿到 A 对象。C 拿

到 A 对象后顺利完成初始化,然后将自己添加到一级缓存中;

4. 回到 B,B 也可以拿到 C 对象,完成初始化,A 可以顺利拿到 B 完成初始化。到这里整个链路

就已经完成了初始化过程了。

6.spring中的bean是线程安全的吗?

Spring 框架并没有对单例 Bean 进行任何多线程的封装处理。

关于单例 Bean 的线程安全和并发问题,需要开发者自行去搞定。

单例的线程安全问题,并不是 Spring 应该去关心的。Spring 应该做的是,提供根据配置,创

建单例 Bean 或多例 Bean 的功能。

当然,但实际上,大部分的 Spring Bean 并没有可变的状态,所以在某种程度上说 Spring 的单例

Bean 是线程安全的。如果你的 Bean 有多种状态的话,就需要自行保证线程安全。最浅显的解决办

法,就是将多态 Bean 的作用域(Scope)由 Singleton 变更为 Prototype。

7.springboot的自动装配原理?

1.自动装配其实就是自动的把第三方的组件(框架)bean,装载到IOC容器中,不需要开发人员额外的去注入第三方bean相关配置,在springboot中启动类上加上@SpringBootApplication即可实现自动装配。

2.@SpringBootApplication组合注解,底层是@Enableautoconfiguration注解,通过引入第三方自定义的springboot starter 插件 中会有一个 加上了@configuration配置类,通过该配置类声

明bean的信息,在通过springboot中约定配置思想,将该配置类的全路径放在 jar中的META-INF\spring.factories,这样的话 springboot就可以知道 第三方jar包对应的配置类位置,从而将第三方jar包配置类中的bean信息 加入到ioc容器中,底层会使用到spring.factoriesLoader完成。引入第三方jar包的 不需要开发者额外配置组件jar包中bean信息 (自动实现装配)

零散篇:

1.线程同步是什么?

即当有一个线程在对内存进行操作的时候,其他线程就不能对这个内存地址进行操作,直到该线程完成操作,其他线程 才能对内存地址进行操作,而其他线程处于等待状态。

2.java里有几种循环?

for,do while,switch

3.sleep和wait方法的区别?

1.对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类

中的。

2.sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持

者,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象

锁。

当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用

notify()方法后本线程才进入对象锁定池准备,获取对象锁进入运行状态。

3.jvm的垃圾回收机制是GC(Garbage Collection),也叫垃圾收集器。GC基本原理:将内存中不再被使用的对象进行回收;

GC中用于回收的方法称为收集器,由于GC需要消耗一些资源和时间,Java在对对象的生命周期特征进行分析后,按照新生代、老年代的方式来对对象进行收集,以尽可能的缩短GC对应用造成的暂停。

4.Nginx的优点有哪些?

1.更快。Nginx可以比其他Web服务器更快地响应请求

2.高扩展性,跨平台

3。高可靠性:用于反向代理,宕机的概率微乎其微

4.低内存消耗

5.热部署

6。接口和抽象类的区别?

抽象类可以有构造函数,而接口不可以;

抽象类的子类是单继承,而接口可以多实现;

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

7.redis哨兵的结构和功能?

监控;自动故障恢复;通知(故障转移);

8.notify()和notifyAll()有什么区别?

notify可能会导致死锁,而notifyAll则不会

任何时候只有一个线程可以获得锁,也就是说只有一个线程可以运行synchronized 中的代码

使用notifyall,可以唤醒 所有处于wait状态的线程,使其重新进入锁的争夺队列中,而notify只能唤

醒一个。

wait() 应配合while循环使用,不应使用if,务必在wait()调用前后都检查条件,如果不满足,必须调

用notify()唤醒另外的线程来处理,自己继续wait()直至条件满足再往下执行。

notify() 是对notifyAll()的一个优化,但它有很精确的应用场景,并且要求正确使用。不然可能导致

死锁。正确的场景应该是 WaitSet中等待的是相同的条件,唤醒任一个都能正确处理接下来的事

项,如果唤醒的线程无法正确处理,务必确保继续notify()下一个线程,并且自身需要重新回到

WaitSet中.

9.springboot的常用注解?

@SpringBootApplication 注解

查看源码可发现,@SpringBootApplication是一个复合注解,包含了@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan这三个注解。

@RestController 是@Controller 和@ResponseBody的结合,一个类被加上@RestController 注解,数据接口中就不再需要添加@ResponseBody。更加简洁。

同样的情况,@RequestMapping(value="",method= RequestMethod.GET ),我们都需要明确请求方式。这样的写法又会显得比较繁琐,于是乎就有了如下的几个注解。

普通风格Rest风格

@RequestMapping(value=“”,method = RequestMethod.GET)

@GetMapping(value =“”)

@RequestMapping(value=“”,method = RequestMethod.POST)

@PostMapping(value =“”)

@RequestMapping(value=“”,method = RequestMethod.PUT)

@PutMapping(value =“”)

@RequestMapping(value=“”,method = RequestMethod.DELETE)

@DeleteMapping(value =“”)

注:MySQL篇中后面几个问题没写答案,可以自行查缺补漏;

如果有什么补充或者问题,请留言

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值