Java 的常見 API 與物件克隆)

瑪拉_以琳發表於2023-03-03

1 Math類

1.1 概述

tips:瞭解內容

檢視API文件,我們可以看到API文件中關於Math類的定義如下:

1576047659085.png

Math類所在包為java.lang包,因此在使用的時候不需要進行導包。並且Math類被final修飾了,因此該類是不能被繼承的。

Math類包含執行基本數字運算的方法,我們可以使用Math類完成基本的數學運算。

要想使用Math類我們就需要先建立該類的物件,那麼建立物件就需要藉助於構造方法。因此我們就需要首先檢視一下API文件,看看API文件中針對Math類有沒有提供對應的構造方法。透過API文件來檢視

一下Math類的成員,如下所示:

1576047846672.png

在API文件中沒有體現可用的構造方法,因此我們就不能直接透過new關鍵字去建立Math類的物件。同時我們發現Math類中的方法都是靜態的,因此在使用的時候我們可以直接透過類名去呼叫。在Math類中

定義了很多數學運算的方法,但是我們並不可能將所有的方法學習一遍,我們主要學習的就是一些常見的方法。

1.2 常見方法

tips:重點講解內容

<font color="red" size="3">常見方法介紹</font>

我們要學習的Math的常見方法如下所示:

public static int abs(int a)                    // 返回引數的絕對值
public static double ceil(double a)                // 返回大於或等於引數的最小整數
public static double floor(double a)            // 返回小於或等於引數的最大整數
public static int round(float a)                // 按照四捨五入返回最接近引數的int型別的值
public static int max(int a,int b)                // 獲取兩個int值中的較大值
public static int min(int a,int b)                // 獲取兩個int值中的較小值
public static double pow (double a,double b)    // 計算a的b次冪的值
public static double random()                    // 返回一個[0.0,1.0)的隨機值

<font color="red" size="3">案例演示</font>

接下來我們就來演示一些這些方法的執行效果,如下所示:

public class MathDemo01 {

    public static void main(String[] args) {

        // public static int abs(int a)         返回引數的絕對值
        System.out.println("-2的絕對值為:" + Math.abs(-2));
        System.out.println("2的絕對值為:" + Math.abs(2));

        // public static double ceil(double a)  返回大於或等於引數的最小整數
        System.out.println("大於或等於23.45的最小整數位:" + Math.ceil(23.45));
        System.out.println("大於或等於-23.45的最小整數位:" + Math.ceil(-23.45));

        // public static double floor(double a) 返回小於或等於引數的最大整數
        System.out.println("小於或等於23.45的最大整數位:" + Math.floor(23.45));
        System.out.println("小於或等於-23.45的最大整數位:" + Math.floor(-23.45));

        // public static int round(float a)     按照四捨五入返回最接近引數的int
        System.out.println("23.45四捨五入的結果為:" + Math.round(23.45));
        System.out.println("23.55四捨五入的結果為:" + Math.round(23.55));

        // public static int max(int a,int b)   返回兩個int值中的較大值
        System.out.println("23和45的最大值為: " + Math.max(23, 45));

        // public static int min(int a,int b)   返回兩個int值中的較小值
        System.out.println("12和34的最小值為: " + Math.min(12 , 34));

        // public static double pow (double a,double b)返回a的b次冪的值
        System.out.println("2的3次冪計算結果為: " + Math.pow(2,3));

        // public static double random()返回值為double的正值,[0.0,1.0)
        System.out.println("獲取到的0-1之間的隨機數為: " + Math.random());
    }

}

執行程式進行測試,控制檯輸出結果如下:

-2的絕對值為:2
2的絕對值為:2
大於或等於23.45的最小整數位:24.0
大於或等於-23.45的最小整數位:-23.0
小於或等於23.45的最大整數位:23.0
小於或等於-23.45的最大整數位:-24.0
23.45四捨五入的結果為:23
23.55四捨五入的結果為:24
23和45的最大值為: 45
12和34的最小值為: 12
2的3次冪計算結果為: 8.0
獲取到的0-1之間的隨機數為: 0.7322484131745958

1.3 演算法小題(質數)

需求:

​ 判斷一個數是否為一個質數

程式碼實現:

public class MathDemo2 {
    public static void main(String[] args) {
        //判斷一個數是否為一個質數
        System.out.println(isPrime(997));
        //997 2~996 995次
    }

    public static boolean isPrime(int number) {
        int count = 0;
        for (int i = 2; i <= Math.sqrt(number); i++) {
            count++;
            if (number % i == 0) {
                return false;
            }
        }
        System.out.println(count);
        return true;
    }
}

1.4 演算法小題(自冪數)

自冪數,一個n位自然數等於自身各個數位上數字的n次冪之和

​ 舉例1:三位數 1^3 + 5^3 + 3^3 = 153

​ 舉例2:四位數 1^4 + 6^4 + 3^4 + 4^3 = 1634

如果自冪數是:

  • 一位自冪數,也叫做:獨身數
  • 三位自冪數:水仙花數 四位自冪數:四葉玫瑰數
  • 五位自冪數:五角星數 六位自冪數:六合數
  • 七位自冪數:北斗七星數 八位自冪數:八仙數
  • 九位自冪數:九九重陽數 十位自冪數:十全十美數

要求1:統計一共有多少個水仙花數。

要求2:(課後作業)證明沒有兩位的自冪數。

要求3:(課後作業)分別統計有多少個四葉玫瑰數和五角星數。(答案:都是3個)

//水仙花數:100 ~ 999
int count = 0;
//得到每一個三位數
for (int i = 100; i <= 999; i++) {
    //個位 十位 百位
    int ge = i % 10;
    int shi = i / 10 % 10;
    int bai = i / 100 % 10;
    //判斷:
    //每一位的三次方之和 跟本身 進行比較。
    double sum = Math.pow(ge, 3) + Math.pow(shi, 3) + Math.pow(bai, 3);
    if (sum == i) {
        count++;
        //System.out.println(i);

        System.out.println(count);
    }
}

2 System類

2.1 概述

tips:瞭解內容

檢視API文件,我們可以看到API文件中關於System類的定義如下:

1576049347968.png

System類所在包為java.lang包,因此在使用的時候不需要進行導包。並且System類被final修飾了,因此該類是不能被繼承的。

System包含了系統操作的一些常用的方法。比如獲取當前時間所對應的毫秒值,再比如終止當前JVM等等。

要想使用System類我們就需要先建立該類的物件,那麼建立物件就需要藉助於構造方法。因此我們就需要首先檢視一下API文件,看看API文件中針對System類有沒有提供對應的構造方法。透過API文件來

檢視一下System類的成員,如下所示:

