Java面试准备
-
String、StringBuffer和StringBuilder区别
String因为不可变,所以线程安全,适用于操作少量数据;
StringBuffer对方法加了同步锁,所以线程安全,适用于多线程操作字符串缓冲区下操作⼤量数据;
StrngBuilder未对方法加同步锁,所以线程不安全,性能比StringBuffer好一点,适用于单线程操作字符串缓冲区下操作⼤量数据。 -
API和SPI区别
API:实现方提供接口和实现,调用方通过接口从而拥有实现方提供的能力。
SPI:接口存在于调用方,调用方确认接口规则,然后由不同的厂商根据这个规则对接口进行实现,从而提供服务。优点:大大提供接口设计的灵活性;缺点:当多个ServiceLoader同时load时,会有并发问题;需要遍历加载所有实现类,不能做到按需加载,效率比较低。 -
3种常见的IO模型
BIO:应用程序发起 read 调用后,会一直阻塞,直到内核把数据拷贝到用户空间
NIO:Selector监听多个channel,非阻塞;线程首先发起 select 调用,询问内核数据是否准备就绪,等内核把数据准备好了,用户线程再发起 read 调用。read 调用的过程(数据从内核空间 -> 用户空间)还是阻塞的。
AIO:通知回调;应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作 -
HashMap的7种遍历方式和性能
性能从高到低:parallelStream>entrySet >stream >keySet>Lambda
参考:https://mp.weixin.qq.com/s/zQBN3UvJDhRTKP6SzcZFKw -
synchronized 和 volatile 的区别
synchronized 关键字和 volatile 关键字是两个互补的存在,⽽不是对⽴的存在!
1.volatile 关键字是线程同步的轻量级实现,所以 volatile 性能肯定⽐ synchronized 关键字要好。但volatile 关键字只能⽤于变量⽽ synchronized 关键字可以修饰⽅法以及代码块
2.volatile关键字可以保证变量的可见性,还可以防止JVM指令重排序,但不能保证数据的原子性; synchronized可以保证变量的可见性和原子性
3.volatile 关键字主要⽤于解决变量在多个线程之间的可⻅性,⽽ synchronized 关键字解决的是多个线程之间访问资源的同步性 -
- 基于数据块传输:应用数据被分割成TCP认为最适合发送的数据块(报文段或者段),再传输给网络层
- 对失序数据包重新排序以及去重:每个包都有一个序列号,能对接收到的数据根据序列号排序,并且去掉重复序列号的数据
- 校验和:TCP将保持它首部和数据的校验和
- 重传机制:在数据包丢失或延迟的情况下,重新发送数据包,直到收到对方的确认应答(ACK)
- 流量控制:TCP连接的每一方都有固定大小的缓冲空间,接收端只允许发送端发送接收端缓冲区能接纳的数据(TCP利用滑动窗口实现流量控制)
- 拥塞控制:当网络拥塞时,减少数据的发送。
-
产生死锁的4个条件:互斥、占有并等待、非抢占、循环等待
-
Cookie
Cookie cookie = new Cookie("username", "Jovan"); //设置 cookie过期时间 cookie.setMaxAge(7 * 24 * 60 * 60); // expires in 7 days //使⽤ Spring 框架提供的 @CookieValue 注解获取特定的 cookie 的值 @GetMapping("/") public String readCookie(@CookieValue(value = "username", defaultValue = "Atta") String username) { return "Hey! My username is " + username; }
-
Cookie 和 Session区别
Cookie 数据保存在客户端(浏览器端), Session 数据保存在服务器端。相对来说 Session 安全性
更⾼。如果使⽤ Cookie 的⼀些敏感信息不要写⼊ Cookie 中,最好能将 Cookie 信息加密然后使
⽤到的时候再去服务器端解密。 -
如何使⽤ Session-Cookie ⽅案进⾏身份验证?
很多时候我们都是通过 SessionID 来实现特定的⽤户, SessionID ⼀般会选择存放在 Redis 中。
详细过程:
1. ⽤户向服务器发送⽤户名、密码、验证码⽤于登陆系统。
2. 服务器验证通过后,服务器为⽤户创建⼀个 Session ,并将 Session 信息存储起来。
3. 服务器向⽤户返回⼀个 SessionID ,写⼊⽤户的 Cookie 。
4. 当⽤户保持登录状态时, Cookie 将与每个后续请求⼀起被发送出去。
5. 服务器可以将存储在 Cookie 上的 SessionID 与存储在内存中或者数据库中的 Session 信息进⾏比较,以验证⽤户的身份,返回给⽤户客户端响应信息的时候会附带⽤户当前的状态。 -
如何基于 JWT 进⾏身份验证?
参考:https://javaguide.cn/system-design/security/jwt-intro.html#signature -
Redis实现分布式锁
Redis实现一定要保证设置指定key的值和过期时间是一个原子操作。这样有个缺陷就是无法避免锁提前过期,设置锁时间长的话又影响性能。这个时候推荐直接基于 Redisson 来做,Redisson提供了一个专门监控锁的Watch Dog,在操作共享资源还未结束前,会不断延长锁的过期时间,进而保证锁不会因为超时而被释放。
下面以 Redisson 的分布式可重⼊锁 RLock为例:// 1.获取指定的分布式锁对象 RLock lock = redisson.getLock("lock"); // 2.拿锁,具有 Watch Dog ⾃动续期机制 lock.lock(); // 3.执⾏业务 ... // 4.释放锁 lock.unlock();
-
数据库高性能–读写分离
如何实现读写分离:- 部署多台数据库,选择其中的⼀台作为主数据库,其他的⼀台或者多台作为从数据库。
- 保证主数据库和从数据库之间的数据是实时同步的,这个过程也就是我们常说的主从复制。
- 系统将写请求交给主数据库处理,读请求交给从数据库处理
-主从复制原理:
1. 主库将数据库中数据的变化写⼊到 binlog 2. 从库连接主库 3. 从库会创建⼀个 I/O 线程向主库请求更新的 binlog 4. 主库会创建⼀个 binlog dump 线程来发送 binlog ,从库中的 I/O 线程负责接收 5. 从库的 I/O 线程将接收的 binlog 写⼊到 relay log 中。 6. 从库的 SQL 线程读取 relay log 同步数据本地(也就是再执⾏⼀遍 SQL )
-
Spring Boot
-
@SpringBootApplication(自动配置如何实现?)
@SpringBootApplication看作是以下3个注解的集合:
@EnableAutoConfiguration:启动SpringBoot的自动配置机制
@ComponentScan:扫描被@Component(@Service,@Controller)注解的bean,注解默认会扫描该类所在的包下所有的类
@Configuration:允许在上下文中注册额外的bean或导入其他配置类(直接或者间接注册bean到IOC容器中) -
自动配置原理:
2.1 引入Starter组件
2.2 SpringBoot基于约定去Starter组件的路径下(META-INF/spring.factories)去找配置类
2.3 SpringBoot使用ImportSelector去导入这些配置类,并根据 @Conditional动态加载配置类里面的Bean到容器 -
RESTful Web服务
@RestController:@Controller和@ResponseBody的集合,其中后者作用代表所标注的函数返回值直接填入Http响应体中。
@PostMapping和@PutMapping区别:插入新数据一般用post;更新数据库一般用put。
@Pathvariable映射URL绑定的占位符@RequestMapping("/getUserById/{name}") public User getUser(@PathVariable("name") String userName){ return userService.selectUser(userName); }
@RequestBody:将请求中的body的json字符串转化为java对象
-
读取配置文件
3.1 @Value(“${property}”) 不是特别推荐
3.2 @PropertySource(“classpath:xx.properties”) 读取指定的properties,再用@Value来配合获取指定属性
3.3 通过@ConfigurationProperties(prefix = “property”)和@Component一起注解到对应properties类上
3.4 通过@ConfigurationProperties(“property”)注解到对应properties类上,并在调用方这边加入@EnableConfigurationProperties(property.class) -
使用Spring Boot Actuator来对项目进行监控
get方法访问/health接口获取应用程序的健康指标 -
请求参数校验
5.1 @RequestBody @Valid
5.2 @Valid @PathVariable @校验注解
5.3 @Valid @RequestParam @校验注解 -
定时任务
启动类加上@EnableScheduling,并在方法加@Scheduled。
//固定速率执行,每5s执行一次
@Scheduled(fixedRate = 5000)
//调用结束到下一次调用之间的固定时间
@Scheduled(fixedDelay = 5000)
//两次调用之间固定的毫秒数
@Scheduled(fixedRate = 5000)
//第一次执行延时1s
@Scheduled(initalDelay= 1000)
-
-
启动流程
8.1 加载EventPublishingRunListener对象
8.2 准备环境变量
8.3 控制台打印SpringBoot的banner标志
8.4 根据不同类型环境创建不同类型的applicationcontext容器
8.5 加载错误报告器(FailureAnalyzers)
8.6 初始化容器对象
8.7 刷新容器
8.8 执行刷新容器后的后置处理逻辑
8.9 调用ApplicationRunner和CommandLineRunner的run方法
8.10 报告启动异常
8.11 返回容器对象 -
Spring Cloud
-
Gateway:用于网关服务,实现请求的转发和路由(功能:安全,监控/指标,限流)
流程:通过匹配规则找到合适的路由,映射到具体的服务,然后请求经过过滤器处理后转发给具体的服务,服务处理后,再经过过滤器处理,最后返回给客户端 -
OpenFeign:轻量级RESTful的HTTP服务客户端,通过过滤器链来处理请求,主要用于远程服务调用
参考:https://mp.weixin.qq.com/s/7EJTSw5WGE5bYbo00nZ4jA
核心思想:- OpenFeign会扫描带有@FeignClient注解的接口,然后为其生成一个动态代理 - 动态代理里面包含接口方法MethodHandler,MethodHandler里面又包含经过MVC Contract解析注解后的元数据 - 发起请求时,MethodHandler会生成一个Request - 负载均衡器Ribbon会从服务列表中选取一个Server,拿到对应的IP地址后,拼接成最后的URL,就可以发起远程服务调用了
-
Ribbon/LoadBalancer:用于客户端负载均衡,将请求分发给不同的微服务实例,
初始化原理:
①Ribbon有一个自动配置类LoadBalancerAutoConfiguration,SpringBoot加载自动配置类,就会去初始化Ribbon
②当我们给RestTemplate或者AsyncRestTemplate添加注解后,Ribbon初始化会去收集加了@LoadBalanced注解的RestTemplate或者AsyncRestTemplate,把它们放到一个List里面
③给RestTemplate添加拦截器:LoadBalancerInterceptor
④从Eureka注册中心获取服务列表,然后存到Ribbon中
⑤加载yaml配置文件,配置好负载均衡配置,创建一个ILoadbalancer实例 -
Sentinel:以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性
参考:https://mp.weixin.qq.com/s/Q7Xv8cypQFrrOQhbd9BOXw -
Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台(一个人干了注册中心Eureka、服务配置Config、服务总线Bus这3个人的活)
-
RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务
-
Seata:分布式事务解决方案
-
-
字符串不可变性
字符串一旦被创建后,其内部创建的字符数组内容是不可改变的。改变的是应用地址。- 字符串在内存中被存储为一系列字符,而这些字符通常被分配在一个连续的内存块中。如果修改字符串,那么可能需要更改内存块大小,这可能导致性能问题和内存分配问题。
- 为了使代码更安全。如果字符可变,可能两个线程同时修改同一个字符串,就可能导致数据竞争和并发问题。
-
基本数据类型和引用数据类型
- 基本数据类型:char、byte,short,int,long,float,double、boolean
赋值方式是传值,并且值存在栈中 - 引用数据类型:object(对象,除了基本数据类型其他都是对象,所有的class和interface类型。数组是对象、函数是对象、正则表达式也是对象)
赋值方式是传址,并且值存在堆中
- 基本数据类型:char、byte,short,int,long,float,double、boolean
-
为什么有了基本数据类型还要有包装类型
包装类型是引用数据类型,提供了将基本数据类型转化为对象的能力,并支持相关类的方法和操作。 -
Spring注解原理
注解的实现基于java的反射机制。
Spring框架的核心功能之一是依赖注入(DI),它可以使用注解自动完成这个过程,而不需要再XML文件中显式配置。 -
JVM GC什么时候触发?
当程序创建一个新的对象或者基本类型的数据,内存空间不足时,会触发GC。
可回收对象:对象没有引用或者对象不可达
判断对象存活:引用计数法和可达性分析法
垃圾回收算法:分代回收 等