java中异常、线程

1.1 异常概念

1.1.1异常的继承体系

  • 学习异常处理的目的
  1. 不能让程序因为一个小小的问题而导致整个程序崩溃。
  • 什么是异常
  1. 在程序编译或运行过程中出现的问题则称为异常,最终会导致JVM非正常停止。
  • 异常的继承体系
  1. Throwable类是Java语言中所有错误或异常的超类
  2. Error:错误
  3. Exception:异常(编译时异常、运行时异常)

 在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处 理异常的方式是中断处理。 

 1.1.2 Throwable常用方法

  • public String getMessage():获得异常信息字符串。
  • public String  toString():获得异常详细信息字符串:类全名:异常原因。
  • public printStackTrace();追踪异常的栈信息。(追踪异常的根源)

即打印异常的详细信息。包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。

注意:在实际开发过程中,出现异常可以把异常的简单类名,拷贝到API中去查。

/*
    Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
	at day05Test01.PrintStackTraceDemo01.main(PrintStackTraceDemo01.java:6)

    异常出现的位置:at day05Test01.PrintStackTraceDemo01.main(PrintStackTraceDemo01.java:6)
    异常的类型:java.lang.ArrayIndexOutOfBoundsException
    异常的原因:3
 */
public class PrintStackTraceDemo01 {
    public static void main(String[] args) {
        int [] arr={12,23,1};
        System.out.println(arr[3]);
    }
}
public class ExceptionDemo01 {
    public static void main(String[] args) {
        test02();
    }
    private static void test01(){
        //创建Throwable对象
        Throwable t=new Throwable("空指针异常");
        //获得异常信息字符串
        System.out.println( t.toString());
        //获得异常详细信息字符串
        System.out.println(t.getStackTrace());
        //追踪异常的根源
        t.getMessage();
    }

    private static void test02(){
        test01();
    }
}

  • 异常分类
  • 编译时异常:checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。 (如日期格式化异常)
  • 运行时异常:runtime异常。在运行时期,检查异常,在编译时期,运行异常不会编译器检测(不报错)。(如数学异常)

 1.1.3 异常和错误的区别

异常和错误的区别

  • 错误一般是由操作系统反馈给JVM的,无法针对错误进行处理,只能修改错误行的代码。
  • 异常一般是JVM反馈给Java程序,可以针对异常进行处理,如果不处理,则结果和错误一样。

异常和错误的判断

扫描二维码关注公众号,回复: 2756793 查看本文章
  • 根据异常的类名进行判断,如果是以Exception结尾,则是异常,否则就是错误。

常见的异常:

  • Java.lang.OutOfMemoryError:Java heap space 堆内存溢出              类全名:异常原因
  • java.lang.ArithemeticException:/by zero:数学运行异常
/*
    堆内存异常:
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at day05Test01.ExceptionDemo02.test01(ExceptionDemo02.java:8)
	at day05Test01.ExceptionDemo02.main(ExceptionDemo02.java:5)

	数学运算异常:
	Exception in thread "main" java.lang.ArithmeticException: / by zero
	at day05Test01.ExceptionDemo02.test01(ExceptionDemo02.java:17)
	at day05Test01.ExceptionDemo02.main(ExceptionDemo02.java:9)
 */
public class ExceptionDemo02 {
    public static void main(String[] args) {
        test01();
    }
    private static void test01(){
       /* //定义一个数组,堆内存异常错误
        int [] arr=new int [1024 * 1024 * 1024];
        System.out.println(arr.length);*/

        //数学运行异常
        System.out.println(100/0);
    }
}

 


异常的处理

 2.1 JVM处理方式

异常处理方法有以下两种

1.JVM处理方式

  • 将异常的信息(异常的类名、异常的原因、异常的位置)打印在控制台
  • 退出JVM,终止程序运行

2.手动处理方式:抛出处理、捕获处理

2.2 捕获异常try...catch

如果异常出现的话,会立刻终止程序,所以我们得处理异常:

1. 该方法不处理,而是声明抛出,由该方法的调用者来处理(throws)。

2. 在方法中使用try-catch的语句块来处理异常。 

try{
    编写可能产生异常的代码
   }catch(异常类型   e){
    处理异常的代码
     //处理日志/打印异常信息/继承抛出异常   
   } 
  • try:该代码块中编写可能产生异常的代码。
  • catch:用来进行某种异常捕获,实现对捕获到的异常进行处理。
  • 注意:try和catch都不能单独使用,必须连用。
import java.io.FileNotFoundException;