1576049535584.png

在API文件中沒有體現可用的構造方法,因此我們就不能直接透過new關鍵字去建立System類的物件。同時我們發現System類中的方法都是靜態的,因此在使用的時候我們可以直接透過類名去呼叫(Nested

Class Summary內部類或者內部介面的描述)。

2.2 常見方法

tips:重點講解內容

<font color="red" size="3">常見方法介紹</font>

我們要學習的System類中的常見方法如下所示:

public static long currentTimeMillis()            // 獲取當前時間所對應的毫秒值(當前時間為0時區所對應的時間即就是英國格林尼治天文臺舊址所在位置)
public static void exit(int status)                // 終止當前正在執行的Java虛擬機器,0表示正常退出,非零表示異常退出
public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length); // 進行數值元素copy

<font color="red" size="3">案例演示</font>

接下來我們就來透過一些案例演示一下這些方法的特點。

<font color="blue" size="2">案例1</font>:演示currentTimeMillis方法

public class SystemDemo01 {

    public static void main(String[] args) {

        // 獲取當前時間所對應的毫秒值
        long millis = System.currentTimeMillis();

        // 輸出結果
        System.out.println("當前時間所對應的毫秒值為:" + millis);

    }

}

執行程式進行測試,控制檯的輸出結果如下:

當前時間所對應的毫秒值為:1576050298343

獲取到當前時間的毫秒值的意義:我們常常來需要統計某一段程式碼的執行時間。此時我們就可以在執行這段程式碼之前獲取一次時間,在執行完畢以後再次獲取一次系統時間,然後計算兩個時間的差值,

這個差值就是這段程式碼執行完畢以後所需要的時間。如下程式碼所示:

public class SystemDemo2 {
    public static void main(String[] args) {
        //判斷1~100000之間有多少個質數

        long start = System.currentTimeMillis();

        for (int i = 1; i <= 100000; i++) {
            boolean flag = isPrime2(i);
            if (flag) {
                System.out.println(i);
            }
        }
        long end = System.currentTimeMillis();
        //獲取程式執行的總時間
        System.out.println(end - start); //方式一:1514 毫秒  方式二:71毫秒
    }

    //以前判斷是否為質數的方式
    public static boolean isPrime1(int number) {
        for (int i = 2; i < number; i++) {
            if (number % i == 0) {
                return false;
            }
        }
        return true;
    }

    //改進之後判斷是否為質數的方式(效率高)
    public static boolean isPrime2(int number) {
        for (int i = 2; i <= Math.sqrt(number); i++) {
            if (number % i == 0) {
                return false;
            }
        }
        return true;
    }
}

<font color="blue" size="2">案例2</font>:演示exit方法

public class SystemDemo01 {

    public static void main(String[] args) {
        
        // 輸出
        System.out.println("程式開始執行了.....");
        
        // 終止JVM
        System.exit(0);
        
        // 輸出
        System.out.println("程式終止了..........");
        
    }
    
}

執行程式進行測試,控制檯輸出結果如下:

程式開始執行了.....

此時可以看到在控制檯只輸出了"程式開始了...",由於JVM終止了,因此輸出"程式終止了..."這段程式碼沒有被執行。

<font color="blue" size="2">案例3</font>:演示arraycopy方法

方法引數說明:

// src:      源陣列
// srcPos:  源數值的開始位置
// dest:    目標陣列
// destPos: 目標陣列開始位置
// length:   要複製的元素個數
public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length); 

程式碼如下所示:

public class SystemDemo01 {

    public static void main(String[] args) {

        // 定義源陣列
        int[] srcArray = {23 , 45 , 67 , 89 , 14 , 56 } ;

        // 定義目標陣列
        int[] desArray = new int[10] ;

        // 進行陣列元素的copy: 把srcArray陣列中從0索引開始的3個元素,從desArray陣列中的1索引開始複製過去
        System.arraycopy(srcArray , 0 , desArray , 1 , 3);

        // 遍歷目標陣列
        for(int x = 0 ; x < desArray.length ; x++) {
            if(x != desArray.length - 1) {
                System.out.print(desArray[x] + ", ");
            }else {
                System.out.println(desArray[x]);
            }

        }

    }

}

執行程式進行測試,控制檯輸出結果如下所示:

0, 23, 45, 67, 0, 0, 0, 0, 0, 0

透過控制檯輸出結果我們可以看到,陣列元素的確進行復制了。

使用這個方法我們也可以完成陣列元素的刪除操作,如下所示:

public class SystemDemo02 {
    public static void main(String[] args) {
        // 定義一個陣列
        int[] srcArray = {23 , 45 , 67 , 89 , 14 , 56 } ;
        // 刪除陣列中第3個元素(67):要刪除67這個元素,我們只需要將67後面的其他元素依次向前進行移動即可
        System.arraycopy(srcArray , 3 , srcArray , 2 , 3);
        // 遍歷srcArray陣列
        for(int x = 0 ; x < srcArray.length ; x++) {
            if(x != desArray.length - 1) {
                System.out.print(srcArray[x] + ", ");
            }else {
                System.out.println(srcArray[x]);
            }
        }
    }
}

執行程式進行測試,控制檯的輸出結果如下所示:

23, 45, 89, 14, 56, 56 

透過控制檯輸出結果我們可以看到此時多出了一個56元素,此時我們只需要將最後一個位置設定為0即可。如下所示:

public class SystemDemo02 {
    public static void main(String[] args) {
        // 定義一個陣列
        int[] srcArray = {23 , 45 , 67 , 89 , 14 , 56 } ;
        // 刪除陣列中第3個元素(67):要刪除67這個元素,我們只需要將67後面的其他元素依次向前進行移動即可
        System.arraycopy(srcArray , 3 , srcArray , 2 , 3);
        // 將最後一個位置的元素設定為0
        srcArray[srcArray.length - 1] = 0 ;
        // 遍歷srcArray陣列
        for(int x = 0 ; x < srcArray.length ; x++) {
            if(x != srcArray.length - 1 ) {
                System.out.print(srcArray[x] + ", ");
            }else {
                System.out.println(srcArray[x]);
            }
        }
    }
}

執行程式進行測試,控制檯輸出結果如下所示:

23, 45, 89, 14, 56, 0

此時我們可以看到元素"67"已經被刪除掉了。67後面的其他元素依次向前進行移動了一位。

arraycopy方法底層細節:

1.如果資料來源陣列和目的地陣列都是基本資料型別,那麼兩者的型別必須保持一致,否則會報錯

2.在複製的時候需要考慮陣列的長度,如果超出範圍也會報錯

3.如果資料來源陣列和目的地陣列都是引用資料型別,那麼子類型別可以賦值給父類型別

