文章目录
一、讲一下对数据结构的理解
数据结构是一种对数据进行组织和存储的方式,它定义了数据之间的关系以及对数据的一些操作;在编程过程中选择合适的数据结构来组织和存储数据是非常重要的,它可能会影响程序的性能和代码的可读性可维护性等,我们在选择合适的数据结构的时候主要会从时间复杂度和空间复杂度两个维度去衡量选择;常见的数据结构包括但不限于数组、链表、栈、队列,Java标准库也提供了许多内置的数据结构,如ArrayList、LinkedList、HashMap等;
二、讲一下MySQL
MySQL是什么?===MySQL是一个开源关系型数据库,用来组织和存储结构化数据,其中数据通过二位表格来进行组织,表格之间可以建立一对一、一对多、多对多这样的关系;关系型数据库强调数据的一致性、事务支持以及SQL语言的使用,包括数据的增删改查;MySQL支持多种存储引擎,InnoDB、MyISAM、Memory等,目前主要使用的是InnoDB存储引擎,因为InnoDB存储引擎支持事务、行级锁、外键支持等;
三、MySQL三种日志的作用
undoLog:回滚日志,记录修改前的旧数据,用于事务回滚保证事务的原子性以及MVCC的实现基础;
redoLog:写前日志,记录修改后的新数据,用于数据库崩溃后数据的恢复保证事务的持久性;
binLog:归档日志,记录对数据的所有更改操作,用于数据备份和主从复制;
如果直接写到磁盘,还要用到undolog、redolog、binlog三种日志吗?–即使数据直接写入磁盘,undo log、redo log和binlog仍然是非常重要的。它们确保了事务的一致性、持久性,并提供了数据恢复、复制的功能。这些日志是数据库系统的核心组成部分,无论是在数据库的正常操作还是在面临故障和灾难恢复时,都起着关键作用。因此,在数据库管理中,这三种日志通常都会启用和配置。
四、WAL的意义
WAL(Write-Ahead Logging,预写日志),其中写的是redoLog日志,它的意义主要在以下几点:—首先是确保了数据的持久化,在对数据的更新操作写入磁盘之前,先写入日志,如果更新操作在写入磁盘之前数据库崩溃了,那么已经记录到日志中的操作就不会丢失,在数据库重启后可以根据redoLog来还原数据,确保事务的原子性和一致性;—预写日志可以有效减少磁盘IO,因为数据更新操作首先是修改Buffer Pool中的数据页,然后将更新操作记录到日志中,而不是直接修改磁盘上的数据,这样可以减少磁盘IO的次数,进而提高数据库的性能;—有利于数据库崩溃恢复:通过重放日志确保数据库的可用性和数据的完整性。
五、聚簇索引与聚合索引
聚簇索引通常在主键列上创建,因而又称为主键索引,而主键索引是数据库中的一种索引类型,用来组织数据行在磁盘上的存储顺序,规定每张表只能有一个主键索引,对表中的数据进行物理排序;
聚合索引一般包含多个列,这些列的组合用于优化多列查询的性能;
六、索引失效的场景
使用函数或表达式对索引列进行操作、使用OR关键字、使用了不支持索引的操作符(例如Not、In、Like关键的通配符在前面的)、数据类型不匹配、隐式转换、联合索引中不符合最左匹配原则等;
七、讲一下HashMap
HashMap是Java开发中常用的一种数据结构,它存储键值对,底层实现基于哈希表,通过hash函数计算key的hash值,然后映射到相应的存储位置,提供了高效的数据检索和插入操作;—HashMap是键值对村簇,其中键必须是唯一的,可以为Null,对应唯一的值,我们可以通过键查询到相应的值;—HashMap中的键值对是无序存储的,不会按照插入的顺序进行排列,如果需要有序的键值对存储可以选择TreeMap;—HashMap具有动态扩容机制,当存储的键值对数量超过一定阈值时,HashMap 会自动调整内部哈希表的大小,以保持性能,这有助于避免哈希冲突的问题;—HashMap的扩容时机,HashMap的当前存储的数据量=容量*装载因子的时候会触发扩容,一般是原来的2倍进行扩容;—HashMap是非线程安全的,这意味着当有多个线程并发访问的时候可能会导致数据不一致以及一些并发问题,如果需要线程安全的版本,可以考虑使用 ConcurrentHashMap。
八、HashMap为什么引入红黑数,什么时候链表变红黑数,为什么不用AVL?
HashMap引入红黑树主要是为了优化发生哈希冲突时的查询效率,一开始当发生Hash冲突的时候,我们使用链地址法来解决,但是随着链表节点越来越多,遍历链表的时间复杂度为O(n),导致查询效率变低,为了解决该问题,Java8引入了红黑树来优化;将查找操作的时间复杂度从线性O(n)降低到对数O(log n),从而提高了性能。
在Java8中,当链表长度达到8并且HashMap容量达到64的时候就会触发链表变红黑树;
AVL 树是一种自平衡二叉搜索树(Self-Balancing Binary Search Tree),它在插入和删除节点时自动进行平衡操作,以保持树的高度平衡,进而保证常数时间的平均查询和插入/删除操作,但它相对复杂,需要更多的维护操作;相比之下,红黑树的自平衡性能较好,并且在维护上更加高效,红黑树通常比AVL树具有更好的空间效率。AVL树需要额外的平衡因子来维护平衡信息,而红黑树只需要一个额外的颜色信息。因此,在存储平衡信息方面,红黑树更加紧凑。
九、Java编程中有小数怎么存,精度丢失怎么解决?
Java编程有小数时,可以使用浮点数float和double来进行存储,但是浮点数在进行计算的时候可能会出现精度丢失的问题,是因为浮点数使用了二进制浮点表示法,浮点数有限的位数、有限的表示范围、优化和近似处理来提高运算性能等;为了解决精度丢失的问题,我们可以使用更高精度的数据类型,BigDecimal,它可以表示任意位数的小数,不受限于固定的位数,可以对舍入进行控制、精确的算术运算,BigDecimal类提供各类算术运算方法,并且其内部实现都考虑了小数的精确位数,并尽可能保持结果的精度。
十、Java基本数据类型
byte 8位,short 16位,int 32位,long 64位,float 32位,double 64位,char 16位,boolean;Java提供八种基本数据类型,用于存储不同类型的数据;这八种基本数据类型Java都提供了对应的包装类,并且提供了自动拆箱和装箱的功能用于基本数据类型和包装类之间的转换;
十一、十万个数怎么找最大的一百个数
考虑使用堆排序,但是这里不是对十万个数进行堆排序,而是对一百个数进行堆排序,并且因为是找出最大的一百个数,所以选择小顶堆:具体步骤:1.将十万个数的前一百个数用来构建小顶堆;2.遍历余下的数字,如果当前数字大于小顶堆的根节点,那么就进行替换,并调整小顶堆;3.重复步骤2直到结束,就找出了最大的一百个数;这种方法的时间复杂度是O(nlogk),n是总数据量,k是需要检索出的数据量;这种方法的优势不需要对十万个数进行排序,只需要维护一百个节点的小顶堆,并且遍历时不需要重复遍历;
十二、Spring的常用注解
@Component:表示该类是Spring容器的一个组件,Spring容器将自动扫描并创建该组件的实例,使它可以在应用程序中被注入和使用;
@Repository:用于标记数据访问层的组件,通常与持久层的Dao类一起使用,告诉Spring这是一个用于数据访问的Bean;
@Service:用于标记服务层组件,通常用于标识业务逻辑的 Bean;
@Controller:用于标记控制器组件,通常用于标识处理 Web 请求的 Bean;
- @RequestMapping(GetMapping、PostMapping):用于映射 HTTP 请求到处理方法,定义了 URL 映射规则,常用于控制器类中;
- @PathVariable:用于从 URL 中提取路径变量值,通常与@RequestMapping一起使用;
- @RequestParam:用于从 HTTP 请求参数中提取参数值,也常与@RequestMapping一起使用;
- @ResponseBody:用于将方法的返回值直接写入 HTTP 响应体,通常用于 RESTful 服务;
- @RequestBody:用于将 HTTP 请求体中的内容绑定到方法的参数对象上,通常用于接收 POST 请求的 JSON 数据。
@Autowired:用于自动装配 Bean,它可以注入其他 Spring 管理的 Bean 到当前 Bean 中。
@Qualifier:与@Autowired一起使用,用于指定要注入的 Bean 的名称,当存在多个符合类型的 Bean 时非常有用。
@Value:用于注入属性值,可以将配置文件中的属性值注入到 Bean 的字段或方法参数中。
@Scope:用于指定 Bean 的作用域,包括单例(Singleton)、原型(Prototype)、会话(Session)、请求(Request)等。
@Configuration:用于标记配置类,通常与@Bean一起使用,用于定义 Bean 的创建和配置。
@Bean:用于在配置类中定义 Bean,它告诉 Spring 容器应该如何创建 Bean。
@Import:用于导入其他配置类,将它们合并到当前配置类中,方便模块化配置。
@Conditional:用于根据条件决定是否创建 Bean,可根据一定条件来控制 Bean 的创建。
@Async:用于标记异步方法,告诉 Spring 将方法调用放到独立的线程中执行。
@Transaction:用于声明事务管理,通常与事务管理相关的注解一起使用,如@Transactional。
@Scheduled:用于定时任务的配置,允许方法定期执行。
十三、Spring事务的常用注解
@EnableTransactionManagement:用于启用Spring的事务管理功能,通常添加到配置类上,告诉Spring在应用中启用事务支持;@Propagation:指定事务的传播行为,@Isolation:指定事务的隔离级别;
@Transactional:最常见的事务注解,应用于类级别和方法级别,你可以指定哪些方法应该在事务管理下运行,可以配置多个属性,例如事务的传播行为、隔离级别、回滚规则等;
十四、事务失效的场景
方法自调用(自调用会绕过代理)、数据库不支持事务、在非public修饰的方法上使用注解、异常未被抛出;
十五、给你很多的数据,怎么判断一个数字在不在里面?
最简单的就是线性搜索,也就是逐一遍历比较,但是该方法时间复杂度是O(n),如果数据量非常,肯定不适合;使用集合来存储这些数据,利用集合中封装的方法就可以很轻松判断一个数字是否存在了,集合中一般使用散列表或者树来组织存储数据,所以时间复杂度为O(1)或者是O(logn),这是一种相对高效的方法,适用于数据量较大的场景;对数据排序后进行二分查找,二分查找的效率是相对较高的,但是对数据排序这个过程可能会比较消耗性能;布隆过滤器,也是一种散列表的形式,底层基于哈希表和一维数组来实现的,用于快速判断一个元素是否存在;
十六、使用SpringScurity实现用户认证和授权时,用户密码是怎么存的?
用户注册时,应用程序将用户密码传递给密码哈希函数,生成哈希值,并将哈希值存储在数据库中。Spring Security提供了多个密码哈希算法的实现,包括BCrypt、SHA-256、MD5等。BCrypt通常被认为是最安全的密码哈希算法之一,因为它不仅进行哈希处理,还包括随机盐(salt)的生成和存储,增加了密码的安全性。
十七、单例设计模式
单例设计模式是创建型设计模式,它确保一个类只有一个实例,并且提供一个全局访问点来获取该实例;单例模式通常用于需要唯一资源管理、配置管理、日志记录等场景;单例模式的构建方法常见有两种,饿汉模式和懒汉模式,饿汉模式就是在程序加载该类的时候就会创建该类的实例,无论程序最后是否使用,这样可能会存在一个资源浪费的问题;懒汉模式就是在程序需要使用的时候才创建该实例,但是这样可能会存在多个线程同时去创建而导致并发访问问题,所以需要使用双重检查锁定(Double-Checked Locking)或其他线程安全的方式来确保只创建一个实例;