异常
- 程序在运行期间出现问题
Throwable包括Error(非常严重的问题)和Exception(可以解决的问题),两个类
Exception:编译期异常
RuntimeException:运行期异常,异常处理掉,程序继续运行
Error;无法治愈的毛病,必须修改代码
- 抛出异常,中断运行
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd")
Date date = sdf.parse("1999-09-09")
System.out.println(date)
}
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd")
Date date = null
try {
date = sdf.parse("1999-09-09")
} catch (ParseException e) {
e.printStackTrace()
}
System.out.println(date)
}
int[] arr = {1,2,3};
try {
System.out.println(arr[3]);
} catch (Exception e) {
System.out.println(e);
}
异常的产生与处理
- 异常的产生

throw,
- 可以在指定的方法中抛出指定的异常,必须写在方法的内部
- new的对象必须是
Exception或者其子类
- 必须处理抛出的异常,如果后面创建的是
RuntimeException或者子类,可以不处理,直接交给JVM;如果创建的是编译异常,则必须要处理
- 对参数进行合法性校验,如果传递的参数有问题,要抛出异常
public static int getElement(int[] arr, int index) {
if (arr == null) {
throw new NullPointerException("传递的数组是空");
}
if (index < 0 || index > arr.length - 1) {
throw new ArrayIndexOutOfBoundsException("索引越界");
}
int i = arr[index];
return i;
}
Objects非空判断
method(null);
public static void method(Object o) {
Objects.requireNonNull(o,"传递的对象是空");
}
throws
- 声明异常
- 当方法内部抛出异常的时候,就必须处理,通过throws处理异常对象
- 必须写在方法声明处
- 后面声明的异常必须是
Exception或者其子类
- 如果抛出多个异常,后面也必须声明多个异常,如果异常对象有子父类关系,直接声明父类异常即可
public static void main(String[] args) throws IOException {
readFile("/User/apple/test");
}
public static void readFile(String path) throws IOException {
if (!path.equals("/User/apple/test.doc")) {
throw new FileNotFoundException("文件路径错误");
}
if (!path.endsWith(".doc")) {
throw new IOException("文件后缀名不同");
}
System.out.println("读取文件");
}
try..catch处理异常
- try中可以抛出多个异常对象,可以用多个catch处理这些异常对象
- 如果try中产生异常,执行catch中的异常处理逻辑,继续执行;如果没有产生异常,则不执行catch中的代码
public static void main(String[] args) throws IOException {
try {
readFile("/User/apple/test");
} catch (Exception e) {
System.out.println(e);
}
System.out.println("后续代码");
}
public static void readFile(String path) throws IOException {
if (!path.equals("/User/apple/test")) {
throw new FileNotFoundException("文件路径错误");
}
if (!path.endsWith(".doc")) {
throw new IOException("文件后缀名不同");
}
System.out.println("读取文件");
}
Throwable
System.out.println(e.getMessage());
System.out.println(e.toString());
at com.mzx.java.DemmThrowable.Demo3Main.readFile(Demo3Main.java:26)
at com.mzx.java.DemmThrowable.Demo3Main.main(Demo3Main.java:9)
e.printStackTrace();
finally代码块
- 无论是否出现异常都能执行
- 不能单独使用,必须和try一起使用
- 不要在finally中写return
- 一般用于资源释放(资源回收),无论程序是否出现异常,最后都要资源释放(IO)
try {
readFile("/User/apple/test");
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("资源释放");
}
多个异常
- 多个异常分别处理,几个异常有几个
try...catch
- 多个异常一次捕获,多次处理,多个
catch中的异常如果有子父类关系,子类必须写在上面;如果try中出现异常,会把异常对象抛给catch处理,会从上到下依次赋值给catch处理,如果父类在上面,下面的子类就是废话
try {
int[] arr = {1,2,3}
//System.out.println(arr[3])
List<Integer> integers = List.of(1, 2, 3)
System.out.println(integers.get(3))
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println(e)
} catch (IndexOutOfBoundsException e) {
System.out.println(e)
}
System.out.println("后续代码")
try {
int[] arr = {1,2,3}
//System.out.println(arr[3])
List<Integer> integers = List.of(1, 2, 3)
System.out.println(integers.get(3))
} catch (IndexOutOfBoundsException e) {
System.out.println(e)
}
System.out.println("后续代码")
- 运行时的异常被抛出可以不处理,既不捕获也不声明抛出,默认给虚拟机
JVM处理,终止程序
注意事项
- 父类异常是什么样,子类异常就是什么样
- 子类重写父类方法,如果父类方法抛出异常,子类抛出异常/子类异常/不抛出异常
- 如果父类方法不抛出异常,子类重写方法产生异常,不能抛出,只能
try...catch处理
自定义异常
- 继承
Exception或者RuntimeException
public class RegisterException extends Exception{
public RegisterException() {
super();
}
public RegisterException(String message) {
super(message);
}
}
多线程
- java属于抢占式调度,哪个线程的优先级高,就优先哪个线程,同一个优先级,随机选择执行
- 并发,指两个或多个事件在同一个时间段发生,交替执行;并行,两个或多个在同一时刻发生,同时执行
- 主线程,执行主方法的路径。JVM执行main方法,main方法会进入栈内存,JVM找操作系统开辟一条main方法通向CPU的执行路径,cpu通过这个路径执行main方法
Thread
- 多次启动一个线程是非法的,特别是当线程已经结束之行后,不能再重新启动
- 创建
Thread,重写run方法
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
for (int i = 0; i < 20; i++) {
System.out.println("main - " + i);
}
}
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("MyThread - " + i);
}
}
}
- 随机执行的原理,当有了一个新线程后,cpu就有了一条新的执行路径,在主线程与新线程之间随机选择执行
- 内存原理