程式碼示例:

public class SystemDemo3 {
    public static void main(String[] args) {
        //public static void arraycopy(資料來源陣列,起始索引,目的地陣列,起始索引,複製個數) 陣列複製
        //細節:
        //1.如果資料來源陣列和目的地陣列都是基本資料型別,那麼兩者的型別必須保持一致,否則會報錯
        //2.在複製的時候需要考慮陣列的長度,如果超出範圍也會報錯
        //3.如果資料來源陣列和目的地陣列都是引用資料型別,那麼子類型別可以賦值給父類型別

        Student s1 = new Student("zhangsan", 23);
        Student s2 = new Student("lisi", 24);
        Student s3 = new Student("wangwu", 25);

        Student[] arr1 = {s1, s2, s3};
        Person[] arr2 = new Person[3];
        //把arr1中物件的地址值賦值給arr2中
        System.arraycopy(arr1, 0, arr2, 0, 3);

        //遍歷陣列arr2
        for (int i = 0; i < arr2.length; i++) {
            Student stu = (Student) arr2[i];
            System.out.println(stu.getName() + "," + stu.getAge());
        }
    }
}

class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 獲取
     *
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 設定
     *
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 獲取
     *
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 設定
     *
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Person{name = " + name + ", age = " + age + "}";
    }
}


class Student extends Person {

    public Student() {
    }

    public Student(String name, int age) {
        super(name, age);
    }
}

3 Runtime

3.1 概述

​ Runtime表示Java中執行時物件,可以獲取到程式執行時設計到的一些資訊

3.2 常見方法

<font color="red" size="3">常見方法介紹</font>

我們要學習的Object類中的常見方法如下所示:

public static Runtime getRuntime()        //當前系統的執行環境物件
public void exit(int status)            //停止虛擬機器
public int availableProcessors()        //獲得CPU的執行緒數
public long maxMemory()                    //JVM能從系統中獲取總記憶體大小(單位byte)
public long totalMemory()                //JVM已經從系統中獲取總記憶體大小(單位byte)
public long freeMemory()                //JVM剩餘記憶體大小(單位byte)
public Process exec(String command)     //執行cmd命令

程式碼示例:

public class RunTimeDemo1 {
    public static void main(String[] args) throws IOException {
        /*
            public static Runtime getRuntime() 當前系統的執行環境物件
            public void exit(int status) 停止虛擬機器
            public int availableProcessors() 獲得CPU的執行緒數
            public long maxMemory() JVM能從系統中獲取總記憶體大小(單位byte)
            public long totalMemory() JVM已經從系統中獲取總記憶體大小(單位byte)
            public long freeMemory() JVM剩餘記憶體大小(單位byte)
            public Process exec(string command) 執行cmd命令
        */

        //1.獲取Runtime的物件
        //Runtime r1 =Runtime.getRuntime();

        //2.exit 停止虛擬機器
        //Runtime.getRuntime().exit(0);
        //System.out.println("看看我執行了嗎?");


        //3.獲得CPU的執行緒數
        System.out.println(Runtime.getRuntime().availableProcessors());//8
        //4.總記憶體大小,單位byte位元組
        System.out.println(Runtime.getRuntime().maxMemory() / 1024 / 1024);//4064
        //5.已經獲取的總記憶體大小,單位byte位元組
        System.out.println(Runtime.getRuntime().totalMemory() / 1024 / 1024);//254
        //6.剩餘記憶體大小
        System.out.println(Runtime.getRuntime().freeMemory() / 1024 / 1024);//251

        //7.執行cmd命令
        //shutdown :關機
        //加上引數才能執行
        //-s :預設在1分鐘之後關機
        //-s -t 指定時間 : 指定關機時間
        //-a :取消關機操作
        //-r: 關機並重啟
        Runtime.getRuntime().exec("shutdown -s -t 3600");


    }
}

3.3 惡搞好基友

需求:

​ 介面上方按鈕預設隱藏

​ 介面中間有一個提示文字和三個按鈕

​ 當你的好基友點選中間三個按鈕的時候就在N秒之後關機,不同的按鈕N的值不一樣

​ 任意一個按鈕被點選之後,上方了按鈕出現。當點選上方按鈕之後取消關機任務

惡搞好基友.png

public class Test {
    public static void main(String[] args) {
        new MyJframe();
    }
}
public class MyJframe extends JFrame implements ActionListener {

    JButton yesBut = new JButton("帥爆了");
    JButton midBut = new JButton("一般般吧");
    JButton noBut = new JButton("不帥,有點磕磣");
    JButton dadBut = new JButton("饒了我吧!");


    //決定了上方的按鈕是否展示
    boolean flag = false;


    public MyJframe() {
        initJFrame();


        initView();


        //顯示
        this.setVisible(true);
    }

    private void initView() {

        this.getContentPane().removeAll();

        if (flag) {
            //展示按鈕
            dadBut.setBounds(50, 20, 100, 30);
            dadBut.addActionListener(this);
            this.getContentPane().add(dadBut);
        }


        JLabel text = new JLabel("你覺得自己帥嗎?");
        text.setFont(new Font("微軟雅黑", 0, 30));
        text.setBounds(120, 150, 300, 50);


        yesBut.setBounds(200, 250, 100, 30);
        midBut.setBounds(200, 325, 100, 30);
        noBut.setBounds(160, 400, 180, 30);

        yesBut.addActionListener(this);
        midBut.addActionListener(this);
        noBut.addActionListener(this);

        this.getContentPane().add(text);
        this.getContentPane().add(yesBut);
        this.getContentPane().add(midBut);
        this.getContentPane().add(noBut);

        this.getContentPane().repaint();
    }

