本帖最后由 xiekai_sjz 于 2018-9-18 21:40 编辑
day05 异常 多线程 今天我们主要学习异常,主要是异常的分类,如何处理异常以及自定义异常。然后我们需要了解一些线程相关的概念。
以下是今天的学习目标:
- 能够辨别程序中异常和错误的区别
- 说出异常的分类
- 说出虚拟机处理异常的方式
- 列举出常见的三个运行期异常
- 能够使用try...catch关键字处理异常
- 能够使用throws关键字处理异常
- 能够自定义异常类
- 能够处理自定义异常类
- 说出进程的概念
- 说出线程的概念
- 能够理解并发与并行的区别
- 能够开启新线程
以下是今天的详细笔记:
异常异常的概念和体系
异常: 指的是程序在执行过程中, 出现的非正常的情况, 最终会导致JVM的非正常停止
注意:
在Java中, 每种异常都有对应的类来描述, 发生了一个异常, 就是出现了这个异常类的"对象"
异常不是语法错误
// 异常的体系结构
java.lang.Throwable // 体系最顶层
|_ Error // 不应该试图捕获的严重问题, 不能处理的错误
|_ Exception // 可以处理的异常
补充:
// 异常信息详解:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
at java.util.HashMap$KeyIterator.next(Unknown Source)
at com.itheima.practice_08.MapTest.main(MapTest.java:31)
解释:
Exception in thread "main": 在名为"main"的线程发生异常
java.util.ConcurrentModificationException: 异常的具体类型, 这里是并发修改异常
at xxx.xxx.xxx: 异常发生的位置, 因为层层方法调用, 所以会出现多个类的方法, 这些方法组成了异常链
只要不是我们的包名, 一般不用看. 是我们的包名, 才看
at com.itheima.practice_08.MapTest.main(MapTest.java:31):
说明这个异常, 发生在com.itheima.practice_08下的MapTest类的main方法中, 具体是MapTest.java文件中的第31行
一般 (MapTest.java:31) 这一部分是可以鼠标点击的, 点击后就直接跳转到报错的这一行
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.itheima.practice_01.ExceptionTest.main(ExceptionTest.java:9)
解释:
/ by zero: 是异常的说明信息, 有的异常有说明信息, 有的没有
其他和上例一样
异常的分类
// 异常的体系结构
java.lang.Throwable // 体系最顶层
|_ Error // 错误
|_ Exception // 编译时异常
|_ RuntimeException // 运行时异常
错误(Error): 不能捕获处理的严重问题. 绝症
必须将程序停下来, 修改代码才能解决
错误的类名都是 "XxxError" 方式
异常(Exception): 可以捕获处理的问题. 发烧感冒吃个药就好了
程序执行起来后, 如果有合适的处理方式, 即使发生异常, 程序也能处理该异常并继续运行
异常的类名都是 "XxxException" 方式
1. 编译时异常:
编译时期就会发生的异常, 必须在编译时期处理 Unhandled exception XxxException
2. 运行时异常:
编译时正常, 运行时才会发生的异常
异常的产生过程解析
异常产生的过程:
1. 当执行的代码发生异常操作时, JVM会创建一个对应的异常类对象, 包含异常的内容, 原因, 位置
如 new ArrayIndexOutOfBoundsException(), new NullPointerException()
2. 如果执行代码的方法没有对异常进行 try...catch 处理, 则会向该方法调用处的方法抛(向上层抛). 如果所有方法(包括main()方法)都没有 try...catch 处理异常, 则该异常会被JVM按照默认的处理方式处理
3. JVM对于异常的默认处理方式是:
1. 将异常信息(内容, 原因, 位置)打印到控制台
2. 终止当前的程序
补充:
异常的应用场景:
[Java] 纯文本查看 复制代码 // 某个方法: 计算两个整数相除的结果
// 在数学中, 任何数都不能除以0, 如果b的值是0, 则会出现问题
public static int divide(int a, int b) {
// 如何告知方法的调用者, b传入0是不对的?
if (b == 0) {
// 制造一个异常
throw new RuntimeException("不能除0"); <-- JVM终止当前方法的执行
}
return a / b;
}
divide(2/1);
try {
divide(2/0); <- 异常 new RuntimeException("不能除0");
} catch(RuntimeException e) { // RuntimeException e = new RuntimeException("不能除0");
sout(e);
e.printStackTrace();
弹窗: "当前网络正忙, 请稍候再试"
}
int result = divide(-1/1);
异常关键字: throw制造异常
throw关键字作用:
在方法中抛出指定的异常对象
格式:
throw new 异常类名("异常原因字符串");
// 示例:
public static void method(int index) {
if (index < 0) {
throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");
}
}
注意:
1. throw 必须写在方法的内部
2. throw 后面new的异常对象, 必须是 "Exception" 或 "Excetion的子类" 的对象
3. 一个方法内部 throw 了一个异常对象, 则该方法可以分为2种情况来处理该异常:
如果 throw 的是"运行时异常"(RuntimeException及其子类)对象, 那么可以不处理
该异常最终会交给JVM处理, 结果就是: 打印异常信息到控制台, 并立刻结束程序
如果 throw 的是"编译时异常"(Exception及其子类), 则必须处理:
处理方式1: throws 抛出
处理方式2: try...catch 捕获
5分钟练习: 抛出异常
需求:
定义一个方法获取指定索引的元素值:
public static int getElement(int[] arr, int index){}
arr为数组, index为索引
在方法内先if判断数组arr是否为null:
如果为null, 则抛出NullPointerException异常, 并将异常原因设置为"数组为null"
然后if判断index是否越界:
如果越界, 则抛出ArrayIndexOutOfBoundsException异常, 并将异常原因设置为"数组索引越界"
如果没有问题, 则返回arr的index索引上的值
在 main() 方法中
创建int数组arr, 存储1,2,3
调用方法, 传入arr和索引5, 查看效果
将arr赋值为null
调用方法, 传入arr和索引0, 查看效果
代码:
[Java] 纯文本查看 复制代码 public class Test {
public static void main(String[] args) {
// 创建int数组arr, 存储1,2,3
int[] arr = {1,2,3};
// 调用方法, 传入arr和索引5, 查看效果
int result = getElement(arr, 5); // Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 数组索引越界
System.out.println(result);
// 将arr赋值为null
arr = null;
// 调用方法, 传入arr和索引0, 查看效果
int result2 = getElement(arr, 0); // Exception in thread "main" java.lang.NullPointerException: 数组为null
System.out.println(result2);
}
// 定义方法, 获取指定数组中指定索引上的值
public static int getElement(int[] arr, int index) {
// 判断传入的数组对象是否为null
if (arr == null) {
// 数组对象为null, 抛出空指针异常进行通知
throw new NullPointerException("数组为null");
}
// 判断传入的索引是否符合要求
if (index < 0 || index > arr.length - 1) {
// 数组长度不符合要求, 抛出数组索引越界异常进行通知
throw new ArrayIndexOutOfBoundsException("数组索引越界");
}
// 如果能执行到这里, 说明 arr和index 都符合要求, 则进行正常的获取元素
return arr[index];
}
}
Objects工具类方法: requireNonNull()非空判断
java.util.Objects: 操作各种对象相关的工具类
// 静态方法
static <T> T requireNonNull(T obj): 检查传入参数是不是null. 是null则抛异常; 非null则返回该对象
static <T> T requireNonNull(T obj, String message): 检查传入参数是不是null. 是null则抛异常, 同时带有原因; 非null则返回该对象
可以用来简化代码的编写:
[Java] 纯文本查看 复制代码 // 普通方式
if (obj == null) {
throw new NullPointerException();
}
// Objects.requireNonNull(T obj)方式
Objects.requireNonNull(obj);
异常的处理方式1: throws声明抛出异常
异常处理的第一种方式:
throws, 声明抛出 (方法自己不处理, 交给方法调用者处理, "甩锅给别人")
作用: 告诉方法的调用者, 调用此方法有可能发生异常, 需要在编写代码时处理
格式:
修饰符 返回值类型 方法名() throws 异常类名1, 异常类名2, ... {
}
[Java] 纯文本查看 复制代码 // 示例
public static void readFile(String filename) throws FileNotFoundException {
if (!"a.txt".equals(filename)) {
throw new FileNotFoundException("路径不对");
}
if (filename.endsWith(".txt")) { // name.startsWith("张")
// 判断当前字符串是否以参数的字符串结尾
}
}
public static void main(String[] args) {
// 调用该方法的地方, 有2个选择: 继续throws声明抛出, 或try catch捕获处理
readFile();
}
注意:
1. throws 必须写在方法声明上
2. throws 后面的异常类名, 一般是 Exception 或 Exception的子类
(RuntimeException及其子类也行, 但是没有什么意义)
3. 方法内部如果抛出了多个异常对象, throws 后也必须声明多个异常
如果抛出的异常对象有子父类关系, 那么直接声明父类异常即可
4. 调用了一个带有 throws 声明抛出异常的方法, 就必须处理该异常:
要么继续声明 throws 抛出
要么 try...catch 捕获处理异常
5分钟练习: 声明抛出异常
需求:
定义方法:
public static void readFile(String filename) throws IOException {}
方法内部判断filename如果内容不是"c:\\a.txt", 就抛出 IOException 对象, 并提示原因"文件路径错误"
在 main() 方法中:
调用 readFile() 方法, 传入随便一个字符串
在 main()方法上使用 throws 声明抛出处理
代码:
[Java] 纯文本查看 复制代码 public class Test {
public static void main(String[] args) throws IOException { // 继续声明抛出, main方法会交给JVM按照默认方式处理
// 调用方法, 该方法声明抛出了一个编译时期异常, 并交给调用者main方法来处理
// 所以main方法也需要在编写代码时进行处理. 2种选择: 1.继续声明抛出 2.try-catch捕获
readFile("dsfdsf");
}
// 定义方法判断传入的文件名
public static void readFile(String filename) throws IOException { // 声明抛出, 交给方法调用者处理
// 判断文件名如果 不是 c盘的a.txt, 就抛出异常进行处理
if (!"c:\\a.txt".equals(filename)) {
// 这种问题属于IO输入输出的问题, 抛出IOException来通知
throw new IOException("文件路径错误"); // 因为IOException是编译时期异常, 必须写代码时进行处理
}
}
}
异常处理方式2: 捕获异常
try...catch:
捕获并处理异常 (方法内部自己处理异常, 不交给别人, "自己背锅")
格式:
[Java] 纯文本查看 复制代码 try {
// 可能产生异常的代码
} catch (异常类名 变量名) {
// 处理异常的代码
// 一般会将异常信息存储到日志中
}
...
} catch (异常类名 变量名) {
// 处理异常的代码
// 一般会将异常信息存储到日志中
}
注意:
1. try 中可能会抛出多种异常, 就可以写多个 catch 分别处理每种异常
2. 如果 try 中产生了异常, 就会从产生异常的那一行代码直接跳转到对应的 catch 中执行处理代码, 然后继续执行 try...catch 之后的其他代码; 如果 try 中没有产生异常, 那就不会执行 catch , 执行完 try 中的代码后, 继续执行 try...catch 之后的其他代码
补充:
[Java] 纯文本查看 复制代码 // JDK 7增加
try {
// 可能发生异常的代码
} catch (异常类型1 | 异常类型2 | 异常类型3 | ... 异常对象名) {
// 处理任意一个异常的代码
}
try {
// 可能发生异常的代码
} catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
// 处理异常的代码
}
5分钟练习: try catch处理异常
需求:
定义方法:
public static void readFile(String filename) throws IOException {}
方法内部判断filename如果内容不是"c:\\a.txt", 就抛出 IOException 对象, 并提示原因"文件路径错误"
在 main() 方法中:
调用 readFile() 方法, 传入随便一个字符串
使用 try...catch 捕获处理, 在catch中输出"文件路径错误"
代码:
[Java] 纯文本查看 复制代码 public class Test {
public static void main(String[] args) {
// 调用方法, 该方法声明抛出了一个编译时期异常, 并交给调用者main方法来处理
// 所以main方法也需要在编写代码时进行处理. 2种选择: 1.继续声明抛出 2.try-catch捕获
// 这里为了不交给JVM导致程序结束, main方法选择自己处理, 让程序继续运行
try { // 将可能发生异常的代码放入try代码块中
readFile("dsfdsf");
} catch (IOException e) {
// catch中捕获相同类型的异常
// 在catch代码块中编写对异常的处理方式
System.out.println("catch的处理方式:提示用户文件路径错误");
}
// try-catch后的代码仍然能够继续执行
System.out.println("程序仍然继续执行...");
}
// 定义方法判断传入的文件名
public static void readFile(String filename) throws IOException { // 声明抛出, 交给方法调用者处理
// 判断文件名如果 不是 c盘的a.txt, 就抛出异常进行处理
if (!"c:\\a.txt".equals(filename)) {
// 这种问题属于IO输入输出的问题, 抛出IOException来通知
throw new IOException("文件路径错误"); // 因为IOException是编译时期异常, 必须写代码时进行处理
}
}
}
Throwable中的3个异常处理方法
java.lang.Throwable
// 成员方法
String getMessage(): 异常的信息. 没有原因返回null
String toString(): 异常的类型和原因信息
void printStackTrace(): 使用标准错误输出流打印异常信息
5分钟练习: 测试Throwable的方法
需求:
定义方法:
public static void readFile(String filename) throws IOException {}
方法内部判断filename如果内容不是"c:\\a.txt", 就抛出 IOException 对象, 并提示原因
在 main() 方法中:
调用 readFile() 方法, 传入随便一个字符串
使用 try...catch 捕获处理, 在catch中调用异常对象的 printStackTrace() 方法
代码:
[Java] 纯文本查看 复制代码 public class Test {
public static void main(String[] args) {
try {
readFile("dsfdsf");
} catch (IOException e) {
// catch代码块中能够获取到IOException对象, 调用方法可以获取到异常对象中包含的信息
System.out.println(e.getMessage()); // 获取有参构造中传入的字符串
System.out.println(e.toString()); // 获取异常类名和有参构造中传入的字符串
e.printStackTrace(); // 将异常的详细信息打印到控制台(红色字)
}
System.out.println("程序仍然继续执行...");
}
// 定义方法判断传入的文件名
public static void readFile(String filename) throws IOException { // 声明抛出, 交给方法调用者处理
// 判断文件名如果 不是 c盘的a.txt, 就抛出异常进行处理
if (!"c:\\a.txt".equals(filename)) {
// 这种问题属于IO输入输出的问题, 抛出IOException来通知
throw new IOException("文件路径错误"); // 因为IOException是编译时期异常, 必须写代码时进行处理
}
}
}
finally代码块
格式:
[Java] 纯文本查看 复制代码 try {
// 可能发生异常的代码
} catch(异常类型 异常变量名) {
// 处理异常
}
...
catch(异常类型 异常变量名) {
// 处理异常
} finally {
// 无论是否发生异常, 是否捕获, 最后都会执行的代码.
// 通常在这里执行释放资源的操作
}
注意:
1. finally 必须和 try...catch 一起使用
2. finally 一般用于释放资源 (IO流时用到)
补充:
[Java] 纯文本查看 复制代码 // IO流操作时经常会这么写
FileReader fr = null;
try {
fr = new FileReader("d:\\a.txt");
int ch = fr.read();
int ch = fr.read();
int ch = fr.read();
int ch = fr.read();
int ch = fr.read();
int ch = fr.read();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fr != null) {
try {
fr.close(); // 释放资源
} catch (IOException e) {
e.printStackTrace();
}
}
}
异常注意事项1: 捕获多个异常的3种方式
捕获多个异常:
1. 多个异常分别 try...catch 处理
2. 一个 try 多个 catch
如果异常存在继承关系, 子类异常在上, 父类异常在下
3. 多个异常, 一次捕获一次处理
用Exception多态捕获
[Java] 纯文本查看 复制代码 // 1. 多个异常分别try...catch处理
try {
// 可能发生异常的代码
} catch (异常类型 变量名) {
// 处理异常
}
try {
// 可能发生异常的代码
} catch (异常类型 变量名) {
// 处理异常
}
// 2. 一个try多个catch
try {
// 可能发生异常的代码
// 可能发生异常的代码
// 可能发生异常的代码
}catch (异常类型1 变量名){ // 子类异常在上
// 处理异常1
}catch (异常类型2 变量名){ // 父类异常在下
// 处理异常2
}
// 3.
try {
// 可能发生异常的代码
// 可能发生异常的代码
// 可能发生异常的代码
// 可能发生异常的代码
// 可能发生异常的代码
}catch (Exception e){ // 父类异常多态接收
// 处理异常
}
运行时异常, 可以不抛出 throws 也不捕获 try catch, 交给JVM处理
[Java] 纯文本查看 复制代码 // 因为可以通过某些代码来避免发生运行时异常
// 通过if判断, 避免可能发生的ArrayIndexOutOfBoundsException
if (i >= 0 || i <= arr.length-1) {
int a = arr[i];
}
// 通过if判断, 避免可能发生的NullPointerException
if (phone != null) {
phone.call();
}
异常注意事项2: finally中有return语句
如果 finally 代码块中有 return 语句, 则永远返回 finally 中的 return 语句的值
应该避免在 finally 中写 return 语句
补充:
[Java] 纯文本查看 复制代码 try {
return a;
} catch() {
return a;
} finally {
a += 10;
return a;
}
异常注意事项3: 子父类继承重写方法时的异常要求
子父类继承关系下, 子类重写父类带有throws的方法:
1. 如果父类抛出多个异常, 子类重写父类方法时可以有3种方式:
a: 抛出和父类相同的异常
b: 抛出父类异常的子类
c: 不抛出异常
2. 父类方法没有抛出异常, 子类重写父类该方法时也不可抛出异常
此时子类产生该异常, 只能捕获处理, 不能声明抛出
一般情况下:
父类方法声明的异常是什么样的, 子类重写的方法声明异常就什么样, 保持一致即可
补充:
[Java] 纯文本查看 复制代码 public class Fu {
public void method() {
// ...
}
}
public class Zi extends Fu {
@Override
public void method() {
// ...
}
}
自定义异常类
如果Java提供的异常类不足以满足我们的需求, 我们也可以自己定义异常类
定义编译时异常: 继承 Exception
定义运行时异常: 继承 RuntimeException
[Java] 纯文本查看 复制代码 // 注册
// 用户名, 密码
// 刘德华 刘德华
为了在抛出异常时, 显示一些提示信息, 我们可以定义2个构造, 一个无参, 一个有String参数
// 自定义编译时异常
public class RegisterException extends Exception {
public RegisterException() {
super();
}
public RegisterException(String message) {
super(message);
}
}
// 自定义运行时异常
public class RegisterException extends RuntimeException {
public RegisterException() {
super();
}
public RegisterException(String message) {
super(message);
}
}
自定义异常案例: 注册异常练习5分钟练习: 模拟注册异常
需求: 模拟注册操作, 如果用户名已存在, 则抛出异常, 并提示"亲, 该用户名已被注册"
步骤:
定义注册异常类RegisterException, 继承RuntimeException
生成无参和有String参数的构造
定义测试类:
1.定义静态成员变量:
private static String[] usernames = {"张三", "李四", "王五"};
数组保存已经注册过的用户名(模拟数据库)
2.使用Scanner获取用户输入的注册的用户名(模拟前端,页面)
3.定义一个方法
public static void checkUsername(String username) {}
对用户输入的中注册的用户名进行判断
遍历存储已经注册过用户名的数组,获取每一个用户名
使用获取到的用户名和用户输入的用户名比较是否相同
true: 用户名已经存在,抛出RegisterException异常,告知用户"亲,该用户名已经被注册";
false: 继续遍历比较
如果循环结束了,还没有找到重复的用户名,提示用户"恭喜您,注册成功!";
4. 调用checkUsername()方法将输入的用户名传入, 查看效果
代码:
[Java] 纯文本查看 复制代码 //public class RegisterException extends RuntimeException {
public class RegisterException extends Exception {
// 生成构造方法可以用快捷键 Alt + Insert
public RegisterException() {
}
public RegisterException(String message) {
super(message);
}
}
public class Test {
// 定义静态的成员变量字符串数组, 保存已经注册过的用户名
static String[] usernames = {"张三", "李四", "王五"};
public static void main(String[] args) {
// 键盘录入要注册的用户名
Scanner sc = new Scanner(System.in);
System.out.println("请输入要注册的用户名:");
String username = sc.next();
// 调用方法判断用户名
try {
checkUsername(username);
} catch (RegisterException e) {
e.printStackTrace();
}
}
// 定义方法: 用于判断传入的用户名是否重复
public static void checkUsername(String username) throws RegisterException {
// 遍历数组, 比较用户录入的用户名和数组中的用户名
for (String name : usernames) {
if (name.equals(username)) {
// 如果数组中的用户名和用户录入的用户名相同, 则说明已被占用
// 通过抛出 注册异常 的方式进行通知
throw new RegisterException("亲, 该用户名已被注册");
}
}
// 如果for循环结束后能执行到这里, 说明没有相同的用户名, 则可以注册
System.out.println("恭喜您,注册成功!");
}
}
多线程计算机基本概念: 并发与并行
并发: (交替执行) 指两个或多个事件在"同一时间段内"发生
并行: (同时执行) 指两个或多个事件在"同一时刻"发生 (同时发生)
我们研究的是"并发"
并发的实现, 是依靠电脑CPU快速地在多个任务之间切换执行实现的
计算机基本概念: 进程
进程: 一个应用程序在内存中的一次执行过程
每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程
进程也是程序的一次执行过程,是系统运行程序的基本单位
系统运行一个程序即是一个进程从创建、运行到消亡的过程
计算机基本概念: 线程
线程: 是进程内的一个独立执行单元 (一条代码执行路径)
一个程序运行后至少有一个进程, 一个进程中可以包含多个线程
公司 > 部门 > 员工
程序 > 进程 > 线程
多线程的好处:
效率高
多个线程之间互不影响
线程的调度
线程的调度方式:
1. 分时调度: 所有线程轮流使用CPU, 平分占用CPU的时间
2. 抢占式调度: 优先让优先级高的线程使用CPU; 如果优先级相同, 则随机选择一个线程执行
1-10
线程1 10 可能性更大
线程2 9
线程3
Java使用的是"抢占式"调度
主线程
Exception in thread "main"
主线程:
我们以前编写的代码, 也在一条线程中执行, 该线程叫作"main线程", 也称为"主线程"
如果我们没有额外创建线程, 那么我们的程序就只有一个线程, 即主线程, 此时程序是"单线程"的
单线程的执行特点:
同一个线程内的代码, 从上往下依次执行
创建多线程程序的第一种方式: 继承Thread类
实现多线程的第一种方式:
1. 定义类, 继承 Thread 类
2. 重写 run() 方法, run方法内部是线程要执行的任务
3. 创建Thread子类的对象, 调用 start() 方法启动线程
java.lang.Thread类: 表示线程. 实现了Runnable接口
void start(): 启动线程, 即让线程开始执行run()方法中的代码
注意:
必须调用 start() 方法来开启线程, 不能直接调用 run() 方法, 调用 run() 会变成单线程
同一个线程对象, 不能多次调用 start() 方法
Java是抢占式调度, 不同线程的代码, 执行顺序是随机的
5分钟练习: 实现多线程
需求:
自定义线程类: MyThread, 继承 Thread
重写 run() 方法:
方法内部使用for循环20次, 打印"run:"+次数
定义测试类, 在main()方法中:
创建 MyThread 类的对象, 调用 start() 方法启动线程
在main方法中单独编写一个for循环, 也是循环20次, 打印"main:"+次数
运行程序查看两个线程的运行效果
代码:
[Java] 纯文本查看 复制代码 public class MyThread extends Thread { // 定义类, 继承Thread类
// 重写run方法
@Override
public void run() { // run方法中定义的就是线程要执行的任务
// for循环20次, 打印"run:"+次数
for (int i = 0; i < 20; i++) {
System.out.println("run:" + i);
}
}
}
public class Test {
public static void main(String[] args) {
/*
注意: main方法中的代码都是运行在main线程中
*/
// 创建自定义子类对象
MyThread myThread = new MyThread();
myThread.start(); // 从这里开始, 新的线程开始执行
// 下面的代码还是在main方法中, 即在main线程中执行
for (int i = 0; i < 20; i++) {
System.out.println("main:" + i);
}
}
}
今日API
java.lang.Throwable: 异常的顶级
// 成员方法
String getMessage(): 异常的信息. 没有原因返回null
String toString(): 异常的类型和原因信息
void printStackTrace(): 使用标准错误输出流打印异常信息
java.lang.Thread类: 表示线程. 实现了Runnable接口
void start(): 启动线程, 即让线程开始执行run()方法中的代码
|
|