字符型常量和字符串常量的区别?
字符常量是单引号引起的一个字符,字符串常量是双引号引起的 0 个或若干个字符。
标识符和关键字的区别是什么?
标识符:变量名
关键字:保留字private
重载和重写的区别
重载就是同样的一个方法能够根据输入数据的不同,做出不同的处理
重写就是当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法
== 和 equals() 的区别
对于基本数据类型来说,== 比较的是值。
对于引用数据类型来说,== 比较的是对象的内存地址。
equals() 不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。
类没有重写 equals()方法 :通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是 Object类equals()方法。
类重写了 equals()方法 :一般我们都重写 equals()方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true(即,认为这两个对象相等)。
String#equals() 和 Object#equals() 有何区别?
String 中的 equals 方法是被重写过的,比较的是 String 字符串的值是否相等。 Object 的 equals 方法是比较的对象的内存地址。
字符串常量池的作用了解吗?
String aa = "ab"; // 放在常量池中
String bb = "ab"; // 从常量池中查找
System.out.println(aa==bb);// true
hashcode()和equals
hashCode() 的作用是获取哈希码(int 整数),也称为散列码。这个哈希码的作用是确定该对象在一些容器(比如 HashMap、HashSet)中的位置。
如果两个对象的hashCode 值不相等,我们就可以直接认为这两个对象不相等。
如果两个对象的hashCode 值相等并且equals()方法也返回 true,我们才认为这两个对象相等。
如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。
几种基本数据类型
4 种整数型:byte、short、int、long
2 种浮点型:float、double
1 种字符类型:char
1 种布尔型:boolean
包装类型的常量池技术了解么?
再范围内直接返回对象,超出对应范围会再去创建新的对象
自动装箱和拆箱了解嘛
装箱:将基本类型用它们对应的引用类型包装起来;
拆箱:将包装类型转换为基本数据类型
深拷贝 浅拷贝 引用拷贝
String 为什么是不可变的?
保存字符串的数组被 final 修饰且为私有的,并且String 类没有提供/暴露修改这个字符串的方法。
String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 不可变。
对于三者使用的总结:
操作少量的数据: 适用 String
单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer
字符串拼接用“+” 还是 StringBuilder?
对象引用和“+”的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。
Java 泛型了解么?什么是类型擦除?介绍一下常用的通配符?
泛型类 泛型方法 泛型接口
泛型的本质是参数化类型 ,即给类型指定一个参数,然后在使用时再指定此参数具体的值,那样这个类型就可以在使用时决定了。
Java 的泛型是伪泛型,这是因为 Java 在运行期间,所有的泛型信息都会被擦掉,这也就是通常所说类型擦除 。
T (type) 表示具体的一个 Java 类型
K V (key value) 分别代表 Java 键值中的 Key Value
E (element) 代表 Element
反射
让我们在运行时有了分析操作类的能力,
框架中都大量使用了反射机制
.class
Class.forName(“类的全路径”);
类加载器loadclass
getClass()
getFileds()
getName()
注解
可以看作是一种特殊的注释,主要用于修饰类、方法或者变量。
继承 Annotation
编译期直接扫描:@Override
运行期通过反射处理 :像框架中自带的注解(比如 Spring 框架的 @Value 、@Component)都是通过反射来进行处理的。
tray-catch-finally
try块: 用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块。
catch块: 用于处理 try 捕获到的异常。
finally 块: 无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。
会被finally里的return 覆盖掉
在以下 2 种特殊情况下,finally 块的代码也不会被执行:
程序所在的线程死亡。
关闭 CPU。
如何使用 try-with-resources 代替try-catch-finally?
把资源放在try()
···
try (Scanner scanner = new Scanner(new File(“test.txt”))) {
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
}
···
IO
序列化: 将数据结构或对象转换成二进制字节流的过程
反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程
java 序列化中如果有些字段不想进行序列化,怎么办?
transient
为什么 Java 中只有值传递?
如果参数是基本类型的话,很简单,传递的就是基本类型的字面量值的拷贝,会创建副本。
如果参数是引用类型,传递的就是实参所引用的对象在堆中地址值的拷贝,同样也会创建副本。
BigDecimal
BigDecimal 可以实现对浮点数的运算,不会造成精度丢失。通常情况下,大部分需要浮点数精确运算结果的业务场景(比如涉及到钱的场景)都是通过 BigDecimal 来做的。
说说 List, Set, Queue, Map 四者的区别?
List(对付顺序的好帮手): 存储的元素是有序的、可重复的。
Set(注重独一无二的性质): 存储的元素是无序的、不可重复的。
Queue(实现排队功能的叫号机): 按特定的排队规则来确定先后顺序,存储的元素是有序的、可重复的。
Map(用 key 来搜索的专家): 使用键值对(key-value)存储,类似于数学上的函数 y=f(x),“x” 代表 key,“y” 代表 value,key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值。
Arraylist 与 LinkedList 区别?
是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
底层数据结构: Arraylist 底层使用的是 Object 数组;LinkedList 底层使用的是 双向链表 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!)
数组和链表的区别
ArrayList扩容机制
以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为 10。
之后ArrayList 每次扩容之后容量都会变为原来的 1.5 倍左右。
ArrayList(int initialCapacity) 会使用指定容量的数组
HashSet、LinkedHashSet 和 TreeSet
都不是线程安全的
HashSet、LinkedHashSet 和 TreeSet 的主要区别在于底层数据结构不同。HashSet 的底层数据结构是哈希表(基于 HashMap 实现)。LinkedHashSet 的底层数据结构是链表和哈希表,元素的插入和取出顺序满足 FIFO。TreeSet 底层数据结构是红黑树,
HashSet、LinkedHashSet 和 TreeSet 的主要区别在于底层数据结构不同。HashSet 的底层数据结构是哈希表(基于 HashMap 实现)。LinkedHashSet 的底层数据结构是链表和哈希表,元素顺序插入和取出。TreeSet 底层数据结构是红黑树
各种使用场景?
Queue和Deque
Queue 是单端队列,只能从一端插入元素,另一端删除元素,实现上一般遵循 先进先出(FIFO) 规则。
Queue 是单端队列,只能从一端插入元素,另一端删除元素,实现上一般遵循 先进先出(FIFO) 规则。
PriorityQueue 利用了二叉堆的数据结构来实现的,底层使用可变长的数组来存储数据
PriorityQueue 通过堆元素的上浮和下沉,实现了在 O(logn) 的时间复杂度内插入元素和删除堆顶元素。
PriorityQueue 是非线程安全的,且不支持存储 NULL 和 non-comparable 的对象。
PriorityQueue 默认是小顶堆,但可以接收一个 Comparator 作为构造参数,从而来自定义元素优先级的先后。
HashMap 和 Hashtable 的区别
线程是否安全: HashMap 是非线程安全的,Hashtable 是线程安全的,因为 Hashtable 内部的方法基本都经过synchronized 修饰。
HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;Hashtable 不允许有 null 键和 null 值,否则会抛出 NullPointerException。
初始容量大小和每次扩充容量大小的不同。
HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于8,数组长度大于64时候转化为红黑树;hashtable 拉链法
HashMap和TreeMap
相比于HashMap来说 TreeMap 主要多了对集合中的元素根据键排序的能力以及对集合内元素的搜索的能力。
HashMap 的底层实现
JDK1.8之前
HashMap 底层是 数组和链表,拉链法保存元素
JDK1.8 之后
当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树
线程生命周期
sleep()和wait()方法区别
两者最主要的区别在于:sleep() 方法没有释放锁,而 wait() 方法释放了锁 。
两者都可以暂停线程的执行。
wait() 通常被用于线程间交互/通信,sleep() 通常被用于暂停执行。
wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。sleep() 方法执行完成后,线程会自动苏醒
synchronized
一说自己对于 synchronized 关键字的了解
synchronized 关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。
修饰实例方法: 作用于当前对象实例加锁,进入同步代码前要获得 当前对象实例的锁
synchronized void method() {
//业务代码
}
修饰静态方法,也就是给当前类加锁,会作用于类的所有对象实例
synchronized static void method() {
//业务代码
}
修饰代码块 :指定加锁对象,对给定对象/类加锁。
synchronized(this|object) 表示进入同步代码库前要获得给定对象的锁。
synchronized(类.class) 表示进入同步代码前要获得 当前 class 的锁
Lock(如ReentrantLock):Java并发包提供的显式锁机制,提供了比synchronized更细粒度的控制,例如尝试锁定、定时等待、可中断、公平性设置等功能,使用try-catch-finally语句确保锁能够正确释放。
volatile关键字
说说 synchronized 关键字和 volatile 关键字的区别
synchronized 关键字和 volatile 关键字是两个互补的存在,而不是对立的存在!
volatile 关键字是线程同步的轻量级实现,所以 volatile 性能肯定比synchronized关键字要好 。但是 volatile 关键字只能用于变量而 synchronized 关键字可以修饰方法以及代码块 。
volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都能保证。
volatile关键字主要用于解决
ThreadLocal
每一个线程都有自己的专属本地变量该如何解决呢,最终的变量是放在了当前线程的 ThreadLocalMap 中,存储以ThreadLocal为 key ,Object 对象为 value 的键值对。
为什么要用线程池?
降低资源消耗
提高响应速度
提高线程的可管理性
MyISAM 和 InnoDB 的区别
InnoDB 支持行级锁(row-level locking)
InnoDB 提供事务支持,具有提交(commit)和回滚(rollback)事务的能力。
InnoDB 支持外键
nnoDB 支持数据库异常崩溃后的安全恢复
并发事务带来哪些问题
脏读幻读不可重复读丢失修改
读未提交 读已提交 可重复读 串行化
三范式
-- 1NF, 第一范式
字段不能再分,就满足第一范式。
-- 2NF, 第二范式
主键唯一
-- 3NF, 第三范式
满足第二范式的前提下,不能出现传递依赖。
SQL
索引分类
聚簇索引(主键索引)、二级索引(辅助索引)
主键索引、普通索引、前缀索引
什么时候适用索引?
字段有唯一性限制的,比如商品编码
经常用于 WHERE 查询条件的字段
经常用于 GROUP BY 和 ORDER BY 的字段
什么时候不需要创建索引?
以上情况的反面
经常更新的字段不用创建索引
索引优化
前缀索引顾名思义就是使用某个字段中字符串的前几个字符建立索引,减小索引字段大小
覆盖索引是指 SQL 中 query 的所有字段,在索引 B+Tree 的叶子节点上都能找得到的那些索引,去建立合适的覆盖索引,避免回表
索引最好设置为 NOT NULL
索引失效最左匹配原则:
如果索引关联了多列(联合索引),要遵守最左前缀法则,最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,索引将部分失效(后面的字段索引失效)
确保你的查询条件(WHERE子句中的字段)上有适当的索引。这是最基本且最有效的优化手段。
联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效。
索引失效情况:索引列运算substring(phone,10,2)
字符串类型字段使用时,不加引号,索引将失效
如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效
用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。
四种隔离级别
脏读:读到其他事务未提交的数据;
不可重复读:前后读取的数据不一致;
幻读:前后读取的记录数量不一致。
读未提交 读已提交 可重复读 串行化
全局锁、表级锁和行锁
全局锁:整个数据库就处于只读状态了,全库逻辑备份
Redis
String 类型的应用场景:缓存对象、常规计数、分布式锁、共享 session 信息等。
List 类型的应用场景:消息队列(但是有两个问题:1. 生产者需要自行实现全局唯一 ID;2. 不能以消费组形式消费数据)等。
Hash 类型:缓存对象、购物车等。
Set 类型:聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动等。
Zset 类型:排序场景,比如排行榜、电话和姓名排序等。
什么是缓存穿透?
缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上。
什么是缓存雪崩?
实际上,缓存雪崩描述的就是这样一个简单的场景:缓存在同一时间大面积的失效,后面的请求都直接落到了数据库上,造成数据库短时间内承受大量请求。
错误处理
@RestControllerAdvice
自定义异常类(int code 和 description)
@ExceptionHandler
public class BusinessException extends RuntimeException {
private final int code;
private final String description;
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public BaseResponse businessExceptionHandler(BusinessException e) {
log.error("businessException: " + e.getMessage(), e);
return ResultUtils.error(e.getCode(), e.getMessage(), e.getDescription());
}
@ExceptionHandler(RuntimeException.class)
public BaseResponse runtimeExceptionHandler(RuntimeException e) {
log.error("runtimeException", e);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, e.getMessage(), "");
}
}
Autowired
自动导入对象到类中
@Component,@Repository,@Service, @Controller
@RestController
@RestController注解是@Controller和@ResponseBody的合集,是 REST 风格的控制器。
@scope
声明 Spring Bean 的作用域,使用方法:
@Configuration
声明配置类
@GetMapping(“users”) @PostMapping(“users”) @PostMapping(“users”)
处理不同形式的请求
HTTP GET方法用于请求访问服务器上的资源。它通常用于读取(或检索)数据,而不会导致数据的改变。GET请求应该是幂等的,意味着多次执行相同的GET请求应当返回相同的结果,而不会对服务器上的数据造成改变。
POST通常用于创建新的资源。例如,@PostMapping(“users”)可能用于在服务器上创建一个新用户。
POST请求不是幂等的,这意味着相同的POST请求被重复执行可能会每次都产生不同的结果,例如每次都创建一个新的用户
前后端传值
@PathVariable 和 @RequestParam
@PathVariable用于获取路径参数{}里面的,@RequestParam用于获取查询参数?后面的。
@RequestBody
用于读取 Request 请求的 json 格式的数据
Spring框架的两大核心思想
通过容器管理对象的生命周期和依赖关系, 实现了松耦合和可配置化。
谈谈自己对于 Spring IoC 的了解
将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理
谈谈AOP理解
为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
@Aspect
@Pointcut
@Before
通过代理机制在运行时织入目标方法,增强了业务代码的模块性和重用性。
Java代理及动态代理与静态代理区别
静态代理:代理类在编译时就已经确定,由程序员创建或特定工具生成,代理类与委托类之间存在明显的依赖关系,代理类在程序运行前就已完成。动态代理:在运行时动态生成代理类及其行为,主要分为JDK动态代理和CGLIB代理。
JDK动态代理:基于接口实现,通过InvocationHandler接口和Proxy类创建代理对象,只能代理实现了接口的类。
什么是 bean?
简单来说,bean 代指的就是那些被 IoC 容器所管理的对象。
MyBatis
#{}和${}的区别是什么?
以通过精心设计的形参变量的值,来改变原SQL语句的使用意图从而产生安全隐患,即为SQL注入攻击
${}:是字符串替换
#{}:是预编译处理,可以防止SQL注入
项目
redis分布式session
session存在服务器上,如果该项目部署在多台服务器上,比如它在A上做了登录,在服务器B上就没他的信息。
把session的信息存在redis里边。
使用session时的登录验证凭证,就是session-id,根据客户端发送的session-id获取session,再从session中获取数据
Spring Session 提供了一套创建和管理 Servlet HttpSession 的方案。Spring Session 提供了集群 Session(Clustered Sessions)功能,默认采用外置的 Redis 来存储 Session 数据,以此来解决 Session 共享的问题
session:
timeout: 86400
store-type: redis
// 一句话
request.getSession().setAttribute(USER_LOGIN_STATE, safetyUser);
request.getSession().getAttribute(USER_LOGIN_STATE)
request.getSession().removeAttribute(USER_LOGIN_STATE);
9.1生成随机的token
9.2将User对象转为HashMap存储
9.3存入redis中
9.4设置key有效时间
redis 分布式锁
redissonClient.getLock()去获得锁
数据
- 加锁:执行setnx,若成功再执行expire添加过期时间
- 解锁:执行delete命令
表设计
要不要标签表?
用户表:用户表添加一列标签列(mysql 用json格式)
队伍表
队伍-用户 表
多线程
线程池ThreadPoolExecutor,通过线程池调用线程,去执行一些任务。
Springmvc
(1)当用户通过浏览器发起一个HTTP请求,请求直接到前端控制器DispatcherServlet;
(2)前端控制器接收到请求以后调用处理器映射器HandlerMapping,处理器映射器根据请求的URL找到具体的Handler,并将它返回给前端控制器;
(3)前端控制器调用处理器适配器HandlerAdapter去适配调用Handler;
(4)处理器适配器会根据Handler去调用真正的处理器去处理请求,并且处理对应的业务逻辑;
(5)当处理器处理完业务之后,会返回一个ModelAndView对象给处理器适配器,HandlerAdapter再将该对象返回给前端控制器;这里的Model是返回的数据对象,View是逻辑上的View。
(6)前端控制器DispatcherServlet将返回的ModelAndView对象传给视图解析器ViewResolver进行解析,解析完成之后就会返回一个具体的视图View给前端控制器。(ViewResolver根据逻辑的View查找具体的View)
(7)前端控制器DispatcherServlet将具体的视图进行渲染,渲染完成之后响应给用户
sping的事务
去看spring transaction manager 部分
事务通常指一系列操作,它们要么全部成功执行,要么全部失败。ACID。@Transactional
bean冲突
唯一标识符和别名
https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design
REST
REST API是围绕资源设计的
GET:检索指定URI处资源的表示形式。响应消息的主体包含所请求资源的详细信息。
POST:在指定的URI处创建新资源。请求消息的主体提供新资源的详细信息。请注意,POST也可以用于触发实际上不创建资源的操作。
PUT:在指定的URI处创建或替换资源。请求消息的主体指定要创建或更新的资源。
PATCH:对资源执行部分更新。请求主体指定要应用于资源的更改集。
DELETE:删除指定URI处的资源。
通常将POST请求应用于集合。新资源将添加到集合中。
PUT请求最常应用于单个项(例如特定客户),而不是集合。
PUT请求必须是幂等的。如果客户端多次提交相同的PUT请求,结果应始终相同(相同的资源将以相同的值进行修改)。POST和PATCH请求不能保证是幂等的。
资源的URI应该基于名词(表示资源)而不是动词(表示对资源的操作)。
/orders?limit=25&offset=50
https://adventure-works.com/create-order // Avoid
collection/item/collection.