    private void initJFrame() {
        //設定寬高
        this.setSize(500, 600);
        //設定標題
        this.setTitle("惡搞好基友");
        //設定關閉模式
        this.setDefaultCloseOperation(3);
        //置頂
        this.setAlwaysOnTop(true);
        //居中
        this.setLocationRelativeTo(null);
        //取消內部預設佈局
        this.setLayout(null);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        Object obj = e.getSource();
        if (obj == yesBut) {
            //給好基友一個彈框
            showJDialog("xxx,你太自信了,給你一點小懲罰");
            try {
                Runtime.getRuntime().exec("shutdown -s -t 3600");
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
            flag = true;
            initView();

        } else if (obj == midBut) {
            System.out.println("你的好基友點選了一般般吧");

            //給好基友一個彈框
            showJDialog("xxx,你還是太自信了,也要給你一點小懲罰");

            try {
                Runtime.getRuntime().exec("shutdown -s -t 7200");
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }

            flag = true;
            initView();


        } else if (obj == noBut) {
            System.out.println("你的好基友點選了不帥");

            //給好基友一個彈框
            showJDialog("xxx,你還是有一點自知之明的,也要給你一點小懲罰");

            try {
                Runtime.getRuntime().exec("shutdown -s -t 1800");
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }

            flag = true;
            initView();
        } else if (obj == dadBut) {
            //給好基友一個彈框
            showJDialog("xxx,這次就饒了你~");

            try {
                Runtime.getRuntime().exec("shutdown -a");
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }

        }
    }

    public void showJDialog(String content) {
        //建立一個彈框物件
        JDialog jDialog = new JDialog();
        //給彈框設定大小
        jDialog.setSize(200, 150);
        //讓彈框置頂
        jDialog.setAlwaysOnTop(true);
        //讓彈框居中
        jDialog.setLocationRelativeTo(null);
        //彈框不關閉永遠無法操作下面的介面
        jDialog.setModal(true);

        //建立Jlabel物件管理文字並新增到彈框當中
        JLabel warning = new JLabel(content);
        warning.setBounds(0, 0, 200, 150);
        jDialog.getContentPane().add(warning);

        //讓彈框展示出來
        jDialog.setVisible(true);
    }
}

4 Object類

4.1 概述

tips:重點講解內容

檢視API文件,我們可以看到API文件中關於Object類的定義如下:

1576053677194.png

Object類所在包是java.lang包。Object 是類層次結構的根,每個類都可以將 Object 作為超類。所有類都直接或者間接的繼承自該類;換句話說,該類所具備的方法,其他所有類都繼承了。

檢視API文件我們可以看到,在Object類中提供了一個無參構造方法,如下所示:

1576053871503.png

但是一般情況下我們很少去主動的建立Object類的物件,呼叫其對應的方法。更多的是建立Object類的某個子類物件,然後透過子類物件呼叫Object類中的方法。

4.2 常見方法

tips:重點講解內容

<font color="red" size="3">常見方法介紹</font>

我們要學習的Object類中的常見方法如下所示:

public String toString()                //返回該物件的字串表示形式(可以看做是物件的記憶體地址值)
public boolean equals(Object obj)        //比較兩個物件地址值是否相等;true表示相同,false表示不相同
protected Object clone()                //物件克隆

<font color="red" size="3">案例演示</font>

接下來我們就來透過一些案例演示一下這些方法的特點。

<font color="blue" size="2">案例1</font>:演示toString方法

實現步驟:

  1. 建立一個學生類,提供兩個成員變數(name , age);並且提供對應的無參構造方法和有參構造方法以及get/set方法
  2. 建立一個測試類(ObjectDemo01),在測試類的main方法中去建立學生物件,然後呼叫該物件的toString方法獲取該物件的字串表現形式,並將結果進行輸出

如下所示:

Student類

public class Student {

    private String name ;       // 姓名
    private String age ;        // 年齡

    // 無參構造方法和有參構造方法以及get和set方法略
    ...
        
}

ObjectDemo01測試類

public class ObjectDemo01 {

    public static void main(String[] args) {

        // 建立學生物件
        Student s1 = new Student("itheima" , "14") ;

        // 呼叫toString方法獲取s1物件的字串表現形式
        String result1 = s1.toString();

        // 輸出結果
        System.out.println("s1物件的字串表現形式為:" + result1);

    }

}

執行程式進行測試,控制檯輸出結果如下所示:

s1物件的字串表現形式為:com.itheima.api.system.demo04.Student@3f3afe78

為什麼控制檯輸出的結果為:com.itheima.api.system.demo04.Student@3f3afe78; 此時我們可以檢視一下Object類中toString方法的原始碼,如下所示:

public String toString() {        // Object類中toString方法的原始碼定義
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

其中getClass().getName()對應的結果就是:com.itheima.api.system.demo04.Student;Integer.toHexString(hashCode())對應的結果就是3f3afe78。

我們常常將"com.itheima.api.system.demo04.Student@3f3afe78"這一部分稱之為物件的記憶體地址值。但是一般情況下獲取物件的記憶體地址值沒有太大的意義。獲取物件的成員變數的字串拼接形式才

算有意義,怎麼實現呢?此時我們就需要在Student類中重寫Object的toString方法。我們可以透過idea開發工具進行實現,具體步驟如下所示:

  1. 在空白處使用快捷鍵:alt + insert。此時會彈出如下的對話方塊

1576055135105.png

  1. 選擇toString,此時會彈出如下的對話方塊

1576055198877.png

同時選擇name和age屬性,點選OK。此時就會完成toString方法的重寫,程式碼如下所示:

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

這段程式碼就是把Student類中的成員變數進行了字串的拼接。重寫完畢以後,再次執行程式,控制檯輸出結果如下所示:

s1物件的字串表現形式為:Student{name='itheima', age='14'}

此時我們就可以清楚的檢視Student的成員變數值,因此重寫toString方法的意義就是以良好的格式,更方便的展示物件中的屬性值

我們再來檢視一下如下程式碼的輸出:

// 建立學生物件
Student s1 = new Student("itheima" , "14") ;

// 直接輸出物件s1
System.out.println(s1);

執行程式進行測試,控制檯輸出結果如下所示:

Student{name='itheima', age='14'}

我們可以看到和剛才的輸出結果是一致的。那麼此時也就證明直接輸出一個物件,那麼會預設呼叫物件的toString方法,因此如上程式碼的等同於如下程式碼:

// 建立學生物件
Student s1 = new Student("itheima" , "14") ;

// 呼叫s1的toString方法,把結果進行輸出
System.out.println(s1.toString());

因此後期為了方便進行測試,我們常常是透過輸出語句直接輸出一個物件的名稱。

小結:

  1. 在透過輸出語句輸出一個物件時,預設呼叫的就是toString()方法
  2. 輸出地址值一般沒有意義,我們可以透過重寫toString方法去輸出對應的成員變數資訊(快捷鍵:atl + insert , 空白處 右鍵 -> Generate -> 選擇toString)
  3. toString方法的作用:以良好的格式,更方便的展示物件中的屬性值
  4. 一般情況下Jdk所提供的類都會重寫Object類中的toString方法

<font color="blue" size="2">案例2</font>:演示equals方法

實現步驟:

  1. 在測試類(ObjectDemo02)的main方法中,建立兩個學生物件,然後比較兩個物件是否相同

程式碼如下所示:

public class ObjectDemo02 {

    public static void main(String[] args) {

        // 建立兩個學生物件
        Student s1 = new Student("itheima" , "14") ;
        Student s2 = new Student("itheima" , "14") ;

        // 比較兩個物件是否相等
        System.out.println(s1 == s2);

    }

}

執行程式進行測試,控制檯的輸出結果如下所示:

false

因為"=="號比較的是物件的地址值,而我們透過new關鍵字建立了兩個物件,它們的地址值是不相同的。因此比較結果就是false。

我們嘗試呼叫Object類中的equals方法進行比較,程式碼如下所示:

// 呼叫equals方法比較兩個物件是否相等
boolean result = s1.equals(s2);

// 輸出結果
System.out.println(result);

執行程式進行測試,控制檯的輸出結果為:

false

為什麼結果還是false呢?我們可以檢視一下Object類中equals方法的原始碼,如下所示:

public boolean equals(Object obj) {        // Object類中的equals方法的原始碼
    return (this == obj);
}

透過原始碼我們可以發現預設情況下equals方法比較的也是物件的地址值。比較記憶體地址值一般情況下是沒有意義的,我們希望比較的是物件的屬性,如果兩個物件的屬性相同,我們認為就是同一個物件;

那麼要比較物件的屬性,我們就需要在Student類中重寫Object類中的equals方法。equals方法的重寫,我們也可以使用idea開發工具完成,具體的操作如下所示:

  1. 在空白處使用快捷鍵:alt + insert。此時會彈出如下的對話方塊

1576056718392.png

  1. 選擇equals() and hashCode()方法,此時會彈出如下的對話方塊

1576057779458.png

點選next,會彈出如下對話方塊:

1576057813175.png
選擇neme和age屬性點選next,此時就會彈出如下對話方塊:

1576057892814.png

取消name和age屬性(因為此時選擇的是在生成hashCode方法時所涉及到的屬性,關於hashCode方法後期再做重點介紹),點選Finish完成生成操作。生成的equals方法和hashCode方法如下:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Student student = (Student) o;
    return Objects.equals(name, student.name) && Objects.equals(age, student.age);    // 比較的是物件的name屬性值和age屬性值
}

@Override
public int hashCode() {
    return 0;
}

hashCode方法我們暫時使用不到,可以將hashCode方法刪除。重寫完畢以後執行程式進行測試,控制檯輸出結果如下所示:

true

此時equals方法比較的是物件的成員變數值,而s1和s2兩個物件的成員變數值都是相同的。因此比較完畢以後的結果就是true。

小結:

