JAVASE基础语法(异常、常用类)

一、异常

1.1 什么是异常

异常就是指不正常。是指代码在运行过程中可能发生错误,导致程序无法正常运行。

package com.atguigu.exception;

public class TestException {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        System.out.println("数组下标为[10]的元素:" + arr[10]);
        //运行时发生 ArrayIndexOutOfBoundsException数组下标越界异常

        System.out.println("数组的长度:" + arr.length);//不执行
    }
}

1.2 异常的类型(怎么划分分类是重点)

Java是面向对象的编程语言,以“对象”为中心。所以,Java也把异常用对象表示。对象是由某种类new出来的,那么每一种异常的情况都会有一个异常的类来描述它。例如:数组下标越界异常 就用 java.lang.ArrayIndexOutOfBoundsException 类来描述它。

因为异常的类型非常多,所以我们有必要学习它们的继承关系图:

作为异常的根类型是java.lang.Throwable类型(当然如果类的角度来说,根类型仍然是Object)。

 Throwable有两大子类:

  • Error:是 Throwable 的子类,用于指示合理的应用程序不应该试图捕获(catch)的严重问题。例如:VirtualMachineError(虚拟机错误)下的子类StackOverflowError(栈内存溢出错误),它在我们无条件递归调用时发生过。这种错误必须停下来,修正我们的程序,或升级硬件,或软件的架构。

  • Exception:Exception 类及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件。

    • 对于Exception系列的异常来说,特别是RuntimeException系列能避免的尽量避免(依赖于程序员的经验或素质)。不能避免的,就要通过try-catch来处理。

Exception下面又可以分为两大类:

  • RuntimeException及其子类:运行时异常,又称为非受检异常。非受检异常是指编译器不会对当前代码做该系列的异常类型检查。例如:ArrayIndexOutOfBoundsException(数组下标越界异常) 或 NullPointerException(空指针异常)等,编译器不会提示你,你的代码可能发生这种异常,直到程序运行时,发生异常,我们才知道。简单的说,编译器检查不出来这个系列的异常。

    • ArrayIndexOutOfBoundsException(数组下标越界异常)

    • NullPointerException(空指针异常)

    • ClassCastException(类型转换异常):向下转型可能发生

    • ArithmeticException(算术异常): a / 0 会发生这个异常

  • RuntimeException系列以外的,包括Exception本身:编译时异常,又称为受检异常。受检异常是指编译器会对当前代码做该系列的异常类型检查,不管这个异常是不是发生,会不会发生,编译器只要认为有可能发生,就会报编译错误,即编译不通过。必须等程序员写好对该异常的处理代码后,才会编译通过。

  • 总结:无论是编译时异常,还是运行时异常,当运行的时候,异常真的发生了,都会导致程序崩溃。它们的区别只是看编译器是否提醒你而已。

package com.atguigu.exception;

public class TestRuntimeException {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        System.out.println("数组下标为[10]的元素:" + arr[10]);
        //这句代码编译器压根不检查,是否下标越界。无论下标是否越界,只要数组名对了,下标是int值,就会编译通过
        //但是编译通过,不代表运行正常
    }
}

 

package com.atguigu.exception;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class TestCheckException {
    public static void main(String[] args) throws FileNotFoundException {
        //FileInputStream:File(文件)Input(输入)Stream(流),这个流是用来读取文件的内容
        //下面这句代码,表示想要从 d:\1.txt文件读取文件内容
        FileInputStream fis =new FileInputStream("d:\\1.txt");
        //此时d:\1.txt文件存不存在,编译器都会提醒你,这段代码可能发生FileNotFoundException
        //编译报错,不是说这个文件一定不存在,只是说可能不存在
        //必须要求程序员说明(通过代码说明)如果文件不存在,怎么办,编译才会通过。
        //例如:我告诉编译器,如果文件不存在,发生了FileNotFoundException,那么main方法不管,程序要挂就挂吧
    }
}

 

1.3 try-catch异常的处理(重点,围绕5个关键字)

