一.数据库的三范式是什么?
第一范式:强调的是列的原子性,即数据库表的每一列都是不可分割的原子数据项。
第二范式:要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性。
第三范式:任何非主属性不依赖于其它非主属性。
二.怎么验证 mysql 的索引是否满足需求?
使用 explain 查看 SQL 是如何执行查询语句的,从而分析你的索引是否满足需求。
explain 语法:explain select * from table where type=1。
三.mysql 问题排查都有哪些手段?
使用 show processlist 命令查看当前所有连接信息。
使用 explain 命令查询 SQL 语句执行计划。
开启慢查询日志,查看慢查询的 SQL。
四.迭代器 Iterator 是什么?
迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。
迭代器通常被称为“轻量级”对象,因为创建它的代价小。
五.Iterator 怎么使用?有什么特点?
Java中的Iterator功能比较简单,并且只能单向移动:
(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。
注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。
六. Iterator 和 ListIterator 有什么区别?
Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。
Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。
一,简述什么是正向代理/反向代理?
1.反向代理(必须掌握)
反向代理服务器位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,即用户直接访问反向代理服务器就可以获得目标服务器的资源。同时,用户不需要知道目标服务器的地址,也无须在用户端作任何设定。反向代理服务器通常可用来作为Web加速,即使用反向代理作为Web服务器的前置机来降低网络和服务器的负载,提高访问效率。
总结:
1. 代理服务器位于用户和服务器之间
2. 用户以为代理服务器就是目标服务器.
3. 用户无需了解真实服务器地址.
4. 反向代理服务器保护了服务器端的信息 (服务器端代理)
2.正向代理(了解)
正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从目标服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。
特点:
1. 代理服务器位于用户与服务器之间
2. 用户了解访问的服务器到底是谁.
3. 只有用户可以使用正向代理 (客户端代理)
4. 正向代理保护了用户的信息.
二,常见跨域方式?
1).JSONP方式 但是JSONP只可以发送GET请求,无法发送其他类型的请求
2).RESTful风格 利用/user/xx实现数据传递
3).CORS,(Cross-origin resource sharing)跨域资源共享 来解决跨域问题。
三,Nacos的配置管理模型以及配置数据的获取?
Nacos配置管理模型中,为了实现更好的环境隔离给出了namespace,group,dataId的概念,一个配置中心可以有多个命名空间,一个命名空间可以有多个分组,一个分组内可以有多个groupId,服务启动时会每隔30描述向配置中心请求一次数据,2.0之前默认采取的时长轮询拉取模式。
四,项目中如何实现服务的调用?
项目中调用服务我们用过两种方式一种方式是RestTemplate,一种是OpenFeign,这两种方式在进行服务调用时,都可以借助Ribbon实现负载均衡。
五,数据库优化的方案有哪些
(1)查询时,能不用* 就不用,尽量写全字段名
(2)索引不是越多越好,每个表控制在5个索引以内
(3)大部分情况连接效率远大于子查询,但是有例外
(4)多用explain 和 profile分析查询语句
(5)连接查询的性能上尽量使用小表驱动大表的原则
(6)查看慢查询日志,找出执行时间长的SQL进行优化
(7)尽量避免使用order by
(8)因为where子句后面的条件是执行顺序是从右到左,所以尽量把能过滤掉大部分数据的条件放在最后。
六,数据库里索引的作用
主要作用是为了提高查询效率,它的执行也是有一定条件的,不是加了索引就一定能够加快查询的效率,由于索引的创建是需要占据内存空间的.以下不适合加索引:
1、如果每次都需要取到所有表记录,无论如何都必须进行全表扫描了,那么是否加索引也没有意义了
2、对非唯一的字段,例如“性别”这种大量重复值的字段,增加索引也没有什么意义
3、对于记录比较少的表,增加索引不会带来速度的优化反而浪费了存储空间,因为索引是需要存储空间的,而且有个致命缺点是对于update/insert/delete的每次执行,字段的索引都必须重新计算更新
4、mysql中通过Explain来查看索引的扫描次数
1、并发编程三要素?
(1)原子性:原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要 么就全部都不执行。
(2)可见性:可见性指多个线程操作一个共享变量时,其中一个线程对变量进行修改后,其他线程可以立 即看到修改的结果。
(3)有序性:有序性,即程序的执行顺序按照代码的先后顺序来执行。
2、Java 线程具有五中基本状态
(1)新建状态(New):
当线程对象对创建后,即进入了新建状态,如:Thread t= new MyThread();
(2)就绪状态(Runnable):
当调用线程对象的 start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程, 只是说明此线程已经做好了准备,随时等待 CPU 调度执行,并不是说执行了 t.start()此线程立即就会执行;
(3)运行状态(Running):
当 CPU 开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
(4)阻塞状态(Blocked):
处于运行状态中的线程由于某种原因,暂时放弃对 CPU 的使用权,停止执行,此时进入阻
塞状态,直到其进入到就绪状态,才 有机会再次被 CPU 调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1)等待阻塞:运行状态中的线程执行 wait()方法,使本线程进入到等待阻塞状态;
2)同步阻塞:线程在获取 synchronized 同步锁失败(因为锁被其它线程所占用), 它会进入同步阻塞状态;
3)其他阻塞:通过调用线程的 sleep()或 join()或发出了 I/O 请求时,线程会进入到阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪状态。
(5)死亡状态(Dead):
线程执行完了或者因异常退出了 run()方法,该线程结束生命周期。
类。
3.说一说抽象类与接口的应用的场景?
抽象类的应用场景:1)规范了一组公共的方法,与状态无关,可以共享的,无需子类分别实现;而另一些方法却需要各个子类根据自己特定状态来实现特定功能;
2)定义一组接口,但不强迫每个实现类都必须实现所有的方法,可用抽象类定义一组方法 体可以是空方法体,由子类选择自己感兴趣的方法来覆盖;
4.什么是进程和线程 他们的有什么关系?区别是什么?
进程:具有一定独立功能的程序,是系统进行资源分配和调度运行的基本单位。
线程:进程的一个实体,是 CPU 调度的苯单位,也是进程中执行运算的最小单位,即执行处理机调度的基本单位,如果把进程理解为逻辑上操作系统所完成的任务,线程则表示完成 该任务的许多可能的子任务之一。
关系:一个进程可有多个线程,至少一个;一个线程只能属于一个进程。同一进程的所有线程共享该进程的所有资源。不同进程的线程间要利用消息通信方式实现同步。
区别:进程有独立的地址空间,而多个线程共享内存;进程具有一个独立功能的程序,线程不能独立运行,必须依存于应用程序中;
5.final,finally,finalize 的区别
final:变量、类、方法的修饰符,被 final 修饰的类不能被继承,变量或方法被 final 修饰则不能被修改和重写。
finally:异常处理时提供 finally 块来执行清除操作,不管有没有异常抛出,此处代码都会被执行。如果 try 语句块中包含 return 语句,finally 语句块是在 return 之后运行;
finalize:Object 类中定义的方法,若子类覆盖了 finalize()方法,在在垃圾收集器将对象从内存中清除前,会执行该方法,确定对象是否会被回收。
6、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 响应用户。
1.在 java 程序中怎么保证多线程的运行安全?
线程安全在三个方面体现:
原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作,(atomic,synchronized);
可见性:一个线程对主内存的修改可以及时地被其他线程看到,(synchronized,volatile);
有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序,(happens-before原则)。
2.mybatis 有几种分页方式?
数组分页:查询出全部数据,然后再list中截取需要的部分。
sql分页:使用limit进行分页
拦截器分页:定义分页拦截器实现Mybatis的Interceptor接口,
重写其中的方法:
1、setProperties:获取配置文件中的值
2、plugin:对不需要进行分页的SQL语句进行过滤
3、intercept:执行拦截的逻辑
RowBounds分页:使用 RowBounds 进行分页,非常方便,不需要在 sql 语句中写 limit,即可完成分页功能。
但是由于它是在 sql 查询出所有结果的基础上截取数据的,所以在数据量大的sql中并不适用,它更适合在返回数据结果较少的查询中使用
最核心的是在 mapper 接口层,传参时传入 RowBounds(int offset, int limit) 对象,即可完成分页
3.rabbitmq 怎么避免消息丢失?
(1.)消息持久化
要想做到消息持久化,必须满足以下三个条件,缺一不可。
1) Exchange 设置持久化
2)Queue 设置持久化
3)Message持久化发送:发送消息设置发送模式deliveryMode=2,代表持久化消息
(2.)ACK确认机制:当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收。
回执ACK有两种情况:
1)自动ACK:消息一旦被接收,消费者自动发送ACK
– 如果消息不太重要,丢失也没有影响,那么自动ACK会比较方便
2)手动ACK:消息接收后,不会发送ACK,需要手动调用
– 如果消息非常重要,不容丢失。那么最好在消费完成后手动ACK,否则接收消息后就自动ACK,
RabbitMQ就会把消息从队列中删除。如果此时消费者宕机,那么消息就丢失了。
(3.)设置集群镜像模式
可以使用 RabbitMQ 的镜集群像模式进行部署,如果主节点在这个特殊的时间段内挂掉了,会自动切换到从节点,这样就保证了高可用性,除非整个集群都挂掉。
(4.)消息补偿机制:
消息补偿机制需要建立在消息要写入数据库日志,发送日志,接受日志,两者的状态必须记录。
然后根据数据库日志记录检验消息发送消费是否成功,不成功,进行消息补偿措施,重新发送消息处理。
4.说一下乐观锁和悲观锁?
乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。
悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻止,直到这个锁被释放。
数据库的乐观锁需要自己实现,在表里面添加一个 version 字段,每次修改成功值加 1,这样每次修改的时候先对比一下,自己拥有的 version 和数据库现在的 version 是否一致,
如果不一致就不修改,这样就实现了乐观锁。
5.Docker的基本组成
Docker主要有以下几部分组成:
Docker Client 客户端
Docker daemon 守护进程
Docker Image 镜像
Docker Container 容器
Docker Registry 仓库
6.Redis 有哪些功能?
·(1.)数据缓存功能:直接基于本机内存进行缓存。
·(2.)分布式锁的功能:
当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问,
分布式锁实现的关键是在分布式的应用服务器外,搭建一个存储服务器,
存储锁信息,这时候就用到了Redis。搭建一个Redis服务器,用Redis服务器来存储锁信息
·(3.)支持数据持久化:redis的持久化指的是redis会把内存的中的数据写入到硬盘中,在redis重新启动的时候加载这些数据,从而最大限度的降低缓存丢失带来的影响。
·(4.) 支持事务:
在并发执行,为更好保证事务的四个特性的实现,通常会对事务加锁,Redis为了性能,采用了乐观锁方式进行事务控制,
它使用watch命令监视给定的key,当exec(提交事务)的时候,如果监视的key从调用watch后发生过变化,则整个事务会失败
redis进行事务控制时,通常是基于如下指令进行实现,例如:
multi 开启事务
exec 提交事务
discard 取消事务
watch 监控,如果监控的值发生变化,则提交事务时会失败
unwatch 去掉监控
·(5.) 支持消息队列:是一种比较简陋的消息队列,尽量不使用Redis实现消息队列功能。
1.依赖注入的3种方式
答:构造器注入,set注入,变量注入。
其中构造器注入不能解决循环依赖的问题,
set注入是在需要的时候进行注入,不会造成循环依赖,
但项目中一般采用变量注入,这样的方式便于维护,也更清楚,可读性高
2.什么是缓存雪崩,防止措施?
缓存由于某个原因(宕机 服务挂掉或不响应)导致大量请求到达后端数据库,导致数据库崩溃。
采用加锁计数,分析用户 让生效时间点均匀分布 ,双缓存(会出现脏数据)
3.GC回收机制
答:GC回收主要是对于堆和方法区进行回收,因为栈和程序计数器都会随着线程的销毁而销毁。
如何判断内存是否能够回收:
一:引用计数法
指的是当创建一个对象时,如果这个对象被引用了,则+1,如果超过一个生命周期,则减一,
当为0的时候就可以回收。缺点有如果两个对象相互引用,则一直不会被回收。
二:可达性分析
用一个root节点,把所有的引用关系绘制成一颗树,
当没有引用到的节点,也就是不00可达的节点,则回收。
root节点可为方法区内的对象或者虚拟机栈中的对象。
4.redis为什么那么快
答:redis采用单进程单线程,并且是加载到内存中执行的,
还有他的io多路复用。io多路复用值非阻塞的io讲多个网络请求一起交由一个线程去执行
避免了线程上下文切换造成的时间以及网络连接所带来的性能消耗
5.HashMap 和 Hashtable 的区别
答案:
1.历史原因:Hashtable 是陈旧API,HashMap 是Java 1.2 引进的Map 接口的一个实现
2.同步性:Hashtable 是线程安全的,也就是说是同步的,而HashMap 是线程序不安全的,不是同步的。
由于同步检查所以Hashtable性能稍慢。
3.值:HashMap 可以插入一个null的key-value
6.数据库建立索引常用的原则是什么?什么情况下不适合建立索引?
答案:
1.在大数据量的表上建立索引才有意义
2.在where子句或是连接条件上经常引用的列上建立索引
3.很少或从不引用的字段和逻辑型的字段,如男或女(是或否)等不要建立索引
1、什么是 java 序列化与反序列化?什么情况下需要序列化?
Java序列化是指把Java对象转换为字节序列的过程,Java反序列化是指把字节序列恢复为Java对象的过程。
当 Java 对象需要在网络上传输或者持久化存储到文件中时,就需要对 Java 对象进行序列化处理。
序列化的实现:类实现 Serializable 接口,这个接口没有需要实现的方法。实现 Serializable 接口是为了告诉 jvm 这个类的对象可以被序列化。
2、并行和并发有什么区别?
并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。
并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。
并行是在多台处理器上同时处理多个任务,并发是在一台处理器上“同时”处理多个任务。
3、HashMap 和 Hashtable 有什么区别?
HashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法。
HashTable同步的,而HashMap是非同步的,效率上比HashTable要高。
HashMap允许空键值,而HashTable不允许
4、线程和进程的区别?
进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程。进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高。
线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位。同一进程中的多个线程之间可以并发执行。
5、迭代器 Iterator 是什么?
迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。
6、final、finally有什么区别?
final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。
finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码
1.JDK 和 JRE 有什么区别?
答:
JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境。
JRE:Java Runtime Environment 的简称,java 运行环境,为 java 的运行提供了所需环境。
具体来说 JDK 其实包含了 JRE,同时还包含了编译 java 源码的编译器 javac,还包含了很多 java 程序调试和分析的工具。简单来说:如果你需要运行 java 程序,只需安装 JRE 就可以了,如果你需要编写 java 程序,需要安装 JDK。
2. 如何实现数组和 List 之间的转换
List转换成为数组:调用ArrayList的toArray方法。
数组转换成为List:调用Arrays的asList方法。
3. String和StringBuilder、StringBuffer的区别?
答:Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。
其中stringBuffer效率高,不安全,stringBuilder效率低,安全。
4.GC是什么?为什么要有GC?
答:GC是垃圾收集的意思,垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。
5.Collection和Collections的区别?
答:Collection是一个接口,它是Set、List等容器的父接口;Collections是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。
6.什么是内存泄露?什么是内存溢出
内存泄露 :是指程序在申请内存后,无法释放已申请的内存空间就造成了内存泄漏,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。
内存溢出: 指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出。
2. 如何实现数组和 List 之间的转换
List转换成为数组:调用ArrayList的toArray方法。
数组转换成为List:调用Arrays的asList方法。
3. String和StringBuilder、StringBuffer的区别?
答:Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。
其中stringBuffer效率高,不安全,stringBuilder效率低,安全。
4.GC是什么?为什么要有GC?
答:GC是垃圾收集的意思,垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。
5.Collection和Collections的区别?
答:Collection是一个接口,它是Set、List等容器的父接口;Collections是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。
6.什么是内存泄露?什么是内存溢出
内存泄露 :是指程序在申请内存后,无法释放已申请的内存空间就造成了内存泄漏,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。
内存溢出: 指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出。
1.方法的重载:
在同一个类中出现方法名相同但参数列表不同方法的现象
注意:方法之间能否构成重载,取决于方法的参数个数与类型,与方法的参数名无关
我们可以通过方法名+参数列表的方式确定要调用的是哪个方法
方法的传值:基本类型传递的是实际值,引用类型传递的是地址
而且方法的参数属于形参,只是格式上需要定义,但是调用方法时起不到限制的作用
形参:定义方法的时候的参数列表
实参:使用方法的时候传入的数据
重载的意义:
是为了方便外界对方法进行调用,什么样的参数程序都可以找到对应的方法来执行,体现的是程序的灵活性
2.方法的重写:
子类继承父类以后,如果子类对父类的功能不满意,可以重写父类的方法
但是重写的时候需要注意如下的规则:两同两小一大
一大:子类方法的修饰符范围 >= 父类方法的修饰符范围–指的是访问控制符
两同:方法名相同,参数列表相同
两小: 子类方法的返回值类型 <= 父类方法的返回值类型【这个大小是继承关系,不是值的大小】
子类方法抛出的异常类型 <= 父类方法抛出的异常类型
注意:如果父类方法的返回值类型是void,子类保持一致即可
注意:子类不可以重写父类的私有方法,还是因为不可见
重写的意义:是在不修改源码的前提下,进行功能的修改和拓展
(OCP原则:面向修改关闭,面向拓展开放)
3.接口和抽象类有什么区别?
1)接口是一种用interface定义的类型
抽象类是一种用class定义的类型
2)接口中的方法都是抽象方法,还有默认方法与静态方法
抽象类中的方法不做限制
3)接口中的都是静态常量
抽象类中可以写普通的成员变量
4)接口没有构造方法,不可实例化
抽象类有构造方法,但是也不可以实例化
5)接口是先天设计的结果,抽象是后天重构的结果
6)接口可以多继承
抽象类只能单继承
4.Java中实现多态的机制是什么?
靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,
而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,
也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。
5.Map接口的特点
map集合的结构是:键值对、KEY与VALUE、Map.Entry<K,V>的映射关系
map中key值不允许重复,如果重复,对应的value会被覆盖
map中的映射关系是无序的
map没有自己的迭代器,所以迭代时通常需要转成set集合来迭代
6.Set接口的特点
set集合没有重复的元素
set集合的元素是无序的
set集合可以存null值,并且null最多有一个
我们自定义对象如果想去重,需要在自定义类中添加重写的equals()与hashCode()
2.== 和 equals 的区别是什么?
基本类型:比较的是值是否相同;
引用类型:比较的是引用是否相同;
3.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?
两个对象的 hashCode()相同,equals()不一定 true。
4.final 在 java 中有什么作用?
final 修饰的类叫最终类,该类不能被继承。
final 修饰的方法不能被重写。
final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。
5.抽象类必须要有抽象方法吗?
抽象类不一定有抽象方法;但是包含一个抽象方法的类一定是抽象类。(有抽象方法就是抽象类,是抽象类可以没有抽象方法)
6.ArrayList 和 LinkedList 的区别是什么?
最明显的区别是 ArrrayList底层的数据结构是数组,支持随机访问,而 LinkedList 的底层数据结构是双向循环链表,不支持随机访问。
使用下标访问一个元素,ArrayList 的时间复杂度是 O(1),而 LinkedList 是 O(n)。
7. 怎么保证缓存和数据库数据的一致性?
合理设置缓存的过期时间。
新增、更改、删除数据库操作时同步更新 Redis,可以使用事物机制来保证数据的一致性。
8. redis 持久化有几种方式?
Redis 的持久化有两种方式,或者说有两种策略:
RDB(Redis Database):指定的时间间隔能对你的数据进行快照存储。
AOF(Append Only File):每一个收到的写命令都通过write函数追加到文件中。