  1. 預設情況下equals方法比較的是物件的地址值
  2. 比較物件的地址值是沒有意義的,因此一般情況下我們都會重寫Object類中的equals方法

<font color="blue" size="2">案例2</font>:物件克隆

​ 把A物件的屬性值完全複製給B物件,也叫物件複製,物件複製

物件克隆的分類:

深克隆和淺克隆

淺克隆:

​ 不管物件內部的屬性是基本資料型別還是引用資料型別,都完全複製過來

​ 基本資料型別複製過來的是具體的資料,引用資料型別複製過來的是地址值。

​ Object類預設的是淺克隆

淺克隆.png

深克隆:

​ 基本資料型別複製過來,字串複用,引用資料型別會重新建立新的

深克隆.png

程式碼實現:

package com.itheima.a04objectdemo;

public class ObjectDemo4 {
    public static void main(String[] args) throws CloneNotSupportedException {
        // protected object clone(int a) 物件克隆 

        //1.先建立一個物件
        int[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};
        User u1 = new User(1, "zhangsan", "1234qwer", "girl11", data);

        //2.克隆物件
        //細節:
        //方法在底層會幫我們建立一個物件,並把原物件中的資料複製過去。
        //書寫細節:
        //1.重寫Object中的clone方法
        //2.讓javabean類實現Cloneable介面
        //3.建立原物件並呼叫clone就可以了
        //User u2 =(User)u1.clone();

        //驗證一件事情:Object中的克隆是淺克隆
        //想要進行深克隆,就需要重寫clone方法並修改裡面的方法體
        //int[] arr = u1.getData();
        //arr[0] = 100;

        //System.out.println(u1);
        //System.out.println(u2);


        //以後一般會用第三方工具進行克隆
        //1.第三方寫的程式碼匯入到專案中
        //2.編寫程式碼
        //Gson gson =new Gson();
        //把物件變成一個字串
        //String s=gson.toJson(u1);
        //再把字串變回物件就可以了
        //User user =gson.fromJson(s, User.class);

        //int[] arr=u1.getData();
        //arr[0] = 100;

        //列印物件
        //System.out.println(user);

    }
}

package com.itheima.a04objectdemo;

import java.util.StringJoiner;



//Cloneable
//如果一個介面裡面沒有抽象方法
//表示當前的介面是一個標記性介面
//現在Cloneable表示一旦實現了,那麼當前類的物件就可以被克降
//如果沒有實現,當前類的物件就不能克隆
public class User implements Cloneable {
    private int id;
    private String username;
    private String password;
    private String path;
    private int[] data;




    public User() {
    }

    public User(int id, String username, String password, String path, int[] data) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.path = path;
        this.data = data;
    }

    /**
     * 獲取
     *
     * @return id
     */
    public int getId() {
        return id;
    }

    /**
     * 設定
     *
     * @param id
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * 獲取
     *
     * @return username
     */
    public String getUsername() {
        return username;
    }

    /**
     * 設定
     *
     * @param username
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * 獲取
     *
     * @return password
     */
    public String getPassword() {
        return password;
    }

    /**
     * 設定
     *
     * @param password
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * 獲取
     *
     * @return path
     */
    public String getPath() {
        return path;
    }

    /**
     * 設定
     *
     * @param path
     */
    public void setPath(String path) {
        this.path = path;
    }

    /**
     * 獲取
     *
     * @return data
     */
    public int[] getData() {
        return data;
    }

    /**
     * 設定
     *
     * @param data
     */
    public void setData(int[] data) {
        this.data = data;
    }

    public String toString() {
        return "角色編號為:" + id + ",使用者名稱為:" + username + "密碼為:" + password + ", 遊戲圖片為:" + path + ", 進度:" + arrToString();
    }


    public String arrToString() {
        StringJoiner sj = new StringJoiner(", ", "[", "]");

        for (int i = 0; i < data.length; i++) {
            sj.add(data[i] + "");
        }
        return sj.toString();
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //呼叫父類中的clone方法
        //相當於讓Java幫我們克隆一個物件,並把克隆之後的物件返回出去。

        //先把被克隆物件中的陣列獲取出來
        int[] data = this.data;
        //建立新的陣列
        int[] newData =new int[data.length];
        //複製陣列中的資料
        for (int i = 0; i < data.length; i++) {
            newData[i] = data[i];
        }
        //呼叫父類中的方法克隆物件
            User u=(User)super.clone();
        //因為父類中的克隆方法是淺克隆,替換克隆出來物件中的陣列地址值
        u.data =newData;
        return u;
    }
}