try{
    可能发生异常的业务代码语句1;
    可能发生异常的业务代码语句2;
    可能发生异常的业务代码语句3;
    可能发生异常的业务代码语句4;
    可能发生异常的业务代码语句5;
}catch(异常的类型1 参数名){ //参数名一般都是写e
    //(1)打印异常信息的代码 ,要么打印到控制台,要么记录到日志, 打印它的目的是便于程序员后期跟踪,查看发生问题的地方,发生问题的原因,便于后期维护代码
    //(2)对异常的处理代码,有的时候引起异常的问题不处理,下面的代码是无法正常运行。
}catch(异常的类型2 参数名){ 
    //
}catch(异常的类型3 参数名){ 
    //
}

执行特点:

(1)try{}没有发生异常,那么所有的catch都不会执行。

(2)try{}中发生了异常,例如:“可能发生异常的业务代码语句3;”发生了异常,try中语句4和语句5不执行。

  • 如果语句3发生的是 异常的类型1的问题,那么就从try“语句3”跳到了 第一个catch 分支执行,下面的两个catch不会执行。

  • 如果语句3发生的是 异常的类型2的问题,那么就从try“语句3”跳到了 第二个catch 分支执行,上面的与下面的两个catch不会执行。

  • 如果语句3发生的是 异常的类型3的问题,那么就从try“语句3”跳到了 第三个catch 分支执行,上面的两个catch不会执行。

  • 如果语句3发生的是 异常的类型4的问题,3个catch都不执行了,就会导致 当前方法 异常结束,即当前方法就挂了。

    • 如果当前方法main方法,main方法是JVM调用的,那么程序就挂了。

    • 如果当前方法其他方法,那么就会“带着这个异常对象”回到调用这个方法的位置,等着调用者处理这个异常。

示例一

没有处理异常的代码:有潜在风险
package com.atguigu.exception;

import java.util.Scanner;

public class TestTryCatch {
    public static void main(String[] args) {
        //需求:从键盘输入2个字符串类型的整数值,然后把它们转为int值,求它们的商
        //本来直接输入int类型的整数,用int类型的变量接收即可,这里为了说明问题,故意把问题复杂化
        Scanner input = new Scanner(System.in);

        System.out.print("请输入第一个整数:");
        String str1 = input.next();//123

        System.out.print("请输入第二个整数:");
        String str2 = input.next();

        int a = Integer.parseInt(str1);
        int b = Integer.parseInt(str2);
        int result = a / b;
        System.out.println("商: " + result);

        input.close();
        //上面的代码,编译器没有发现潜在的问题
        //说明一会发生的异常,都是运行时异常
    }
}
加上处理异常的代码:更健壮
  • 如果能通过条件判断避免的,就不用try-catch,例如:除数为0的情况。

  • 如果不能通过条件判断的避免的,就需要用try-catch处理,来增强程序的健壮性

  • 如果有潜在的异常问题,不判断避免,也不用try-catch处理,程序就会很脆弱,一旦发生问题,程序就挂了。以后大家是写服务器端程序,服务器不能随便就挂了。

package com.atguigu.exception;

import java.util.Scanner;

public class TestTryCatch {
    public static void main(String[] args) {
        //需求:从键盘输入2个字符串类型的整数值,然后把它们转为int值,求它们的商
        //本来直接输入int类型的整数,用int类型的变量接收即可,这里为了说明问题,故意把问题复杂化
        Scanner input = new Scanner(System.in);
        int a = 0;

        while (true) {
            try {
                //选中要用try包围的代码,按快捷键Ctrl  + Alt  +T
                System.out.print("请输入第一个整数:");
                String str1 = input.next();//123
                //如果输入张三,Integer.parseInt(str1);代码会报错 java.lang.NumberFormatException
                //NumberFormatException数字格式化异常, “张三" 转换格式,转换为int失败了
                //(1)想要避免这个异常,也能做到,但是需要学习后面的知识,比如正则,判断str1中是不是纯数字(现在还未学习)
                //(2)也可以使用try-catch
                a = Integer.parseInt(str1);
                break;//如果Integer.parseInt(str1)没有发生异常,break就会执行
                     //如果Integer.parseInt(str1)发生异常,break不执行,而且跳到catch执行
            } catch (NumberFormatException e) {
                e.printStackTrace();//打印异常的信息到控制台
            }
        }

        int b;//声明改到while上面,为了提升b变量的作用域
        while(true) {
            try {
                System.out.print("请输入第二个整数:");
                String str2 = input.next();
                b = Integer.parseInt(str2);
                if(b != 0 ){
                    break;
                }else{
                    System.out.println("除数不能为0!");
                }
            } catch (NumberFormatException e) {
                e.printStackTrace();//打印异常的信息到控制台
            }
        }

        int result = a / b;
        System.out.println("商: " + result);

        input.close();
    }
}

 