public class TryCatchDemo {
    public static void main(String[] args) {
        try {
            read("b.txt");
        }catch (FileNotFoundException e){
            System.out.println(e);
        }
        System.out.println("over");
    }
    /*
        我们当前的这个方法中有编译器异常
     */
    public static void read(String path) throws FileNotFoundException{
        if(!path.equals("a.txt")){
            //我假设如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常  throw
            throw new FileNotFoundException("文件不存在");
        }
    }
}

 多个异常使用捕获又该如何处理呢

  • 多个异常分别处理
  • 多个异常一次捕获,多次处理。
  • 多个异常一次捕获,一次处理。
  • 一般我们使用一次捕获多次处理的方式
try{
    编写可能会出现异常的代码
   }catch(异常类型A e){
    //当try中出现A类型异常,就用该catch来捕获
        处理异常代码
        //记录日志/打印异常信息/继续抛出异常
    }catch(异常类型B e){
        //当try中出现B类型异常,就用该catch来捕获
        处理异常代码
//记录日志/打印异常信息/继续抛出异常

       }

  • 注意:这种异常处理方式,要求多个catch的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。
  •  格式说明
  • try代码块:编写可能出现异常的代码,只要try里面有一行代码出现了异常,该行代码后面的代码就不会执行了。
  • catch代码块:编写出现异常之后要执行的代码,如果try代码块中的代码没有出现异常,则不会执行该代码块的代码。  
  • 是否捕获异常只需要捕获Exception异常即可? 
  • 不是,因为实际开发,有时需要针对不同的异常有不同的处理方式,如果针对所有异常的处理方式都是一样的。则可以直接捕获Exception即可。  
public class ExceptionDemo03 {
    public static void main(String[] args) {
        int a=0;
        test01(a);
    }
    //使用捕获处理异常
    private static void test01(int a){
        try{
            int result=100/a;
            System.out.println("result="+result);

            //定义数组
            int[] arr={1,2,3,4};
            System.out.println(arr[3]);

            //定义字符串
            String str="abc";

            System.out.println(str.length());

            System.out.println(str.charAt(4));
        }catch (ArithmeticException e){
            e.getStackTrace();
            System.out.println("进来了吗");
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("又进来了吗 ");
        }catch (NullPointerException e){
            System.out.println("又又进来了吗");
        }catch (Exception e){
            System.out.println("又又又进来了吗");
        }
        System.out.println("come here");
    }
}

 2.3 finally代码块

