当前位置:   article > 正文

Java SE基础知识详解第[9]期—面向对象进阶(多态、内部类、常用API)_需求: 使用面向对象编程模拟:设计一个电脑对象,可以安装2个usb设备 鼠标:被安装时

需求: 使用面向对象编程模拟:设计一个电脑对象,可以安装2个usb设备 鼠标:被安装时

写在前面:

        每一个不曾起舞的日子,都是对生命的辜负。

        希望看到这里的每一个人都能努力学习,不负韶华,成就更好的自己。


        以下仅是个人学习过程中的一些想法与感悟,Java知识博大精深,作为初学者,个人能力有限,哪里写的不够清楚、明白,还请各位不吝指正,欢迎交流与讨论。如果有朋友因此了解了一些知识或对Java有了更深层次的理解,从而进行更进一步的学习,那么这篇文章的意义也就达到了。

目录

1.面向对象三大特征之三:多态

1.1多态的概述,多态的形式

1.2多态的好处

1.3多态下引用数据类型的类型转换

1.4多态的综合案例

2.内部类

2.1内部类概述

2.2内部类之一:静态内部类[了解]

2.3内部类之二:成员内部类[了解]

2.4内部类之三:局部内部类[了解]

2.5内部类之四:匿名内部类概述[重点]

2.6匿名内部类真实使用场景演示

3常用API

3.1Object

3.2Objects

3.3StringBuilder

3.4Math

3.5System

3.6BigDecimal


面向对象进阶(多态、内部类、常用API)

1.面向对象三大特征之三:多态

1.1多态的概述,多态的形式

什么是多态?

        同类型的对象,执行同一个行为,会表现出不同的行为特征。

多态的常见形式

        父类类型 对象名称 = new 子类构造器;

        接口 对象名称 = new 实现类构造器;