try-catch和循环的嵌套关系对比

示例二:

没有处理异常的代码:有潜在风险
package com.atguigu.exception;

import java.util.InputMismatchException;
import java.util.Scanner;

public class TestTryCatch3 {
    public static void main(String[] args) {
        //需求:从键盘输入2个整数值,求它们的商
        Scanner input = new Scanner(System.in);


        System.out.print("请输入第一个整数:");
        int  a = input.nextInt();

        System.out.print("请输入第二个整数:");
        int b = input.nextInt();

        int result = a / b;
        System.out.println("商: " + result);

        input.close();
        //上面的代码,编译器没有发现潜在的问题
        //说明一会发生的异常,都是运行时异常
    }
}
加上处理异常的代码:更健壮
package com.atguigu.exception;

import java.util.InputMismatchException;
import java.util.Scanner;

public class TestTryCatch3 {
    public static void main(String[] args) {
        //需求:从键盘输入2个整数值,求它们的商
        Scanner input = new Scanner(System.in);

        int a = 0;
        while (true) {
            try {
                System.out.print("请输入第一个整数:");
                a = input.nextInt();
                break;
                //如果a = input.nextInt();发生异常,break不执行,跳到catch执行
                //如果a = input.nextInt();没有发生异常,break执行,结束循环,catch不会执行
            } catch (InputMismatchException e) {//输入不匹配异常,本来想要让用户输入int值,结果它输入其他的
                e.printStackTrace();//在控制台打印异常信息

                /*
                a = input.nextInt();代码发生异常,因为我们输入的是张三回车,
                而nextInt()只能从输入通道中读取int值,它发现通道里面只有张三回车,
                nextInt()不读取张三,就报异常了。
                为了把张三回车给读取掉,需要加一句代码。 input.nextLine()这样才可以把张三以及张三后面的回车符一起读取掉,
                这样呢,用户才能有机会重新输入
                 */
                input.nextLine();

            }

        }

        int b;
        while (true) {
            try {
                System.out.print("请输入第二个整数:");
                b = input.nextInt();
                if (b != 0) {
                    break;
                } else {
                    System.out.println("除数不能为0!");
                }
            } catch (Exception e) {
                e.printStackTrace();
                input.nextLine();
            }
        }


        int result = a / b;
        System.out.println("商: " + result);

        input.close();
    }
}

1.4 try-catch-finally

try{
    可能发生异常的业务代码语句1;
    可能发生异常的业务代码语句2;
    可能发生异常的业务代码语句3;
    可能发生异常的业务代码语句4;
    可能发生异常的业务代码语句5;
}catch(异常的类型1 参数名){ //参数名一般都是写e
    //(1)打印异常信息的代码 ,要么打印到控制台,要么记录到日志, 打印它的目的是便于程序员后期跟踪,查看发生问题的地方,发生问题的原因,便于后期维护代码
    //(2)对异常的处理代码,有的时候引起异常的问题不处理,下面的代码是无法正常运行。
}catch(异常的类型2 参数名){ 
    //
}catch(异常的类型3 参数名){ 
    //
}finally{
    //无论上面的try是否发生了异常,
    //也不管catch是否可以捕获异常,
    //就算try或catch有return语句,finally块都会执行
    //一般是资源关闭代码写到这里面
    //如果finally里面写了return语句,try,catch中的return语句就失效了
}

示例一

异常没有处理:
package com.atguigu.exception;

import java.util.InputMismatchException;
import java.util.Scanner;

public class TestTryCatchFinally1 {
    public static void main(String[] args) {
        //需求:输入一个整数,然后打印它
        Scanner input = new Scanner(System.in);

        System.out.print("请输入一个整数:");
        int num = input.nextInt();
        System.out.println("num = " + num);//(1)

        input.close();//(2)
        System.out.println("下面的代码:我爱尚硅谷!");//(3)
        //没有异常处理代码,一旦input.nextInt()发生异常,下面所有代码(1)(2)(3)都不执行了,程序就挂了
    }
}
有正确捕获异常:
package com.atguigu.exception;

import java.util.InputMismatchException;
import java.util.Scanner;

