1.同名不同参数名之间的方法之间互相称为重载,而重写是指子类重新构造和父类一摸一样的方法

2.重载是编译期确定下来的要使用哪个方法,而重写是运行时绑定的,即看对象引用指向的具体对象类型

只有返回类型不同,可以被叫做重载吗

不是,编译期会报错的

方法签名是由:方法名称 + 参数类型 + 参数个数组成的一个唯一值,这个唯一值就是方法签名,而 JVM

(Java 虚拟机)就是通过这个方法签名来决定调用哪个方法的

如果把返回类型也包含进去,那就不知道调用哪个了

运行时异常:

都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。

运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。

非运行时异常(编译异常):

是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。如IOException、SQLException等

throw:

如果代码可能会引发某种错误,可以创建一个合适的异常类实例并抛出它,这就是抛出异常。

throws:

在Java中,当前执行的语句必属于某个方法,Java解释器调用main方法执行开始执行程序。若方法中存在检查异常,如果不对其捕获,那必须在方法头中显式声明该异常,以便于告知方法调用者此方法有异常,需要进行处理。 在方法中声明一个异常,方法头中使用关键字throws,后面接上要声明的异常。若声明多个异常,则使用逗号分割。

try-catch底层?

class字节码指令中会有异常表,表示哪一行到哪一行的代码可能有什么异常:

  • from 可能发生异常的起始点
  • to 可能发生异常的结束点
  • target 上述from和to之前发生异常后的异常处理者的位置
  • type 异常处理者处理的异常的类信息

什么时候不走finally?

try或catch中进入了死循环

虚拟机退出

守护线程中可能不会走finally就被回收了

finally的执行顺序

finally正常情况下都会被执行的

如果finally里有return语句,那么它就是整个try-catch-finally结构的返回结果

若没有,就得看try or catch里的return语句了

程序出现了异常会发生什么?

运行时异常:

  • 被try-catch捕获:JVM会将异常对象交给对应的catch处理,异常不会向上抛出(catch块中没有继续抛),处理完后,线程仍然继续执行代码
  • 在方法声明上被抛出:交给上一级的方法进行处理,直到找到能处理该异常的catch块,但运行时异常一般没必要写方法声明上抛出,因为不写同时也不对其try-catch捕获的话,默认就是向上一级方法抛出
  • 无法被处理:没有相应catch块处理,该异常会被交给线程的异常处理器,如果没设置异常处理器,那就走JVM默认的异常处理器,即线程终止,标准输出打印信息,即使有了异常处理器去处理,在处理器处理完后,抛异常的线程仍然会被终止,所以想让线程抛异常后还能继续执行下去,只能用try-catch捕捉异常

非运行时异常:

在编译时就必须被处理,所以和运行时异常走一样的逻辑

如何设置异常处理器?

为特定线程设置处理器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Thread thread = new Thread(() -> { 
// 线程的运行代码
});

thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
// 处理未捕获的异常
System.out.println("线程 " + t.getName() + " 发生异常: " + e.getMessage());
}
});

thread.start();
为所有线程设置默认处理器:
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
// 处理未捕获的异常
System.out.println("线程 " + t.getName() + " 发生异常: " + e.getMessage());
}
});

案例:

异常被捕获,仍然执行代码,抛异常之前的代码都能正常执行:

打印:线程仍然执行,i = 10b = 9

定义异常处理器处理异常:

打印:

![](/images/e353185520d693ae246972e875b7ab13.png)

OutOfMemoryError

StackOverflowError

NoClassDefFoundError

封装

就是利用一些抽象数据结构把数据和基于数据的操作封装成一个“黑盒子”,用户使用时不需要关注具体实现

直接使用就好了

继承

就是用来表示is-a的关系,比如父类是动物,子类是猫,就是用来表示这种关系。子类有着父类的全部属性和

方法。如果子类新增方法,那就是is-like-a关系

多态

同一方法在不同对象上有不同表现

多态的话我觉得就只在运行时才具有,如重写

即对象引用指向的具体类型只有在运行时才会被确定下来

要想实现多态就得满足以下条件

1.有接口实现or类继承

2.子类重写父类的方法

3.父类的引用指向子类的对象

组合

has-a关系,一个类的成员变量是其他类的对象

组合更适合面向接口开发,而且更加简单和灵活,相对于继承,能用组合就用组合

继承得有明确的is-a关系

如何扩容

1.检查加入新元素后是否会超过数组的容量,如果超过,就进行一次扩容

2.设置新的容量为老容量的1,5

最多不超过2^31-1 (Java 8中ArrayList的容量最大是Integer.MAX_VALUE - 8,即2^31-9。这是由于在Java 8中,ArrayList内部实现进行了一些改进,使用了一些数组复制的技巧来提高性能和内存利用率,而这些技巧需要额外的8个元素的空间来进行优化。)

3.申请一个容量为老容量的1.5倍的新数组,然后把原数组的元素复制上去

ArrayList的序列化

数据库死锁是指多个并发事务中,出现了彼此相互等待的情况,导致所有事务卡在那里了,无法继续执行

原因:其实就是长时间无法获取想要的资源且无法放弃获取

如何解决?

大部分的现在数据管理系统都有自动干预功能,即可以选择多个or一个事务回滚来释放锁

还能手动强制回滚

mysql自己也能解决死锁

1.定期检测死锁机制,检测到死锁后,MySQL会自动选择终止一个or多个事务来释放锁

2.设置事务持有锁的超时时间(InnoDB_lock_wait_timeout)。即如果事务持有锁的时间超过这个阈值

就会对这个事务进行回滚

长事务

IO是什么?

从计算机结构来讲,I就是Input,o就是output,那么IO描述的就是描述计算机系统与外部设备通信的过程

如输入设备(键盘),输出设备(显示屏)就是外部设备,网卡和硬盘也是外部设备

从应用程序角度来说,IO是指应用程序通过系统调用请求操作系统内核空间执行与“外部设备的交互”的过程

当应用程序发起IO调用后,会经历两个阶段

1.内核等待IO设备准备好数据

2.内核把数据从内核空间拷贝到用户空间

UNIX下的五种IO模型

UNIX 系统下, IO 模型一共有 5 种:同步阻塞 I/O同步非阻塞 I/OI/O 多路复用信号驱动 I/O异步 I/O

jtea/linux/五种IO模型.md at master · jmilktea/jtea

Java的三种IO模型

阻塞IO

应用程序发起IO调用后,会一直阻塞,直到成功拿到数据

连接一多起来,每来一次连接就得开一个线程,费内存。且线程阻塞住,不能去干其他事情

NIO 非阻塞IO

同步非阻塞

应用程序轮询 询问内核数据是否就绪,避免了一直阻塞。但不断轮询是很耗CPU资源的

IO多路复用

有个重要角色selector会去监听客户端的channel,然后数据准备好了就主动通知客户端可以来读数据了

实现了一个线程就可以监听多个Client的功能

目前支持IO多路复用的系统调用有三种:select(几乎所有的系统都支持),poll,epoll

异步IO