【面试深度解析】掌上先机后端面试(Java基础能力夯实)

欢迎关注公众号(通过文章导读关注:【11来了】),及时收到 AI 前沿项目工具及新技术的推送!

在我后台回复 「资料」 可领取编程高频电子书
在我后台回复「面试」可领取硬核面试笔记

文章导读地址:点击查看文章导读!

感谢你的关注!

文章目录

  • 掌上先机后端面试(Java基础能力夯实)
    • 题目分析
      • 1、对于 SQL 优化的理解
      • 2、left join 和 exists 的区别和使用场景
      • 3、left join 和 inner join 的区别
      • 4、查找在 A 表中存在,在 B 表中不存在的记录用什么比较好?
      • 5、说说 ArrayList 和 LinkedList 区别
      • 6、ArrayList 扩容的过程
      • 7、它们两个适用的场景
      • 8、ArrayList 和 LinkedLisst 怎么变成线程安全
      • 9、Collections 加锁的原理
      • 10、了解哪些设计模式?
      • 11、写一个懒汉式单例
      • 12、synchronized 能加在哪些地方?
      • 13、volatile 不是直接保证可见性了,为啥还要 DCL?
      • 14、聚簇索引和非聚簇索引的区别
      • 15、回表什么时候会发生?

掌上先机后端面试(Java基础能力夯实)

题目分析

1、对于 SQL 优化的理解

SQL 优化的目的就是让查询速度变快,也就是让 SQL 查询尽量走索引,来优化查询速度

那么常用的一般就是最左前缀原则和覆盖索引

  • 最左前缀原则:表示在联合索引的情况下, 让 where 语句中的条件遵循最左前缀原则,来让 SQL 语句可以走索引
  • 覆盖索引:让 select 中的字段都为索引字段,那么就不需要二次回表查询,大幅提高 SQL 查询速度

还有其他的一些 SQL 优化,比如 order by 的优化,也就是让 where 语句的条件 + order by 语句的条件结合起来符合最左前缀原则,那么也是会走索引的

还有 in 和 exists 的优化,这两个关键字都是对两个表进行筛选数据的,原则就是小表驱动大表,先过滤掉大部分的数据,只留下一小部分数据再去另一个表中做筛选,速度肯定是比拿着很多的数据去另一个表中做筛选要快很多的!

上边我主要说了从哪些方面可以对 SQL 进行优化,具体的细节这里先不写了(比较多),如果需要的话可以找我领取《面试突击》的 pdf 文档,里边都有解析。

2、left join 和 exists 的区别和使用场景

先说一下这两个的区别:

  • left join 是连表查询,会返回左边表的所有数据,如果右表有匹配的记录则显示右表中对应的记录,如果右表中没有匹配的记录,则右表对应的列全部为 Null
  • exists 是一种子查询,返回 true 或 false,用于判断在另一个表中是否有对应的记录,但是并不需要这些记录的具体内容

适用场景:

  • left join 适用于需要保留左表中的全部记录,并且只关心右表中匹配的行记录的情况,但是如果使用 left join 处理大量数据时,可能会有性能问题
  • exists 适用于判断某个条件是否满足,也就是某些记录在另一个表中是否存在,并不关心这些记录的具体内容是什么

这里给出 exists 的使用示例,以及使用建议:

当 A 表的数据集小于 B 表的数据集时,使用 exists

将主查询 A 的数据放到子查询 B 中做条件验证,根据验证结果(true 或 false)来决定主查询的数据是否保留

select * from A where exists (select 1 from B where B.id = A.id)

3、left join 和 inner join 的区别

两者的区别:

  • join 和 inner join 是相同的,inner join 可以简写为 join,表示两个表的交集
  • left join 是左连接,以左表为主,查询左表所有数据,显示右表与左表相关联部分的数据

图示:

有如下两个表:Table A 和 Table B,使用 left join 和 inner join 区别如图所示:

在这里插入图片描述

4、查找在 A 表中存在,在 B 表中不存在的记录用什么比较好?

通过使用 left join 和 not exists 就可以实现

  • LEFT JOIN

使用 left join 的话,对 A 和 B 两个表联表查,如果 B 表中的字段为 null 说明在 B 表中不存在

select A.* from A left join B on A.id=B.id where B.id is null
  • NOT EXISTS