5 Objects類

5.1 概述

tips:瞭解內容

檢視API文件,我們可以看到API文件中關於Objects類的定義如下:

1576058492444.png

Objects類所在包是在java.util包下,因此在使用的時候需要進行導包。並且Objects類是被final修飾的,因此該類不能被繼承。

Objects類提供了一些物件常見操作的方法。比如判斷物件是否相等,判斷物件是否為null等等。

接下來我們來檢視一下API文件,看一下Objects類中的成員,如下所示:

1576058659628.png

我們可以發現Objects類中無無參構造方法,因此我們不能使用new關鍵字去建立Objects的物件。同時我們可以發現Objects類中所提供的方法都是靜態的。因此我們可以透過類名直接去呼叫這些方法。

5.2 常見方法

tips:重點講解內容

<font color="red" size="3">常見方法介紹</font>

我們要重點學習的Objects類中的常見方法如下所示:

public static String toString(Object o)                     // 獲取物件的字串表現形式
public static boolean equals(Object a, Object b)            // 比較兩個物件是否相等
public static boolean isNull(Object obj)                    // 判斷物件是否為null
public static boolean nonNull(Object obj)                    // 判斷物件是否不為null

我們要了解的Objects類中的常見方法如下所示:

public static <T> T requireNonNull(T obj)                    // 檢查物件是否不為null,如果為null直接丟擲異常;如果不是null返回該物件;
public static <T> T requireNonNullElse(T obj, T defaultObj) // 檢查物件是否不為null,如果不為null,返回該物件;如果為null返回defaultObj值
public static <T> T requireNonNullElseGet(T obj, Supplier<? extends T> supplier)    // 檢查物件是否不為null,如果不為null,返回該物件;如果                                                             // 為null,返回由Supplier所提供的值

上述方法中的T可以理解為是Object型別。

<font color="red" size="3">案例演示</font>

接下來我們就來透過一些案例演示一下Objects類中的這些方法特點。

<font color="blue" size="2">案例1</font>:演示重點學習方法

實現步驟:

  1. 建立一個學生類,提供兩個成員變數(name , age);並且提供對應的無參構造方法和有參構造方法以及get/set方法,並且重寫toString方法和equals方法
  2. 建立一個測試類(ObjectsDemo01), 在該類中編寫測試程式碼

如下所示:

Student類

public class Student {

    private String name ;       // 姓名
    private String age ;        // 年齡

    // 其他程式碼略
    ...
        
}

ObjectsDemo01測試類

public class ObjectsDemo01 {

    public static void main(String[] args) {

        // 呼叫方法
        method_04() ;

    }

    // 測試nonNull方法
    public static void method_04() {

        // 建立一個學生物件
        Student s1 = new Student("itheima" , "14") ;

        // 呼叫Objects類中的nonNull方法
        boolean result = Objects.nonNull(s1);

        // 輸出結果
        System.out.println(result);

    }

    // 測試isNull方法
    public static void method_03() {

        // 建立一個學生物件
        Student s1 = new Student("itheima" , "14") ;

        // 呼叫Objects類中的isNull方法
        boolean result = Objects.isNull(s1);

        // 輸出結果
        System.out.println(result);

    }

    // 測試equals方法
    public static void method_02() {

        // 建立兩個學生物件
        Student s1 = new Student("itheima" , "14") ;
        Student s2 = new Student("itheima" , "14") ;

        // 呼叫Objects類中的equals方法,比較兩個物件是否相等
        boolean result = Objects.equals(s1, s2);     // 如果Student沒有重寫Object類中的equals方法,此處比較的還是物件的地址值

        // 輸出結果
        System.out.println(result);

    }

    // 測試toString方法
    public static void method_01() {

        // 建立一個學生物件
        Student s1 = new Student("itheima" , "14") ;

        // 呼叫Objects中的toString方法,獲取s1物件的字串表現形式
        String result = Objects.toString(s1);       // 如果Student沒有重寫Object類中的toString方法,此處還是返回的物件的地址值

        // 輸出結果
        System.out.println(result);

    }

}

<font color="blue" size="2">案例2</font>:演示需要了解的方法

public class ObjectsDemo02 {

    public static void main(String[] args) {

        // 呼叫方法
        method_03();

    }

    // 演示requireNonNullElseGet
    public static void method_03() {

        // 建立一個學生物件
        Student s1 = new Student("itheima" , "14") ;

        // 呼叫Objects物件的requireNonNullElseGet方法,該方法的第二個引數是Supplier型別的,檢視原始碼我們發現Supplier是一個函式式介面,
        // 那麼我們就可以為其傳遞一個Lambda表示式,而在Supplier介面中所定義的方法是無參有返回值的方法,因此具體呼叫所傳入的Lambda表示式如下所示
        Student student = Objects.requireNonNullElseGet(s1, () -> {
            return new Student("itcast", "14");
        });

        // 輸出
        System.out.println(student);

    }

    // 演示requireNonNullElse
    public static void method_02() {

        // 建立一個學生物件
        Student s1 = new Student("itheima" , "14") ;

        // 呼叫Objects物件的requireNonNullElse方法
        Student student = Objects.requireNonNullElse(s1, new Student("itcast", "14"));

        // 輸出
        System.out.println(student);

    }

    // 演示requireNonNull
    public static void method_01() {

        // 建立一個學生物件
        Student s1 = new Student("itheima" , "14") ;

        // 呼叫Objects物件的requireNonNull方法
        Student student = Objects.requireNonNull(s1);

        // 輸出
        System.out.println(student);

    }

}

注:瞭解性的方法可以可以作為擴充套件影片進行下發。

6 BigInteger類

6.1 引入

​ 平時在儲存整數的時候,Java中預設是int型別,int型別有取值範圍:-2147483648 ~ 2147483647。如果數字過大,我們可以使用long型別,但是如果long型別也表示不下怎麼辦呢?

​ 就需要用到BigInteger,可以理解為:大的整數。

​ 有多大呢?理論上最大到42億的21億次方

​ 基本上在記憶體撐爆之前,都無法達到這個上限。

6.2 概述

檢視API文件,我們可以看到API文件中關於BigInteger類的定義如下:

Snipaste_2022-09-04_21-36-01.png

BigInteger所在包是在java.math包下,因此在使用的時候就需要進行導包。我們可以使用BigInteger類進行大整數的計算

6.3 常見方法

<font color="red" size="3">構造方法</font>