常用方法
System.out.println(new MyThread().getName());
System.out.println(Thread.currentThread().getName());
MyThread mt = new MyThread();
mt.setName("xiaoming");
public MyThread(String name) {
super(name);
}
for (int i = 0
System.out.println(i)
Thread.sleep(1000)
}
Runnable
- 由打算通过某一线程执行其实例的类来实现,必须定义
run方法
public class RunnableImpl implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + " - " + i);
}
}
}
public static void main(String[] args) {
RunnableImpl run = new RunnableImpl();
Thread t = new Thread(run);
t.start();
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + " - " + i);
}
}
与Thread的区别
Runnable避免了单继承的局限性,实现了Runnable接口还能继承其他的类
- 增强了程序的扩展性,把设置线程任务和开启新线程进行分离,用
Runnable接口决定任务,用Thread的start方法,开启新线程
匿名内部类创建
- 简化代码,把子类继承父类,重写父类的方法,创建子类的对象合一步完成;把实现类接口,重写接口中的方法,创建实现类对象合成一步完成
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
},"lisi").start();
new Thread("zhangsan") {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}.start();
线程安全
- 原理,多个线程同时操作某一个变量或者一段代码,当一个线程在操作变量,而同时又被另一个线程操作,导致变量结果异常

- 让一个线程在访问共享数据的时候,无论是否失去了cpu的执行权,让其他线程保持等待,执行完后,让其他线程访问
同步代码块
- 放需要同步的代码块
- 同步代码块的锁对象,可以使用任意的对象,但是必须保证多个线程使用的锁对象是同一个
- 锁对象的作用,把同步代码块锁住,只让一个线程在同步代码块中执行
Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 正在卖出第 " + ticket + " 张票");
ticket --;
} else break;
}
}
}
- 原理,当一个线程检测
synchronized代码块,发现有锁对象,进入执行,另一个线程访问时,发现没有锁对象,等待;等待第一个线程执行完代码,归还锁对象后,才能进入;同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁进不去同步
- 同步,保证了只能有一个线程在同步中执行共享数据,但是导致程序频繁地判断锁,降低效率
同步方法
public synchronized void payTicket() {
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 正在卖出第 " + ticket + " 张票");
ticket --;
};
}
- 静态同步方法,锁对象是本类的
class属性,class文件对象的反射
public static synchronized void payTicketStatic() {
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 正在卖出第 " + ticket + " 张票");
ticket --;
};
}
synchronized (RunnableImpl.class) {
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 正在卖出第 " + ticket + " 张票");
ticket --;
}
}
Lock
Lock l = new ReentrantLock();
@Override
public void run() {
while (true) {
l.lock();
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 正在卖出第 " + ticket + " 张票");
ticket --;
} else {
break;
}
l.unlock();
}
}
- 更好的写法,配合
try...catch...finally,即使释放锁
Lock l = new ReentrantLock();
@Override
public void run() {
while (true) {
l.lock();
if (ticket > 0) {
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + " 正在卖出第 " + ticket + " 张票");
ticket --;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
l.unlock();
}
} else {
l.unlock();
break;
}
}
}
线程状态

