视频地址:https://www.bilibili.com/video/BV1Cv411372m
此笔记是:P113 - P119
1.面向对象三大特征之三:多态(重点)
1.1 多态的概述、多态的形式
-
什么是多态?
- 同类型的对象,执行同一个行为,会表现出不同的行为特征。
- 例如:下面举得例子,同类型的对象(都是动物类型的),执行同一个行为(run方法)
- 同类型的对象,执行同一个行为,会表现出不同的行为特征。
-
多态的常见形式
- 接口也可以看成是一种父类(理解成干爹),实现类即为它的子类。
父类类型 对象名称 = new 子类构造器; 接口 对象名称 = new 实现类构造器;
/**
父类 Animal.java
*/
public class Animal {
public String name = "父类动物名称";
public void run(){
System.out.println("动物可以跑~~");
}
}
/**
子类 Dog.java
*/
public class Dog extends Animal{
public String name = "子类狗名称";
@Override
public void run() {
System.out.println("🐕跑的贼溜~~~~~");
}
}
/**
子类 Tortoise.java
*/
public class Tortoise extends Animal{
public String name = "子类乌龟名称";
@Override
public void run() {
System.out.println("🐢跑的非常慢~~~");
}
}
-
多态中成员访问特点
- 方法调用:编译看左边,运行看右边。
- 变量调用:编译看左边,运行也看左边。(多态侧重行为多态)
/** 测试 Test.java */ public class Test { public static void main(String[] args) { // 目标:先认识多态的形式 // 父类 对象名称 = new 子类构造器(); Animal a = new Dog(); a.run(); // 方法调用:编译看左,运行看右 【执行时,编译看Animal里面是否有run方法,但真正跑的时候走的是真实对象Dog里面的run方法。 System.out.println(a.name); // 变量调用:编译看左,运行也看左 故打印出来为:父类动物名称 Animal a1 = new Tortoise(); a1.run(); System.out.println(a1.name); //打印结果为:父类动物名称 } }
- 那么为什么是这样的呢?
- 设计层面上的考虑,因为多态侧重行为(编译的时候方法代表行为),运行看右边对象才能实现在不同对象下表现不同行为。
- 从重写角度来说,父类的方法可能会是抽象的。最终还是子类的重写方法才可以跑,一定是要看右边的。
- 但变量的话,无论子类还是父类变量都是可以用的,写代码的时候,已经说了是访问父类的(看左边),然后变量没有多态的概念,所以变量不再去看右边了。
-
多态的前提
- 有继承/实现关系;有父类引用指向子类对象;有方法重写。
1.2 多态的好处
-
在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
-
定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性与便利。
-
多态下会产生的一个问题:多态下不能使用子类的独有功能。
- 因为编译的时候要看父类(左边的),子类独有行为父类里面是无的,编译阶段就会报错的。
- 因为编译的时候要看父类(左边的),子类独有行为父类里面是无的,编译阶段就会报错的。
1.3 多态下引用数据类型的类型转换
-
自动类型转换(从子到父):子类对象赋值给父类类型的变量指向。
-
强制类型转换吗(从父到子)
-
此时必须进行强制类型转换:子类 对象变量 = (子类)父类类型的变量
-
作用:可以解决多态下的劣势,可以实现调用子类独有的功能。
-
注意: 如果转型后的类型和对象真实类型不是同一种类型,那么在转换的时候就会出现ClassCastException
Animal t = new Tortoise(); Dog d = (Dog)t; // 强制类型转换,编译阶段不报错的(注意:有继承或者实现关系编译阶段可以强制,没有毛病)【因为官方会觉得t Animal可能真的指向Dog类型,允许转Dog的】但运行时出现异常 ClassCastException
-
-
Java建议强转转换前使用instanceof判断当前对象的真实类型,再进行强制转换
- 有时候是必须要加判断的,例如在方法中:
- 有时候是必须要加判断的,例如在方法中:
1.4 多态的综合案例
-
USB的接口类:接入、拔出
public interface USB { void connect(); // 接入 void unconnect(); // 拔出 }
-
2个USB的实现类:鼠标、键盘
-
键盘实现类
public class KeyBoard implements USB{ // 键盘必须要有自己的名字(商标名称) private String name; // 有参构造器 public KeyBoard(String name) { this.name = name; } @Override public void connect() { System.out.println(name + "成功的接入了设备了~~~"); } @Override public void unconnect() { System.out.println(name + "成功的从设备弹出了~~~"); } /** 独有功能 */ public void keyDown(){ System.out.println(name + "写下了:老铁,6666,下次再来哦,老弟~~~~"); } // 无参构造器 public String getName() { return name; } public void setName(String name) { this.name = name; } }
-
鼠标实现类
public class Mouse implements USB{ private String name; public Mouse(String name) { this.name = name; } @Override public void connect() { System.out.println(name + "成功的接入了设备了~~~"); } @Override public void unconnect() { System.out.println(name + "成功的从设备弹出了~~~"); } /** 独有功能 */ public void click(){ System.out.println(name + "双击点亮小红心~~~~"); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
-
-
创建一个电脑对象,创建USB设备对象,安装启动
public class Computer { private String name; public Mouse(String name) { this.name = name; } /** 提供安装USB设备的入口。 */ public void installUSB(USB u){ // 这里是多态:父类接口作为入参,所有的实例(子类)对象都能进来 // 这里的 USB 可能是鼠标也可能是键盘 u.connect(); //这个是通用功能 // 独有功能 // 调用独有功能时不能直接 u.click() 或者 u.keyDown() 因为它并不知道对象具体指谁 if(u instanceof Mouse){ Mouse m = (Mouse) u; m.click(); }else if(u instanceof KeyBoard) { KeyBoard k = (KeyBoard) u; k.keyDown(); } u.unconnect();//这个是通用功能 } public String getName() { return name; } public void setName(String name) { this.name = name; } }
-
开始组装
public class Test { public static void main(String[] args) { // a、创建电脑对象 Computer c = new Computer(); // b、创建USB设备,鼠标对象,键盘对象 //Mouse u = new Mouse("罗技鼠标"); 这样写也成,但多数是下面多态写法! USB u = new Mouse("罗技鼠标"); c.installUSB(u); USB k = new KeyBoard("双飞燕键盘"); c.installUSB(k); } }
2.内部类
2.1 内部类概述
-
内部类就是定义在一个类里面的类,里面的类可以理解成(寄生),外部类可以理解成(宿主)。
public class People{ // 内部类 public class Heart{ } }
-
内部类的使用场景、作用
- 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构可以选择使用内部类来设计。【例如:人是一个类,要关于这个人的心脏信息,则在人里面定义一个心脏类】
- 内部类通常可以方便访问外部类的成员,包括私有的成员。
- 内部类提供了更好的封装性,内部类本身就可以用 private protectecd 等修饰,封装性可以做更多控制。
-
内部类的分类
- 静态内部类[了解]
- 成员内部类(非静态内部类) [了解]
- 局部内部类[了解]
- 匿名内部类(重点)
2.2 内部类之一:静态内部类[了解]
-
什么是静态内部类?
- 有static修饰,属于外部类本身。
- 它的特点和使用与普通类是完全一样的,类有的成分它都有【静态成员变量、实例成员变量、Getter、Setter、有参无参构造器、普通方法】,只是位置在别人里面而已。
public class Outer{ // 静态成员内部类 public static class Inner{ } }
-
静态内部类创建对象的格式:
格式:外部类名.内部类名 对象名 = new 外部类名.内部类构造器; 范例:Outer.Inner in = new Outer.Inner();
-
静态内部类的访问拓展:
-
1、静态内部类中是否可以直接访问外部类的静态成员?
- 可以,外部类的静态成员只有一份可以被共享访问。
- 可以,外部类的静态成员只有一份可以被共享访问。
-
2、静态内部类中是否可以直接访问外部类的实例成员?
-
不可以的,外部类的实例成员必须用外部类对象访问。
-
为什么不能访问?
-
因为hobby属于外部类对象的,直接访问是不知道 要访问的是外部类那个对象的hobby。
-
若非要访问外部类成员,必须 new外部类对象
-
-
-
2.3 内部类之二:成员内部类[了解]
-
什么是成员内部类?
- 无static修饰,属于外部类的对象。
- JDK 16之前,成员内部类中不能定义静态成员,JDK 16开始也可以定义静态成员了。
public class Outer { // 成员内部类 public class Inner { } }
-
成员内部类创建对象的格式:
格式:外部类名.内部类名 对象名 = new 外部类构造器.new 内部类构造器(); 范例:Outer.Inner in = new Outer().new Inner();
-
成员内部类的访问拓展:
- 1、成员内部类中是否可以直接访问外部类的静态成员?
- 可以,外部类的静态成员只有一份可以被共享访问。
- 2、成员内部类的实例方法中是否可以直接访问外部类的实例成员?
- 可以的,因为必须先有外部类对象,才能有成员内部类对象,所以可以直接访问外部类对象的实例成员。
- 可以的,因为必须先有外部类对象,才能有成员内部类对象,所以可以直接访问外部类对象的实例成员。
- 1、成员内部类中是否可以直接访问外部类的静态成员?
-
静态内部类与成员内部类
- 成员内部类:一定先外部类对象,才有内部类对象。【例如:心脏不可能独立,一定先有个人对象再创建心脏对象】
- 静态内部类:其实可以独立出来的,可以先不外部类对象。
2.4 内部类之三:局部内部类[了解]
-
局部内部类 (鸡肋语法,了解即可)
-
局部内部类放在方法、代码块、构造器等执行体中。
-
局部内部类的类文件名为: 外部类$N内部类.class。
-
2.5 内部类之四:匿名内部类概述[重点]
-
匿名内部类:
- 本质上是一个没有名字的局部内部类,定义在方法中、代码块中、等。
- 作用:方便创建子类对象,最终目的为了简化代码编写。
-
格式:
new 类|抽象类名|或者接口名() { 重写方法; };
Animal a = new Animal() { public void run() { } }; a. run();
-
正常我们是
public class Test { public static void main(String[] args) { Animal a = new Tiger(); a.run(); } } class Tiger extends Animal{ @Override public void run() { System.out.println("老虎跑的快~~~"); } } abstract class Animal{ public abstract void run(); }
-
现在匿名内部类是
public class Test { public static void main(String[] args) { Animal a = new Animal(){ @Override public void run() { System.out.println("老虎跑的快~~~"); } }; a.run(); } } abstract class Animal{ public abstract void run(); }
-
特点总结:
- 匿名内部类是一个没有名字的内部类。
- 匿名内部类写出来就会产生一个匿名内部类的对象。例如:上面例子,并不是产生了Animal,Animal是抽象类不会创建对象,匿名内部类创建的就只是匿名内部类的对象
- 匿名内部类的对象类型相当于是当前new的那个的类型的子类类型。例如:上面例子,相当于是Animal的子类类型
2.6 匿名内部类常见使用形式
-
匿名内部类在开发中的使用形式了解
- 某个学校需要让老师,学生,运动员一起参加游泳比赛
/*游泳接口*/ public interface Swimming { void swim(); }
/* 测试类*/ public class JumppingDemo { public static void main(String[] args) { //需求:goSwimming方法 } // 定义一个方法让所有角色进来一起比赛 public static void goSwimming(Swimming swimming) { swimming.swim(); } }
/**
【之前方法】目标:掌握匿名内部类的使用形式(语法)
*/
public class Test2 {
public static void main(String[] args) {
Swimming s = new Student();
go(s);
}
/**
学生 老师 运动员可以一起参加游泳比赛
*/
public static void go(Swimming s){ // 接口充当一种父类,所有子类对象都可以送进来
System.out.println("开始。。。");
s.swim();
System.out.println("结束。。。");
}
}
class Student implement Swimming{
@Override
public void swim() {
System.out.println("学生快乐的自由泳🏊");
}
}
interface Swimming{
void swim();
}
/**
【匿名内部类】目标:掌握匿名内部类的使用形式(语法)
*/
public class Test2 {
public static void main(String[] args) {
Swimming s = new Swimming() {
@Override
public void swim() {
System.out.println("学生快乐的自由泳🏊");
}
};
go(s);
System.out.println("--------------");
Swimming s1 = new Swimming() {
@Override
public void swim() {
System.out.println("老师泳🏊的贼快~~~~~");
}
};
go(s1);
System.out.println("--------------");
/**
匿名内部类可以作为方法的实际参数进行传输。
*/
go(new Swimming() {
@Override
public void swim() {
System.out.println("运动员🏊的贼快啊~~~~~");
}
});
}
/**
学生 老师 运动员可以一起参加游泳比赛
*/
public static void go(Swimming s){
System.out.println("开始。。。");
s.swim();
System.out.println("结束。。。");
}
}
interface Swimming{
void swim();
}
- 使用总结:
- 匿名内部类可以作为方法的实际参数进行传输。
2.7 匿名内部类真实使用场景演示
-
匿名内部类在开发中的真实使用场景演示
- 给按钮绑定点击事件
// 为按钮绑定点击事件监听器。 btn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("登录一下~~"); } }); // btn.addActionListener(e -> System.out.println("登录一下~~"));
- 给按钮绑定点击事件
-
具体
import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** 目标:通过GUI编程 理解匿名内部类的真实使用场景 */ public class Test3 { public static void main(String[] args) { // 1、创建窗口 JFrame win = new JFrame("登录界面"); JPanel panel = new JPanel(); win.add(panel); //添加桌布 // 2、创建一个按钮对象 JButton btn = new JButton("登录"); // 注意:讲解匿名内部类的使用 btn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(win, "点我一下,说明爱我!"); } }); //btn.addActionListener( e ->JOptionPane.showMessageDialog(win, "别说话!!") ); // 3、把按钮对象添加到桌布上展示 panel.add(btn); // 4、展示窗口 win.setSize(400, 300); win.setLocationRelativeTo(null); win.setVisible(true); } }
-
使用总结:
- 开发中不是我们主动去定义匿名内部类的,而是别人需要我们写或者我们可以写的时候才会使用。
- 匿名内部类的代码可以实现代码进一步的简化(回扣主题)
3.常用API
3.1 Object
3.1.1 to String方法
-
什么是API?
- API(Application Programming interface) 应用程序编程接口。
- 简单来说:就是Java帮我们已经写好的一些方法,我们直接拿过来用就可以了。
-
Object类的作用:
- Object类的方法是一切子类对象都可以直接使用的,所以我们要学习Object类的方法。
- 一个类要么默认继承了Object类,要么间接继承了Object类,Object类是Java中的祖宗类。
-
Object类的常用方法:
-
Object的toString方法:
-
问题引出
- 开发中直接输出对象,默认输出对象的地址其实是毫无意义的。
- 开发中输出对象变量,更多的时候是希望看到对象的内容数据而不是对象的地址信息。
-
toString存在的意义
- 父类toString()方法存在的意义就是为了被子类重写,以便返回对象的内容信息,而不是地址信息!!
- 父类toString()方法存在的意义就是为了被子类重写,以便返回对象的内容信息,而不是地址信息!!
3.1.2 equals方法
-
Object的equals方法:
-
问题思考
- 直接比较两个对象的地址是否相同完全可以用“==”替代equals。
-
equals存在的意义
- 父类equals方法存在的意义就是为了被子类重写,以便子类自己来定制比较规则。【下面重写例子IDEA中可自动生成】
- 父类equals方法存在的意义就是为了被子类重写,以便子类自己来定制比较规则。【下面重写例子IDEA中可自动生成】
3.2 Objects
-
Objects概述
- Objects类与Object还是继承关系,Objects类是从JDK 1.7开始之后才有的。
- Objects是一个工具类,提供了一些方法去完成一些功能。
-
官方在进行字符串比较时,没有用字符串对象的的equals方法,而是选择了Objects的equals方法来比较。
@Override public boolean equals(Object o) { // 1、判断是否是同一个对象比较,如果是返回true。 if (this == o) return true; // 2、如果o是null返回false 如果o不是学生类型返回false ...Student != ..Pig if (o == null || this.getClass() != o.getClass()) return false; // 3、说明o一定是学生类型而且不为null Student student = (Student) o; return sex == student.sex && age == student.age && Objects.equals(name, student.name); }
-
使用Objects的equals方法在进行对象的比较结果是一样的,但是会更安全。
public class Test { public static void main(String[] args) { String s1 = "itheima"; String s2 = new String("itheima"); System.out.println(s1.equals(s2)); // true // 尽管s2创建对象,但还是比较的是内容。String类提供的equals,拿的是object类的,但String类已经重写了。 }
-
Objects的常见方法
-
源码分析
public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b)); }
3.3 StringBuilder
-
StringBuilder概述
- StringBuilder是一个可变的字符串类,我们可以把它看成是一个对象容器。
- 作用:提高字符串的操作效率,如拼接、修改等。
-
StringBuilder 构造器
-
StringBuilder常用方法
/** 目标:学会使用StringBuilder操作字符串,最终还需要知道它性能好的原因 */ public class StringBuilderDemo1 { public static void main(String[] args) { StringBuilder sb = new StringBuilder(); // 相当于现在sb里面什么都没有,如:"" sb.append("a"); // append用于拼接内容 sb.append("b"); sb.append("c"); sb.append(1); sb.append(false); sb.append(3.3); sb.append("abc"); System.out.println(sb); // 打印结果为:abc1false3.3abc StringBuilder sb1 = new StringBuilder(); // 支持链式编程 sb1.append("a").append("b").append("c").append("我爱你中国"); System.out.println(sb1); // 打印结果为:abc我爱你中国 // 反转 sb1.reverse().append("110"); System.out.println(sb1); // 打印结果为:国中你爱我cba110 System.out.println(sb1.length()); // 取长度 打印结果为:11 /** 注意:StringBuilder只是拼接字符串的手段:效率好。 最终的目的还是要恢复成String类型。 */ StringBuilder sb2 = new StringBuilder(); sb2.append("123").append("456"); // check(sb2); //会发现不行,因为check要的是string类型,而s2是StringBuilder类型 // 恢复成String类型 String rs = sb2.toString(); check(rs); // 打印结果为:123456 } public static void check(String data){ System.out.println(data); } }
-
String类拼接字符串原理图
-
StringBuilder提高效率原理图
-
案例——打印整型数组内容
public class StringBuilderTest2 { public static void main(String[] args) { int[] arr1 = null; System.out.println(toString(arr1)); // 打印结果为:null int[] arr2 = {10, 88, 99}; System.out.println(toString(arr2)); // 打印结果为:[10,,88,99] int[] arr3 = {}; System.out.println(toString(arr3));// 打印结果为:[] } /** 1、定义方法接收任意整型数组,返回数组内容格式 */ public static String toString(int[] arr){ if(arr != null){ // 2、开始拼接内容。 StringBuilder sb = new StringBuilder("["); for (int i = 0; i < arr.length; i++) { sb.append(arr[i] ).append(i == arr.length - 1 ? "" : ", "); } // 下面不能用这个替换:sb.append(arr[i] ).append(i == arr.length - 1 ? "]" : ", "); // 因为如传入的为int[] arr3 = {}; 打印结果为:[ sb.append("]"); return sb.toString(); }else { return null; } } }
3.4 Math
-
Math类
-
包含执行基本数字运算的方法,Math类没有提供公开的构造器。
-
如何使用类中的成员呢?
- 看类的成员是否都是静态的,如果是,通过类名就可以直接调用
-
Math 类的常用方法
/** 目标:Math类的使用。 Math用于做数学运算。 Math类中的方法全部是静态方法,直接用类名调用即可。 方法: 方法名 说明 public static int abs(int a) 获取参数a的绝对值: public static double ceil(double a) 向上取整 public static double floor(double a) 向下取整 public static double pow(double a, double b) 获取a的b次幂 public static long round(double a) 四舍五入取整 小结: 记住。 */ public class MathDemo { public static void main(String[] args) { // 1.取绝对值:返回正数 System.out.println(Math.abs(10)); // 10 System.out.println(Math.abs(-10.3)); // 10.3 // 2.向上取整: 5 System.out.println(Math.ceil(4.00000001)); // 5.0 System.out.println(Math.ceil(4.0)); // 4.0 // 3.向下取整:4 System.out.println(Math.floor(4.99999999)); // 4.0 System.out.println(Math.floor(4.0)); // 4.0 // 4.求指数次方 System.out.println(Math.pow(2 , 3)); // 2^3 = 8.0 // 5.四舍五入 10 System.out.println(Math.round(4.49999)); // 4 System.out.println(Math.round(4.500001)); // 5 System.out.println(Math.random()); // 0.0 - 1.0 (包前不包后) // 拓展: 3 - 9 之间的随机数 (0 - 6) + 3 // [0 - 6] + 3 int data = (int)(Math.random() * 7) + 3; System.out.println(data); } }
-
3.5 System
-
System 类概述
- System的功能是通用的,都是直接类名调用即可,所以System不能被实例化。
- System也是一个工具类,代表了当前系统,提供了一些与系统相关的方法。
-
System 类的常用方法
-
时间毫秒值
- 计算机认为时间是有起点的,起始时间: 1970年1月1日 00:00:00
- 时间毫秒值:指的是从1970年1月1日 00:00:00走到此刻的总的毫秒数,应该是很大的。 1s = 1000ms。
-
原因:
-
1969年8月,贝尔实验室的程序员肯汤普逊利用妻儿离开一个月的机会,开始着手创造一个全新的革命性的操作系统,他使用B编译语言在老旧的PDP-7机器上开发出了Unix的一个版本。
随后,汤普逊和同事丹尼斯里奇改进了B语言,开发出了C语言,重写了UNIX。
1970年1月1日 算C语言的生日long time = System.currentTimeMillis(); System.out.println(time);//1575465416955
-
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.Arrays;
/**
目标:System系统类的使用。
System代表当前系统。(虚拟机系统)
静态方法:
1.public static void exit(int status):终止JVM虚拟机,非0是异常终止。
2.public static long currentTimeMillis():获取当前系统此刻时间毫秒值。(重点)
3.可以做数组的拷贝。
arraycopy(Object var0, int var1, Object var2, int var3, int var4);
* 参数一:原数组
* 参数二:从原数组的哪个位置开始赋值。
* 参数三:目标数组
* 参数四:赋值到目标数组的哪个位置
* 参数五:赋值几个。
*/
public class SystemDemo {
public static void main(String[] args) {
System.out.println("程序开始。。。");
// System.exit(0); // JVM终止!
// 2、计算机认为时间有起源:返回1970-1-1 00:00:00 走到此刻的总的毫秒值:时间毫秒值。
long time = System.currentTimeMillis();
System.out.println(time);
// 计算10万次循环在计算机中所花时间
long startTime = System.currentTimeMillis();
// 进行时间的计算:性能分析
for (int i = 0; i < 100000; i++) {
System.out.println("输出:" + i);
}
long endTime = System.currentTimeMillis();
System.out.println((endTime - startTime)/1000.0 + "s");
// 3、做数组拷贝(了解)
/**
arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length)
参数一:被拷贝的数组
参数二:从哪个索引位置开始拷贝
参数三:复制的目标数组
参数四:粘贴位置
参数五:拷贝元素的个数
*/
int[] arr1 = {10, 20, 30, 40, 50, 60, 70};
int[] arr2 = new int[6]; //希望拷贝:[0, 0, 0, 0, 0, 0] ==> [0, 0, 40, 50, 60, 0]
System.arraycopy(arr1, 3, arr2, 2, 3);
System.out.println(Arrays.toString(arr2)); // 打印结果:[0, 0, 40, 50, 60, 0]
System.out.println("程序结束。。。。");
}
}
3.6 BigDecimal
-
BigDecimal作用
- 用于解决浮点型运算精度失真的问题
- 用于解决浮点型运算精度失真的问题
-
使用步骤
- 创建对象BigDecimal封装浮点型数据(最好的方式是调用方法)
public static BigDecimal valueOf(double val): 包装浮点数成为BigDecimal对象。
-
BigDecima常用API
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;
/**
目标:BigDecimal大数据类。
引入:
浮点型运算的时候直接+ * / 可能会出现数据失真(精度问题)。
BigDecimal可以解决浮点型运算数据失真的问题。
BigDicimal类:
包:java.math.
创建对象的方式(最好的方式:)
public static BigDecimal valueOf(double val) :包装浮点数成为大数据对象。
方法声明
public BigDecimal add(BigDecimal value) 加法运算
public BigDecimal subtract(BigDecimal value) 减法运算
public BigDecimal multiply(BigDecimal value) 乘法运算
public BigDecimal divide(BigDecimal value) 除法运算
public double doubleValue(): 把BigDecimal转换成double类型。
*/
public class BigDecimalDemo {
public static void main(String[] args) {
// 浮点型运算的时候直接+ * / 可能会出现数据失真(精度问题)。
System.out.println(0.09 + 0.01);
System.out.println(1.0 - 0.32);
System.out.println(1.015 * 100);
System.out.println(1.301 / 100);
System.out.println("-------------------------");
double a = 0.1;
double b = 0.2;
double c = a + b;
System.out.println(c);
System.out.println("--------------------------");
// 包装浮点型数据成为大数据对象 BigDeciaml
BigDecimal a1 = BigDecimal.valueOf(a);
BigDecimal b1 = BigDecimal.valueOf(b);
BigDecimal c1 = a1.add(b1); // +
// BigDecimal c1 = a1.subtract(b1); // -
// BigDecimal c1 = a1.multiply(b1); // *
// BigDecimal c1 = a1.divide(b1); // /
System.out.println(c1); // 打印结果为:0.3
// 用BigDecimal运算出来后还要转成double类型的
// 目的:double
double rs = c1.doubleValue();
System.out.println(rs); // 已转成double类型 打印结果为:0.3
// 注意事项:BigDecimal是一定要精度运算的
BigDecimal a11 = BigDecimal.valueOf(10.0);
BigDecimal b11 = BigDecimal.valueOf(3.0);
//BigDecimal c11 = a11.divide(b11);
//System.out.println(c11); 用上面直接运算会崩,因为除不尽,直接报错!
/**
参数一:除数 参数二:保留小数位数 参数三:舍入模式
HALF_UP 指四舍五入的意思
*/
BigDecimal c11 = a11.divide(b11, 2, RoundingMode.HALF_UP); // 3.3333333333
System.out.println(c11); // 打印结果为:3.33
System.out.println("-------------------");
}
}