try{
    //可能出现异常的代码
   }catch(异常类型   变量名){
    //处理异常的代码
   }finally{
    //不管是否出现异常都要执行代码}
  • finally代码块特点:只有代码执行流程进入了try代码块,不管是否出现异常,都会执行该代码块中的代码。
  • finally代码块的作用:用来释放资源,比如关闭流,关闭数据库资源。
public class ExceptionDemo04 {
    public static void main(String[] args) {
        int a=10;
        int result=test01(a);
        System.out.println("result="+result);
    }
    //使用捕获处理异常
    private static int test01(int a){
        if(a != 0){
            try {
                int result=100/a;
                //退出JVM,终止程序运行。
                //System.exit(0);
                return result;
            }catch (ArithmeticException e){
                System.out.println("进来了吗");
            }finally {
                System.out.println("关闭资源数据库");
            }
            System.out.println("come here");
        }
        return 0;
    }
}

 


2.4 抛出异常throw和声明异常throws

  • throw关键字作用:将异常对象抛给方法调用者并结束当前方法的运行。
  • throw关键字的使用格式:throw new 异常类名(“异常信息字符串”);
  • throw关键字的使用位置:使用在方法体中
  •  throws关键字作用:将方法体中可能出现的异常标识出来报告给方法调用者,让方法调用者注意处理异常。
  • throws关键字的使用格式:修饰符  返回值类型  方法名(参数列表) throws  异常类名1,异常类名2...{}
  • throws关键字的使用位置:使用在方法声明上。
public class ExceptionDemo05 {
    public static void main(String[] args) {
        int a=0;
        int result=test01(a);
        System.out.println("result="+result);
        System.out.println("come here");
    }
    private static int test01(int a) throws ArithmeticException,NullPointerException{
        try {
            int result=100/a;
            return result;
        }catch (ArithmeticException e){
            System.out.println("你的网络不给力哦...");
        }
        return 0;
    }
}

 

2.5 方法重写异常处理注意点

  •  方法重写时异常处理注意事项(针对编译时异常而言)
  1. 父类方法中没有声明异常时,子类重写方法时也不能声明异常。
  2. 父类方法中有声明异常时,子类重写方法时可以声明小于等于父类声明的异常。
  3. 小结:子类重写方法时不能大于父类声明的异常

2.6 编译时异常和运行时异常

  •  什么是编译时异常:除了运行时异常以外的所有异常都是编译时异常。
  • 什么是运行异常:只要是RuntimeException或其子类异常都属于运行时异常。
  • 编译时异常的特点:
  1. 方法体中抛出的异常是编译时异常,则要求必须要处理。
  2. 方法声明中声明的异常是编译时异常,则要求方法调用者一定要处理。
  • 运行时异常的特点:
  1. 方法体中抛出的异常是运行时异常,则可以处理,也可以不处理。
  2. 方法声明中声明异常是运行时异常,则方法调用者可以处理也可以不处理。
  • 为什么Java编译器对运行时异常处理如此松散?
  • 因为运行时异常一般是可以通过程序员良好的编程习惯避免的。 

3.1自定义异常概述

  • 为什么需要自定义异常类: 我们说了Java中不同的异常类,分别表示着某一种具体的异常情况,那么在开发中总是有些异常情况是SUN没有定义 好的,此时我们根据自己业务的异常情况来定义异常类。,例如年龄负数问题,考试成绩负数问题。
    在上述代码中,发现这些异常都是JDK内部定义好的,但是实际开发中也会出现很多异常,这些异常很可能在JDK中 没有定义过,例如年龄负数问题,考试成绩负数问题.那么能不能自己定义异常呢? 
  • 异常类如何定义: 
  • 1. 自定义一个编译期异常: 自定义类 并继承于 java.lang.Exception 。 2. 自定义一个运行时期的异常类:自定义类 并继承于 java.lang.RuntimeException 。 

 3.2 自定义异常的案例

1. 在Person类的有参数构造方法中,进行年龄范围的判断。

2. 若年龄为负数或大于200岁,则抛出NoAgeException异常,异常提示信息“年龄数值非法”。

3. 要求:在测试类中,调用有参数构造方法,完成Person对象创建,并进行异常的处理。

public class Person {
    private int age;

    public Person() {
        super();
    }

    public Person(int age) throws NoAgeException {
        setAge(age);
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) throws NoAgeException {
        //判断年龄是否合法
        if(age<0 || age>200){
            throw new NoAgeException("你不是地球人,滚回火星去吧...");
        }
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                '}';
    }
}




/*
    自定义异常类
 */
public class NoAgeException extends Exception {
    //无参构造
    public NoAgeException() {
    }

    //有参构造
    public NoAgeException(String message){
        super(message);
    }
}






public class ExceptionDemo {
    public static void main(String[] args) throws NoAgeException {
        //创建Person对象
        Person p=new Person();
        p.setAge(300);
        System.out.println("p");
    }
}

  • 自定义异常的步骤:
  1. 创建一个类,类名:XxxException
  2. 继承官方的异常类:
  • RuntimeException:运行时异常
  • Exception:编译时异常
  • 3.提供构造方法:
  • 有参构造
  • 无参构造

4.1 并发与并行

  • 学习多线程的目的:为了让多个任务能够并行执行,提高程序的执行效率。
  • 并行和并发的概念:
  • 并发:两个或多个任务在同一时间执行,比如任务A和任务B在18:23:30
  • 并发:两个或多个任务在同一时间段执行,比如任务A和任务B在18:23:30到18:25:30执行了。

4.2 线程和进程

  •  进程的概念:一个正在运行中的程序就是一个进程。
  • 线程的概念:进程中的一个独立的执行路径(执行单元)
  • 进程和线程案例:
  1. 可以理解为进程
  2. 可以理解为线程
  • 进程的作用:用来封装线程,为线程执行任务提供资源(内存资源)
  • 线程的作用:用来执行代码的
  • 创建线程的方式1:继承Thread类
  1. 创建一个类继承Thread类
  2. 重写run方法:将线程任务相关的代码写在该方法中
  3. 创建Thread子类对象,调用start方法开启线程。
/*
    自定义线程
 */
public class MyThread extends Thread {
    @Override
    public void run(){
        for (int i = 0; i < 10; i++) {
            System.out.println("run..."+i);
        }
    }
}



public class ThreadDemo {
    public static void main(String[] args) {
        //创建线程对象,相当于招聘一个工人
        MyThread t=new MyThread();
        // 开启线程:开辟新的执行路径执行run方法的代码
        t.start();
        // 不要手动调用run方法:不会开启新的执行路径,而是在当前线程执行run方法的代码         
        //  t.run();
        for (int i = 0; i < 10; i++) {
            System.out.println("main..."+i);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/Huangyuhua068/article/details/81414698