public class TestTryCatchFinally2 {
    public static void main(String[] args) {
        //需求:输入一个整数,然后打印它
        Scanner input = new Scanner(System.in);

        try {
            System.out.print("请输入一个整数:");
            int num = input.nextInt();
            System.out.println("num = " + num);//(1)
        } catch (InputMismatchException e) {
            e.printStackTrace();//打印异常
        }


        input.close();//(2)
        System.out.println("下面的代码:我爱尚硅谷!");//(3)
        //上面异常正确捕获,
        //input.nextInt()发生异常,下面的代码(1)不执行,(2)(3)正常执行
    }
}
没有正确捕获异常
package com.atguigu.exception;

import java.util.Scanner;

public class TestTryCatchFinally3 {
    public static void main(String[] args) {
        //需求:输入一个整数,然后打印它
        Scanner input = new Scanner(System.in);

        try {
            System.out.print("请输入一个整数:");
            int num = input.nextInt();
            System.out.println("num = " + num);//(1)
        } catch (ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();//打印异常
        }
        input.close();//(2)
        System.out.println("下面的代码:我爱尚硅谷!");//(3)
        //上面异常没有正确捕获,
        //input.nextInt()发生异常,下面的代码(1)(2)(3)都不执行
    }
}
加finally
package com.atguigu.exception;

import java.util.InputMismatchException;
import java.util.Scanner;

public class TestTryCatchFinally4 {
    public static void main(String[] args) {
        //需求:输入一个整数,然后打印它
        Scanner input = new Scanner(System.in);

        try {
            System.out.print("请输入一个整数:");
            int num = input.nextInt();
            System.out.println("num = " + num);
        } catch (InputMismatchException e) {
            e.printStackTrace();//打印异常
        } finally {
            input.close();
            System.out.println("下面的代码:我爱尚硅谷!");
        }
        //加finally,无论异常是否正确捕获
        //input.nextInt()发生异常,下面的代码(1)不执行,(2)(3)执行
    }
}

示例2:加return

执行finally和try的return
package com.atguigu.exception;

public class TestTryCatchFinally5 {
    public static void main(String[] args) {
        int a = getANumber();
        System.out.println(a);//结果是1
    }

    public static int getANumber(){
        try {
            return 1;
        }catch(Exception e){
            return 2;
        }finally {
            System.out.println("finally");
        }
    }
}
执行finally和catch的return
package com.atguigu.exception;

public class TestTryCatchFinally6 {
    public static void main(String[] args) {
        int a = getANumber();
        System.out.println(a);//返回2
    }

    public static int getANumber(){
        try {
            System.out.println(1/0);
            return 1;
        }catch(Exception e){
            return 2;
        }finally {
            System.out.println("finally");
        }
    }
}
执行finally和finally的return
package com.atguigu.exception;

public class TestTryCatchFinally7 {
    public static void main(String[] args) {
        int a = getANumber();
        System.out.println(a);//返回3
    }

    public static int getANumber(){
        try {
            System.out.println(1/0);
            return 1;
        }catch(Exception e){
            return 2;
        }finally {
            return 3;
        }
    }
}

1.5 throws

1.5.1 throws的用法

这个关键字,用于在方法的(形参列表)后面,方法体{}的前面,写明该方法可能发生xx类型的异常,在该方法中并未“处理”,需要调用者来处理这些异常。

注意:如果调用者是main方法,那么main方法就应该选择try-catch,否则就等价于异常没有处理,一旦发生,程序就挂了。

如果调用者不是main方法,还可以继续throws,让调用 调用者的地方来处理。

