异常处理以及包

137 阅读15分钟

8.1 Java异常

1、什么是异常?
异常:(字面意思),不正常。
    不是异常:(1)编译错误,语法错误,无法允许
            (2)逻辑错误,语法正确,永远得不到正确的结果
    大部分异常:
            大部分时候可以正常运行,只是某些情况会无法运行。
            某些情况:A:用户的非法输入
                    B:网络中断
                    C:磁盘空间已满
                    ....
    特殊异常:
            违反了程序的逻辑规定,例如:下标越界,空指针异常,算术异常等(尽量避免)
​
2、Java如何表示异常?
Java中一切皆对象。同样,异常也是用某些类型的对象来表示。
例如:下标越界异常 ==> ArrayIndexOutOfBoundsException
     空指针异常   ==> NullPointerException
     算术异常     ==> ArithmeticException
     类型转换异常  ==> ClassCastException
     ....
import java.util.Scanner;
​
public class TestException {
    public static void main(String[] args) {
/*        int a;
        System.out.println(a);*///        System.out.println(add(1,1));
        
        Scanner input = new Scanner(System.in);
        
/*        System.out.print("请输入一个整数:");
        int num = input.nextInt();
        System.out.println("num = " + num);
        
        input.close();*///        int[] arr = {1,2,3,4,5};
//        System.out.println(arr[5]);
//
//        System.out.println(1/0);
​
        //FileInputStream fis = new FileInputStream("d:\1.txt");//表示我要读取d:\1.txt文件的 内容
        
    }
​
    public static int add(int a, int b){
        return a - b;
    }
}
​

8.2 异常体系结构

3、异常的类型体系结构
(1)根类型(作为异常和错误的根类型):java.lang.Throwable
    类的根仍然是Object。
​
    Throwable 类是 Java 语言中所有错误或异常的超类。
    只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句“抛”出。
    类似地,只有此类或其子类之一才可以是 catch (“捕获”)子句中的参数类型。
    两个子类的实例,ErrorException,通常用于指示发生了异常情况。