多态中成员访问特点

        方法调用:编译看(等号左边,运行看等号右边。

        变量调用:编译看左边,运行也看左边。(多态侧重行为多态)。

        示例代码如下:

动物类

  1. public abstract class Animal {
  2. public String name = "父类动物";
  3. public abstract void run();
  4. }

狗类

  1. public class Dog extends Animal{
  2. public String name = "子类狗";
  3. @Override
  4. public void run() {
  5. System.out.println("狗跑得快");
  6. }
  7. }

乌龟类

  1. public class Tortoise extends Animal{
  2. public String name = "子类乌龟";
  3. @Override
  4. public void run() {
  5. System.out.println("乌龟跑得慢");
  6. }
  7. }

测试类

  1. public class Test {
  2. public static void main(String[] args) {
  3. // 1.多态的形式 父类类型 对象名称 = new 子类构造器;
  4. Animal dog = new Dog();
  5. dog.run(); // 狗跑得快 方法调用:编译看左边,运行看右边
  6. System.out.println(dog.name); // 父类动物 变量调用:编译看左边,运行也看左边(多态侧重行为多态)
  7. Animal tortoise = new Tortoise();
  8. tortoise.run(); // 乌龟跑得慢
  9. System.out.println(tortoise.name); // 父类动物
  10. }
  11. }

多态的前提

        有继承/实现关系;有父类引用指向子类对象;有方法重写。

1.2多态的好处

        在多态形式下,右边对象可以实现解耦合,便于扩展和维护。

        定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性与便利性。

多态下会产生的一个问题

        编译看左边,等号左边父类中没有子类的独有功能,编译不通过,因此多态下不能使用子类的独有功能,为了解决此问题,需要进行强制类型转换。

1.3多态下引用数据类型的类型转换

        自动类型转换(从子到父):子类对象赋值给父类类型的变量指向。

强制类型转换(从父到子):

        此时必须进行强制类型转换:子类 对象变量 = (子类)父类类型的变量

        作用:可以解决多态下的劣势,可以实现调用子类独有的功能。

        注意:如果转型后的类型和对象真实类型不是同一种类型,有继承或实现关系,在编译阶段,语法上可以强制类型转换,但是在运行实际转换的时候会出现ClassCastException错误

        因此在强制转换前使用instanceof判断当前对象的真实类型,再进行强制转换。

        格式为:变量名 instanceof 真实类型 

        判断关键字左边的变量指向的对象的真实类型是否是右边的类型或者是其子类类型,是则返回true,反之返回false。

        示例代码如下:

动物类

  1. public abstract class Animal {
  2. public abstract void run();
  3. }

狗类

  1. public class Dog extends Animal{
  2. @Override
  3. public void run() {
  4. System.out.println("狗跑得快");
  5. }
  6. /*
  7. 独有功能
  8. */
  9. public void lookDoor() {
  10. System.out.println("狗看门");
  11. }
  12. }

乌龟类

  1. public class Tortoise extends Animal{
  2. @Override
  3. public void run() {
  4. System.out.println("乌龟跑得慢");
  5. }
  6. /*
  7. 独有方法
  8. */
  9. public void layEggs() {
  10. System.out.println("乌龟会下蛋");
  11. }
  12. }

测试类

  1. public class Test {
  2. public static void main(String[] args) {
  3. // 自动类型转换
  4. Animal a = new Dog();
  5. a.run();
  6. // 强制类型转换 子类 对象变量 = (子类)父类类型的变量
  7. Animal a2 = new Tortoise();
  8. a2.run();
  9. Tortoise t = (Tortoise) a2; // 从父类类型到子类类型必须进行强制类型转换
  10. t.layEggs();
  11. // 如果转型后的类型和对象真实类型不是同一种类型,那么在转换的时候就会出现ClassCastException
  12. // Dog d = (Dog)a2; // 报错,转型后的类型与真实类型不同
  13. // 在强制转换前使用instanceof判断当前对象的真实类型,再进行强制转换
  14. if (a2 instanceof Dog) { // 若a2指向的对象为Dog类型,则强制转换为Dog类型
  15. Dog d = (Dog) a2;
  16. d.lookDoor();
  17. } else if (a2 instanceof Tortoise) { // 若a2指向的对象为Tortoise类型,则强制转换为Tortoise类型
  18. Tortoise t2 = (Tortoise) a2;
  19. t2.layEggs();
  20. }
  21. }
  22. }

        程序运行结果如下:

狗跑得快

乌龟跑得慢

乌龟会下蛋

乌龟会下蛋

1.4多态的综合案例

需求:

        使用面向对象编程模拟:设计一个电脑对象,可以安装2个USB设备

        鼠标:被安装时可以完成接入、调用点击功能、拔出功能。

        键盘:被安装时可以完成接入、调用打字功能、拔出功能。

分析:

        ①定义一个USB的接口(申明USB设备的规范必须是:可以接入和拔出)。

        ②提供2个USB实现类代表鼠标和键盘,让其实现USB接口,并分别定义独有功能。

        ③创建电脑对象,创建2个USB实现类对象,分别安装到电脑中并触发功能的执行。

        示例代码如下:

USB接口:

  1. public interface USB {
  2. // 接入、拔出
  3. void connect();
  4. void unconnect();
  5. }

Mouse类

  1. public class Mouse implements USB{
  2. private String name;
  3. public Mouse() {
  4. }
  5. public Mouse(String name) {
  6. this.name = name;
  7. }
  8. @Override
  9. public void connect() {
  10. System.out.println(name + "鼠标成功连接");
  11. }
  12. @Override
  13. public void unconnect() {
  14. System.out.println(name + "鼠标成功断开");
  15. }
  16. /*
  17. 独有功能
  18. */
  19. public void click() {
  20. System.out.println("鼠标点击");
  21. }
  22. }

KeyBoard类

  1. public class KeyBoard implements USB{
  2. private String name;
  3. public KeyBoard() {
  4. }
  5. public KeyBoard(String name) {
  6. this.name = name;
  7. }
  8. @Override
  9. public void connect() {
  10. System.out.println(name + "键盘成功连接");
  11. }
  12. @Override
  13. public void unconnect() {
  14. System.out.println(name + "键盘成功断开");
  15. }
  16. /*
  17. 独有功能
  18. */
  19. public void keyDown() {
  20. System.out.println("键盘打字");
  21. }
  22. public String getName() {
  23. return name;
  24. }
  25. public void setName(String name) {
  26. this.name = name;
  27. }
  28. }

Computer类

  1. public class Computer {
  2. private String name;
  3. public Computer() {
  4. }
  5. public Computer(String name) {
  6. this.name = name;
  7. }
  8. public void start() {
  9. System.out.println(name + "电脑开机了");
  10. }
  11. /*
  12. 提供安装USB设备的入口
  13. */
  14. public void installUSB(USB usb) { // 父类接口作为入参,所有实现类对象均可作为参数传入
  15. usb.connect(); // USB设备的通用功能
  16. // 不同设备的独有功能:先判断、再强转
  17. if (usb instanceof KeyBoard) {
  18. KeyBoard keyBoard = (KeyBoard) usb;
  19. keyBoard.keyDown(); // 调用键盘的独有功能
  20. } else if (usb instanceof Mouse) {
  21. Mouse mouse = (Mouse) usb;
  22. mouse.click(); // 调用鼠标的独有功能
  23. }
  24. usb.unconnect(); // USB设备的通用功能
  25. }
  26. }

测试类

  1. public class Test {
  2. public static void main(String[] args) {
  3. // 创建电脑对象
  4. Computer computer = new Computer("麦本本");
  5. computer.start();
  6. // 创建鼠标、键盘对象
  7. USB u = new KeyBoard("deiog");
  8. computer.installUSB(u);
  9. System.out.println("--------");
  10. USB u2 = new Mouse("X2");
  11. computer.installUSB(u2);
  12. }
  13. }

        程序运行结果如下:

麦本本电脑开机了

deiog键盘成功连接

键盘打字

deiog键盘成功断开

--------

X2鼠标成功连接

鼠标点击

X2鼠标成功断开

2.内部类

2.1内部类概述

        内部类就是定义在一个类里面的类,里面的类可以理解成(寄生),外部类可以理解成(宿主)。

        其定义格式如下图所示。

 

内部类的使用场景、作用

        当一个类的内部还有一个部分需要一个完整的结构进行描述(如汽车类中的发动机类、人类中的心脏类等),而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构可以选择使用内部类来设计。

        内部类通常可以方便访问外部类的成员,包括私有的成员。

        内部类提供了更好的封装性,内部类本身就可以用private protectecd等修饰,封装性可以做更多控制。

2.2内部类之一:静态内部类[了解]

什么是静态内部类?

        有static修饰,属于外部类本身。

        它的特点和使用与普通类是完全一样的,类有的成分它都有,只是位置在里面而已。

        静态内部类定义格式如下图所示:

 

静态内部类创建对象的格式

        格式:外部类名.内部类名 对象名 = new 外部类名.内部类构造器;

        范例:Outer.Inner in = new Outer.Inner();

静态内部类中是否可以直接访问外部类的静态成员?

        可以,外部类的静态成员只有一份可以被共享访问。

静态内部类中是否可以直接访问外部类的实例成员?

        不可以,外部类的实例成员必须用外部类对象访问。

        示例代码如下图所示:

  1. /*
  2. 外部类
  3. */
  4. public class Outer {
  5. public static int age = 18;
  6. private String hobby;
  7. /*
  8. 内部类
  9. */
  10. public static class Inner {
  11. private String name;
  12. public static String SchoolName;
  13. public Inner() {
  14. }
  15. public Inner(String name) {
  16. this.name = name;
  17. }
  18. public void show() {
  19. System.out.println("名称:" + name);
  20. System.out.println(age); // 静态内部类中可以直接访问外部类的静态成员,外部类的静态成员只有一份可以被共享访问
  21. // System.out.println(hobby); // 报错,静态内部类中不可以直接访问外部类的实例成员,必须用外部类对象间接访问
  22. Outer outer = new Outer();
  23. System.out.println(outer.hobby); // 外部类的实例成员属于对象,在使用时必须创建对象才能访问
  24. }
  25. public String getName() {
  26. return name;
  27. }
  28. public void setName(String name) {
  29. this.name = name;
  30. }
  31. }
  32. }

2.3内部类之二:成员内部类[了解]

什么是成员内部类?

        无static修饰,属于外部类的对象。

        JDK16之前,成员内部类中不能定义静态成员,JDK 16开始也可以定义静态成员了。

成员内部类创建对象的格式

        格式:外部类名.内部类名 对象名 = new 外部类构造器.new 内部类构造器;

        范例:Outer.Inner in = new Outer().new Inner();

成员内部类中是否可以直接访问外部类的静态成员?

        可以,外部类的静态成员只有一份可以被共享访问。

成员内部类的实例方法中是否可以直接访问外部类的实例成员?

        可以,因为必须先有外部类对象,才能有成员内部类对象,所以可以直接访问外部类对象的实例成员。

在成员内部类中访问所在外部类对象

        格式:外部类名.this

        示例代码如下所示:

  1. class People {
  2. private int heartbeat = 150;
  3. public class Heart {
  4. private int heartbeat = 110;
  5. public void show() {
  6. int heartbeat = 78;
  7. System.out.println(heartbeat); // 78
  8. System.out.println(this.heartbeat); // 110
  9. System.out.println(People.this.heartbeat); // 150
  10. }
  11. }
  12. }

2.4内部类之三:局部内部类[了解]

        局部内部类(鸡肋语法,了解即可)

        局部内部类放在方法、代码块、构造器等执行体中。

        局部内部类的类文件名为:外部类$N内部类.class

2.5内部类之四:匿名内部类概述[重点]

什么是匿名内部类?

        本质上是一个没有名字的局部内部类,定义在方法中、代码块中等。

        作用:方便创建子类对象,最终目的为了简化代码编写。

        匿名内部类的格式:

 

采用匿名内部类的形式,特点总结:

        匿名内部类是一个没有名字的内部类。

        匿名内部类写出来就会产生一个匿名内部类的对象

        匿名内部类的对象类型相当于是当前new的那个的类型的子类类型,无需构建子类,直接重写方法,省去子类继承、子类中方法重写的步骤。

        示例代码如下:

  1. public class Test {
  2. public static void main(String[] args) {
  3. // 原方式:构建子类,继承,创建子类对象,调用方法
  4. // Animal animal = new Tiger();
  5. // animal.run(); // 老虎跑得快
  6. // 采用匿名内部类的形式,无需构建子类,直接重写方法,省去子类继承、子类中方法重写的步骤
  7. Animal animal = new Animal() {
  8. @Override
  9. public void run() {
  10. System.out.println("老虎跑得快");
  11. }
  12. };
  13. animal.run(); // 老虎跑得快
  14. }
  15. }
  16. abstract class Animal {
  17. public abstract void run();
  18. }
  19. //class Tiger extends Animal {
  20. // @Override
  21. // public void run() {
  22. // System.out.println("老虎跑得快");
  23. // }
  24. //}

匿名内部类常见使用形式

        需求:某个学校需要让老师,学生,运动员一起参加游泳比赛

        示例代码如下:

  1. public class Test2 {
  2. public static void main(String[] args) {
  3. Swimming s1 = new Swimming() {
  4. @Override
  5. public void swim() {
  6. System.out.println("学生游泳");
  7. }
  8. };
  9. go(s1);
  10. System.out.println("------");
  11. // 匿名内部类可以作为方法的实际参数进行传输
  12. go(new Swimming() { // 省略创建变量的步骤,直接将匿名内部类对象指向作为参数传入
  13. @Override
  14. public void swim() {
  15. System.out.println("老师游泳");
  16. }
  17. });
  18. }
  19. /**
  20. * 需求:学生、老师可以一起参加游泳比赛
  21. */
  22. public static void go(Swimming s) {
  23. System.out.println("开始");
  24. s.swim();
  25. System.out.println("结束");
  26. }
  27. }
  28. interface Swimming {
  29. void swim();
  30. }

        程序运行结果如下:

开始

学生游泳

结束

------

开始

老师游泳

结束

        注:匿名内部类可以作为方法的实际参数进行传输。

2.6匿名内部类真实使用场景演示

        如下图所示,给按钮绑定点击事件。

        示例代码如下:

  1. public class Test3 {
  2. public static void main(String[] args) {
  3. // 1.创建窗口
  4. JFrame win = new JFrame("登陆界面");
  5. JPanel panel = new JPanel();
  6. win.add(panel);
  7. // 2.创建一个按钮对象
  8. JButton btn = new JButton("登陆");
  9. // 绑定监听器(监听是否点击按钮)
  10. btn.addActionListener(new AbstractAction() {
  11. @Override
  12. public void actionPerformed(ActionEvent e) {
  13. JOptionPane.showMessageDialog(win, "点击");
  14. }
  15. });
  16. // 最简化的代码 内部类的目的是简化代码
  17. // btn.addActionListener(e -> JOptionPane.showMessageDialog(win, "点击"));
  18. // 3.把按钮对象添加到桌布中展示
  19. panel.add(btn);
  20. // 4.展示窗口
  21. win.setSize(400, 300);
  22. win.setLocationRelativeTo(null);
  23. win.setVisible(true);
  24. }
  25. }

        使用总结:开发中不是我们主动去定义匿名内部类的,而是别人需要我们写或者我们可以写的时候才会使用。匿名内部类的代码可以实现代码进一步的简化。

3常用API

什么是API?

        API(Application Programming interface) 应用程序编程接口。简单来说:就是Java帮我们已经写好的一些方法,我们直接拿过来用就可以了。

3.1Object

Object类的作用

        一个类要么默认继承了Object类,要么间接继承了Object类,Object类是Java中的祖宗类。

        Object类的方法是一切子类对象都可以直接使用的。

Object类的常用方法

方法名

说明

public String toString()

默认是返回当前对象在堆内存中的地址信息:

类的全限名(从包名开始)@内存地址(16进制的地址)

public boolean equals(Object o)

默认是比较当前对象与另一个对象的地址是否相同

相同返回true,不同返回false

(1)toString()

问题引出

        开发中直接输出对象,默认输出对象的地址其实是毫无意义的,开发中输出对象变量,更多的时候是希望看到对象的内容数据而不是对象的地址信息。

toString存在的意义

        父类toString()方法存在的意义就是为了被子类重写,以便返回对象的内容信息,而不是地址信息。

子类快速重写toString()方法

        ①如下图所示,在子类中右键空白处,点击【生成】。

②如下图所示,在弹出的菜单栏中选择【toString()】,一键重写toString方法。

 

③如下图所示,在弹出的页面中选择对象中要打印的内容信息(默认全选),选择完毕后,点击确认即可,此时在调用时,打印的是对象中的内容信息而不是堆内存中的地址信息。

 

        toString()重写方法示例代码如下:

  1. @Override
  2. public String toString() {
  3. return "Student{" +
  4. "name='" + name + '\'' +
  5. ", sex=" + sex +
  6. ", age=" + age +
  7. '}';
  8. }

(2)equals(Object o)

问题引出

        直接比较两个对象的地址是否相同完全可以用“==”替代equals。

equals存在的意义

        父类equals方法存在的意义就是为了被子类重写,以便子类自己来定制比较规则

子类快速重写equals()方法

        ①如下图所示,在子类中右键空白处,点击【生成】。

 

        ②如下图所示,在弹出的菜单栏中选择【equals()和hashCode()】,一键重写equals()方法。

 

        ③在弹出的页面中,信息保持默认,选择【下一个】->【完成】,此时在调用时,比对的是对象中的内容信息而不是堆内存中的地址信息。

        toString()方法重写与equals()方法重写示例代码如下:

  1. public class Student { // 所有的类都直接或间接继承Object类
  2. private String name;
  3. private char sex;
  4. private int age;
  5. public Student() {
  6. }
  7. public Student(String name, char sex, int age) {
  8. this.name = name;
  9. this.sex = sex;
  10. this.age = age;
  11. }
  12. /*
  13. 重写toString()方法
  14. */
  15. // 自动生成
  16. @Override
  17. public String toString() {
  18. return "Student{" +
  19. "name='" + name + '\'' +
  20. ", sex=" + sex +
  21. ", age=" + age +
  22. '}';
  23. }
  24. /*
  25. 重写equals(Object o)方法
  26. 若两个对象内容一样,则认为二者相等
  27. */
  28. /* @Override
  29. public boolean equals(Object obj) {
  30. // 1.判断o是不是学生类型
  31. if (obj instanceof Student) {
  32. // name、char、sex是Student中独有的属性,需要将obj进行强制类型转换,否则无法访问Student独有属性
  33. Student stu = (Student) obj;
  34. // 2.判断两个对象的内容是否一致
  35. // if (this.name.equals(stu.name) & (this.sex == stu.sex) & (this.age == stu.age)) {
  36. // return true; // 判断完成,所有的值均相同,返回true
  37. // } else {
  38. // return false; // 至少有一项的值不相同,返回false
  39. // }
  40. return this.name.equals(stu.name) & (this.sex == stu.sex) & (this.age == stu.age);
  41. } else {
  42. return false; // 学生类只能与学生类比较,否则为false
  43. }
  44. }*/
  45. // 自动生成
  46. @Override
  47. public boolean equals(Object o) {
  48. // 判断两个对象是否是同一对象(地址是否相同)
  49. if (this == o) return true;
  50. // 判断传入的对象是否为空 || 当前this对象与传入的对象类型是否不同 若有一个为真,则返回false getClass():取类型
  51. if (o == null || getClass() != o.getClass()) return false;
  52. // 判断完毕,开始比对
  53. Student student = (Student) o;
  54. return sex == student.sex &&
  55. age == student.age &&
  56. Objects.equals(name, student.name);
  57. }
  58. @Override
  59. public int hashCode() {
  60. return Objects.hash(name, sex, age);
  61. }
  62. // getter、setter方法
  63. }

3.2Objects

        Objects是一个工具类,提供了一些方法去完成一些功能。

Objects类的常用方法

方法名

说明

public static boolean equals(Object a, Object b)

比较两个对象,底层会先进行非空判断,再进行equals比较。

Objects(a, b)比较结果同a.equals(b),但是其避免了空指针异常,更安全。

public static boolean isNull(Object obj)

判断变量是否为null ,为null返回true ,

否则返回false。

Objects.isNull(a)等同于a == null

3.3StringBuilder

StringBuilder概述

        StringBuilder是一个可变的字符串类,我们可以把它看成是一个对象容器。

        作用:提高字符串的操作效率,如拼接、修改等。

StringBuilder构造器

名称

说明

public StringBuilder()

创建一个空白的可变的字符串对象,不包含任何内容

public StringBuilder(String str)

创建一个指定字符串内容的可变字符串对象

StringBuilder常用方法

方法名称

说明

public StringBuilder append

(任意类型)

添加数据并返回StringBuilder对象本身

public StringBuilder reverse()

将对象的内容反转

public int length()

返回对象内容长度

public String toString()

通过toString()就可以实现把StringBuilder转换为 String

        示例代码如下:

  1. public class StringBuilderDemo1 {
  2. public static void main(String[] args) {
  3. StringBuilder sb = new StringBuilder();
  4. sb.append("a");
  5. sb.append("b");
  6. sb.append("c");
  7. sb.append("d");
  8. sb.append("123");
  9. System.out.println(sb); // abcd123
  10. StringBuilder sb2 = new StringBuilder();
  11. // 支持链式编程
  12. sb2.append("a").append("b").append("c").append("张三李四");
  13. System.out.println(sb2); // abc张三李四
  14. // 内容反转
  15. sb2.reverse().append("d");
  16. System.out.println(sb2); // 四李三张cbad
  17. // 获取对象内容长度
  18. int length = sb2.length();
  19. System.out.println(length); // 8
  20. // StringBuilder只是拼接字符串的手段,Java行业规范主要使用String类型,因此最终仍然要转换为String类型
  21. // 把StringBuilder类型数据转换为String类型数据
  22. String rs = sb2.toString();
  23. System.out.println(rs); // 四李三张cbad 此时数据为String类型
  24. }
  25. }

        注:StringBuilder类型数据支持链式编程,可以连续多次使用append()方法拼接字符串,如sb.append("a").append("b").append("c").append("张三李四");

StringBuilder提高效率原理

        String类拼接字符串原理如下图所示。

 

        其内存机制执行步骤如下:

        Step1:在栈内存中执行main方法。执行代码第一行,在栈内存中生成String类型变量s1,用来存储在堆内存的常量池中生成字符串”a”的地址值。

        Step2:执行代码第二行,在堆内存中new一个StringBuilder对象(String类型底层也是基于StringBuilder类型运算的),将s1指向的值”a”与常量池中生成的字符串”b”拼接到该StringBuilder对象中,再通过toString()将数据类型转换成String类型,再将该对象的地址值存储到栈内存中生成的String类型变量s2中(s2指向该字符串对象)。

        Step3:同理,执行第三行代码。

        Step4:执行第4行代码,将String类型变量s3内存储的堆内存中的字符串对象打印出来。

存在问题:

        String是不可变的字符串类型,每次进行字符串拼接时,都要new出一个StringBuilder对象和一个String对象,且在拼接过程中不断地丢弃之前的对象,十分浪费内存空间。

        StringBuilder类拼接字符串原理如下图所示。

        采用StringBuilder拼接字符串对象只会在堆内存中new一个StringBuilder对象,用于存储拼接的字符串,并其将地址存储到栈内存中生成的StringBuilder变量中。拼接过程中直接将常量池中的”abc字符串对象拼接到对象中,中途不会产生额外对象,节省内存空间,提高效率。

案例—打印整型数组内容

        需求:设计一个方法用于将任意整型数组的内容输出为字符串,要求输出成如下格式:“该数组内容为:[11, 22, 33, 44, 55]”

分析:

        1、定义一个方法,要求该方法能够接收数组,并输出数组内容。

        2、定义一个静态初始化的数组,调用该方法,并传入该数组。

        示例代码如下:

  1. public class StringBuilderTest2 {
  2. public static void main(String[] args) {
  3. int[] arr1 = null;
  4. System.out.println(toString(arr1)); // null
  5. int[] arr2 = {};
  6. System.out.println(toString(arr2)); // 该数组内容为:[]
  7. int[] arr3 = {10, 2, 30, 888};
  8. System.out.println(toString(arr3)); // 该数组内容为:[10,2,30,888]
  9. }
  10. // 1.定义方法接收任意整型数组,返回数组内容
  11. public static String toString(int[] arr) {
  12. // 判断数组是否为null
  13. if (Objects.isNull(arr)) {
  14. return null; // 若是null,则不进行拼接,直接返回null
  15. }
  16. // 2.开始拼接内容
  17. StringBuilder sb = new StringBuilder("该数组内容为:[");
  18. for (int i = 0; i < arr.length; i++) {
  19. sb.append(arr[i]).append(i == arr.length - 1 ? "" : ",");
  20. }
  21. sb.append("]");
  22. return sb.toString(); // 拼接结束后将StringBuilder类型对象转换为String类型并返回
  23. }
  24. }

        一句话总结StringBuilder与String:StringBuilder是拼接字符串的手段,String是目的(最后需要转换为String类型)。

3.4Math

Math类概述

        包含执行基本数字运算的方法,Math类没有提供公开的构造器(不能创建对象)

        Math严格讲属于工具类,成员都是静态的,通过类名就可以直接调用

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)