【修饰符】 class 类名{
    【修饰符】 返回值类型 方法名(【形参列表】) throws 异常类型列表{
        
    }
}
【修饰符】 class 类名{
    【修饰符】 返回值类型 main(【形参列表】) {
        try{
            b(实参列表);
        }catch(异常类型 e){
            //....
        }
        //其他代码(正常执行)
        
    }
    修饰符】 返回值类型 b(【形参列表】) throws 异常类型列表{
        c(实参列表);//语句1  
        其他语句2 ; 
        //如果c方法中的语句B发生异常,等价于语句1 这句代码发生异常,其他语句2不会执行,b方法就会挂掉,并且带着异常对象回到main方法
    }
    修饰符】 返回值类型 c(【形参列表】) throws 异常类型列表{
        语句A;
        语句B;//可能发生异常的代码
        语句C;
        //如果语句B发生异常,语句C不会执行,c方法就会挂掉,并且带着异常对象回到b方法。
    }
}
【修饰符】 class 类名{
    【修饰符】 返回值类型 main(【形参列表】) {
         b(实参列表);
        
    }
    修饰符】 返回值类型 b(【形参列表】) {
        try{
            c(实参列表);//语句1 
        }catch(异常类型 e){
            //....
        }
         其他语句2 ; 
        //如果c方法中的语句B发生异常,等价于语句1 这句代码发生异常,因为这里有try-catch把异常捕获了,
        //那么其他语句2会执行
    }
    修饰符】 返回值类型 c(【形参列表】) throws 异常类型列表{
        语句A;
        语句B;//可能发生异常的代码
        语句C;
        //如果语句B发生异常,语句C不会执行,c方法就会挂掉,并且带着异常对象回到b方法。
    }
}

1.5.2 方法重写的要求

方法的重载(Overload)方法的重写(Override)
声明的位置同一个类中 或 父子类中父子类中
权限修饰符不看应该 > 或 = 被重写方法的权限修饰符,并且被重写方法不能是private
其他修饰符不看不能是static,final
返回值类型不看基本数据类型和void:必须相同 引用数据类型:应该 < 或 = 被重写方法的返回值类型
方法名必须相同必须相同
(形参列表)必须不同(类型、个数、顺序不同,和形参名无关)必须相同(类型、个数、顺序相同,和形参名无关)
throws 异常类型不看如果被重写方法没有加throws 编译时异常类型列表,那么重写时,不可以再加throws 编译时异常类型列表。 如果被重写方法加throws 编译时异常类型列表,那么重写时,throws后面的编译时类型异常 必须满足 < 或 = 的关系。 和运行时类型的异常无关。
父类Father示例代码
package com.atguigu.exception;

import java.io.FileNotFoundException;

public class Father {
    public void m1()throws Exception{
        //....
    }

    public void m2()throws Exception{
        //....
    }

    public void m3()throws FileNotFoundException {
        //....
    }

    public void m4(){
        //....
    }

    public void m5(){

    }
}
子类Son示例代码
package com.atguigu.exception;

import java.io.FileNotFoundException;

public class Son extends Father{
    @Override
    public void m1() throws Exception {//throws的异常类型与父类相同
        ///
    }

    //FileNotFoundException < Exception
    @Override
    public void m2() throws FileNotFoundException {
        //....
    }

/*    //Exception > FileNotFoundException
    @Override
    public void m3() throws Exception {//错误
        //...
    }*/

/*    @Override
    public void m4() throws FileNotFoundException {//错误,因为父类被重写方法没有throws编译时异常类型
        //....
    }*/

    //运行时异常,编译器根本不检查
    public void m5()throws ArithmeticException{

    }
}

1.5.3 java.lang.Cloneable接口

Cloneable接口:克隆接口,克隆就是复制的意思。

在java.lang.Object类中有一个clone方法:

protected native Object clone() throws CloneNotSupportedException;

权限修饰符:protected(受保护),表示这个方法只能在本类、本包、其他包的子类本身中调用。

说明:重写clone方法的类,还必须实现java.lang.Cloneable接口。

  • 如果子类不重写clone方法,不能在子类以外的地方,调用子类对象的clone方法。例如:Student类没有重写clone方法,就不能在测试类TestCloneable中调用学生对象的clone方法

  • 如果子类不实现Cloneable接口,那么就算子类重写clone方法,也会发生CloneNotSupportedException异常。

  • 调用clone方法时,编译器会提醒你需要处理CloneNotSupportedException异常,因为CloneNotSupportedException属于编译时受检异常。

Student类
package com.atguigu.exception;

public class Student implements Cloneable{
    private String name;
    private int score;

    public Student() {
    }

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }

    //重写时把方法的返回值类型从Object修改为Student,这是可以的  <=
    //重写时权限修饰符可以从protected修改为public,这是可以的   >=
    @Override
    public Student clone() throws CloneNotSupportedException {
        return (Student) super.clone();
    }
}
测试类TestCloneable
package com.atguigu.exception;