- 无限等待状态,一个正在无限等待另一个线程执行一个特别的(唤醒)动作的线程,线程之间的通信
- 消费者/生产者模型
Object obj = new Object();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (obj) {
System.out.println("告知老板要的包子种类");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("收到包子");
System.out.println("------------------");
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj) {
System.out.println("老师在5s之后做好了");
obj.notify();
}
}
}
}).start();
- 计时等待,线程睡醒,进入
Runnable/Blocked状态
obj.wait(50)
Thread.sleep(5000)
obj.notify()
obj.notifyAll()
等待唤醒机制
- 多个线程并发执行,需要线程操作一份数据,有规律地执行,需要线程之间的通信
wait和nofiy
- wait等待,notify唤醒,唤醒等待时间最长的
- 必须在同步代码块,或者同步方法中使用
- 锁对象可以是任意对象,任意对象都可以调用,必须要由同一个锁对象调用
线程池
ExecutorService threadPool = Executors.newFixedThreadPool(2);
threadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
threadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
threadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
threadPool.shutdown();
Lambda表达式
- 函数式思想,强调做什么
- 以
Runnable为例,类似闭包
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}).start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName());
}).start();
格式
public static void invokeCook(Cook cook) {
cook.makeFood();
}
invokeCook(new Cook() {
@Override
public void makeFood() {
System.out.println("吃饭了");
}
});
invokeCook(() -> {
System.out.println("吃饭了");
});
Person[] arr = {
new Person("xiaoming", 18),
new Person("zhangsan", 17),
new Person("lisi", 20),
new Person("wangwu", 14),
};
Arrays.sort(arr, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o2.getAge() - o1.getAge();
}
});
for (Person person : arr) {
System.out.println(person);
}
System.out.println("------------");
Arrays.sort(arr, (Person o1, Person o2) -> {
return o1.getAge() - o2.getAge();
});
for (Person person : arr) {
System.out.println(person);
}
public static void invokeCalculator (int a, int b, Calculator calculator) {
System.out.println(calculator.add(a,b));
}
invokeCalculator(3, 4, new Calculator() {
@Override
public int add(int a, int b) {
return a + b;
}
});
invokeCalculator(3,4,(int a, int b) -> {
return a + b;
});
优化
- 凡是可以根据上下文推导出来的内容,都可以省略不写
- 参数列表,括号中的参数列表的数据类型,可以省略不写;括号中的参数如果是有一个,那么类型和()都可以省略
- 方法体,如果{}中的代码只有一行,无论是否有返回值,都可以省略({}、return、分号),要一起省略
- 无参数
new Thread( () -> System.out.println(Thread.currentThread().getName())).start();
Arrays.sort(arr, (o1, o2) -> o1.getAge() - o2.getAge());
public static String invokeStudent(String name, Student student) {
return student.name(name);
}
System.out.println(invokeStudent("zhangsan", name -> name));
前提
- 必须要有接口,而且接口中只有一个抽象方法
- 必须要有上下文推断
- 函数式接口,有且只有一个抽象方法的接口