public BigInteger(int num, Random rnd)         //獲取隨機大整數,範圍:[0 ~ 2的num次方-1]
public BigInteger(String val)                 //獲取指定的大整數
public BigInteger(String val, int radix)     //獲取指定進位制的大整數
    
下面這個不是構造,而是一個靜態方法獲取BigInteger物件
public static BigInteger valueOf(long val)     //靜態方法獲取BigInteger的物件,內部有最佳化

構造方法小結:

  • 如果BigInteger表示的數字沒有超出long的範圍,可以用靜態方法獲取。
  • 如果BigInteger表示的超出long的範圍,可以用構造方法獲取。
  • 物件一旦建立,BigInteger內部記錄的值不能發生改變。
  • 只要進行計算都會產生一個新的BigInteger物件

<font color="red" size="3">常見成員方法</font>

BigDecimal類中使用最多的還是提供的進行四則運算的方法,如下:

public BigInteger add(BigInteger val)                    //加法
public BigInteger subtract(BigInteger val)                //減法
public BigInteger multiply(BigInteger val)                //乘法
public BigInteger divide(BigInteger val)                //除法
public BigInteger[] divideAndRemainder(BigInteger val)     //除法,獲取商和餘數
public  boolean equals(Object x)                         //比較是否相同
public  BigInteger pow(int exponent)                     //次冪、次方
public  BigInteger max/min(BigInteger val)                 //返回較大值/較小值
public  int intValue(BigInteger val)                     //轉為int型別整數,超出範圍資料有誤

程式碼實現:

package com.itheima.a06bigintegerdemo;

import java.math.BigInteger;

public class BigIntegerDemo1 {
    public static void main(String[] args) {
        /*
            public BigInteger(int num, Random rnd) 獲取隨機大整數,範圍:[0~ 2的num次方-11
            public BigInteger(String val) 獲取指定的大整數
            public BigInteger(String val, int radix) 獲取指定進位制的大整數

            public static BigInteger valueOf(long val) 靜態方法獲取BigInteger的物件,內部有最佳化

            細節:
            物件一旦建立裡面的資料不能發生改變。
        */


        //1.獲取一個隨機的大整數
        /* Random r=new Random();
            for (int i = e; i < 100; i++) {
            BigInteger bd1 = new BigInteger(4,r);
            System.out.println(bd1);//[@ ~ 15]}
            }
        */

        //2.獲取一個指定的大整數,可以超出long的取值範圍
        //細節:字串中必須是整數,否則會報錯
        /* BigInteger bd2 = new BigInteger("1.1");
            System.out.println(bd2);
        */

        /*
            BigInteger bd3 = new BigInteger("abc");
            System.out.println(bd3);
         */

        //3.獲取指定進位制的大整數
        //細節:
        //1.字串中的數字必須是整數
        //2.字串中的數字必須要跟進位制吻合。
        //比如二進位制中,那麼只能寫日和1,寫其他的就報錯。
        BigInteger bd4 = new BigInteger("123", 2);
        System.out.println(bd4);

        //4.靜態方法獲取BigInteger的物件,內部有最佳化
        //細節:
        //1.能表示範圍比較小,只能在long的取值範圍之內,如果超出long的範圍就不行了。
        //2.在內部對常用的數字: -16 ~ 16 進行了最佳化。
        //  提前把-16~16 先建立好BigInteger的物件,如果多次獲取不會重新建立新的。
        BigInteger bd5 = BigInteger.valueOf(16);
        BigInteger bd6 = BigInteger.valueOf(16);
        System.out.println(bd5 == bd6);//true


        BigInteger bd7 = BigInteger.valueOf(17);
        BigInteger bd8 = BigInteger.valueOf(17);
        System.out.println(bd7 == bd8);//false


        //5.物件一旦建立內部的資料不能發生改變
        BigInteger bd9 =BigInteger.valueOf(1);
        BigInteger bd10 =BigInteger.valueOf(2);
        //此時,不會修改參與計算的BigInteger物件中的借,而是產生了一個新的BigInteger物件記錄
        BigInteger result=bd9.add(bd10);
        System.out.println(result);//3

    }
}
package com.itheima.a06bigintegerdemo;

import java.math.BigInteger;

public class BigIntegerDemo2 {
    public static void main(String[] args) {
        /*
            public BigInteger add(BigInteger val) 加法
            public BigInteger subtract(BigInteger val) 減法
            public BigInteger multiply(BigInteger val) 乘法
            public BigInteger divide(BigInteger val) 除法,獲取商
            public BigInteger[] divideAndRemainder(BigInteger val) 除法,獲取商和餘數
            public boolean equals(Object x) 比較是否相同
            public BigInteger pow(int exponent) 次冪
            public BigInteger max/min(BigInteger val) 返回較大值/較小值
            public int intValue(BigInteger val) 轉為int型別整數,超出範圍資料有誤
        */

        //1.建立兩個BigInteger物件
        BigInteger bd1 = BigInteger.valueOf(10);
        BigInteger bd2 = BigInteger.valueOf(5);

        //2.加法
        BigInteger bd3 = bd1.add(bd2);
        System.out.println(bd3);

        //3.除法,獲取商和餘數
        BigInteger[] arr = bd1.divideAndRemainder(bd2);
        System.out.println(arr[0]);
        System.out.println(arr[1]);

        //4.比較是否相同
        boolean result = bd1.equals(bd2);
        System.out.println(result);

        //5.次冪
        BigInteger bd4 = bd1.pow(2);
        System.out.println(bd4);

        //6.max
        BigInteger bd5 = bd1.max(bd2);


        //7.轉為int型別整數,超出範圍資料有誤
        /* BigInteger bd6 = BigInteger.valueOf(2147483647L);
         int i = bd6.intValue();
         System.out.println(i);
         */

        BigInteger bd6 = BigInteger.valueOf(200);
        double v = bd6.doubleValue();
        System.out.println(v);//200.0
    }
}

6.4 底層儲存方式:

對於計算機而言,其實是沒有資料型別的概念的,都是0101010101,資料型別是程式語言自己規定的,所以在實際儲存的時候,先把具體的數字變成二進位制,每32個bit為一組,儲存在陣列中。

陣列中最多能儲存元素個數:21億多

陣列中每一位能表示的數字:42億多

理論上,BigInteger能表示的最大數字為:42億的21億次方。

但是還沒到這個數字,電腦的記憶體就會撐爆,所以一般認為BigInteger是無限的。

儲存方式如圖所示:

bigInteger的底層原理.png

7 BigDecimal類

7.1 引入

首先我們來分析一下如下程式的執行結果:

public class BigDecimalDemo01 {

    public static void main(String[] args) {
        System.out.println(0.09 + 0.01);
    }

}