public class TestCloneable {
    public static void main(String[] args) {
        Student s = new Student("张三", 100);
        //复制一个s对象
/*        Student s2 = s;//不是复制,是两个变量s,s2都指向同一个对象
        s.setName("张四");
        System.out.println(s);
        System.out.println(s2);*/

        try {
            Student s2 = s.clone();//克隆一个对象,复制一个属性值一模一样的对象
            //s对象在调用clone()方法,s对象是Student类型,
            //只能在Student类中调用这个方法
            //解决办法:让Student类重写clone方法
            s2.setName("张四");
            System.out.println(s);
            System.out.println(s2);
        } catch (CloneNotSupportedException e) {//编译器提醒我们要try-catch处理,说明CloneNotSupportedException是编译时(受检)异常
            e.printStackTrace();
        }
    }
}

1.6 练习题讲解

练习题1

package com.atguigu.exer2;

public class Test2 {
    public static void main(String[] args) {
        int test = test(3,5);
        System.out.println(test);//8
    }

    public static int test(int x, int y){
        int result = x;
        try{
            if(x<0 || y<0){//条件不成立
                return 0;//不执行
            }
            result = x + y;//执行 result = 8
            return result; //执行它
            /*
            return有两个作用:
            (1)返回结果给调用者    先把result变量的值8返回给调用者
            (2)结束当前方法      本来应该结束当前方法的执行,但是因为有finally,要先去执行finally
                            //执行完finally,结束当前方法的执行
             */
        }finally{
            result = x - y;//修改result ,result = -2
        }
    }
}

练习题2

package com.atguigu.exer3;

public class Test3 {
    static int i = 0;

    public static void main(String[] args) {
        System.out.println(test());//2
    }

    public static int test(){
        try{
            return ++i;
            /*
            return有两个作用:
            (1)返回结果给调用者    计算++i的值,i=1,返回i的值1的给调用者
            (2)结束当前方法      本来应该结束当前方法的执行,但是因为有finally,要先去执行finally
             */
        }finally{
            return ++i;
            /*
            return有两个作用:
            (1)返回结果给调用者    计算++i的值,i=2,返回i的值2的给调用者
            (2)结束当前方法      结束当前方法的执行
             */
        }
    }
}

1.7 自定义异常

1、为什么要自定义?

虽然核心类库中已经给我们预备了很多的异常类型了,但是我们有时候仍然需要自定义异常类型。因为异常的类型名最好能够清晰的描述我们发生异常的问题,即异常类型名最好能见名知意。系统预定义的这些异常类型,可能并不能准确的描述你的业务层面的一些异常问题,所以就最好自定义。

2、如何自定义异常类型

  • 必须继承Throwable或其子类。通常会继承Exception(继承它,你的自定义异常就是受检异常)或RuntimeException(继承它,你的自定义异常就是非受检异常)

    • 因为只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。

    • 类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。

  • 建议自定义异常时,提供无参构造以及能为从父类继承的message属性赋值的有参构造

3、注意

自定义异常需要通过throw语句抛出,同样可以throws告诉调用者处理,最后仍然使用try-catch。

自定义异常类型:钱不能为负数的异常
package com.atguigu.exception;

//钱不能为负数的异常
public class MoneyCannotNegativeException extends Exception{
    public MoneyCannotNegativeException() {
    }

    public MoneyCannotNegativeException(String message) {
        super(message);
    }
}
自定义异常类型:钱不够异常
package com.atguigu.exception;

//钱不够异常
public class MoneyNotEnoughException extends Exception{
    public MoneyNotEnoughException() {
    }

    public MoneyNotEnoughException(String message) {
        super(message);
    }
}
银行账号类Account
package com.atguigu.exception;

public class Account {
    private String id;//账号
    private double balance;//余额

    public Account() {
    }