(2Error:用于指示合理的应用程序不应该试图捕获的严重问题。
    例如:VirtualMachineError(虚拟机错误)
       它的子类:StackOverflowError(栈内存溢出,在无限循环递归见过)
               OutOfMemoryError(堆内存溢出)
(3Exception:指出了合理的应用程序想要捕获的条件。
​
Exception又可以分为两大类:
A:运行时异常:RuntimeException及其子类,建议能靠程序员的检查,代码的判断等避免,尽量避免。
    例如:
         下标越界异常 ==> ArrayIndexOutOfBoundsException
         空指针异常   ==> NullPointerException
         算术异常     ==> ArithmeticException
         类型转换异常  ==> ClassCastException
    编译器不会提前”检查或提示“你的代码会发生xx运行时异常。无论你这个异常是否真的会发生。
B:编译时异常:Exception下面除了  运行时异常,其余都是编译时异常。
    编译器 会 明确“提示”你的代码“可能”发生xx编译时异常,哪怕你的代码并不会发生这个异常。
    而且要求你“必须”提前编写好异常处理的代码,否则编译器不会放过你的,编译不通过。
​
    比喻:
        消防部分要求所有的公司都要准备消防器材。没有准备这些,一定不让开业。
​
    例如:FileNotFoundException:文件找不到异常
         IOException
         SQLException
         ....

8.3 异常的抛出和处理机制

4、Java中的异常抛出和处理机制
当程序发生异常时,JVM会创建一个对应类型的异常对象,并且“抛”出,
如果该语句的外围有try..catch处理,可以“捕获”该异常,那么程序可以继续,
否则该异常对象就会被抛给上级,直到main,导致程序终止。

工具类

public class ArrayTools {
    // 对给定的数组通过给定的角标获取元素。
    public static int getElement(int[] arr, int index) {
        int element = arr[index];
        return element;
    }
}

测试类

public class ExceptionDemo {
    public static void main(String[] args) {
        int[] arr = { 34, 12, 67 };
        int num = ArrayTools.getElement(arr, 4);
        System.out.println("num=" + num);
        System.out.println("over");
    }
}

上述程序执行过程图解:

异常产生过程.png

1562772282750.png

8.5 try-catch处理

5、异常的处理关键字
(1try-catch
try{
    编写可能发生异常的业务代码
}catch(异常的类型1 异常对象名){ //异常对象名习惯上写e
    捕获到“异常的类型1”的异常对象后,要如何处理的代码。
    例如:打印异常信息也是一种处理,什么都不写也是一种处理。
}catch(异常的类型2 异常对象名){ //异常对象名习惯上写e
    捕获到“异常的类型2”的异常对象后,要如何处理的代码。
        例如:打印异常信息也是一种处理,什么都不写也是一种处理。
}
​
多个catch分支,从上往下判断,如果上面的匹配了,下面的就不看了。
如果都没有匹配,就相当于没有编写try-catch,即异常没有被处理。
JVM会自动抛给上级,如果一直到main方法都没有处理,就挂了。
​
​
快捷键:选中要加try-catch的代码,按Ctrl + Alt + T
​
(2)JDK1.7新特性
try{
    编写可能发生异常的业务代码
}catch(异常的类型1 | 异常的类型2 异常对象名){ //异常对象名习惯上写e
    捕获到“异常的类型1,2”的异常对象后,要如何处理的代码。
    例如:打印异常信息也是一种处理,什么都不写也是一种处理。
}catch(异常的类型3 异常对象名){ //异常对象名习惯上写e
    捕获到“异常的类型3”的异常对象后,要如何处理的代码。
        例如:打印异常信息也是一种处理,什么都不写也是一种处理。
}
import java.util.InputMismatchException;
import java.util.Scanner;
​
public class TestTryCatch1 {
    public static void main(String[] args) {
        //从键盘输入一个正整数
        Scanner input = new Scanner(System.in);
        
        int num;
        while(true) {
            try {
                System.out.print("请输入一个正整数:");
                num = input.nextInt();
​
                if (num <= 0) {
                    System.out.println("请重新输入,要求是正整数!");
                }else{
                    break;
                }
            } catch (InputMismatchException e) {
                System.out.println("请重新输入,要求是整数");
                input.nextLine();
//               String str=  input.nextLine();//清理上次错误的输入数据
//                System.out.println("错误数据:" + str);
            }
        }
        System.out.println("num = " + num);
        
        input.close();
        
        
    }
}
​
import java.util.InputMismatchException;
import java.util.Scanner;
​
public class TestTryCatch2 {
    public static void main(String[] args) {
        //从键盘输入一个正整数
        Scanner input = new Scanner(System.in);
​
        int num = 0;
        try {
            while (true) {
                System.out.print("请输入一个正整数:");
                num = input.nextInt();
​
                if (num <= 0) {
                    System.out.println("请重新输入,要求是正整数!");
                } else {
                    break;
                }
            }
        } catch (InputMismatchException e) {
            System.out.println("请重新输入,要求是整数");//无法重新输入,因为catch完就执行下面的代码
            input.nextLine();
        }
        System.out.println("num = " + num);
​
        input.close();
​
​
    }
}
import java.util.InputMismatchException;
import java.util.Scanner;
​
public class TestTryCatch3 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
​
        int bei;
        while(true) {
            try {
                System.out.print("请输入被除数:");
                bei = input.nextInt();
                break;
            } catch (Exception e) {
                input.nextLine();
                System.out.println("刚才输入的不是整数");
            }
        }
​
        int chu;
        while(true) {
            try {
                System.out.print("请输入除数:");
                chu = input.nextInt();
​
                System.out.println("商:" + bei/chu);
​
                break;
            } catch (InputMismatchException e) {
                input.nextLine();
                System.out.println("刚才输入的不是整数");
            }catch(ArithmeticException e){ //多个catch
                System.out.println("除数不能为0");
            }
        }
​
        input.close();
​
    }
}
​

8.6 异常信息的打印

6、异常的输出
(1)System.out.println(xx); ==》 普通信息输出
(2)System.err.println(xx); ==> 错误信息输出
(3)e.printStackTrace();  ==> 标准的异常信息输出
    输出异常的详细信息,包括异常的类型、异常的message说明信息、异常的堆栈跟踪信息。
​
import java.util.InputMismatchException;
import java.util.Scanner;
​
public class TestTryCatch4 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
​
        int bei;
        while(true) {
            try {
                System.out.print("请输入被除数:");
                bei = input.nextInt();
                break;
            } catch (Exception e) {
                input.nextLine();
//                System.out.println("刚才输入的不是整数");
                System.err.println("刚才输入的不是整数");
            }
        }
​
        int chu;
        while(true) {
            try {
                System.out.print("请输入除数:");
                chu = input.nextInt();
​
                System.out.println("商:" + bei/chu);
​
                break;
            } catch (InputMismatchException e) {
                input.nextLine();
//                System.out.println("刚才输入的不是整数");
                System.err.println("刚才输入的不是整数");
            }catch(ArithmeticException e){
//                System.out.println("除数不能为0");
                System.err.println("除数不能为0");
            }
        }
​
        input.close();
​
    }
}
​
import java.util.InputMismatchException;
import java.util.Scanner;
​
public class TestTryCatch5 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
​
        int bei;
        while(true) {
            try {
                System.out.print("请输入被除数:");
                bei = input.nextInt();
                break;
            } catch (Exception e) {
                input.nextLine();
//                System.out.println("刚才输入的不是整数");
//                System.err.println("刚才输入的不是整数");
                e.printStackTrace();
            }
        }
