今天去 OPPO 面试,被问麻了
前言
最近有位读者去面试了 oppo,给大家整理了面试真题的答案。希望对大家有帮助哈,一起学习,一起进步。
-
聊聊你印象最深刻的项目,或者做了什么优化。
-
项目提到分布式锁,你们是怎么使用分布式锁的?
-
常见的分布式事务解决方案
-
你们的接口幂等是如何保证的?
-
你们的 MySQL 架构是怎样的?
-
常见的索引结构有?哈希表结构属于哪种场景?
-
给你 ab,ac,abc 字段,你是如何加索引的?
-
数据库隔离级别是否了解?你们的数据库默认隔离级别是?为什么选它?
-
RR 隔离级别实现原理,它是如何解决不可重复读的?
-
你们项目使用了 RocketMQ 对吧?那你知道如何保证消息不丢失吗?
-
事务消息是否了解?场景题:比如下单清空购物车,你是如何设计的?
-
如何快速判断一个数是奇数还是偶数,除开对 2 取其余呢。
-
Spring 声明式事务原理?哪些场景事务会失效?
-
你们是微服务架构吗?如果你来设计一个类似淘宝的系统,你怎么划分微服务?
-
你们是怎么分库分表的?分布式 ID 如何生成?
-
所有异常的共同祖先是?运行时异常有哪几个?
1. 聊聊你印象最深刻的项目,或者做了什么优化。
大家平时做的项目,如果很多知识点跟面试八股文相关的话,就可以相对条理清晰地写到简历去。
-
比如缓存数据库相关的,查询为空,你设置了一个-1 到缓存,代表数据库没记录。下次判断-1,就不查库了,以解决缓存穿透的问题。
-
又比如你设置缓存过期时间比较分散,解决缓存击穿问题,都可以条理清晰写到简历去,这样面试官很可能会问你相关的问题,这时候就对答如流啦。
还有平时你做的项目,有一些比较好的设计,都可以说一下哈,比如你是如何保证数据一致性的,怎么优化接口性能的。
2. 你项目提到分布式锁,你们是怎么使用分布式锁的?
一般你讲述你做的项目时,面试官会根据你项目涉及的一些面试点,然后抽他感兴趣的一两个来问。所以大家对哪些知识点熟悉,讲述项目时,就说你用该知识点,解决了什么问题。
3. 常见分布式事务解决方案
分布式事务:就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单来说,分布式事务指的就是分布式系统中的事务,它的存在就是为了保证不同数据库节点的数据一致性。
聊到分布式事务,大家记得这两个理论哈:CAP 理论 和 BASE 理论
分布式事务的几种解决方案:
-
2PC(二阶段提交)方案、3PC
-
TCC(Try、Confirm、Cancel)
-
本地消息表
-
最大努力通知
-
seata
2PC(二阶段提交)方案
2PC,即两阶段提交,它将分布式事务的提交拆分为 2 个阶段:prepare 和 commit/rollback,即准备阶段和提交执行阶段。在 prepare 准备阶段需要等待所有参与子事务的反馈,因此可能造成数据库资源锁定时间过长,不适合并发高以及子事务生命周长较长的业务场景。并且协调者宕机的话,所有的参与者都收不到提交或回滚指令。
3PC
两阶段提交分别是:CanCommit,PreCommit 和 doCommit,这里不再详述。3PC 利用超时机制解决了 2PC 同步阻塞问题,避免资源被永久锁定,进一步加强了整个事务过程的可靠性。但是 3PC 同样无法应对类似的宕机问题,只不过出现很多数据源中数据不一致问题的概率更小。
TCC
TCC 采用了补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:Try-Confirm-Cancel
-
try 阶段:尝试去执行,完成所有业务的一致性检查,预留必须的业务资源。
-
Confirm 阶段:该阶段对业务进行确认提交,不做任何检查,因为 try 阶段已经检查过了,默认 Confirm 阶段是不会出错的。
-
Cancel 阶段:若业务执行失败,则进入该阶段,它会释放 try 阶段占用的所有业务资源,并回滚 Confirm 阶段执行的所有操作。
TCC 方案让应用可以自定义数据库操作的粒度,降低了锁冲突,可以提升性能。但是应用侵入性强,try、confirm、cancel 三个阶段都需要业务逻辑实现。
本地消息表
ebay 最初提出本地消息表这个方案,来解决分布式事务问题。业界目前使用这种方案是比较多的,它的核心思想就是将分布式事务拆分成本地事务进行处理。可以看一下基本的实现流程图:
最大努力通知
最大努力通知方案的目标,就是发起通知方通过一定的机制,最大努力将业务处理结果通知到接收方。
seata
Saga 模式是 Seata 提供的长期事务解决方案。核心思想是将长事务拆分为多个本地短事务,由 Saga 事务协调器协调,如果正常结束那就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。
Saga 的并发度高,但是一致性弱,对于转账,可能发生用户已扣款,最后转账又失败的情况。
4. 你们的接口幂等是如何保证的?
如果你调用下游接口超时了,是不是考虑重试?如果重试,下游接口就需要支持幂等啦。
实现幂等一般有这 8 种方案:
-
select+insert+主键/唯一的索引冲突
-
直接 insert + 主键/唯一索引冲突
-
状态机密等
-
抽取防重表
-
token 令牌
-
悲观锁(如 select for update,很少用)
-
乐观说
-
分布式锁
大家平时是用哪个方案解决幂等的,最后结合工作实际讲讲哈。
5. 你们的 mySQL 架构是怎样的?
大家可以结合自己公司的 MySQL 架构聊聊。
数据的库高可用方案
-
双机主备
-
一主一从
-
一主多从
-
MariaDB 同步多主机
-
数据库中间件
5.1 双机主备
-
优点:一个机器故障了可以自动切换,操作比较简单。
-
缺点:只有一个库在工作,读写压力大,未能实现读写分离,并发也有一定限制
5.2 一主一从
-
优点:从库支持读,分担了主库的压力,提升了并发度。一个机器故障了可以自动切换,操作比较简单。
-
缺点:一台从库,并发支持还是不够,并且一共两台机器,还是存在同时故障的机率,不够高可用。
5.3 一主多从
-
优点:多个从库支持读,分担了主库的压力,明显提升了读的并发度。
-
缺点:只有一台主机写,因此写的并发度不高
5.4 MariaDB 同步多主机集群
-
有代理层实现负载均衡,多个数据库可以同时进行读写操作;各个数据库之间可以通过 Galera Replication 方法进行数据同步,每个库理论上数据是完全一致的。
-
优点:读写的并发度都明显提升,可以任意节点读写,可以自动剔除故障节点,具有较高的可靠性。
-
缺点:数据量不支持特别大。要避免大事务卡死,如果集群节点一个变慢,其他节点也会跟着变慢。
5.5 数据库中间件
-
mycat 分片存储,每个分片配置一主多从的集群。
-
优点:解决高并发高数据量的高可用方案
-
缺点:维护成本比较大。
6. 常见的索引结构有?哈希表结构属于哪种场景?
哈希表、有序数组和搜索树。
-
哈希表这种结构适用于只有等值查询的场景
-
有序数组适合范围查询,用二分法快速得到,时间复杂度为 O(log(N))。查询还好,如果是插入,就得挪动后面所有的记录,成本太高。因此它一般只适用静态存储引擎,比如保存 2018 年某个城市的所有人口信息。
-
B+树适合范围查询,我们一般建的索引结构都是 B+树。
7.给你 ab,ac,abc 字段,你是如何加索引的?
这主要考察联合索引的最左前缀原则知识点。
-
这个最左前缀可以是联合索引的最左 N 个字段。比如组合索引(a,b,c)可以相当于建了(a),(a,b),(a,b,c)三个索引,大大提高了索引复用能力。
-
最左前缀也可以是字符串索引的最左 M 个字符。
因此给你 ab,ac,abc 字段,你可以直接加 abc 联合索引和 ac 联合索引即可。
8. 数据库隔离级别是否了解?你们的数据库默认隔离级别是?为什么选它?
四大数据库隔离级别,分别是读未提交,读已提交,可重复读,串行化(Serializable)。
-
读未提交:事务即使未提交,却可以被别的事务读取到的,这级别的事务隔离有脏读、重复读、幻读的问题。
-
读已提交:当前事务只能读取到其他事务提交的数据,这种事务的隔离级别解决了脏读问题,但还是会存在不可重复读、幻读问题;
-
可重复读:限制了读取数据的时候,不可以进行修改,所以解决了不可重复读的问题,但是读取范围数据的时候,是可以插入数据,所以还会存在幻读问题。
-
串行化:事务最高的隔离级别,在该级别下,所有事务都是进行串行化顺序执行的。可以避免脏读、不可重复读与幻读所有并发问题。但是这种事务隔离级别下,事务执行很耗性能。
MySQL 选择 Repeatable Read(可重复读)作为默认隔离级别,我们的数据库隔离级别选的是读已提交。
8.1 为什么 MySQL 的默认隔离离别是 RR?
binlog 的格式也有三种:statement,row,mixed。设置为 statement 格式,binlog 记录的是 SQL 的原文。又因为 MySQL 在主从复制的过程是通过 binlog 进行数据同步,如果设置为读已提交(RC)隔离级别,当出现事务乱序的时候,就会导致备库在 SQL 回放之后,结果和主库内容不一致。
比如一个表 t,表中有两条记录:
CREATE TABLE t (a int(11) DEFAULT NULL,b int(11) DEFAULT NULL,PRIMARY KEY a (a),KEY b(b)) ENGINE=InnoDB DEFAULT CHARSET=latin1;insert into t1 values(10,666),(20,233);
两个事务并发写操作,如下:
在读已提交(RC)隔离级别下,两个事务执行完后,数据库的两条记录就变成了(30,666)、(20,666)。这两个事务执行完后,binlog 也就有两条记录,因为事务 binlog 用的是 statement 格式,事务 2 先提交,因此 update t set b=666 where b=233 优先记录,而 update t set a=30 where b=666 记录在后面。
当 bin log 同步到从库后,执行 update t set b=666 where b=233 和 update t set a=30 where b=666 记录,数据库的记录就变成(30,666)、(30,666),这时候主从数据不一致啦。
因此 MySQL 的默认隔离离别选择了 RR 而不是 RC。RR 隔离级别下,更新数据的时候不仅对更新的行加行级锁,还会加间隙锁(gap lock)。事务 2 要执行时,因为事务 1 增加了间隙锁,就会导致事务 2 执行被卡住,只有等事务 1 提交或者回滚后才能继续执行。
并且,MySQL 还禁止在使用 statement 格式的 binlog 的情况下,使用 READ COMMITTED 作为事务隔离级别。
我们的数据库隔离级别最后选的是读已提交(RC)。
那为什么 MySQL 官方默认隔离级别是 RR,而有些大厂选择了 RC 作为默认的隔离级别呢?
-
提升并发
RC 在加锁的过程中,不需要添加 Gap Lock 和 Next-Key Lock 的,只对要修改的记录添加行级锁就行了。因此 RC 的支持的并发度比 RR 高得多,
-
减少死锁
正是因为 RR 隔离级别增加了 Gap Lock 和 Next-Key Lock 锁,因此它相对于 RC,更容易产生死锁。
9. RR 隔离级别实现原理,它是如何解决不可重复读的?
9.1 什么是不可重复读
先回忆下什么是不可重复读。假设现在有两个事务 A 和 B:
-
事务 A 先查询 Jay 的余额,查到结果是 100
-
这时候事务 B 对 Jay 的账户余额进行扣减,扣去 10 后,提交事务
-
事务 A 再去查询 Jay 的账户余额发现变成了 90
事务 A 被事务 B 干扰到了!在事务 A 范围内,两个相同的查询,读取同一条记录,却返回了不同的数据,这就是不可重复读。
9.2 undo log 版本链 + Read View 可见性规则
RR 隔离级别实现原理,就是 MVCC 多版本并发控制,而 MVCC 是是通过 Read View+ Undo Log 实现的,Undo Log 保存了历史快照,Read View 可见性规则帮助判断当前版本的数据是否可见。
Undo Log 版本链长这样:
Read view 的几个重要属性
-
m_ids:当前系统中那些活跃(未提交)的读写事务 ID, 它数据结构为一个 List。
-
min_limit_id:表示在生成 Read View 时,当前系统中活跃的读写事务中最小的事务 id,即 m_ids 中的最小值。
-
max_limit_id:表示生成 Read View 时,系统中应该分配给下一个事务的 id 值。
-
creator_trx_id: 创建当前 Read View 的事务 ID
Read view 可见性规则如下:
-
如果数据事务 IDtrx_id < min_limit_id,表明生成该版本的事务在生成 Read View 前,已经提交(因为事务 ID 是递增的),所以该版本可以被当前事务访问。
-
如果 trx_id>= max_limit_id,表明生成该版本的事务在生成 Read View 后才生成,所以该版本不可以被当前事务访问。
-
如果 min_limit_id =<trx_id< max_limit_id,需要分 3 种情况讨论
3.1 如果 m_ids 包含 trx_id,则代表 Read View 生成时刻,这个事务还未提交,但是如果数据的 trx_id 等于 creator_trx_id 的话,表明数据是自己生成的,因此是可见的。
3.2 如果 m_ids 包含 trx_id,并且 trx_id 不等于 creator_trx_id,则 Read View 生成时,事务未提交,并且不是自己生产的,所以当前事务也是看不见的;
3.3 如果 m_ids 不包含 trx_id,则说明你这个事务在 Read View 生成之前就已经提交了,修改的结果,当前事务是能看见的。
9.3 RR 如何解决不可重复读
查询一条记录,基于 MVCC,是怎样的流程
-
获取事务自己的版本号,即事务 ID
-
获取 Read View
-
查询得到的数据,然后 Read View 中的事务版本号进行比较。
-
如果不符合 Read View 的可见性规则, 即就需要 Undo log 中历史快照;
-
最后返回符合规则的数据
假设存在事务 A 和 B,SQL 执行流程如下
在可重复读(RR)隔离级别下,一个事务里只会获取一次 Read View,都是副本共用的,从而保证每次查询的数据都是一样的。
假设当前有一张 core_user 表,插入一条初始化数据,如下:
基于 MVCC,我们来看看执行流程
-
A 开启事务,首先得到一个事务 ID 为 100
-
B 开启事务,得到事务 ID 为 101
-
事务 A 生成一个 Read View,read view 对应的值如下
变量值 m_ids100,101max_limit_id102min_limit_id100creator_trx_id100
然后回到版本链:开始从版本链中挑选可见的记录:
由图可以看出,最新版本的列 name 的内容是孙权,该版本的 trx_id 值为 100。开始执行 read view 可见性规则校验:
min_limit_id(100)=<trx_id(100)<102;creator_trx_id = trx_id =100;
由此可得,trx_id=100 的这个记录,当前事务是可见的。所以查到是 name 为孙权的记录。
-
事务 B 进行修改操作,把名字改为曹操。把原数据拷贝到 undo log,然后对数据进行修改,标记事务 ID 和上一个数据版本在 undo log 的地址。
-
事务 B 提交事务
-
事务 A 再次执行查询操作,因为是 RR(可重复读)隔离级别,因此会复用老的 Read View 副本,Read View 对应的值如下
变量值 m_ids100,101max_limit_id102min_limit_id100creator_trx_id100
然后再次回到版本链:从版本链中挑选可见的记录:
从图可得,最新版本的列 name 的内容是曹操,该版本的 trx_id 值为 101。开始执行 read view 可见性规则校验:
min_limit_id(100)=<trx_id(101)<max_limit_id(102);因为m_ids{100,101}包含trx_id(101),并且creator_trx_id (100) 不等于trx_id(101)
所以,trx_id=101 这个记录,对于当前事务是不可见的。这时候呢,版本链 roll_pointer 跳到下一个版本,trx_id=100 这个记录,再次校验是否可见:
min_limit_id(100)=<trx_id(100)< max_limit_id(102);因为m_ids{100,101}包含trx_id(100),并且creator_trx_id (100) 等于trx_id(100)
所以,trx_id=100 这个记录,对于当前事务是可见的,所以两次查询结果,都是 name=孙权的那个记录。即在可重复读(RR)隔离级别下,复用老的 Read View 副本,解决了不可重复读的问题。
10. 你们项目使用了 RocketMQ 对吧?那你知道如何保证消息不丢失吗?
一个消息从生产者产生,到被消费者消费,主要经过这 3 个过程:
-
生产者产生消息
-
消息发送到存储端,保存下来
-
消息推送到消费者,消费者消费完,ack 应答。
因此如何保证 MQ 不丢失消息,可以从这三个阶段阐述:
-
生产者保证不丢消息
-
存储端不丢消息
-
消费者不丢消息
10.1 生产者保证不丢消息
生产端如何保证不丢消息呢?确保生产的消息能顺利到达存储端。
如果是 RocketMQ 消息中间件的话,Producer 生产者提供了三种发送消息的方式,分别是:
-
同步发送
-
异步发送
-
单向发送
生产者要想发消息时保证消息不丢失,可以:
-
采用同步方式发送,send 消息方法返回成功状态,即消息正常到达了存储端 Broker。
-
如果 send 消息异常或者返回非成功状态,可以发起重试。
-
可以使用事务消息,RocketMQ 的事务消息机制就是为了保证零丢失来设计的
10.2 存储端不丢消息
如何保证存储端的消息不丢失呢?确保消息持久化到磁盘,那就是刷盘机制嘛。
刷盘机制分同步刷盘和异步刷盘:
-
同步刷盘:生产者消息发过来时,只有持久化到磁盘,RocketMQ 的存储端 Broker 才返回一个成功的 ACK 响应。它保证消息不丢失,但是影响了性能。
-
异步刷盘:只要消息写入 PageCache 缓存,就返回一个成功的 ACK 响应。这样提高了 MQ 的性能,但是如果这时候机器断电了,就会丢失消息。
除了同步刷盘机制,还有一个维度需要考虑。Broker 一般是集群部署的,有主节点和从节点。消息到 Broker 存储端,只有主节点和从节点都写入成功,才反馈成功的 ack 给生产者。这就是同步复制,它保证了消息不丢失,但是降低了系统的吞吐量。与之对应即是异步复制,只要消息写入主节点成功,就返回成功的 ack,它速度快,但是会有性能问题。
10.3 消费阶段不丢消息
消费者执行完业务逻辑,再反馈会 Broker 说消费成功,这样才可以保证消费阶段不丢消息。
11. 事务消息是否了解?场景题:比如下单清空购物车,你是如何设计的?
事务消息主要用来解决消息生产者和消息消费者的数据一致性问题。我们先来回忆一下:一条普通的消息队列消息,从产生到被消费,经历的流程:
-
生产者产生消息,发送到 MQ 服务器
-
MQ 收到消息后,将消息持久化到存储系统。
-
MQ 服务器返回 ACk 到生产者。
-
MQ 服务器把消息 push 给消费者
-
消费者消费完消息,响应 ACK
-
MQ 服务器收到 ACK,认为消息消费成功,即在存储中删除消息。
消息队列的事务消息流程是怎样的呢?
-
生产者产生消息,发送一条半事务消息到 MQ 服务器
-
MQ 收到消息后,将消息持久化到存储系统,这条消息的状态是待发送状态。
-
MQ 服务器返回 ACK 确认到生产者,此时 MQ 不会触发消息推送事件
-
生产者执行本地事务
-
如果本地事务执行成功,即 commit 执行结果到 MQ 服务器;如果执行失败,发送 rollback。
-
如果是正常的 commit,MQ 服务器更新消息状态为可发送;如果是 rollback,即删除消息。
-
如果消息状态更新为可发送,则 MQ 服务器会 push 消息给消费者。消费者消费完就回 ACK。
-
如果 MQ 服务器长时间没有收到生产者的 commit 或者 rollback,它会反查生产者,然后根据查询到的结果执行最终状态。
我们举个下订单清空购物车的例子吧。订单系统创建完订单后,然后发消息给下游系统购物车系统,清空购物车。
-
生产者(订单系统)产生消息,发送一条半事务消息到 MQ 服务器
-
MQ 收到消息后,将消息持久化到存储系统,这条消息的状态是待发送状态。
-
MQ 服务器返回 ACK 确认到生产者,此时 MQ 不会触发消息推送事件
-
生产者执行本地事务(订单创建成功,提交事务消息)
-
如果本地事务执行成功,即 commit 执行结果到 MQ 服务器;如果执行失败,发送 rollback。
-
如果是 commit 正常提交,MQ 服务器更新消息状态为可发送;如果是 rollback,即删除消息。
-
如果消息状态更新为可发送,则 MQ 服务器会 push 消息给消费者(购物车系统)。消费者消费完(即拿到订单消息,清空购物车成功)就应答 ACK。
-
如果 MQ 服务器长时间没有收到生产者的 commit 或者 rollback,它会反查生产者,然后根据查询到的结果(回滚操作或者重新发送消息)执行最终状态。
有些伙伴可能有疑惑,如果消费者消费失败怎么办呢?那数据是不是不一致啦?所以就需要消费者消费成功,执行业务逻辑成功,再反馈 ack 嘛。如果消费者消费失败,那就自动重试嘛,接口支持幂等即可。
12. 如何快速判断一个数是奇数还是偶数,除开对 2 取余呢。
判断一个数是奇数还是偶数,我们最容易想到的就是对 2 取余。
if( x % 2 )// 奇数else// 偶数
还有一种方法,就是与 1 相与( &1),具体实现如下:
if( x & 1 )// 奇数else// 偶数
13. Spring 声明式事务原理?哪些场景事务会失效?
13.1 声明式事务原理
spring 声明式事务,即 @Transactional,它可以帮助我们把事务开启、提交或者回滚的操作,通过 Aop 的方式进行管理。
在 spring 的 bean 的初始化过程中,就需要对实例化的 bean 进行代理,并且生成代理对象。生成代理对象的代理逻辑中,进行方法调用时,需要先获取切面逻辑,@Transactional 注解的切面逻辑类似于 @Around,在 spring 中是实现一种类似代理逻辑。
13.2 spring 声明式事务哪些场景会失效
-
方法的访问权限必须是 public,其他 private 等权限,事务失效
-
方法被定义成了 final 的,这样会导致事务失效。
-
在同一个类中的方法直接内部调用,会导致事务失效。
-
一个方法如果没交给 spring 管理,就不会生成 spring 事务。
-
多线程调用,两个方法不在同一个线程中,获取到的数据库连接不一样的。
-
表的存储引擎不支持事务
-
如果自己 try...catch 误吞了异常,事务失效。
-
错误的传播
14. 你们是微服务架构嘛?如果你来设计一个类似淘宝的系统,你怎么划分微服务?
可以按业务领域、功能、重要程度进行划分。
-
可以按业务领域,把用户、社区、商品信息、消息等模块等划分。
-
单一功能职责,按功能拆分,比如订单、支付、物流、权限。
-
按重要程度划分,区分核心和非核心功能,比如支付、订单就是核心功能。
15. 你们是怎么分库分表的?分布式 ID 如何生成?
如果是我们公司的话,使用了水平分库的方式,就是一个用户注册时,就划分了属于哪个数据库,然后具体的表结构是一样的。
业界还有垂直分库,就是按照不同的系统中的不同业务进行拆分,比如拆分成用户库、订单库、积分库、商品库,把它们部署在不同的数据库服务器。
分表的话也有水平分表和垂直分表,垂直分表就是将一些不常用的、数据较大或者长度较长的列拆分到另外一张表,水平分表就是可以按照某种规则(如 hash 取模、range),把数据切分到多张表去。一张订单表,按时间 range 拆分如下:
range 划分利于数据迁移,但是存在数据热点问题。hash 取模划分,不会存在明显的热点问题,但是不利于扩容。可以 range+hash 取模结合使用。
分布式 ID 可以使用雪花算法生成
雪花算法是一种生成分布式全局唯一 ID 的算法,生成的 ID 称为 Snowflake IDs。这种算法由 Twitter 创建,并用于推文的 ID。
一个 Snowflake ID 有 64 位。
-
第 1 位:Java 中 long 的最高位是符号位代表正负,正数是 0,负数是 1,一般生成 ID 都为正数,所以默认为 0。
-
接下来前 41 位是时间戳,表示了自选定的时期以来的毫秒数。
-
接下来的 10 位代表计算机 ID,防止冲突。
-
其余 12 位代表每台机器上生成 ID 的序列号,这允许在同一毫秒内创建多个 Snowflake ID。
16. 所有异常的共同的祖先是?运行时异常有哪几个?
Java 异常的顶层父类是 Throwable,它生了两个儿子,大儿子叫 Error,二儿子叫 Exception。
-
Error:是程序⽆法处理的错误,一般表示系统错误,例如虚拟机相关的错误 OutOfMemoryError
-
Exception:程序本身可以处理的异常。它可以分为 RuntimeException(运行时异常)和 CheckedException(可检查的异常)。
什么是 RuntimeException(运行时异常)?
运行时异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
常见的 RuntimeException 异常:
-
NullPointerException:空指针异常
-
ArithmeticException:出现异常的运算条件时,抛出此异常
-
IndexOutOfBoundsException:数组索引越界异常
-
ClassNotFoundException:找不到类异常
-
IllegalArgumentException(非法参数异常)
小伙伴们有兴趣想了解内容和更多相关学习资料的请点赞收藏+评论转发+关注我,后面会有很多干货。
****************************************************************