四舍五入

public static int max (int a,int b)

获取两个int值中的较大值

public static double pow (double a,double b)

返回a的b次幂的值

public static double random ()

返回值为double的随机值,范围[0.0,1.0)

        示例代码如下:

  1. public class MathDemo {
  2. public static void main(String[] args) {
  3. // 1. public static int abs (int a) 获取参数绝对值
  4. System.out.println(Math.abs(10.3)); // 10.3
  5. System.out.println(Math.abs(-9)); // 9
  6. System.out.println("--------");
  7. // 2. public static double ceil (double a) 向上取整
  8. System.out.println(Math.ceil(2.5)); // 3.0
  9. System.out.println(Math.ceil(-3)); // -3.0
  10. System.out.println("--------");
  11. // 3. public static double floor (double a) 向下取整
  12. System.out.println(Math.floor(3.9)); // 3.0
  13. System.out.println(Math.floor(-1.8)); // -2.0
  14. System.out.println("--------");
  15. // 4. public static int round (float a) 四舍五入
  16. System.out.println(Math.round(4.4999999)); // 4
  17. System.out.println(Math.round(5.5)); // 6
  18. System.out.println("--------");
  19. // 5. public static int max (int a,int b) 获取两个int值中的较大值
  20. System.out.println(Math.max(35, 56)); // 56
  21. System.out.println("--------");
  22. // 6. public static double pow (double a,double b) 返回a的b次幂的值
  23. System.out.println(Math.pow(2, 4)); // 16.0
  24. System.out.println(Math.pow(3, 2)); // 9.0
  25. System.out.println("--------");
  26. // 7. public static double random () 返回值为double的随机值,范围[0.0,1.0)
  27. System.out.println(Math.random()); // 0.0-1.0(左闭右开)之间的一个随机数
  28. System.out.println("--------");
  29. // 拓展:生成 3 - 9 之间的随机数 ( 0 + 6 ) + 3
  30. // [0,6] + 3
  31. int date = (int) ((Math.random() * 7) + 3);
  32. // Math.random()--> [0,1) (Math.random() * 6)-->[0,6) 不满足[0,6]
  33. // 因此必须(Math.random() * 7)-->[0,7),然后进行int类型强制转换,丢弃 >= 6的数字的小数部分,强制转为6
  34. // 但是个人认为,这样并不是随机生成,这样会导致 >= 6的数字全变为6,提高了6的出现可能性,并不是随机
  35. }
  36. }

