Java异常
Java异常时java提供的一种识别及响应错误的一执行机制
Java异常机制可以是程序中的异常处理代码和正常业务代码分离
Java异常机制用的关键字
Try catch finally throw throws
• try – 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
• catch – 用于捕获异常。catch用来捕获try语句块中发生的异常。
• finally – finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
• throw – 用于抛出异常。
• throws – 用在方法签名中,用于声明该方法可能抛出的异常
(3)了解throws和throw的基本用法
throws是用于声明抛出的异常,而throw是用于抛出异常。
MyException是继承于Exception的子类。test()的try语句块中产生ArithmeticException异常(除数为0),并在catch中捕获该异常;接着抛出MyException异常。main()方法对test()中抛出的MyException进行捕获处理。
Objects非空判断
* `public static <T> T requireNonNull(T obj)`:查看指定引用对象不是null。
**声明异常**:将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws进行声明,让调用者去处理。
关键字**throws**运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).
**声明异常格式:**
~~~
修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{ }
~~~
Throws用于进行异常类的声明,若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开
捕获异常try..catch 如果程序抛出异常我们会利己处理异常
try{
编写可能会出现异常的代码
}catch(异常类型 e){
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}
**finally**:有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。
多个异常使用捕获又该如何处理呢?
1. 多个异常分别处理。
2. 多个异常一次捕获,多次处理。
3. 多个异常一次捕获一次处理。
一般我们是使用一次捕获多次处理方式,格式如下:
```java
try{
编写可能会出现异常的代码
}catch(异常类型A e){ 当try中出现A类型异常,就用该catch来捕获.
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}catch(异常类型B e){ 当try中出现B类型异常,就用该catch来捕获.
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}
```
> 注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。
* 运行时异常被抛出可以不处理。即不捕获也不声明抛出。
* 如果finally有return语句,永远返回finally中的结果,避免该情况.
* 如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。
* 父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出
我们说了Java中不同的异常类,分别表示着某一种具体的异常情况,那么在开发中总是有些异常情况是SUN没有定义好的,此时我们根据自己业务的异常情况来定义异常类。例如年龄负数问题,考试成绩负数问题等等。
在上述代码中,发现这些异常都是JDK内部定义好的,但是实际开发中也会出现很多异常,这些异常很可能在JDK中没有定义过,例如年龄负数问题,考试成绩负数问题.那么能不能自己定义异常呢?
**什么是自定义异常类:**
在开发中根据自己业务的异常情况来定义异常类.
自定义一个业务逻辑异常: **RegisterException**。一个注册异常类。
**异常类如何定义:**
1. 自定义一个编译期异常: 自定义类 并继承于`java.lang.Exception`。
2. 自定义一个运行时期的异常类:自定义类 并继承于`java.lang.RuntimeException`。
多线程
并行和并发
并发值两个或多个事件在同一时间内发生
并行指两个或多个事件在同一时刻发生
4.3 创建线程类
Java使用`java.lang.Thread`类代表**线程**,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。Java中通过继承Thread类来**创建**并**启动多线程**的步骤如下:
1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
2. 创建Thread子类的实例,即创建了线程对象
3. 调用线程对象的start()方法来启动该线程
构造方法
Thread()分配一个新的线程对象
Thread(string name)分配一个指定名称的新的线程对象
Thread(runnable target)分配一个带有指定目标的新的线程对象
Thread(runnable target,string name)分配一个带有指定目标的新的线程对象并指定名字
常用方法
getName()获取当前线程名称
start()导致该线程开始执行,java虚拟机调用该线程的run方法
run()此线程要执行的任务在此处定义代码
sleep(long millis)是当前正在执行的线程以毫秒数暂停
currentThread()返回正在执行的线程对象的引用
第二种创建线程的方式实现runnable接口
步骤:
定义runnable接口的实现类,并重写该方法的run()方法run()方法同样是线程要执行的代码体
床架runnable实现类的实例,并将此实例作为作为thread的target来创建thread对象,该thread才是真正的线程对象
Start()执行
Thread和runnable的区别
如果一个类继承thread,则不适合资源共享,如果实现runnable接口,则很容易实现资源共享
实现runnable接口比继承thread类所具有的优势
- 适合多个相同的程序代码的线程共享一个资源
- 避免java单继承的局限性增加程序的奖状性
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立
- 线程池只能实现runable或callable类线程,不能直接放入继承的Thread类
匿名内部类实现线程的创建
线程安全
如果多个线程同时运行,并且代码是同一个程序每次运行结果和单个的结果是一样的,而其他变量的值也和预期是一样的,这就是线程安全
当使用多个线程访问同一资源时,且多个线程对资源有写的操作是就容易出现线程安全问题,java提供了同步机制synchronized来解决
- 同步代码块
- Synchronized(同步锁){
- 需要进行同步操作的代码
- }
- Synchronized(同步锁){
1锁对象可以是任意类型
多个线程对象使用同一把锁
- 同步方法
- 使用synchronized修饰的方法就叫同步方法,在线程执行该方法的时候其他线程只能在外等待
- lock锁
- public void lock()加同步锁
- public void unlock()释放同步锁
线程状态
New()新建
Runnable()可运行
Blocked()锁住塞
Waiting()无线等待
Timed waiting()计时等待
Teminated()被终止
Timed waiting()计时等待
常用的是调用sleep()方法,
Blocked()锁住塞
根据同步机制,当线程A和线程B采用的是同一个锁南无如果线程A获取到锁静茹runnable状态,那么线程B就会进入所阻塞状态
Waiting()无线等待
一个线程调用了摸个对象的object.wait()方法就会等待另一个线程调用此对象的objectnotify()方法,或者object.notifyAll()方法体现为多个线程的相互通信
就是在一个线程进行了规定操作后,就进入等待状态(**wait()**), 等待其他线程执行完他们的指定代码过后 再将其唤醒(**notify()**);在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程。
wait/notify 就是线程间的一种协作机制。
线程池
一个可以容纳多个线程的容器其中的线程可以反复使用,省去了创建线程对象的操作
1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
使用线程池中线程对象的步骤:
1. 创建线程池对象。
2. 创建Runnable接口子类对象。(task)
3. 提交Runnable接口子类对象。(take task)
4. 关闭线程池(一般不做)。
Runnable实现类代码:
~~~java
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我要一个教练");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("教练来了: " + Thread.currentThread().getName());
System.out.println("教我游泳,交完后,教练回到了游泳池");
}
}
~~~
线程池测试类:
~~~java
public class ThreadPoolDemo {
public static void main(String[] args) {
// 创建线程池对象
ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
// 创建Runnable实例对象
MyRunnable r = new MyRunnable();
//自己创建线程对象的方式
// Thread t = new Thread(r);
// t.start(); ---> 调用MyRunnable中的run()
// 从线程池中获取线程对象,然后调用MyRunnable中的run()
service.submit(r);
// 再获取个线程对象,调用MyRunnable中的run()
service.submit(r);
service.submit(r);
// 注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。
// 将使用完的线程又归还到了线程池中
// 关闭线程池
//service.shutdown();
}
}
~~~
Lambda表达式
函数式编程思想
这个`RunnableImpl`类只是为了实现`Runnable`接口而存在的,而且仅被使用了唯一一次,所以使用匿名内部类的语法即可省去该类的单独定义,即匿名内部类:
```java
public class Demo04ThreadNameless {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多线程任务执行!");
}
}).start();
}
}
```
Lambda的语法非常简洁,完全没有面向对象复杂的束缚。但是使用时有几个问题需要特别注意:
1. 使用Lambda必须具有接口,且要求**接口中有且仅有一个抽象方法**。
无论是JDK内置的`Runnable`、`Comparator`接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。
2. 使用Lambda必须具有**上下文推断**。
也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
> 备注:有且仅有一个抽象方法的接口,称为“**函数式接口**”。