使用 not exists 的话,子查询不为空的话,表示 A 表的数据在 B 表中存在,再通过 not exists 取反即可

select A.* from A where not exists (select 1 from B where A.id = B.id)

性能表现:

  • 使用 left join 时,如果 A 表数据量比较大,B 表数据量比较小时,性能表现较好
  • 使用 not exists 时,如果 A 表数据量比较小,B 表数据量比较大时,性能表现较好

5、说说 ArrayList 和 LinkedList 区别

ArrayList 底层是基于 数组 实现的,而 LinkedList 底层是基于 双向链表 实现的 ,这里主要说一下数组和链表的一些区别即可,区别主要表现在访问、插入、删除这三个操作上:

在这里插入图片描述

  • 对于插入元素和删除元素的性能来说
    • LinkedList 要好一些,如果在头尾插入元素时间复杂度为 O(1),如果在指定位置插入元素,那么时间复杂度是 O(n)
    • 而向 ArrayList 中间插入元素时,需要将后边的元素都向后移动一位,时间复杂度为 O(n)
  • 是否支持快速访问
    • ArrayList 可以根据下标快速访问元素,时间复杂度 O(1)
    • LinkedList 不可以快速访问元素,需要遍历链表,时间复杂度 O(n)

综上,可以发现 LinkedList 和 ArrayList 相比,没有太大优势,一般在项目中不会使用 LinkedList

6、ArrayList 扩容的过程

ArrayList 底层是基于数组实现的,是动态数组,那么它的大小也是动态变化的,这里我说一下扩容的流程:

当添加元素时,发现容量不足,则对数组进行扩容,大小扩容为原来的 1.5 倍,int newCapacity = oldCapacity + (oldCapacity >> 1),通过位运算进行扩容后容量的计算(位运算比较快)

ArrayList 会创建一个新的数组,表示扩容后的数组,将旧数据全部复制到新数组中去,再让 ArrayList 中的数组引用指向新的数组即可

7、它们两个适用的场景

一般不会使用 LinkedList,而是会选择 ArrayList,因为 LinkedList 如果向中间插入元素的话,复杂度也是 O(N) 的(需要先遍历找到要插入的位置),所以相比于 ArrayList 来说并没有太大的优势

8、ArrayList 和 LinkedLisst 怎么变成线程安全

这里主要说一下 ArrayList,LinkedList 的原理也是类似的

要保证 ArrayList 的线程安全,有两种方法:

  • 使用 Collections.synchronizedList
    • 这种方法比较简单粗暴,生成的集合中所有的操作都是通过 synchronized 来保证线程安全的
  • 使用 CopyOnWriteArrayList
    • 这种方法会在每次修改数据时,创建一个新的数据副本,在新数据上进行修改
    • 对于读操作来说,如果数据修改完毕,数组指针指向了新数组,那就可以读到最新数据;如果数据没有修改完毕,数组指针还是指向就数组,那么读取的还是旧数据

适用场景和性能表现:

  • 对于 Collections.synchronizedList 来说,由于读操作是直接读,不需要额外的操作,而写操作需要复制一份新的副本,读操作远远快于写操作,因此它适用于读多写少的场景
  • 对于 CopyOnWriteArrayList 来说,由于所有操作都是通过 synchronized 来保证同步的,读和写的加锁后性能都差不多,因此它适用于读和写比较平均的场景

9、Collections 加锁的原理

Collections.synchronizedList 的原理就是给所有的操作都包装上了一层 synchronized 来保证操作是同步的

注意事项:

通过 Collections.synchronizedList 保证线程安全时,如果使用迭代器 Iterator 来遍历列表的时候,需要手动添加 synchronized 来保证线程安全,在 for each 的时候,也需要手动添加 synchronized 来保证线程安全,因为 for each 的底层也是通过 Iterator 来实现的