3.5System

        System是一个工具类,代表了当前系统,提供了一些与系统相关的方法。

System类的常用方法

方法名

说明

public static void exit(int status)

终止当前运行的 Java 虚拟机(JVM),非零表示异常终止

public static long currentTimeMillis()

返回从1970-1-1 0:00 走到到此刻总的毫秒值

返回当前系统的时间毫秒值形式

public static void arraycopy

(数据源数组,起始索引,目的地数组, 起始索引,拷贝个数)

数组拷贝

        示例代码如下:

  1. public class SystemDemo {
  2. public static void main(String[] args) {
  3. System.out.println("程序开始");
  4. // public static void exit(int status) 终止当前运行的 Java 虚拟机,非零表示异常终止
  5. // System.exit(0); // JVM终止
  6. // public static long currentTimeMillis() 返回从1970-1-1 0:00 走到到此刻总的毫秒值 返回当前系统的时间毫秒值形式
  7. // long time = System.currentTimeMillis();
  8. // System.out.println(time);
  9. // 进行时间计算:性能分析
  10. // long startTime = System.currentTimeMillis();
  11. // for (int i = 0; i < 100000; i++) {
  12. // System.out.println("输出:" + i);
  13. // }
  14. // long endTime = System.currentTimeMillis();
  15. // System.out.println((endTime - startTime) / 1000.0);
  16. // public static void arraycopy (数据源数组,起始索引,目的地数组, 起始索引,拷贝个数) 数组拷贝
  17. /* arraycopy(Object src, int srcPos,
  18. Object dest, int destPos,
  19. int length);
  20. 参数一:被拷贝的数组
  21. 参数二:开始复制的索引
  22. 参数三:拷贝的目标数组
  23. 参数四:开始粘贴的索引
  24. 参数五:拷贝元素长度(个数)
  25. */
  26. int[] arr1 = {10, 20, 30, 40, 50, 60, 70};
  27. int[] arr2 = new int[6]; // [0, 0, 0, 0, 0, 0] --> [0, 0, 40, 50, 60, 0]
  28. System.arraycopy(arr1, 3, arr2, 2, 3);
  29. // 把任意数组的内容转换为String类型并返回
  30. String rs = Arrays.toString(arr2);
  31. System.out.println(rs);
  32. System.out.println("程序结束");
  33. }
  34. }

