Java面试准备

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如何保证传输的可靠性

    1. 基于数据块传输:应用数据被分割成TCP认为最适合发送的数据块(报文段或者段),再传输给网络层
    2. 对失序数据包重新排序以及去重:每个包都有一个序列号,能对接收到的数据根据序列号排序,并且去掉重复序列号的数据
    3. 校验和:TCP将保持它首部和数据的校验和
    4. 重传机制:在数据包丢失或延迟的情况下,重新发送数据包,直到收到对方的确认应答(ACK)
    5. 流量控制:TCP连接的每一方都有固定大小的缓冲空间,接收端只允许发送端发送接收端缓冲区能接纳的数据(TCP利用滑动窗口实现流量控制)
    6. 拥塞控制:当网络拥塞时,减少数据的发送。
  • 产生死锁的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. 部署多台数据库,选择其中的⼀台作为主数据库,其他的⼀台或者多台作为从数据库。
    2. 保证主数据库和从数据库之间的数据是实时同步的,这个过程也就是我们常说的主从复制。
    3. 系统将写请求交给主数据库处理,读请求交给从数据库处理

    -主从复制原理:

     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

    1. @SpringBootApplication(自动配置如何实现?)
      @SpringBootApplication看作是以下3个注解的集合:
      @EnableAutoConfiguration:启动SpringBoot的自动配置机制
      @ComponentScan:扫描被@Component(@Service,@Controller)注解的bean,注解默认会扫描该类所在的包下所有的类
      @Configuration:允许在上下文中注册额外的bean或导入其他配置类(直接或者间接注册bean到IOC容器中)

    2. 自动配置原理:
      2.1 引入Starter组件
      2.2 SpringBoot基于约定去Starter组件的路径下(META-INF/spring.factories)去找配置类
      2.3 SpringBoot使用ImportSelector去导入这些配置类,并根据 @Conditional动态加载配置类里面的Bean到容器

    3. 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对象

    4. 读取配置文件
      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)

    5. 使用Spring Boot Actuator来对项目进行监控
      get方法访问/health接口获取应用程序的健康指标

    6. 请求参数校验
      5.1 @RequestBody @Valid
      5.2 @Valid @PathVariable @校验注解
      5.3 @Valid @RequestParam @校验注解

    7. 定时任务
      启动类加上@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

    1. Gateway:用于网关服务,实现请求的转发和路由(功能:安全,监控/指标,限流)
      流程:通过匹配规则找到合适的路由,映射到具体的服务,然后请求经过过滤器处理后转发给具体的服务,服务处理后,再经过过滤器处理,最后返回给客户端

    2. OpenFeign:轻量级RESTful的HTTP服务客户端,通过过滤器链来处理请求,主要用于远程服务调用
      参考:https://mp.weixin.qq.com/s/7EJTSw5WGE5bYbo00nZ4jA
      核心思想:

       - OpenFeign会扫描带有@FeignClient注解的接口,然后为其生成一个动态代理
       - 动态代理里面包含接口方法MethodHandler,MethodHandler里面又包含经过MVC Contract解析注解后的元数据
       - 发起请求时,MethodHandler会生成一个Request
       - 负载均衡器Ribbon会从服务列表中选取一个Server,拿到对应的IP地址后,拼接成最后的URL,就可以发起远程服务调用了
      
    3. Ribbon/LoadBalancer:用于客户端负载均衡,将请求分发给不同的微服务实例,
      初始化原理:
      ①Ribbon有一个自动配置类LoadBalancerAutoConfiguration,SpringBoot加载自动配置类,就会去初始化Ribbon
      ②当我们给RestTemplate或者AsyncRestTemplate添加注解后,Ribbon初始化会去收集加了@LoadBalanced注解的RestTemplate或者AsyncRestTemplate,把它们放到一个List里面
      ③给RestTemplate添加拦截器:LoadBalancerInterceptor
      ④从Eureka注册中心获取服务列表,然后存到Ribbon中
      ⑤加载yaml配置文件,配置好负载均衡配置,创建一个ILoadbalancer实例

    4. Sentinel:以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性
      参考:https://mp.weixin.qq.com/s/Q7Xv8cypQFrrOQhbd9BOXw

    5. Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台(一个人干了注册中心Eureka、服务配置Config、服务总线Bus这3个人的活)

    6. RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务

    7. Seata:分布式事务解决方案

  • 字符串不可变性
    字符串一旦被创建后,其内部创建的字符数组内容是不可改变的。改变的是应用地址。

    1. 字符串在内存中被存储为一系列字符,而这些字符通常被分配在一个连续的内存块中。如果修改字符串,那么可能需要更改内存块大小,这可能导致性能问题和内存分配问题。
    2. 为了使代码更安全。如果字符可变,可能两个线程同时修改同一个字符串,就可能导致数据竞争和并发问题。
  • 基本数据类型和引用数据类型

    1. 基本数据类型:char、byte,short,int,long,float,double、boolean
      赋值方式是传值,并且值存在栈中
    2. 引用数据类型:object(对象,除了基本数据类型其他都是对象,所有的class和interface类型。数组是对象、函数是对象、正则表达式也是对象)
      赋值方式是传址,并且值存在堆中
  • 为什么有了基本数据类型还要有包装类型
    包装类型是引用数据类型,提供了将基本数据类型转化为对象的能力,并支持相关类的方法和操作。

  • Spring注解原理
    注解的实现基于java的反射机制。
    Spring框架的核心功能之一是依赖注入(DI),它可以使用注解自动完成这个过程,而不需要再XML文件中显式配置。

  • JVM GC什么时候触发?
    当程序创建一个新的对象或者基本类型的数据,内存空间不足时,会触发GC。
    可回收对象:对象没有引用或者对象不可达
    判断对象存活:引用计数法和可达性分析法
    垃圾回收算法:分代回收 等

  • 34
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值