异常
- 指的是java程序运行期间出现的不正常情况,导致jvm终止程序的运行
java是面向对象的语言,产生的每个异常其实都是一个异常对象,每个异常对象一定会有所属的异常类
- 常见的异常类有:
-ClassCastException
NullPointerException
ArrayIndexOutOfBoundsException
ArithmeticException
java中默认将异常抛给jvm处理,而jvm处理的方式就是中断运行,将异常信息输出到控制台
-
Throwable类: 是 Java 语言中所有错误或异常类的父类
- Error类(错误): 表示错误,不可以通过代码进行纠正使得程序继续运行,只能事先避免
- Exception类(异常):表示异常,可以通过代码进行纠正使得程序继续运行 -
异常分类
- 编译异常: 程序在编译期间出现的异常,如果不处理,程序无法通过编译
- 运行异常: 程序在运行期间出现的异常,如果不处理,程序可以通过编译,但在运行的时候会出现异常
异常的产生和处理
- throw关键字的作用
- 在java中,提供了一个throw关键字,它用来抛出一个指定的异常对象
- throw用在方法内,来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行
//产生异常并抛出
throw new ArrayIndexOutOfBoundsException();
声明处理异常_
- 使用throws关键字将问题标识出来, 表示当前方法不处理异常,而是提醒给调用者, 让调用者来处理
- throws可以一次抛一个或多个异常
- 使用声明处理异常,处理完后,如果程序运行期间没有出现异常,程序可以继续往下执行
- 使用声明处理异常,处理完后,如果程序运行期间有出现异常,程序不可以继续往下执行
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Text {
public static void main(String[] args) throws ParseException {
// 编译异常,声明处理
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse("2000-0-0");
System.out.println(date);
// 运行异常
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日");
Date date2 = sdf2.parse("2000-0-0");
System.out.println(date2);
}
}
声明处理异常用来处理编译异常
捕获处理异常
- 捕获处理异常也是一种处理异常的方式,这种处理异常的方式处理完异常后,无论程序是否发生异常,程序都可以继续往下执行
格式:
try{
// 可能会发生异常的代码
}catch(异常类型 变量名){
// 打印异常的信息,发生异常后需要执行的代码
}
- 注意:
- try,catch都不能单独使用
- try中的代码如果发生了异常,try中发生异常位置之后的代码就不执行了 - 执行流程:
- 首先执行try中的代码
- 如果try中的代码发生了异常,就会执行catch里面的代码,执行完catch里面的代码后,程序继续往下执行
- 如果try中的代码没有发生异常,就不会执行catch里面的代码,而是继续往下执行 - 获取异常信息:
Throwable类
- public String getMessage():获取异常的描述信息,原因(提示给用户的时候,就提示错误原因
- public void printStackTrace():打印异常的跟踪栈信息并输出到控制台
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Text {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日");
try{
Date date2 = sdf2.parse("2000-0-0");
}catch (ParseException e){
// 获取异常的描述信息,原因(提示给用户的时候,就提示错误原因
e.getMessage();
// 打印异常的跟踪栈信息并输出到控制台
e.printStackTrace();
}
}
}
finally
- 因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码在正常情况下一定会被执行的
try{
//可能会出现异常的代码
}catch(异常的类型 变量名){
//处理异常的代码或者打印异常的信息
}finally{
//无论异常是否发生,都会执行这里的代码 (正常情况,都会执行finally中的代码,一般用来释放资源)
}
异常注意事项_
- try/catch/finally都不可以单独使用
- 运行时异常被抛出可以不处理(不捕获也不声明抛出)
- 在try/catch后可以追加finally代码块,其中的代码一定会被执行,通常用于资源回收 (特殊条件下不会执行)
方法重写时的注意事项_
- 父类的方法抛出异常,子类覆盖(重写)父类方法时,只能抛出相同的异常或该异常子集
- 父类的方法未抛出的异常,子类覆盖(重写)父类方法时,只能处理,不能抛出
自定义异常
- 自定义异常类分类
- 自定义编译期异常: 自定义类 并继承于java.lang.Exception
- 自定义运行时期异常:自定义类 并继承于java.lang.RuntimeException
// 编译异常
public class MyException1 extends Exception {
}
// 运行异常
public class MyException2 extends RuntimeException {
}
多线程
并行:指两个或多个事件在同一时刻发生(同时执行)
并发:指两个或多个事件在同一个时间段内发生(交替执行)
进程与线程_
进程:进程是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程
进程是应用程序的可执行单元
一个应用程序可以有多个进程
每个进程执行都会有独立的内存空间
线程:是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一条线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序
- 线程是进程的可执行单元
- 一个进程可以有多条线程
- 每个线程执行都会有独立的内存空间
java只有单进程,然后有多线程
一个进程一次只能执行一条线程,所以java中只有多线程并发,没有多线程并行
线程的调度_
- 分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间
- 抢占式调度:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性)
Java线程的调度方式: 抢占式
Thread类
- java.lang.Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例
构造方法_
public Thread(Runnable target)
:创建一个带有指定任务的线程对象,通过参数Runnable指定任务public Thread(Runnable target,String name)
:创建一个带有指定任务的线程对象并指定线程名字
常用方法_
public String getName()
:获取当前线程名称public void start()
:导致此线程开始执行 (Java虚拟机调用此线程的run方法)public void run()
:此线程要执行的任务在此处定义代码public static void sleep(long millis)
:使当前正在执行的线程以指定的毫秒数暂停执行public static Thread currentThread()
:返回对当前正在执行的线程对象的引用
继承方式创建线程_
class MyThread extends Thread{
@Override
// 重写run() 方法
public void run() {
System.out.println("通过继承方式来创建线程");
}
}
public class Text {
public static void main(String[] args){
// 输出 通过继承方式来创建线程
new MyThread().start();
}
}
实现方式创建线程_
- Runnable是一个任务接口,里面有一个抽象方法run(),可以在run方法中书写线程的任务代码
class MyThread implements Runnable{
@Override
public void run() {
System.out.println("通过实现类方式来创建线程");
}
}
public class Text {
public static void main(String[] args){
// 输出 通过实现类方式来创建线程
new Thread(new MyThread()).start();
}
}
匿名内部类方式_
public class Text {
public static void main(String[] args){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("通过匿名内部类方式来创建线程");
}
}).start();
// 输出 通过匿名内部类方式来创建线程
}
}
创建并启动多条线程_
// 用实现的方式
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "创建了一条线程");
}
}
public class Text {
public static void main(String[] args) {
new Thread(new MyThread(),"窗口1").start(); // 输出 窗口1创建了一条线程
new Thread(new MyThread(),"窗口2").start(); // 输出 窗口2创建了一条线程
new Thread(new MyThread(),"窗口3").start(); // 输出 窗口3创建了一条线程
new Thread(new MyThread(),"窗口4").start(); // 输出 窗口4创建了一条线程
}
}
实现方式创建线程的优势_
- 实现Runnable接口比继承Thread类所具有的优势
适合多个相同的程序代码的线程去共享同一个资源(任务)
可以避免java中的单继承的局限性
增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立
线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类