3.6BigDecimal

        如下图所示,浮点型运算的时候直接+ * / 可能会出现数据失真(精度问题)。

BigDecimal作用:用于解决浮点型运算精度失真问题。

        使用方法:创建对象BigDecimal封装浮点型数据(最好的方式是调用方法)。

        public static BigDecimal valueOf(double val); 包装浮点数成为BigDecimal对象。

BigDecima常用API

方法名

说明

public BigDecimal add(BigDecimal b)

加法

public BigDecimal subtract(BigDecimal b)

减法

public BigDecimal multiply(BigDecimal b)

乘法

public BigDecimal divide(BigDecimal b)

除法

public BigDecimal divide (另一个BigDecimal对象,精确几位,舍入模式)

除法

        示例代码如下:

  1. public class BigDecimalDemo {
  2. public static void main(String[] args) {
  3. // 浮点型运算的时候直接+ * / 可能会出现数据失真(精度问题)。
  4. System.out.println(0.09 + 0.01); // 0.09999999999999999
  5. System.out.println(1.0 - 0.32); // 0.6799999999999999
  6. System.out.println(1.015 * 100); // 101.49999999999999
  7. System.out.println(1.301 / 100); // 0.013009999999999999
  8. double c = 0.1 + 0.2;
  9. System.out.println(c); // 0.30000000000000004
  10. System.out.println("-------------------------");
  11. // 包装浮点数成为BigDecimal对象
  12. // BigDecimal d1 = new BigDecimal(0.1); // 仍然会出现精读问题,不推荐使用
  13. BigDecimal d1 = BigDecimal.valueOf(0.1);
  14. BigDecimal d2 = BigDecimal.valueOf(0.2);
  15. System.out.println("------加法------");
  16. BigDecimal d = d1.add(d2); // 调用BigDecimal对象的add方法
  17. System.out.println(d); // 0.3 重写了toString方法,打印内容而不是地址
  18. System.out.println("------减法------");
  19. BigDecimal e = d1.subtract(d2); // -0.1
  20. System.out.println(e);
  21. System.out.println("------乘法------");
  22. BigDecimal f = d1.multiply(d2); // 0.02
  23. System.out.println(f);
  24. System.out.println("------除法------");
  25. BigDecimal g = d1.divide(d2); // 0.5
  26. System.out.println(g);
  27. System.out.println("------");
  28. // 注:BigDecimal一定是精确运算,否则报错
  29. BigDecimal a1 = BigDecimal.valueOf(10.0);
  30. BigDecimal b1 = BigDecimal.valueOf(3.0);
  31. // BigDecimal c1 = a1.divide(b1);
  32. // System.out.println(c1); // 报错,值是3.333333333... 无法精确计算
  33. /* 调用divide()的重载方法进行精度保留计算
  34. divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
  35. 参数一:除数
  36. 参数二:保留几位小数
  37. 参数三:舍入模式
  38. */
  39. BigDecimal c1 = a1.divide(b1, 2, RoundingMode.HALF_UP);
  40. System.out.println(c1); // 3.33 保留两位小数,第三位小数四舍五入
  41. System.out.println("------");
  42. // 在运算结束后,需要转成double基本类型,进行下一步的业务(BigDecimal是手段,double是目的)
  43. double rs = d.doubleValue();
  44. System.out.println(rs); // 0.3 double类型
  45. }
  46. }

写在最后:

        感谢读完!

        纵然缓慢,驰而不息!加油!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/233576
推荐阅读
相关标签
  

闽ICP备14008679号