    public Account(String id, double balance) {
        this.id = id;
        this.balance = balance;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public double getBalance() {
        return balance;
    }

    public void withdraw(double money) throws MoneyCannotNegativeException, MoneyNotEnoughException {
        if(money < 0){
//            System.out.println("取款不能为负数");
//            throw new MoneyCannotNegativeException();
            throw new MoneyCannotNegativeException("取款不能为负数!");
        }else if(money > balance){
//            System.out.println("余额不足!");
//            throw new MoneyNotEnoughException();
            throw new MoneyNotEnoughException("余额不足!");
        }else{
            balance -= money;
        }
    }

    public void save(double money) throws MoneyCannotNegativeException {//存款
        if(money < 0){
//            System.out.println("存款不能为负数");
//            throw new MoneyCannotNegativeException();
            throw new MoneyCannotNegativeException("存款不能为负数!");
        }else{
            balance += money;
        }
   }

    @Override
    public String toString() {
        return "Account{" +
                "id='" + id + '\'' +
                ", balance=" + balance +
                '}';
    }
}
测试类
package com.atguigu.exception;

public class TestAccount {
    public static void main(String[] args) {
        Account a = new Account("111111", 5000);
        try {
            a.save(500);
            System.out.println("存500后:"+a);
        } catch (MoneyCannotNegativeException e) {
            e.printStackTrace();
        }

        try {
            a.save(-500);
            System.out.println("存-500后:"+a);
        } catch (MoneyCannotNegativeException e) {
            e.printStackTrace();
        }

        try {
            a.withdraw(500);
            System.out.println("取500后:"+a);
        } catch (MoneyCannotNegativeException e) {
            e.printStackTrace();
        } catch (MoneyNotEnoughException e) {
            e.printStackTrace();
        }

        try {
            a.withdraw(6000);
            System.out.println("取6000后:"+a);
        } catch (MoneyCannotNegativeException e) {
            e.printStackTrace();
        } catch (MoneyNotEnoughException e) {
            e.printStackTrace();
        }
    }
}

1.8 思考题:throw与throws有什么区别

throwthrows
位置方法体里面(形参列表)的后面,方法体的前面
作用抛出一个异常的对象告诉调用者该方法可能发生xx类型的异常
后面接的东西不同throw 后面跟一个异常对象throws后面跟一个或多个的异常的类型名
使用形式throw new 异常类型(【实参列表】);【修饰符】 返回值类型 方法名(【形参列表】) throws 异常类型1,异常类型2{ }

二、根父类

Object 是类层次结构的根类。每个类都使用 Object 作为超类。所有对象(包括数组)都继承这个类的方法。

public String toString():
    默认返回对象的运行时类型(new对象的类型)+@+对象的hashCode值是十六进制形式。类似于虚拟地址。
    建议所有子类都重写此方法。重写的快捷键:Alt + Insert 或 Ctrl + O。
    打印对象时,会自动调用toString()。
protected Object clone()throws CloneNotSupportedException
    如果子类对象要调用clone方法,要求子类重写clone方法。
    并且子类要实现java.lang.Cloneable接口,否则会报CloneNotSupportedException。
public final Class<?> getClass():返回此对象的运行时类(new对象的类型)。
protected void finalize()throws Throwable 
    这个方法已经被废弃了。
    比喻:它用来留临终遗言
    实际:当一个类的对象在被GC(垃圾回收器)回收之前(即彻底要在JVM中消失了),会自动调用这个对象的finalize()。通常在这个方法中,编写释放该对象占用的一些系统资源。
    有些公司的题库比较老,有一个这样的面试题:finally,final,finalize的区别?
    finally是与try-catch一起使用的关键字。
    final是用来修改类、方法、变量的关键字。
    finalize是Object中一个方法,用于释放系统资源的方法,现在已经废弃了。
public boolean equals(Object obj):指示其他某个对象是否与此对象“相等”。 
    如果用模板生成,就没有问题,但是如果自己手动重写,需要遵守如下要求:强烈建议用快捷键生成,不要手动重写。
    (1)自反性:自己和自己比较返回true
    (2)传递性:x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也一定返回true
    (3)对称性:x.equals(y)返回true,那么y.equals(x)也一定返回true
    (4)一致性:x..equals(y)前面返回true,x和y的属性都没有修改,下面再次调用x..equals(y)也要返回true
    (5)一个非空对象.equals(null)一定返回false
public int hashCode():返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。 
    只有把对象放到哈希结构的容器中,这个方法才有意义,否则没有意义。(等后面再学习它)
    但是,记住,equals和hashCode方法一定要一起重写。
    如果用模板生成,就没有问题,但是如果自己手动重写,需要遵守如下要求:强烈建议用快捷键生成,不要手动重写。
    (1)如果两个对象equals方法返回true,两个对象的hashCode()的结果必须相同。
    (2)如果两个对象的hashCode()不同,那么这个两个对象equals方法返回false。
    (3)如果两个对象的hashCode()相同,那么这两个对象的equals方法可能是true,可能是false。

 

示例代码1:getClass()

package com.atguigu.object;

import com.atguigu.exception.Father;
import com.atguigu.exception.Son;

public class TestObjectAPI {
    public static void main(String[] args) {
        Object obj = "hello";//多态引用
        System.out.println(obj.getClass());//class java.lang.String
        /*
        obj的编译时类型是Object,看左边
        obj的运行时类型是String,看右边
         */

        Object obj2 = 1;
        System.out.println(obj2.getClass());//class java.lang.Integer
        //Object是引用数据类型,不能赋值基本数据类型的值。如果给它基本数据类型的值,会自动装箱为包装类的对象。

        Father f = new Son();
        System.out.println(f.getClass());//class com.atguigu.exception.Son

        String str = "hello";
        System.out.println(str.getClass());//class java.lang.String
    }
}

示例代码2:finalize(了解)

package com.atguigu.object;

public class Demo {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("我轻轻的走了,不带走一段代码....");
    }
}
package com.atguigu.object;