這段程式碼比較簡單,就是計算0.09和0.01之和,並且將其結果在控制檯進行輸出。那麼按照我們的想法在控制檯輸出的結果應該為0.1。那麼實際的執行結果是什麼呢?我們來執行一下程式,控制檯的輸出

結果如下所示:

0.09999999999999999

這樣的結果其實就是一個丟失精度的結果。為什麼會產生精度丟失呢?

在使用float或者double型別的資料在進行數學運算的時候,很有可能會產生精度丟失問題。我們都知道計算機底層在進行運算的時候,使用的都是二進位制資料; 當我們在程式中寫了一個十進位制資料 ,在

進行運算的時候,計算機會將這個十進位制資料轉換成二進位制資料,然後再進行運算,計算完畢以後計算機會把運算的結果再轉換成十進位制資料給我們展示; 如果我們使用的是整數型別的資料進行計算,那

麼在把十進位制資料轉換成二進位制資料的時候不會存在精度問題; 如果我們的資料是一個浮點型別的資料,有的時候計算機並不會將這個資料完全轉換成一個二進位制資料,而是將這個將其轉換成一個無限的

趨近於這個十進數的二進位制資料; 這樣使用一個不太準確的資料進行運算的時候, 最終就會造成精度丟失;為了提高精度,Java就給我們提供了BigDecimal供我們進行資料運算。

7.2 概述

檢視API文件,我們可以看到API文件中關於BigDecimal類的定義如下:

1576132679789.png

BigDecimal所在包是在java.math包下,因此在使用的時候就需要進行導包。我們可以使用BigDecimal類進行更加精準的資料計算。

7.3 常見方法

<font color="red" size="3">構造方法</font>

要用BigDecimal類,那麼就需要首先學習一下如何去建立BigDecimal的物件。透過檢視API文件,我們可以發現Jdk中針對BigDecimal類提供了很多的構造方法,但是最常用的構造方法是:

1576134383441.png

瞭解完常見的構造方法以後,我們接下來就重點介紹一下常見的成員方法。

<font color="red" size="3">常見成員方法</font>

BigDecimal類中使用最多的還是提供的進行四則運算的方法,如下:

public BigDecimal add(BigDecimal value)                // 加法運算
public BigDecimal subtract(BigDecimal value)        // 減法運算
public BigDecimal multiply(BigDecimal value)        // 乘法運算
public BigDecimal divide(BigDecimal value)            // 觸發運算

接下來我們就來透過一些案例演示一下這些成員方法的使用。

<font color="blue" size="2">案例1</font>:演示基本的四則運算

程式碼如下所示:

public class BigDecimalDemo01 {

    public static void main(String[] args) {

        // 建立兩個BigDecimal物件
        BigDecimal b1 = new BigDecimal("0.3") ;
        BigDecimal b2 = new BigDecimal("4") ;

        // 呼叫方法進行b1和b2的四則運算,並將其運算結果在控制檯進行輸出
        System.out.println(b1.add(b2));         // 進行加法運算
        System.out.println(b1.subtract(b2));    // 進行減法運算
        System.out.println(b1.multiply(b2));    // 進行乘法運算
        System.out.println(b1.divide(b2));      // 進行除法運算

    }

}

執行程式進行測試,控制檯輸出結果如下:

4.3
-3.7
1.2
0.075

此時我們可以看到使用BigDecimal類來完成浮點數的計算不會存在損失精度的問題。

<font color="blue" size="2">案例2</font>:演示除法的特殊情況

如果使用BigDecimal型別的資料進行除法運算的時候,得到的結果是一個無限迴圈小數,那麼就會報錯:ArithmeticException。 如下程式碼所示:

public class BigDecimalDemo02 {

    public static void main(String[] args) {

        // 建立兩個BigDecimal物件
        BigDecimal b1 = new BigDecimal("1") ;
        BigDecimal b2 = new BigDecimal("3") ;

        // 呼叫方法進行b1和b2的除法運算,並且將計算結果在控制檯進行輸出
        System.out.println(b1.divide(b2));

    }

}

執行程式進行測試,控制檯輸出結果如下所示:

Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
    at java.base/java.math.BigDecimal.divide(BigDecimal.java:1716)
    at com.itheima.api.bigdecimal.demo02.BigDecimalDemo02.main(BigDecimalDemo02.java:14)

針對這個問題怎麼解決,此時我們就需要使用到BigDecimal類中另外一個divide方法,如下所示:

BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)

上述divide方法引數說明:

divisor:            除數對應的BigDecimal物件;
scale:                精確的位數;
roundingMode:        取捨模式;
取捨模式被封裝到了RoundingMode這個列舉類中(關於列舉我們後期再做重點講解),在這個列舉類中定義了很多種取捨方式。最常見的取捨方式有如下幾個:
UP(直接進1) , FLOOR(直接刪除) , HALF_UP(4舍五入),我們可以透過如下格式直接訪問這些取捨模式:列舉類名.變數名

接下來我們就來演示一下這些取捨模式,程式碼如下所示:

public class BigDecimalDemo02 {

    public static void main(String[] args) {

        // 呼叫方法
        method_03() ;

    }

    // 演示取捨模式HALF_UP
    public static void method_03() {

        // 建立兩個BigDecimal物件
        BigDecimal b1 = new BigDecimal("0.3") ;
        BigDecimal b2 = new BigDecimal("4") ;

        // 呼叫方法進行b1和b2的除法運算,並且將計算結果在控制檯進行輸出
        System.out.println(b1.divide(b2 , 2 , RoundingMode.HALF_UP));

    }

    // 演示取捨模式FLOOR
    public static void method_02() {

        // 建立兩個BigDecimal物件
        BigDecimal b1 = new BigDecimal("1") ;
        BigDecimal b2 = new BigDecimal("3") ;

        // 呼叫方法進行b1和b2的除法運算,並且將計算結果在控制檯進行輸出
        System.out.println(b1.divide(b2 , 2 , RoundingMode.FLOOR));

    }

    // 演示取捨模式UP
    public static void method_01() {

        // 建立兩個BigDecimal物件
        BigDecimal b1 = new BigDecimal("1") ;
        BigDecimal b2 = new BigDecimal("3") ;

        // 呼叫方法進行b1和b2的除法運算,並且將計算結果在控制檯進行輸出
        System.out.println(b1.divide(b2 , 2 , RoundingMode.UP));

    }

}

小結:後期在進行兩個數的除法運算的時候,我們常常使用的是可以設定取捨模式的divide方法。

7.4 底層儲存方式:

把資料看成字串,遍歷得到裡面的每一個字元,把這些字元在ASCII碼錶上的值,都儲存到陣列中。

bigdecimal儲存原理.png

相關文章