​
        int chu;
        while(true) {
            try {
                System.out.print("请输入除数:");
                chu = input.nextInt();
​
                System.out.println("商:" + bei/chu);
​
                break;
            } catch (InputMismatchException e) {
                input.nextLine();
//                System.out.println("刚才输入的不是整数");
//                System.err.println("刚才输入的不是整数");
                e.printStackTrace();
            }catch(ArithmeticException e){
//                System.out.println("除数不能为0");
//                System.err.println("除数不能为0");
                e.printStackTrace();
            }
        }
​
        input.close();
​
    }
}
​

8.7 finally

7finally关键字
(1try-catch-finally
try{
    编写可能发生异常的业务代码
}catch(异常的类型1 异常对象名){ //异常对象名习惯上写e
    捕获到“异常的类型1”的异常对象后,要如何处理的代码。
    例如:打印异常信息也是一种处理,什么都不写也是一种处理。
}catch(异常的类型2 异常对象名){ //异常对象名习惯上写e
    捕获到“异常的类型2”的异常对象后,要如何处理的代码。
        例如:打印异常信息也是一种处理,什么都不写也是一种处理。
}finally{
    必须执行的代码。
    无论try{}是否发生异常,也不管catch是否能够捕获异常,或者有没有catch都无所谓,
    必须执行finally里面的代码。
    甚至try{},catch{}有return语句,都阻挡不了finally的执行。
​
    通常这里面编写的是资源释放的代码。例如:IO流的关闭、数据库链接的断开、网络断开等代码。
}
​
​
(2try-finally
try{
    编写可能发生异常的业务代码
}finally{
​
}

​
public class TestFinally {
    public static void main(String[] args) {
/*        try{
            int a = 1;
            int b = 0;
            System.out.println(a/b);
        }catch(ArithmeticException e){
//            e.printStackTrace();
            System.out.println("除数不能为0");
        }finally{
            System.out.println("finally");
        }*/
/*        try{
            int a = 1;
            int b = 1;
            System.out.println(a/b);
        }catch(ArithmeticException e){
            e.printStackTrace();
        }finally{
            System.out.println("finally");
        }*/
/*        try{
            int a = 1;
            int b = 0;
            System.out.println(a/b);
        }catch(NullPointerException e){
//            e.printStackTrace();
            System.out.println("空指针异常");
        }finally{
            System.out.println("finally");
        }*/
​
/*        try{
            int a = 1;
            int b = 0;
            System.out.println(a/b);
            return;//结束当前方法,这里结束main
        }catch(ArithmeticException e){
            e.printStackTrace();
            return;
        }finally{
            System.out.println("finally");
        }*/
​
        /*try{
            int a = 1;
            int b =1 ;
            System.out.println(a/b);
            System.exit(0);//退出JVM  了解
        }catch(ArithmeticException e){
            e.printStackTrace();
        }finally{
            System.out.println("finally");
        }*/
    }
}
​
import java.util.InputMismatchException;
import java.util.Scanner;
​
public class TestFinally2 {
    public static void main(String[] args) {
        //从键盘输入一个正整数
        Scanner input = new Scanner(System.in);
​
        int num;
        while(true) {
​
            try {
                System.out.print("请输入一个正整数:");
                num = input.nextInt();
​
                if (num <= 0) {
                    System.out.println("请重新输入,要求是正整数!");
                }else{
                    break;
                }
            } catch (InputMismatchException e) {
                System.out.println("请重新输入,要求是整数");
                input.nextLine();
//               String str=  input.nextLine();//清理上次错误的输入数据
//                System.out.println("错误数据:" + str);
            }/*finally{
                input.close();//暂时还不能关闭
            }*/
        }
        System.out.println("num = " + num);
​
​
        input.close();
​
    }
}
​

8.8 throws

8throws关键字
用于在方法签名(或者称为方法头)中表示当前方法“可能”发生xx类型异常,当前方法并未处理,需要调用者注意和处理的。
【修饰符】 返回值类型 方法名(【形参列表】)throws 异常类型列表{
    方法体代码
}
​
理论上,throws后面所有可能发生的异常类型都可以列出来的。
但是通常情况下,一般只列出编译时类型异常,或非常重要的,需要特别提醒调用者的运行时异常类型。
​
例如:java.lang.Object类,
    protected Object clone() throws CloneNotSupportedException
    如果对象的类不支持 Cloneable 接口,则重写 clone 方法的子类也会抛出此异常,以指示无法复制某个实例。
​
重写:父子类中(包含父接口与实现类)
    方法名相同
    形参列表也相同(形参的类型、个数、顺序)
    返回值类型(<=),(基本数据类型和void是必须相同,引用数据类型<=)
    权限修饰符(>=)(但是要求父类被重写的方法在子类中是可见的,即不能是private,跨包不能是缺省),
    其他修饰符(静态方法,final方法等不允许被重写),
    throws 异常类型列表:
            如果父类被重写方法的方法签名后面没有 “throws  编译时异常类型”,那么重写方法时,方法签名后面也不能出现“throws  编译时异常类型”。
            如果父类被重写方法后面有throws 编译时异常类型列表,重写时①不加throws 编译时异常类型(异常处理了)
                                                            ②继续throws 编译时异常类型,但是要求该异常类型要<=父类throws后面的编译时异常类型
            对于throws后面的运行时异常类型不做任何要求。
​
public class Rectangle implements Cloneable{
    private double length;
    private double width;
​
    public Rectangle() {
    }
​
    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }
​
    public double getLength() {
        return length;
    }
​
    public void setLength(double length) {
        this.length = length;
    }
​
    public double getWidth() {
        return width;
    }
​
    public void setWidth(double width) {
        this.width = width;
    }
​
    @Override
    public String toString() {
        return "Rectangle{" +
                "length=" + length +
                ", width=" + width +
                '}';
    }
​
    @Override
    protected Rectangle clone() throws CloneNotSupportedException {
        return (Rectangle) super.clone();
    }
}
​
public class TestRectangle {
    public static void main(String[] args) {
        Rectangle r = new Rectangle(5,3);
        System.out.println(r);
​
        //需求:根据r对象“复制一个,克隆一个”同样长和宽的矩形对象。
        /*
        java.lang.Object类中有protected Object clone() throws CloneNotSupportedException
        按理说Object类有这个方法,所有子类都应该有。
        但是因为该方法的权限修饰符(访问控制修饰符)是protected,
        可见性范围:
            本类:Object本类
            本包:java.lang包
            其他包子类中:
                如果是Rectangle这个子类的对象要调用,必须是Rectangle这个子类中。
                而此时是在TestRectangle类中。
        解决方案:Rectangle类必须重写Object类的clone方法。
         */
        try {
            Rectangle other = r.clone();
            System.out.println(other);
        } catch (CloneNotSupportedException e) {
            System.out.println("无法完成克隆");
        }
​
        System.out.println("下面的代码");
    }
}
​

8.9 throw

9throw关键字
throw用于 在成员方法的方法体、构造器的方法体、代码块(后面反射再学)中用于“手动”抛出一个异常对象。
    通常情况下,异常对象都是由JVM帮我们new,帮我们抛出的,但是有时候我们也可以“自己”new异常对象,自己抛异常对象。
语法格式:
    throw new 异常类型(【实参列表】);
​
如果在throw语句的外围没有try..catch,那么throw语句会代替return语句结束当前方法,带回异常对象。
public class TestThrow {
    public static void main(String[] args) {
        System.out.println(max(4,2,3,1,6,3));
        try {
            System.out.println(max());
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            System.out.println(max(null));
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            System.out.println(max(new int[]{}));
        } catch (Exception e) {
            e.printStackTrace();
        }
​
        try {
            System.out.println(max(new int[0]));
        } catch (Exception e) {
            e.printStackTrace();
        }
​
        try {
            System.out.println(max(1,2));
        } catch (Exception e) {
            e.printStackTrace();
        }
​
    }
​
    public static int max(int... nums){
        if(nums == null || nums.length == 0){
            throw new IllegalArgumentException("nums不代表一组int整数");
        }
​
        int max = nums[0];
        for (int i = 1; i < nums.length; i++) {
            if(nums[i] > max){
                max = nums[i];
            }
        }
        return max;
    }
}
​

8.10 自定义异常

10、自定义异常类型
要求:自定义异常类型必须继承Throwable或者其子类,通常都是继承ExceptionRuntimeException.
        如果是继承Exception类型,那么你的自定义异常类型是编译时异常类型。
        如果是继承RuntimeException类型,那么你的自定义异常类型是运行时异常类型。
建议:自定义异常类型中包含几个构造器:无参构造,有参构造(给message赋值的构造器)
注意:自定义异常类型的对象只能程序员自己new,自己throw

​
public class NotATriangleException extends Exception{
    public NotATriangleException() {
    }
​
    public NotATriangleException(String message) {
        super(message);
    }
}
​

​
public class TriangleBaseNotNegativeException extends Exception{
    public TriangleBaseNotNegativeException() {
    }
​
    public TriangleBaseNotNegativeException(String message) {
        super(message);
    }
}
​
public class Triangle {
    private final double a;
    private final double b;
    private final double c;
​
    public Triangle(double a, double b, double c) throws TriangleBaseNotNegativeException,NotATriangleException {
        if(a < 0 || b < 0 || c <0){
//            System.out.println("三角形边长不能为负数");
            throw new TriangleBaseNotNegativeException("三角形边长不能为负数");
        }
​
        if(a+b<=c || b+c<=a || a+c<=b){
            throw new NotATriangleException("三角形边长必须满足任意两边之和大于第三边");
        }
​
        this.a = a;
        this.b = b;
        this.c = c;
    }
​
    public double getA() {
        return a;
    }
​
    public double getB() {
        return b;
    }
​
    public double getC() {
        return c;
    }
​
    @Override
    public String toString() {
        return "Triangle{" +
                "a=" + a +
                ", b=" + b +
                ", c=" + c +
                '}';
    }
}
​
/*
表示三角形的边长不能为负数,以及三角形的边长没有满足任意两边之和大于第三边的要求
 */
public class TestDefineException {
    public static void main(String[] args) {
        try {
            Triangle t = new Triangle(3,4,5);
            System.out.println(t);
        } catch (TriangleBaseNotNegativeException e) {
            e.printStackTrace();
        } catch (NotATriangleException e) {
            e.printStackTrace();
        }
​
        try {
            Triangle t = new Triangle(-3,4,5);
            System.out.println(t);
        } catch (TriangleBaseNotNegativeException e) {
            e.printStackTrace();
        } catch (NotATriangleException e) {
            e.printStackTrace();
        }
​
        try {
            Triangle t = new Triangle(1,1,5);
            System.out.println(t);
        } catch (TriangleBaseNotNegativeException e) {
            e.printStackTrace();
        } catch (NotATriangleException e) {
            e.printStackTrace();
        }
    }
}
​

9.1 包装类

9.1.1 认识包装类

1、什么是包装类?
基本数据类型->包装类
byte    <->  Byte
short   <->  Short
int     <->  Integer
long    <->  Long
float   <->  Float
double  <->  Double
char    <->  Character
boolean <->  Boolean2、为什么要用包装类?
Java是面向对象的编程语言,很多的语法和API的设计都是面向“对象”设计的,
或者是针对引用数据类型设计的,不支持基本数据类型,
例如:
    泛型、集合等都不支持基本数据类型。
​
Java不是“纯”面向对象语言。Java设计时保留了C/C++语言的8种基本数据类型,
以及针对这些基本数据类型而设计的丰富的运算符。
另外,因为基本数据类型比较简单,简洁,在不同的平台下面8种基本数据类型占用内存的宽度,
运算的规则都完全相同。
​

9.1.2 装箱与拆箱

3、那么基本数据类型与包装类之间如何转换呢?
在JDK1.5之前,是非常麻烦的,必须手动转换。
装箱:把基本数据类型的值包装为“对象”
拆箱:把“对象”拆解为基本数据类型的值
​
在JDK1.5之后,可以自动装箱与拆箱。
注意:自动装箱与拆箱只能支持对应类型之间。
import org.junit.Test;
​
import java.util.ArrayList;
​
public class TestWrapper {
    @Test
    public void test1(){
        //手动装箱
        int num = 1;
        Integer obj = new Integer(num);//手动装箱
        ArrayList list = new ArrayList();//它是一个集合,是一个容器,装对象的容器
//        list.add(obj);
​
        list.add(num);
    }
    
    @Test
    public void test2(){
        Integer obj1 = new Integer(100);
        Integer obj2 = new Integer(200);
        int sum = obj1.intValue() + obj2.intValue();//手动拆箱
        System.out.println("sum = " + sum);
    }
    
    @Test
    public void test3(){
        //自动装箱
        int num = 1;
        Integer obj = num;
    }
​
    @Test
    public void test4(){
        //自动拆箱
        Integer obj1 = new Integer(100);
        Integer obj2 = new Integer(200);
        int sum = obj1 + obj2;
        System.out.println("sum = " + sum);
    }
​
    @Test
    public void test5(){
        int num = 1;
//        Double d = num;//报错,int和Double不是对应类型
​
        Double d2 = 1d;//数字后面加D,表示是double类型
    }
}
​

9.1.3 API

4、包装类的一些好用的API
(1)基本数据类型与字符串之间的转换API
A:基本数据类型 -> 字符串 ,通常用的是拼接一个字符串,例如:""
B:字符串 -> 基本数据类型
    包装类.parseXxx(字符串)
   例如:
     Integer.parseInt(字符串)
     Double.parseDouble(字符串)
​
(2)数据类型的最大最小值
    包装类.MAX_VALUE
    包装类.MIN_VALUE
​
(3)字符转大小写
A:之前
    大写->小写 +32
    小写->大写 -32
B:现在
Character.toUpperCase(小写):转大写
Character.toLowerCase(大写):转小写
​
(4)十进制转二进制、八进制、十六进制(了解)
import org.junit.Test;
​
public class TestAPI {
    @Test
    public void test1(){
        int num = 1;
        String str = num + "";//基本数据类型 --> String
    }
    
    @Test
    public void test2(){
        String str1 = "123";
        String str2 = "456";
        
        int num1 = Integer.parseInt(str1);
        int num2 = Integer.parseInt(str2);
        
        int sum = num1 + num2;
        System.out.println("sum = " + sum);
    }
​
    @Test
    public void test3(){
        String str1 = "12.3";
        String str2 = "45.6";
​
        double num1 = Double.parseDouble(str1);
        double num2 = Double.parseDouble(str2);
​
        double sum = num1 + num2;
        System.out.println("sum = " + sum);
    }
​
    @Test
    public void test4(){
        System.out.println(Byte.MAX_VALUE);
        System.out.println(Byte.MIN_VALUE);
​
        System.out.println(Integer.MAX_VALUE);
        System.out.println(Integer.MIN_VALUE);
    }
​
    @Test
    public void test5(){
        char letter = 'a';
        char bigLetter = (char)(letter - 32 );
        System.out.println("letter = " + letter);
        System.out.println("bigLetter = " + bigLetter);
    }
​
    @Test
    public void test6(){
        char letter = 'a';
        char bigLetter = Character.toUpperCase(letter);
        System.out.println("letter = " + letter);
        System.out.println("bigLetter = " + bigLetter);
    }
​
    @Test
    public void test7(){
        char letter = 'A';
        char bigLetter = (char)(letter - 32 );
        System.out.println("letter = " + letter);
        System.out.println("bigLetter = " + bigLetter);
    }
​
    @Test
    public void test8(){
        char letter = 'A';
        char bigLetter = Character.toUpperCase(letter);
        System.out.println("letter = " + letter);
        System.out.println("bigLetter = " + bigLetter);
    }
​
    @Test
    public void test9(){
        int num = 250;
​
        System.out.println("二进制:" + Integer.toBinaryString(num));//11111010
        System.out.println("八进制:" + Integer.toOctalString(num));//372
        System.out.println("十六进制:" + Integer.toHexString(num));//fa
    }
}
​

9.1.4 包装类对象的特点

5、包装类的特点
(1)部分包装类对象可以被缓存,缓存的常量对象就会被共享
Byte,Short,Integer,Long:会缓存部分对象  -128 ~ 127
Float,Double:不会缓存任何对象
Character:会缓存 0-127 范围的字符
Boolean:会缓存true,false
​
哪些情况会使用缓存对象?
A:在缓存范围内
B:自动装箱 或 用包装类的valueOf()方法得到的包装类对象
​
(2)包装类对象不可变(面试题经常考)
当包装类对象的值修改时,其实是指向了新对象。
​
(3)包装类对象计算时,
除了两个包装类对象 ==和!=,其余的都是自动拆箱计算的。
​
​

​
import org.junit.Test;
​
public class TestSpecial {
    @Test
    public void test1(){
        Integer i1 = 1;//Integer i1 = new Integer(1);
        Integer i2 = 1;//Integer i2 = i1;
​
        System.out.println(i1 == i2);//true
    }
​
    @Test
    public void test2(){
        Integer i1 = 200;//Integer i1 = new Integer(200);
        Integer i2 = 200;//Integer i2 = new Integer(200);
​
        System.out.println(i1 == i2);//比较地址值 false
    }
​
    @Test
    public void test3(){
        Integer i1 = new Integer(1);
        Integer i2 = new Integer(1);
        System.out.println(i1 == i2);//false
    }
​
    @Test
    public void test4(){
        Integer i1 = Integer.valueOf(1);
        Integer i2 = Integer.valueOf(1);
        System.out.println(i1 == i2);//true
    }
}
​
import org.junit.Test;
​
public class TestSpecial2 {
    @Test
    public void test1(){
        int x = 1;
        Integer y = 1;
        Data data = new Data();
        System.out.println("调用之前:" + x +"," + y +",data.num " + data.num);//1,1,0
​
        change(x,y,data);
​
        System.out.println("调用之后:" + x +"," + y +",data.num " + data.num);//1,1,1
    }
​
    /*
    形参是基本数据类型,实参给形参的是数据值的副本,形参的修改和实参无关。
    形参是引用数据类型,实参给形参的是地址值的副本,正常来说,形参修改了对象的值,相当于实参自己修改了对象的值。
     */
    public void change(int a, Integer b, Data c){
        Integer j = 1;
        System.out.println(b == j);//true 比较地址值
        a++;
        b++;//等价于 b = Integer.valueOf(b)  b指向了新对象
​
        Integer i = 2;
        System.out.println(b == i);//true 比较地址值
        c.num++;
    }
}
​
class Data{
    int num;
​
}

day0325_包装对象不可变.png


​
import org.junit.Test;
​
public class TestSpecial3 {
    @Test
    public void test1(){
        Integer i = 1;
        Double d = 1.0;
        //System.out.println(i == d);//无法比较,因为它们是比较地址值,比较地址值时,只能相同类型或父子类类型才能比较地址值
    }
​
    @Test
    public void test2(){
        Integer i = 1;
        double d = 1.0;
        System.out.println(i == d);//包装类与基本数据类型,会拆箱,把Integer拆箱为int,然后int自动升级为double
    }
​
    @Test
    public void test3(){
        Integer i = 1;
        Integer j = 1;
​
        System.out.println(i + j);//包装类与包装类的其他计算,都拆箱为基本数据类型 int + int
    }
​
    @Test
    public void test4(){
        Integer i = 1;
        Double j = 1.0;
​
        System.out.println(i + j);//包装类与包装类的其他计算,都拆箱为基本数据类型 int + double
    }
    @Test
    public void test5(){
        Integer i = 1;
        Double j = 1.0;
​
        System.out.println(i > j);//包装类与包装类的其他计算,都拆箱为基本数据类型 int > double
    }
}
​

9.1.5 equals和==

两个对象比较是否相等:
(1)==
比较的是两个“对象”的“首地址”,只有这“两个”对象是同一个对象时,才会返回true。
例如:
        Integer i1 = 1;
        Integer i2 = 1;
        System.out.println(i1 == i2);//true,使用了同一个缓存对象
​
        String s1 = "hello";
        String s2 = "hello";
        System.out.println(s1 == s2);//true ,这里也使用共享的字符串常量对象
​
        Integer i1 = 190;
        Integer i2 = 190;
        System.out.println(i1 == i2);//false,不是同一个对象
​
(2)boolean equals(Object obj)
如果某个类“没有”重写Object类的equals方法,那么equals方法和==一样比较对象的首地址。
如果某个类“重写”了Object类的equals方法,那么执行的一定是重写后的代码,一般重写都是比较内容。
    重写equals的快捷键是:Alt + Insert,选择重写equals和hashCode方法。
​
由此可知,Integer,String它们都重写了equals方法。
​
结论:
    以后只要是对象比较是否相等,不管它是否重写equals方法,都调用equals方法来比较。

​
import org.junit.Test;
​
import java.util.Scanner;
​
public class TestInteger {
    @Test
    public void test1(){
        Integer i1 = 1;
        Integer i2 = 1;
        System.out.println(i1 == i2);//true,使用了同一个缓存对象
    }
​
    @Test
    public void test2(){
        Integer i1 = 190;
        Integer i2 = 190;
        System.out.println(i1 == i2);//false,不是同一个对象
        System.out.println(i1.equals(i2));//true
    }
​
    @Test
    public void test3(){
        String s1 = "hello";
        String s2 = "hello";
        System.out.println(s1 == s2);//true ,这里也使用共享的字符串常量对象
    }
​
    @Test
    public void test4(){
        String s1 = "hello";
        Scanner input = new Scanner(System.in);
        System.out.print("请输入一个字符串(hello):");
        String s2 = input.next();
        System.out.println(s1 == s2);//false
        System.out.println(s1.equals(s2));//true
​
        input.close();
    }
}
​
import java.util.Objects;
​
public class Student {
    private int id;
    private String name;
    private int score;
​
    public Student(int id, String name, int score) {
        this.id = id;
        this.name = name;
        this.score = score;
    }
​
    public int getId() {
        return id;
    }
​
    public void setId(int id) {
        this.id = id;
    }
​
    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{" +
                "id=" + id +
                ", name='" + name + ''' +
                ", score=" + score +
                '}';
    }
​
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
​
        Student student = (Student) o;
​
        if (id != student.id) return false;
        if (score != student.score) return false;
        return Objects.equals(name, student.name);
    }
​
    @Override
    public int hashCode() {//这个方法先忽略
        int result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        result = 31 * result + score;
        return result;
    }
}
​

​
import org.junit.Test;
​
public class TestEquals {
    @Test
    public void test1(){
        Student s1 = new Student(1,"张三",89);
        Student s2 = new Student(1,"张三",89);
​
        System.out.println(s1 == s2);//false
        System.out.println(s1.equals(s2));//false(没重写)  true(重写)
    }
}
​