public class TestDemo {
    public static void main(String[] args) {
        Demo d = new Demo();
        d = null;//d变量不引用上面的对象了,那么上面的对象就成垃圾了

        System.gc();//主动呼叫GC来回收内存垃圾

        try {
            Thread.sleep(5000);//停秒,别那么着急结束main
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

示例代码3:equals

Person类
package com.atguigu.object;

public class TestDemo {
    public static void main(String[] args) {
        Demo d = new Demo();
        d = null;//d变量不引用上面的对象了,那么上面的对象就成垃圾了

        System.gc();//主动呼叫GC来回收内存垃圾

        try {
            Thread.sleep(5000);//停秒,别那么着急结束main
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
测试类
package com.atguigu.object;

import java.util.Scanner;

public class TestPerson {
    public static void main(String[] args) {
        Person p1 = new Person("张三",23);
        Person p2 = new Person("张三",23);
        System.out.println(p1 == p2);//false
        //==比较的是对象的首地址,new了两次,两个对象的首地址一定是不同的
        System.out.println(p1.equals(p2));
        /*
        (1)如果Person类没有重写equals方法,从Object继承的equals也是比较对象的首地址。
        (2)如果想要Person对象调用equals方法时,比较对象的属性值,那么就应该重写equals方法。
        重写equals方法的快捷键:Alt + Insert
         */

      //  System.out.println(p1.equals(p1));//比较地址值就可以了
        System.out.println(p1.equals(null));
        //p1如果为null,就发生空指针异常了,不会进入equals方法
        //p1如果不为null,一个非空对象与一个null一定是不相等,就返回false

        System.out.println(p1.equals("张三"));
        //p1是Person类型的,“张三"是String类型,类型不同,返回false

        System.out.println("=======================");
        Integer i = 1;
        Integer j = 1;
        System.out.println(i == j);//true

        Integer a = 200;
        Integer b = 200;
        System.out.println(a==b);//false
        System.out.println(a.equals(b));//true
        //Integer重写了equals方法,比较两个包装类对象的数据值

        System.out.println("===================");
        Scanner input = new Scanner(System.in);

        System.out.print("请输入姓名:");
        String name = input.next();

        //判断输入的姓名是否是张三
        System.out.println(name == "张三");
        System.out.println(name.equals("张三"));
        //String类重写了equals方法

        //结论:以后凡是引用数据类型,包括字符串,包装类,你自己写的类的对象比较是否相等
        //都不要用==,而是用equals方法

        input.close();
    }
}

示例代码4:hashCode

package com.atguigu.object;

public class TestHashCode {
    public static void main(String[] args) {
        System.out.println("Aa".hashCode());//2112
        System.out.println("BB".hashCode());//2112
        System.out.println("Aa".equals("BB"));//false

        Person p1 = new Person("张三",23);
        Person p2 = new Person("张三",23);
        System.out.println(p1.hashCode());//24022543
        System.out.println(p2.hashCode());//24022543
        System.out.println(p1.equals(p2));//true
    }
}

三、native(了解)

在Object类中,我没看到了

public native int hashCode();
protected native Object clone() throws CloneNotSupportedException;

native:本地的,原生的。

在Java中,表示这个方法的方法体不是用Java语言实现的,而是调用底层的C或C++的代码。所以,在Java层面看不到它的方法体。

但是,在Java中你可以当成普通的Java方法一样调用,一样重写。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值