异常线程
Posted hbwyh-myworld
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了异常线程相关的知识,希望对你有一定的参考价值。
第一章 异常
1.1 概念及作用
1).“异常”:指程序在“运行时”JVM遇到了无法处理的数据,或者表达式,这种情况就叫:异常。(异常指的并不是语法错误)
2).JVM遇到这种异常情况,通常是在控制台打印异常信息,然后结束掉程序(这是我们不希望看到的)
3).“异常处理”的作用:就是允许我们的程序遇到这种异常情况后,可以跳过这段代码,继续执行后续的代码。
前提:我们程序员在写代码的时候必须提前预知到这种异常情况,并预先做预处理。
1.2 异常产生的过程解析
1).JVM执行到有异常的代码;
2).JVM要识别出这种异常;
3).JVM到类库中找到描述这个异常的“异常类”,并创建对象;
4).判断我们的代码中是否希望“捕获这种异常”:
是:去执行“捕获异常的代码”(catch);
否:在控制台打印异常信息,并结束程序(这是我们不希望看到的)
1.3 Java异常体系结构及分类
1)Throwable(顶层的异常父类)
|--Error(错误):不希望程序捕获并处理,因为无法处理。
|--Exception(异常):希望程序捕获并处理的异常情况;
|--RuntimeException(运行时异常):Java不要求强制捕获这种异常,这种异常通常是通过一些判断可以避免的异常。
|--除RuntimeException外的其它异常(编译期异常):Java要求必须强制捕获并处理,否则无法编译通过。这种异常通常无法通过判断避免。
2)Throwable中常用方法:
1)public void printStackTrace() :打印异常的详细信息(异常的跟踪栈信息并输出到控制台)。
包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。
2)public String getMessage() :获取发生异常的原因。
提示给用户的时候,就提示错误原因。
3)public String toString() :获取异常的类型和异常描述信息(不用)。
第二章 异常的处理
2.1 基本方式try...catch语句及执行流程
1).基本语法:
try{
//可能出现异常的代码
}catch(异常类名 变量名){
//如果try中出现了“异常类名”的异常,这里会被捕获,
//会执行这里的代码。
}
2).示例代码:
public static void main(String[] args) {
updateStudent();
}
public static void updateStudent(){
Scanner sc = new Scanner(System.in);
while(true) {
System.out.println("请输入学号:");
try {
int stuNo = sc.nextInt();
System.out.println("你输入的学号是:" + stuNo);
break;//正常情况,结束循环
} catch (InputMismatchException e) {
System.out.println("你的输入有误!");
sc = new Scanner(System.in);
(Scanner的bug,需写这步。否则,陷入死循环)
}
}
System.out.println("后续代码....");
}
3)执行流程
出现异常时:会在异常那步直接跳到catch步骤中
2.2 常见的几种异常
1).空指针异常:NullPointerException(运行时异常)(针对集合类)
注意:当应用程序在需要对象的情况下尝试使用null时抛出。 这些包括:
(1)调用一个null对象的实例方法。
(2)访问或修改null对象的字段。
(3)以null的长度,好像是一个数组。
(4)访问或修改null的插槽,就像它是一个阵列一样。
(5)抛出null ,好像是一个Throwable价值。
(1)String s = null;
(注意:null的长度不存在,相同意思的String s=””的长度为0;其次,null无地址,String s=””有地址)
System.out.println(s.length());//编译通过,运行时抛出:NullPointerException
//最好要先做非空判断,然后再做其它处理
(2)String s = null;
if(s != null){
System.out.println(s.length());
}
2) .数组下标越界异常:ArrayIndexOutOfBoundsException(运行时异常)(针对数组)
注意:上述异常类继承自IndexOutOfBoundsException
int[] arr = {1,2,3};
System.out.println(arr[3]);//编译通过,运行时抛出:ArrayIndexOutOfBoundsException
//最好先判断索引是否有效
int[] arr = {1,2,3};
if(3 < arr.length){
System.out.println(arr[3]);
}
3).数字转换异常:NumberFormatException(运行时异常)
String str = "24a";
int age = Integer.parseInt(str);
4).算术运算异常:ArithmeticException(运行时异常)
int a = 10;
int b = 0;
System.out.println(a / b);//编译通过,运行时抛出:ArithmeticException
5) .转换异常:ParseException(编译期异常)
--必须要捕获,否则编译错误
String str = "2018-08-08";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try{
Date date = sdf.parse(str);
}catch(ParseException e){
.....
}
注意:还可以在main方法中向JDK中throws ParseException()
2.3 try...catch...catch...语句及执行流程
注意:(1)这种异常处理方式,要求多个catch中的异常不能相同;
(2)若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理.
(3)找到一个异常后,不再执行try中的后续代码。
2.4 声明抛出异常throws
1)声明异常:将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws进行声明,让调用者去处理。
2)格式:修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{ }
注意:在throws后面可以写多个异常类
3)编码:
public static void main(String[] args) {
int[] arr = {432, 432, 43, 253};
try {
System.out.println("总和:" + sum(arr));
} catch (NullPointerException e) {
System.out.println("调用方法,接收到一个异常....");
}
System.out.println(sum(null));
}
public static int sum(int[] arr) throws NullPointerException, ArrayIndexOutOfBoundsException,ArithmeticException {
int sum = 0;
for (int n : arr) {
sum += n;
}
return sum;
}
注意:如果throws后面的是“编译期异常”,调用的代码必须要使用try...catch...或者继续抛出,否则编译错误。
如果throws后面的是“运行时异常”,调用处可以不处理,编译可以通过,但不建议。
自定义异常不写throws;
2.5 抛出异常throw
public static void main(String[] args) {
int[] arr = {432, 432, 43, 253};
try {
System.out.println("总和:" + sum(null));
} catch (NullPointerException e) {
System.out.println("调用方法,接收到一个异常....");
System.out.println("异常信息:" + e.getMessage());
}
}
public static int sum(int[] arr) {
int sum = 0;
if (arr == null) {
throw new NullPointerException("哥们,你给我传了一个空指针,你太坏了....");
}
for (int n : arr) {
sum += n;
}
return sum;
}
注意:如果throw抛出的是一个“运行时异常”,调用处可以不处理;
如果throw抛出了一个“编译期异常”,方法声明处必须显示的使用throws声明此异常,调用处也必须处理,否则编译错误;(即,编译期异常时,throw和throws都要抛出,也必须要处理异常。)
2.6 Objects类的非空判断
1)代码:
class Student{
private String name;//要求姓名不能为:null
....
public Student(String name){
/*
if(name == null){
throw new NullPointerException("...");
}
if(...){
}*/
this.name = Objects.requireNonNull(name);//省去一个一个的非空判断
}
}
main(){
Student stu = new Student("张三");
Student stu2 = new Student(null);
}
2)throws和throw的使用区别:
throws:表示“声明”此方法可能会产生某种异常,如果产生,请JVM将此异常对象抛给调用者。如果没有产生异常,则正常执行完毕。
public static void sum() throws NullPointerException{
...
}
throw:表示立即“抛出一个异常对象”
public static void sum(){
if(...){
throw new NullPointerException("...");
}
}
2.7 try...catch...finally语句
public String readFile(){
try{
1.打开文件
2.读取文件内容;
4.return 文件内容;
}catch(Exception e){
//异常情况
return null;
}finally{
//这里是:无论是否出现异常,都会被执行的
3.关闭文件
}
}
示例代码:
public static void main(String[] args) {
String s = show();
System.out.println("返回值:" + s);
}
public static String show(){
try {
System.out.println("1.打开文件");
System.out.println("2.读取文件--出异常" + (10 / 0));
return "呵呵呵";
} catch (Exception e) {
System.out.println("异常了....");
return null;
}finally {
System.out.println("3.关闭文件");
}
}
第三章 自定义异常
3.1 概述
class Student{
private int age;
public void setAge(int age){
//假如我们要求年龄必须在15到50之间
if(age < 15 || age > 50){
throw new 自定义异常();
}
this.age = age;
}
}
为什么需要自定义异常:
1).类库中没有我们需要的异常;
3.2 自定义异常_自定义异常练习:
1).自定义异常的步骤:
A).自定义异常类--继承自Exception或者它的某个子类
(RuntimeException/非RuntimeException(建议));
B).建议:添加一个String参数的构造方法,用于设置“异常信息”
2).示例代码:
1).定义异常类:
public class AgeException extends Exception{
public AgeException(String msg) {
super(msg);
}
}
(继承Exception默认继承编译器异常,extends RunTimeException 继承运行期异常 )
2).使用异常类:
public class Student {
private int age;
public void setAge(int age) throws AgeException {
if (age < 15 || age > 50) {
throw new AgeException("年龄错误,必须在15到50岁之间!");
}
this.age = age;
}
}
3).测试代码:
public class Demo {
public static void main(String[] args) {
Student stu = new Student();
try {
stu.setAge(100);
} catch (AgeException e) {
System.out.println("异常信息:" + e.getMessage());
}
}
}
------------------------------------------------------------
第四章 多线程
4.1 进程与线程
1)“进程”:“进程”是操作系统的概念,一个独立运行的程序,就是一个“进程”。
2).“线程”:“线程”是由“进程创建”的,一个进程可以创建任意多的线程,每个线程都包含一些代码。线程中的代码会同主进程或者其他线程“同时运行”。
3).“多进程”:在某个时间段内,同时运行多个程序;
4).“多线程”:一个程序可以同时启动多个线程,也就意味着可以使多段代码“同时运行”,可以提高程序的运行效率。
我们之前写的程序都是:单线程程序--后面的代码总是等前面的代码执行完毕才能执行。
要使我们的程序可以“同时做多件事”,就需要制作多线程程序。
4.2 并发与并行:
1).并行:多个线程同时开始运行;
2).并发:多个线程同时访问同一资源(CPU)。(指两个或多个事件在同一个时间段内发生。)
4.3 创建线程类_继承Thread及匿名对象的方式:
1).制作多线程程序,必须在设计阶段就要确定下来:哪些代码需要以独立的线程运行,从而来 制作线程类。
2).步骤:
(1).定义线程类,继承自Thread
(2).重写run()方法;
(3).启动线程:
(3.1).创建一个线程对象;
(3.2).调用对象的start()方法启动线程;
3).示例代码:
1).定义线程:
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("从网络接收外部数据....");
}
}
}
2).启动线程:
public static void main(String[] args) {
//先启动线程
MyThread t = new MyThread();
t.start();//启动线程,后,主方法继续向下运行
for (int i = 0; i < 1000; i++) {
System.out.println("从控制台接收数据.....");
}
}
4.4 创建线程类_关于线程的几个说明:
1).匿名内部类的方式实现线程:
new Thread(){
public void run(){
for (int i = 0; i < 1000; i++) {
System.out.println("从控制台接收数据.....");
}
}
}.start();
2).重写的是run().但启动线程调用的是:start()
3).对于一个“线程对象”,只能调用一次start(),不能多次调用start();
MyThrad t = new MyThread();
t.start();
...
t.start();//编译通过,运行时异常
以上是关于异常线程的主要内容,如果未能解决你的问题,请参考以下文章