// Iterator
synchronized (list) {
    Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext())
        // ...
}
// for each
synchronized (list) {
   for (Integer i : list) {
       try {
           TimeUnit.SECONDS.sleep(1);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.println(i + ",");
   }
}

10、了解哪些设计模式?

可以说一说你自己了解到的设计模式,比如单例模式、工厂模式、适配器模式、代理模式,这些都是比较常见的

像在 Spring 中的话,单例模式、工厂模式、代理模式都有使用

在 Spring MVC 中用到了适配器模式

还有 Netty 中的责任链模式

MQ 中的

11、写一个懒汉式单例

  • 线程不安全的懒汉式单例

懒汉式单例也就是在真正需要使用这个对象的时候再去加载这个单例对象,通过 static 来保证这个对象是唯一的

public class Singleton {
  private static Singleton instance;
  private Singleton() {}
  public static Singleton getInstance() {
      if (instance == null) {
          instance = new Singleton();
      }
      return instance;
  }
}
  • 线程安全的懒汉式单例

当然上边写出的懒汉式单例还存在 线程安全 的问题,当多个线程同时来创建的时候,可能会创建多个单例对象,因此需要通过 synchronized 和 volatile 来进一步优化,synchronized 保证线程间的同步,将单例对象声明为 volatile 禁止指令重排

public class Singleton {
  private static volatile Singleton instance;
  private Singleton() {}
  public static Singleton getInstance() {
      if (instance == null) {
          synchronized (Singleton.class) {
              if (instance == null) {
                  instance = new Singleton();
              }
          }
      }
      return instance;
  }
}

volatile 禁止指令重排

这里的指令重排是指底层硬件为了提升执行效率,对指令进行重排序,比如在上边的代码中 instance = new Singleeton() ,这一行代码对应的字节码有 3 个核心步骤:先创建一个 Singleton 对象、调用 Singleton 的初始化方法、将 Singleton 对象的引用赋值给 instance 变量

那么一般初始化方法的执行速度是比较慢的,可能会被重排序,排到后边去,指令重排序前后对比如下:

在这里插入图片描述

如果发生了指令重排,单线程环境下下是不会有问题的

但是在多线程环境下,第一个线程执行将 Singleton 的引用赋值给 instance 了,但是此时还没有执行完 Singleton 的初始化方法, 如果第二个线程进来,拿到这个 instance 变量,使用了这个未初始化完毕的 Singleton 实例对象,从而导致一些问题出现

12、synchronized 能加在哪些地方?

synchronized 加在不同的地方上,对应锁住的对象也是不同的,那么相对的锁粒度也是不同的

在具体使用中,要注意锁粒度越大,并发度就越低

  • synchronized 加在方法上,锁的是 this 对象

比如创建一个在 Phone 对象的方法加上 synchronized 锁,如果创建 Phone phone = new Phone() 对象,那么锁定的就是这一个 phone 实例对象

class Phone {
   public synchronized void sendSMS() throws Exception {
      System.out.println("--------sendSMS");
   }

   public synchronized void sendEmail() throws Exception {
      System.out.println("--------sendEmail");
   }

   public void getHello() {
      System.out.println("---------getHello");
   }
}
  • synchronized 加在静态方法上,锁的是Class对象,也就是字节码对象

加在静态方法上,锁定 Class 对象,这个锁的粒度就相比于锁定实例对象要大了

因为实例对象可以创建多个,而 Class 对象在 JVM 运行期间,只有一份

class Phone {
   public static synchronized void sendSMS() throws Exception {
      System.out.println("--------sendSMS");
   }
}
  • synchronized 保证同步代码块同步执行,可以自己执行加锁对象
// 使用对象作为锁
public void someMethod() {
    synchronized (lockObject) {
        // 需要同步的代码
    }
}
// 使用类的 Class 对象作为锁
public void someMethod() {
    synchronized (MyClass.class) {
        // 需要同步的静态代码
    }
}

13、volatile 不是直接保证可见性了,为啥还要 DCL?

我们还是先列出来 DCL(双端检锁)的代码:

if (instance == null) {
    synchronized (Singleton.class) {
        if (instance == null) {
            instance = new Singleton();
        }
    }
}

给 instance 变量加上 volatile 是保证了 instance 变量的原子性,如果不使用 DCL 的话,如下,假设多个线程同时进入到 synchronized 代码块中,那么第一个线程拿到锁,进来初始化 instance 变量,之后释放锁,第二个线程再拿到锁,进来之后他并不知道 instance 变量已经被初始化了,于是又初始化了一下,违背了单例模式

if (instance == null) {
    synchronized (Singleton.class) {
        instance = new Singleton();
    }
}

14、聚簇索引和非聚簇索引的区别

这个是 MySQL 中基础的内容了,不多赘述了

  • 聚簇索引:在聚簇索引中,数据行的物理存储顺序与索引中的顺序相同。换句话说,聚簇索引决定了表中数据的物理存储顺序。每个表只能有一个聚簇索引,通常是由主键自动创建。在InnoDB存储引擎中,聚簇索引的叶子节点直接包含行数据
  • 非聚簇索引:非聚簇索引(也称为二级索引或辅助索引)与聚簇索引不同,它在索引结构中存储的是 索引键值和指向数据行的指针(在InnoDB中通常是主键值)。非聚簇索引的叶子节点不包含实际的数据行,而是包含指向数据行的引用。一个表可以有多个非聚簇索引。

15、回表什么时候会发生?

当使用 SQL 查询时,如果走了索引,但是要查询的列并不全在索引上,因此还需要回表查询完整的数据

在非聚簇索引中,叶子节点保存的是主键的值,如果查询走的非聚簇索引,但是要查询的数据不只有主键的值,还有其他值,此时在非聚簇索引中拿到主键值,还需要再去聚簇索引回表查询,根据主键值查询到整行数据

  • 聚簇索引和非聚簇索引如下,这里画图比较简略了
    • 根据非聚簇索引查询的话,是通过普通的索引字段进行判断的(比如在 name 上建立索引,那就是通过 name 字段去非聚簇索引上进行查询)
    • 根据聚簇索引查询的话,是通过主键进行判断的,直接从 SQL 语句中拿到主键值或者从非聚簇索引中拿到主键值,去聚簇索引中进行查询

请添加图片描述

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/368519.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HTML音频标签

新增的语义化的标签: 即直接给了一个具象化的盒子。 新增的多媒体标签: 视频格式: 当都不支持的时候会显示文字。 video仍然是可以看成一个盒子。 音频格式: 新增的input 表单控件: 新增的表单属性: 提示文…

MyBatis 的XML实现方法

MyBatis 的XML实现方法 MyBatis 的XML实现方法前情提示创建mapper接口添加配置创建xml文件操作数据库insert标签delete标签select标签resultMap标签 update标签sql标签,include标签 MyBatis 的XML实现方法 前情提示 关于mybatis的重要准备工作,请看MyBatis 的注解实现方法 创…

Java SWT Composite 绘画

Java SWT Composite 绘画 1 Java SWT2 Java 图形框架 AWT、Swing、SWT、JavaFX2.1 Java AWT (Abstract Window Toolkit)2.2 Java Swing2.3 Java SWT (Standard Widget Toolkit)2.4 Java JavaFX 3 比较和总结 1 Java SWT Java SWT(Standard Widget Toolkit&#xff…

Power BI案例-链接Mysql方法

Power BI案例-连锁Mysql 方法1-通过组件mysql-connector-net-8.3.0: 选择文件–获取数据–选择MySQL数据库–选择链接 提示无组件,选择了解详细情况 弹出浏览器,选择下载 不用登陆,可以直接下载 下载的组件如下&#xff1a…

【开源】基于JAVA+Vue+SpringBoot的陕西非物质文化遗产网站

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 设计目标2.2 研究内容2.3 研究方法与过程2.3.1 系统设计2.3.2 查阅文献2.3.3 网站分析2.3.4 网站设计2.3.5 网站实现2.3.6 系统测试与效果分析 三、系统展示四、核心代码4.1 查询民间文学4.2 查询传统音乐4.3 增改传统舞…

代码随想录算法训练营Day46|139.单词拆分、多重背包理论基础、背包问题总结

目录 139.单词拆分 方法一:回溯法 算法实现 方法二:背包问题 算法实现 多重背包理论基础 思路 算法实现 背包问题总结 前言 背包递推公式 遍历顺序 0-1背包 完全背包 139.单词拆分 题目链接 文章链接 方法一:回溯法 在回溯专题…

Endnote常见设置(硕士毕业论文参考文献修改)

1、根据大多数期刊或学校使用的标准,英文名首字母大写后续字母小写。 2、需要手动调整Endnote中的参考文献相关内容 3、关于姓名大小写设置 AS IS是不更改大小写,EndNote库中文献的大小是什么样,Word中就显示什么样。选择Normal为首字母大…

HDMI2.1之eARC简介-Dolby Atmos和DTS:X

文章目录 eARC目的更大的带宽更高质量音频支持对象型音频与CEC(Consumer Electronics Control)的兼容性: 适应流媒体发展Dolby AtmosDTS:X高分辨率音频更高的音频位深度和采样率低延迟音频 对象型音频格式独立对象三维定位动态音场适应性和灵…

嵌入式——串行外围设备接口(SPI)

目录 一、初识SPI 1. 介绍 2. 特性 补: 二、物理层 1. SS (Slave Select) 2. SCK (Serial Clock) 3. MOSI (Master Output, Slave Input) 4. MISO (Master Input&#xff0…

虚拟机Windows Server 2016 安装 MySQL8

目录 一、下载MySQL8 1.下载地址: 2.创建my.ini文件 二、安装步骤 第一步:命令窗口 第二步:切换目录 第三步:安装服务 第四步:生成临时密码 第五步:启动服务 第六步: 修改密码 三…

【Linux系统化学习】进程替换

目录 进程程序替换 替换原理 ​编辑替换函数 函数解释 命名理解 函数使用 execl execlp execv execvp 调用其它程序 进程程序替换 替换原理 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个…

0203-2-输入输出系统

第六章:输入输出系统 I/O系统的功能,模型和接口 I/O系统管理的对象是I/O设备和相应的设备控制器。 I/O系统的基本功能 隐藏物理设备的细节与设备的无关性提高处理机和I/O设备的利用率对I/O设备进行控制确保对设备的正确共享错误处理 I/O软件的层次结…

重写Sylar基于协程的服务器(4、协程调度模块的设计)

重写Sylar基于协程的服务器(4、协程调度模块的设计) 重写Sylar基于协程的服务器系列: 重写Sylar基于协程的服务器(0、搭建开发环境以及项目框架 || 下载编译简化版Sylar) 重写Sylar基于协程的服务器(1、日…

2.03作业

1.请编程实现单向循环链表的头插,头删,尾插,尾删。 2.请编程实现单向循环链表约瑟夫环 约瑟夫环:用循环链表编程实现约瑟夫问题 n个人围成一圈,,从某人开始报数,1,2,......,m,数到…

牛客-NC249946-小d和孤独的区间

牛客-小d和孤独的区间 题目如上 1 s 1s 1s的时间限制,说明我们应该找到一种“巧法” 根据提示,我们只需要找到“中间全部都是 0 0 0,只有一个1的区间”即可 但是在找的途中,我们不仅要顾及当前 1 1 1之前的 0 0 0的个数&#xff…

JProfiler for Mac:提升性能和诊断问题的终极工具

在当今的高性能计算和多线程应用中,性能优化和问题诊断是至关重要的。JProfiler for Mac 是一个强大的性能分析工具,旨在帮助开发者更好地理解其应用程序的运行情况,提升性能并快速诊断问题。 JProfiler for Mac 的主要特点包括:…

常见API

文章目录 Math类1.1 概述1.2 常见方法 System类2.1 概述2.2 常见方法 Runtime3.1 概述3.2 常见方法 Object类4.1 概述4.2 常见方法 Objects类5.1 概述5.2 常见方法 BigInteger类6.1 引入6.2 概述6.3 常见方法6.4 底层存储方式: 7 BigDecimal类7.1 引入7.2 概述7.3 常…

PAT-Apat甲级题1005(python和c++实现)

PTA | 1005 Spell It Right 1005 Spell It Right 作者 CHEN, Yue 单位 浙江大学 Given a non-negative integer N, your task is to compute the sum of all the digits of N, and output every digit of the sum in English. Input Specification: Each input file cont…

kubernetes-深入理解Pod对象:调度

一、创建一个Pod的工作流程 Pod创建流程: 1、kubectl 向 APIServer 发起一个 Pod 创建请求。 2、APIServer 接受到 Pod 创建请求后,不是直接创建 Pod,而是将 Pod 的数据写入 etcd 中,待写入操作执行完成,APIServer 将…

vue3 之 组合式API - setup选项

首先我们先看下setup选项的写法和执行时机 setup选项中写代码的特点 语法糖 总结 setup选项的执行时机? beforeCreate钩子之前 自动执行 setup写代码的特点是什么? 定义数据 函数 然后以对象方式return 经过语法糖的封装更简单的使用组合式API set…