第07章 面向对象编程(中级部分OOP)

news2024/10/6 5:58:09

文章目录

  • IDE(集成开发环境)
    • IDEA介绍
    • idea运行
    • 包的作用
    • 包基本语法
    • 包的本质分析(原理)
    • 包的命名
      • 命名规则
      • 命名规范
    • 常用的包
    • 如何引入包
    • 注意事项和使用细节
  • 访问修饰符【modifier】
    • 基本介绍
    • 使用的注意事项
  • 封装【encapsulation】
    • 介绍
    • 封装的好处和理解
    • 封装的实现步骤
    • 封装练习
  • 继承【extends】
    • 基本介绍和示意图
    • 继承的基本语法
    • 继承的优点
    • 继承的细节
    • 继承的本质分析[重点]
    • 练一练
  • super
    • 基本介绍
    • 基本语法
    • super的使用细节
    • super和this的比较
  • 重写/覆盖[OverWrite]
    • 基本介绍
    • 注意事项和使用细节
    • 重载和重写的对比
  • 多态
    • 多态基本介绍
    • 多态的具体体现
    • 多态的注意事项和细节讨论
      • 多态的前提是:
      • 什么叫向上转型:
      • 向下转型
    • 动态绑定机制
      • 测试题
    • 多态数组
    • 多态参数
  • Object类详解
    • equals方法
    • 如何重写equals方法
    • hashCode方法
    • toString方法
      • 基本介绍
    • finalize方法
  • 断点调试
      • debug 快捷键
  • 项目-零钱通
    • 化繁为简、先死后活
  • 总结
  • 作业
    • 说出 == 和equals的区别
    • 什么是多态,多态的具体体现
    • java的动态绑定机制

IDE(集成开发环境)

IDEA介绍

在这里插入图片描述

idea运行

在这里插入图片描述
java代码运行快捷键: command + R

生成构造器: command + N

查看类继承关系: control + H

自动生成变量名: .var

查看idea的模板快捷键或者自定义模板快捷键: file → settings → editor → Live Templates

包的作用

  • 区分相同名字的类。

  • 当类很多时,可以很好的管理类。

  • 控制访问范围。【访问修饰符时详细讲解】

包基本语法

package com.zhangch;

说明: package关键字,表示打包的操作。

com.zhangch: 表示包名。

包的本质分析(原理)

包的本质实际上就是创建不同的文件夹来保存类文件。
在这里插入图片描述

包的命名

命名规则

只能包含数字、字母、下划线、小圆点,但不能数字开头,不能是关键字或保留字

命名规范

一般是小写字母 + 小圆点

com.公司名.项目名.业务模块名

比如:

com.sina.crm.user // 用户模块

com.sina.crm.order // 订单模块

com.sina.crm.utils // 工具类

常用的包

java.lang.* // lang包是基本包,默认引入

java.util.* // util 包, 系统提供的工具包,工具类,比如之前使用的Scanner

java.net.* // 网络包,之后会用于网络开发

java.awt.* // java界面开发, GUI

如何引入包

// 建议:需要什么类,就导入对应的类。不建议使用*
import java.util.Scanner; // 表示只会引入java.util包下的Scanner类。
import java.util.*;  //表示将java.util 包下的所有类都引入

注意事项和使用细节

1、package 的作用是声明当前类所在的包,需要放在文件的最上面,一个类中最多只有一句package。

2、import放在类package的下面,在类定义的前面,可以有多句且没有顺序要求

// 一个类中最多只有一个package语句,且放在最前面。
// package语句用来表示当前类所在的包。
package com.test;

// import放在package下面,放在类定义的前面,可以有多句且没有顺序要求。
import java.util.Arrays;

public class Import01 {
    public static void main(String[] args) {
        int[] arr = {-1, 20, 33, 43, 4334};
        Arrays.sort(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + "\t");
        }
    }
}

访问修饰符【modifier】

基本介绍

java提供四种访问控制修饰符号, 用于控制方法和属性(成员变量)的访问权限(范围)。

1、公开级别: public修饰,表示方法和属性对外公开。

2、受保护级别: protected修饰,表示对子类和同一个包中的类公开。

3、默认级别:没有修饰符,向同一个包中的类公开。

4、私有级别: 用private修饰,只有类本身可以访问,不对外公开。
在这里插入图片描述

使用的注意事项

1、修饰符可以用来修饰类中的属性,成员方法以及类。

2、只有默认public可以修饰类,并且遵循上述访问权限的特点。

3、使用修饰符的属性和方法,在访问时的规则完全一致。

封装【encapsulation】

介绍

封装就是把抽象出的数据[属性]对数据的操作[方法]封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作[方法],才能对数据进行操作。

典型的封装案例是电视机,我们不需要纠结于电视内部启动的原理,只需要知道启动方法即可。

封装的好处和理解

1、隐藏实现的细节,直接调用即可。

2、可以对数据进行验证,保证数据的安全性和合理性。

封装的实现步骤

  • 将属性进行私有化 private【不能直接修改属性】

  • 提供一个公共的set方法,用于对属性进行判断并赋值。

  • 提供一个公共的get方法,用于获取属性的值。

封装练习

package com.encap;

public class Encapsulation01 {
    public static void main(String[] args) {
        Person person = new Person("ZhangSan",30, 10000);

        System.out.println(person.getInfo());

        System.out.println("年龄为:" + person.getAge());
        System.out.println("薪水为: " + person.getSalary());
    }
}


// 1、年龄在1-120之间,否则使用默认值,
// 2、工资不能直接查看
// 3、name 的长度在 2-6个字符之间。
class Person{
    public String name;
    private int age;
    private double salary;

    public Person() {
    }

    // 有三个属性的构造器
    public Person(String name, int age, double salary) {
        this.setName(name);
        setAge(age);
        setSalary(salary);
    }

    public void setName(String name) {
        if (name.length() > 2 && name.length() <= 6) {
            this.name = name;
        }else{
            System.out.println("名字的输入长度有问题,应该在2-6个字符之间,使用默认名");
            this.name = "无名人";
        }
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age > 0 && age < 150){
            this.age = age;
        } else{
            System.out.println("年龄输入的范围有问题,在1-150之间");
            this.age = 18;
        }
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String getInfo(){
        return "名字为:" + this.name + " 年龄为:"  + this.age + " 薪水为: " + this.salary;
    }
}

继承【extends】

基本介绍和示意图

继承的主要优点是解决代码复用,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。

继承的基本语法

class 字类 extends 父类{

}
  • 子类会自动拥有父类定义的属性和方法
  • 父类又叫 超类、基类
  • 子类又叫 派生类
    在这里插入图片描述

继承的优点

1、代码的复用性提高了

2、代码的扩展性和维护性大大提高

继承的细节

1、子类继承了所有的属性和方法, 但是私有属性和方法不能在子类中直接访问,要通过父类提供的get等公共的方法去访问

2、子类必须调用父类的构造器,完成父类的初始化。

默认在子类的构造器中使用 super();

3、当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类中没有提供无参构造器,则必须在子类的每个构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作。

super("zhang", 40);

4、如果希望指定去调用父类的某个构造器,则可以显式的调用一下super(参数列表);

5、super 在使用时,必须放在构造器的第一行且super只能在构造器中使用

6、super() 和 this() 都只能放在构造器的第一行,因此这样两个方法不能共存在同一个构造器。

7、java所有类都是Object类的子类,或者说Object类是所有类的父类

8、父类构造器的调用不限于直接父类,将一直向上追溯,直到Object类(顶级父类)

构造器则是从最上级父类(Object)开始初始化,逐级初始化,直到最后所有子类的初始化结束。

9、子类最多只能继承一个父类(指直接继承), 即 java 中的单继承机制

思考: 如何让A类 继承 B类 和 C类 → A → B B→C

10、不能滥用继承,需遵循子类和父类之间必须满足 is-a 的逻辑关系,即包含。

继承的本质分析[重点]

继承的本质是抽象:类是对 对象 的抽象,继承是对某一批类的抽象。

内存图

在这里插入图片描述

package com.encap;

public class ExtendsTheory {
    public static void main(String[] args) {
        Son son = new Son();
        // 这时请大家注意,要按照查找关系来返回信息。
        // 1) 首先看子类是否有该属性
        // 2)如果子类有这个属性,并且可以访问,则返回信息
        // 3) 如果子类没有这个属性,就看父类有没有这个属性,如果父类有该属性,并且可以访问,则返回该信息
        // 4) 如果父类没有按照3的规则找到,则继续找上级父类,直到Object
        // 注意,假设属性在父类中存在,但是没有权限访问,那在内存中,该属性也是存在的,只不过父类中需要提供访问的方法
        // 如果在父类中有属性不能访问的话,就会报错,不会再继续向上一级查找,即使上一级有该属性。
        System.out.println(son.name);  // 返回 大头儿子
        System.out.println(son.age);  // 返回的是  39
        System.out.println(son.hobby); // 旅游
    }
}


class GrandPa {
    String name = "大头爷爷";
    String hobby = "旅游";
}

class Father extends GrandPa {
    String name = "大头爸爸";
    int age = 39;
}

class Son extends Father {
    String name = "大头儿子";
}

练一练

1、请思考输出是什么?

package com.encap.extend_;

public class Exercise01 {
    public static void main(String[] args) {
        B b = new B();
    }
}

class A{
    A(){
        System.out.println("a");
    }
    A(String name) {
        System.out.println("a name");
    }
}

class B extends A{
    B(){
        this("abc");
        System.out.println("b");
    }
    B(String name){
        System.out.println("b name");
    }
}

super

基本介绍

super代表父类的引用,用于访问父类的属性、方法、构造器

基本语法

1、访问父类的属性,但不能访问父类中的 private 属性

2、访问父类的方法,不能访问父类中的 private 方法

3、访问父类的构造器。 super(参数列表); 只能放在构造器的第一句,只能出现一句。

super的使用细节

1、调用父类的构造器的好处。「分工明确,父类属性由父类初始化,子类的属性由子类初始化」

2、当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super,如果子类和父类没有重名,则super、this、直接访问的效果都是一致的。

super 方法直接从父类开始查找方法或者属性,而this和直接访则是从本类开始查找。

3、super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员,如果多个父类中都有同名的成员,使用super访问遵循就近原则。 A → B → C

super和this的比较

在这里插入图片描述

重写/覆盖[OverWrite]

基本介绍

方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法。

注意事项和使用细节

方法重写也叫方法覆盖,需要满足下面的条件。

1、子类的方法名称、参数,要和父类方法的参数、名称完全一致。

2、子类方法的返回类型和父类方法的返回类型一样,或者子类的返回类型为父类返回类型的子类。

比如: 父类返回类型为Object,子类的返回类型为String类型

父类为:  public Object getInfo(){retuen null;};
子类为:  public String getInfo(){retuen null;};

3、子类方法不能缩小父类方法的访问权限, 可以放大访问权限。

// public 的访问权限大于 默认的 访问权限,所以满足重写
void sayOk(){};
public void sayOk(){};

重载和重写的对比

在这里插入图片描述

多态

多态基本介绍

方法或对象具有多种形态。是面向对象的三大特征,多态是建立在封装和继承基础之上的。

多态的具体体现

1、方法的多态: 重写和重载就能体现多态。

2、对象的多态:【背下来,记住】

  • 一个对象的编译类型和运行类型可以不一致
  • 编译类型在定义对象时,就已经被确定,不能再改变
  • 运行类型是可以变化的
  • 编译类型看定义时 = 号的左边,运行类型看 = 号的右边
// 可以让父类的引用指向子类的类型。
Animal animal = new Dog(); //animal编译类型是Animal, 运行类型是Dog  
animal = new Cat();    // animal的运行类型变成了Cat,编译类型仍然为Animal

多态的注意事项和细节讨论

多态的前提是:

1、两个对象(类)存在继承关系。

2、多态的向上转型

3、多态的向下转型

4、类的属性没有重写之说,当调用类的属性时,属性的值直接对应编译类型得到,如果编译类型中没有,则去父类中查找属性

package com.encap.poly;

public class PolyDetail {
  public static void main(String[] args) {
      Animal animal = new Dog();
      // 判断运行结果
      System.out.println(animal.count);
  }
}

class Animal{
  int count = 10;
}

class Dog extends Animal{
  int count = 20;
}

5、instanceOf比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX类型的子类型

String str = "hello";
str isstanceOf Object;    // true。   判断对象的运行类型【String】 是否为 XXX(Object)类型或 XX(Object)类型的子类型。

什么叫向上转型:

1、本质:父类的引用指向了子类的对象

2、语法:父类类型 引用名 = new 子类类型();

Animal animal = new Cat();

3、使用特点:

  • 编译类型看左边,运行类型看右边。

  • 可以调用父类中的所有成员(需遵循父类的访问权限)

  • 不能调用子类中的特有成员

对上面两句的解释:在编译阶段,能调用哪些成员,是由编译类型来决定的。javac指令

  • 最终运行效果看运行类型的具体实现。 即调用成员时,按照从子类(运行类型)开始查找成员,找到则调用,找到没权限则报错。 java指令

向下转型

1、语法: 子类类型 引用名 = (子类类型) 父类引用;

Cat cat = (Cat)animal;

2、只能强转父类的引用,不能强转父类的对象。

3、要求父类的引用必须指向的是当前目标类型的对象

4、当向下转型后,可以调用子类类型中的所有成员

Animal animal = new Cat();   // 向上转型,子类的对象,指向父类的引用名
Cat cat = (Cat)animal;   // 向下转型, 将父类的引用进行强转,转成 当前目标类型的对象。    
// animal此引用名指向堆中的Cat类,这是向上转型,此时编译类型为Animal,但运行类型为 Cat    
// 向下转型则为,将堆中的Cat类重新创建一个引用,所以要求父类的引用必须指向的是当前目标类型的对象。

动态绑定机制

1、当调用对象方法的时候,该方法会对该对象的内存地址/运行类型进行绑定。即为方法从运行类型开始查找。

2、当调用对象属性时,则没有动态绑定机制,即为,属性哪个类调用,就用哪个类的属性
在这里插入图片描述

第二点是在方法中使用到属性,所以是对应着声明类。

「多态的前提」是,「类的属性没有重写之说,当调用类的属性时,属性的值直接对应编译类型得到,如果编译类型中没有,则去父类中查找属性」 。属性没有重写机制,则不需要从子类向父类查找属性,记住这两种情况即可。

测试题

package com.encap.test;

public class Test {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.getSum()); //  40
        System.out.println(a.getSum1()); // 20
    }
}

class A {
    public int i = 10;

    public int getI() {
        return i;
    }

    public int getSum() {
        return getI() + 10;
    }

    public int getSum1() {
        return i + 10;
    }

}

class B extends A{
    public int i = 20;

    public int getI() {
        return i;
    }

//    public int getSum() {
//        return getI() + 20;
//    }

//    public int getSum1() {
//        return i + 10;
//    }
}

多态数组

多态数组的定义类型为父类类型,里面保存的实际元素类型为子类类型。

package com.encap.extend_.Exercise03;

public class Test {
    public static void main(String[] args) {
        PC pc = new PC("inter", 16, 256, "IBM");

//        创建多态数组方法一:
//        Computer[] c = new Computer[2];
//        c[0] = pc;
//        c[1] = new Computer("Mac", 256, 512);
        // 创建多态数组方法二:
        Computer[] c = {pc, new Computer("Mac", 256, 512)};
        for (int i = 0; i < c.length; i++) {
            if (c[i] instanceof PC){
                // 向下转型方法一:
                ((PC)c[i]).printInfo();
                // 向下转型方法二:
//                PC p1 = (PC)c[i];
//                p1.printInfo();
            } else{
                System.out.println(c[i].getDetail());
            }
        }
    }
}


class PC extends Computer{
    private String brand;

    public PC(String CPU, int memory, int disk, String brand) {
        super(CPU, memory, disk);
        this.brand = brand;
    }

    public void printInfo(){
        System.out.println("PC信息为:");
        System.out.println(getDetail() + "品牌为: " + brand);
    }
}

class Computer {
    private String CPU;
    private int memory;
    private int disk;

    public Computer(String CPU, int memory, int disk) {
        this.CPU = CPU;
        this.memory = memory;
        this.disk = disk;
    }

    public String getDetail(){
        return "电脑的详细信息:" + "CPU: " + CPU + " 内存为: " + memory  + " 硬盘为:" + disk;
    }
}

多态参数

方法定义的形参类型为父类类型,实参类型则允许传入子类类型。

Object类详解

equals方法

== 和 equals方法的对比:

1、== : 既可以判断基本类型,又可以判断引用类型

  • 如果判断基本类型,判断的是值是否相等。比如:int i = 10; double d = 10.0; 返回为true

  • 判断引用类型,则判断的是地址是否相等,即判断是不是同一个对象。

在这里插入图片描述

2、equals:是Object类中的方法,只能判断引用类型

3、默认判断的是地址是否相等,子类中往往重写Object中的该方法,用于判断内容是否相等。比如:Integer\String [可以看看Integer和String的源码]

public boolean equals(Object obj) {
        return (this == obj); // ==判断引用类型,得地址
    }
public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

IDEA Debug 如何进入源码.docx

如何重写equals方法

题目:判断两个Person对象的内容是否相等,如果两个Person对象的各个属性值都一样,则返回true,反之返回false。

package Equals01;

import java.util.Objects;

public class Exercise01 {
    public static void main(String[] args) {
        Person person = new Person("zhangch", 18, '男');
        Person person1 = new Person("zhangch", 18, '男');
        System.out.println(person.equals(person1));
    }
}

class Person{
    private String name;
    private int age;
    private char gender;

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        Person person = (Person) o;
        return age == person.age && gender == person.gender && Objects.equals(name, person.name);
    }
}

hashCode方法

在这里插入图片描述

1、提高具有哈希结构容器的效率;

2、两个引用,如果指向的是同一个对象,则哈希值肯定是一样的;

3、两个引用,如果指向的是不同对象,则哈希值是不一样的;

4、哈希值主要根据地址号来定,不能完全将哈希值等价于地址

5、后面在集合中,hashCode如果需要的话,也会进行重写。

toString方法

基本介绍

1、默认返回:全类名[包名 + 类名] + @ + 哈希值的十六进制[将哈希值转为十六进制]

【查看Object 的toString方法】:

getClass().getName(): 输出全类名

Integer.toHexString(hashCode()): 将对象hashCode值转为16进制字符串。
在这里插入图片描述

2、子类往往重写 toString 方法,用于返回对象的基本属性信息。 快捷键:command + N
在这里插入图片描述
3、当直接输出一个对象时,toString 方法会被默认的调用。如果重写则调用重写后的方法
在这里插入图片描述

finalize方法

1、当对象被回收时,系统自动调用该对象的 finalize 方法,子类可以重写该方法,做一些释放资源的操作。

2、什么时候回被回收,当某个对象没有任何引用时,则JVM就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁对象,在销毁对象之前,会先调用对象的 finalize 方法

3、垃圾回收机制的调用,是由系统来决定[有专门的垃圾回收算法,即gc]的,也可以通过System.gc() 主动触发垃圾回收机制

package com.object_;

public class Finalize_ {
    public static void main(String[] args) {
        Car bmw = new Car("BMW");
        // 现在Car对象就是一个垃圾,则执行回收(销毁)对象。
        // 在销毁对象前,会调用该对象的finalize方法
        bmw = null;
        // 主动调用垃圾回收器,不一定成功,也许垃圾回收器正在忙。
        System.gc();
        // 程序员就可以在 finalize 中,写自己的业务逻辑代码(比如释放资源:数据库连接、打开文件....)
        System.out.println("程序退出了");
    }
}

class Car{
    private String name;

    public Car(String name) {
        this.name = name;
    }

    @Override
    protected void finalize() throws Throwable {
        // 重写 finalize 方法
        System.out.println("我们销毁汽车" + name);
        System.out.println("释放了资源 ......");
    }

    public String getName() {
        return name;
    }

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

断点调试

1、通过断点调试,一步步的查看源码执行的过程,从而发现错误所在。

2、在断点调试中,代码处于运行状态,此时,是以对象的运行类型来执行的。

debug 快捷键

F7 跳入:跳入到方法内 Step Into

F8 跳过:逐行执行代码 Step Over

shift + F8 跳出: 跳出方法 Step Out

F9 resume,执行到下一个断点

将光标放在某个变量上,可以看到该变量的最新数据。
在这里插入图片描述

项目-零钱通

化繁为简、先死后活

package com.test;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

public class SmallChange {

    //化繁为简
    //1. 先完成显示菜单,并可以选择菜单,给出对应提示
    //2. 完成零钱通明细
    //3. 完成收益入账
    //4. 消费
    //5. 退出
    //6. 用户输入4退出时,给出提示"你确定要退出吗? y/n",必须输入正确的y/n ,否则循环输入指令,直到输入y 或者 n
    //7. 在收益入账和消费时,判断金额是否合理,并给出相应的提示
    //定义相关的变量
    boolean loop = true;  // 为false时,程序运行结束
    Scanner scanner = new Scanner(System.in);
    String key = null;  // 用户选择的菜单信息
    double money = 0;  // 每笔消费或收入信息
    double balance = 0;  // 余额资产信息
    String details = "-----------------零钱通明细------------------";
    String note = ""; // 消费明细
    Date date = null; // date 是 java.util.Date 类型,表示日期
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm"); //可以用于日期格式化的

    public void Menu(){
        do {
            switch (mainMenu()){
                case "1":{
                    setDetails();
                    break;
                }
                case "2":{
                    income();
                    break;
                }
                case "3":{
                    pay();
                    break;
                }
                case "4":{
                    //当用户退出while ,进行判断
                    if (exit().equals("y")) {
                        loop = false;
                    }
                    break;
                }
                default:
                    System.out.println("请重新输入数字");
            }
        }while (loop);
        System.out.println("-----退出了零钱通项目-----");

    }

    public String mainMenu(){
        System.out.println("\n================零钱通菜单===============");
        System.out.println("\t\t\t1 零钱通明细");
        System.out.println("\t\t\t2 收益入账");
        System.out.println("\t\t\t3 消费");
        System.out.println("\t\t\t4 退     出");
        System.out.print("请选择(1-4): ");
        return scanner.next();
    }

    public void setDetails(){
        System.out.println(details);
    }

    public void income(){
        System.out.print("收益入账金额:");
        money = scanner.nextDouble();
        //money 的值范围应该校验 -》 一会在完善
        //老师思路, 编程思想
        //找出不正确的金额条件,然后给出提示, 就直接break
        if(money <= 0) {
            System.out.println("收益入账金额 需要 大于 0");
            return;
        }
        //找出正确金额的条件
        balance += money;
        //拼接收益入账信息到 details
        date = new Date(); //获取当前日期
        details += "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t" + balance;
    }

    public void pay(){
        System.out.print("消费金额:");
        money = scanner.nextDouble();
        //money 的值范围应该校验 -》 一会在完善
        //找出金额不正确的情况
        //过关斩将 校验方式.
        if(money <= 0 || money > balance) {
            System.out.println("你的消费金额 应该在 0-" + balance);
            return;
        }
        System.out.print("消费说明:");
        note = scanner.next();
        balance -= money;
        //拼接消费信息到 details
        date = new Date(); //获取当前日期
        details += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t" + balance;
    }

    public String exit(){
        //用户输入4退出时,给出提示"你确定要退出吗? y/n",必须输入正确的y/n ,
        // 否则循环输入指令,直到输入y 或者 n
        // 老韩思路分析
        // (1) 定义一个变量 choice, 接收用户的输入
        // (2) 使用 while + break, 来处理接收到的输入时 y 或者 n
        // (3) 退出while后,再判断choice是y还是n ,就可以决定是否退出
        // (4) 建议一段代码,完成一个小功能,尽量不要混在一起
        String choice = "";
        do { //要求用户必须输入y/n ,否则就一直循环
            System.out.println("你确定要退出吗? y/n");
            choice = scanner.next();
        // 此处的while判断是重点。
        } while (!"y".equals(choice) && !"n".equals(choice));

        return choice;
    }
}
package com.test;


public class SmallChangeSysOOP {
    public static void main(String[] args) {
        SmallChange smallChange = new SmallChange();
        smallChange.Menu();
    }
}

总结

java创建对象的流程,从默认值 → 显式赋值 → 构造器赋值 流程,中间注意super的使用。

1、在 常量区 加载类相关信息,比如属性和方法,属性取默认值。

2、调用对象的构造器,调用构造器中的super方法,再调用父类对应构造器super方法,逐级往上调用。

3、当运行完父类的super()方法时,此时进行父类类属性的显式赋值,然后构造器赋值。

4、然后继续走父类构造器下的其他代码。

5、当父类的super走完后,子类的属性才能进行显式赋值,然后构造器赋值。

作业

在这里插入图片描述

package Exercise.Exercise06;

public class BankAccount {
    private double balance;
    public BankAccount(double initialBalance){
        this.balance = initialBalance;
    }

    public void deposit(double amount){
        balance += amount;
    }

    public void withDraw(double amount){
        balance -= amount;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}

package Exercise.Exercise06;

public class CheckingAccount extends BankAccount{
    public CheckingAccount(double initialBalance) {
        super(initialBalance);
    }

    @Override
    public void deposit(double amount) {
        super.deposit(amount - 1);
    }

    @Override
    public void withDraw(double amount) {
        super.withDraw(amount + 1);
    }
}

package Exercise.Exercise06;

public class SavingAccount extends BankAccount{
    private int count = 3;
    private double rate = 0.01;

    public SavingAccount(double initialBalance) {
        super(initialBalance);
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public double getRate() {
        return rate;
    }

    public void setRate(double rate) {
        this.rate = rate;
    }

    @Override
    public void deposit(double amount) {
        if (count > 0){
            super.deposit(amount);
        } else {
            super.deposit(amount - 1);
        }
        count --;
    }

    @Override
    public void withDraw(double amount) {
        if (count > 0){
            super.deposit(amount);
        } else {
            super.deposit(amount + 1);
        }
        count --;
    }

    public void earnMonthlyInterest(){
        count = 3;
        super.deposit(getBalance() * rate);
    }
}

package Exercise.Exercise06;

public class Main {
    public static void main(String[] args) {
        CheckingAccount checkingAccount = new CheckingAccount(1000);
        checkingAccount.deposit(100);
        System.out.println(checkingAccount.getBalance());
        checkingAccount.withDraw(100);
        System.out.println(checkingAccount.getBalance());

        SavingAccount savingAccount = new SavingAccount(1000);
        savingAccount.deposit(100);
        savingAccount.deposit(100);
        System.out.println(savingAccount.getBalance());
        savingAccount.deposit(100);
        System.out.println(savingAccount.getBalance());
        savingAccount.deposit(100);
        System.out.println(savingAccount.getBalance());

        savingAccount.earnMonthlyInterest();
        System.out.println(savingAccount.getBalance());
    }
}

说出 == 和equals的区别

在这里插入图片描述

什么是多态,多态的具体体现

多态:

方法或对象具有多种形态,是OOP的第三大特征,是建立在封装和继承基础之上

多态的具体体现:

1、方法多态:

1)重载体现多态;2)重写体现多态

2、对象多态:

1)对象的编译类型和运行类型可以不一致,编译类型在定义时,就已经确定,不能再进行变化

2)对象的运行类型是可以变化的,可以通过**getClass()来查看运行类型**。

3)编译类型看 = 号的左边,运行类型看 = 号的右边。

java的动态绑定机制

1、当调用对象的方法时,该方法会从对象的内存地址/运行类型地址开始查找,

2、当调用对象的属性时,没有动态绑定机制,在哪里声明,哪里使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/165451.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【阶段四】Python深度学习02篇:深度学习基础知识:神经网络可调超参数:优化器

本篇的思维导图: 神经网络可调超参数:优化器 优化器相当于是用来调解神经网络模型的‘手柄’。 代码 # 编译神经网络,

【C语言】小王带您轻松实现动态内存管理(简单易懂)

在上文通讯录制作中&#xff0c;动态通讯录的使用中就用到了动态内存管理&#xff0c;如果有同学想看一看是如何运用的内存管理函数的&#xff0c;请参考这篇文章&#xff0c;接下来我们一起学习动态内存管理的相关知识。【C语言】使用C语言实现静态、动态的通讯录&#xff08;…

浅显易懂的三次握手与四次挥手

目录 一、三次握手 什么是三次握手&#xff1f; 三次握手图解&#xff1a; 过程解析&#xff1a; &#xff08;1&#xff09;第一次握手&#xff1a; &#xff08;2&#xff09;第二次握手&#xff1a; &#xff08;3&#xff09;第三次握手&#xff1a; 二、四次挥手 …

已解决Python读取20GB超大文件内存溢出报错MemoryError

已解决Python读取20GB超大文件内存溢出报错MemoryError 文章目录报错问题报错翻译报错原因解决方法1解决方法2&#xff08;推荐使用&#xff09;帮忙解决报错问题 日常数据分析工作中&#xff0c;难免碰到数据量特别大的情况&#xff0c;动不动就2、3千万行&#xff0c;如果…

操作系统进程调度算法

进程调度 高级调度&#xff08;作业调度&#xff09;&#xff1a;按一定的原则从外存的作业后备队列中挑选一个作业调入内存&#xff0c;并创建进程。每个作业只调入一次&#xff0c;调出一次。作业调入时会建立PCB&#xff0c;调出时会撤销PCB。 中级调度&#xff08;内存调度…

【历史上的今天】1 月 16 日:互联网工程任务组(IETF)成立;AMD 收购 NexGen;eBay 的第一位员工出生

整理 | 王启隆 透过「历史上的今天」&#xff0c;从过去看未来&#xff0c;从现在亦可以改变未来。 今天是 2023 年 1 月 16 日&#xff0c;在 26 年前的今天&#xff0c;国家电力公司组建成立。电力是运作着我们生活的基本&#xff0c;国家电力公司成立于 1997 年 1 月 16 日…

《深度学习入门基于python的理论与实现》chap2感知机 笔记

《深度学习入门:基于python的理论与实现》chap2 感知机 笔记 3个月前正式开始入坑AI的时候就是看的这本书&#xff0c;当时比较粗略地看到了第六章&#xff0c;没有记笔记&#xff0c;现在来重温一下 文章目录《深度学习入门:基于python的理论与实现》chap2 感知机 笔记2.1 什么…

【阶段四】Python深度学习05篇:深度学习项目实战:卷积神经网络的定义、卷积网络的结构与卷积层的原理

本篇的思维导图: 卷积神经网络的定义 卷积神经网络,简称为卷积网络,与普通神经网络的区别是它的卷积层内的神经元只覆盖输入特征局部范围的单元,具有稀疏连接(sparse connectivity)和权重共享(weight shared)的特点,而且其中的过滤器可以做到对图像关键特征的…

基于Power BI的品牌销售金额帕累托分析

一、原理 帕累托于1906年提出了著名的关于意大利社会财富分配的研究结论&#xff1a;20&#xff05;的人口掌握了80&#xff05;的社会财富。这个结论对大多数国家的社会财富分配情况都成立。因此&#xff0c;该法则又被称为80/20法则。 二、数据源 已知某终端表1《商品信息》…

GO 语言 Web 开发实战一

xdm&#xff0c;咱今天分享一个 golang web 实战的 demo go 的 http 包&#xff0c;以前都有或多或多的提到一些&#xff0c;也有一些笔记在我们的历史文章中&#xff0c;今天来一个简单的实战 HTTP 编程 Get 先来一个 小例子&#xff0c;简单的写一个 Get 请求 拿句柄 设置…

VMware Workstation 17 Pro的下载和安装

目录 一、下载 二、安装 三、检查网络连接 方式一&#xff08;简便版&#xff09; 方式二&#xff08;麻烦版&#xff09; 一、下载 下载地址&#xff1a; Windows 虚拟机 | Workstation Pro | VMware | CN 1、进入该网址后&#xff0c;往下翻&#xff0c;有两个选项&…

并查集是什么?怎么模拟实现?如何应用?

目录 一、什么是并查集&#xff1f; 二、并查集可以解决哪些问题&#xff1f; 三、并查集的模拟实现 3.1、并查集的定义 3.2、查询两个元素是否是同一个集合 3.3、合并两个集合 3.4、求集合个数 3.5、并查集完整代码 小结 一、什么是并查集&#xff1f; 我们可以想象这…

九、MySQL 常用函数汇总(2)

文章目录一、条件判断函数1.1 IF(expr,v1,v2)函数1.2 IFNULL(v1,v2)函数1.3 CASE函数二、系统信息函数2.1 获取MySQL版本号、连接数和数据库名的函数2.2 获取用户名的函数2.3 获取字符串的字符集和排序方式的函数2.4 获取最后一个自动生成的ID值的函数三、加密函数3.1 加密函数…

东宝商城项目(三)——用户注册功能的实现(后端)

本文是我做项目过程中记录的学习笔记&#xff0c;用于记录项目开发流程&#xff0c;第一次做项目有很多不懂的地方&#xff0c;本文可读性暂时很差。 我目前的学习目标是走完项目开发流程&#xff0c;知道独立开发一个项目并让项目上线需要经历哪些步骤&#xff0c;需要学到哪些…

java.util.ConcurrentModificationException: null异常

创作背景&#xff1a;在加强for循环中使用了remove操作 原因&#xff1a; 在官方文档中ConcurrentModificationException的介绍如下&#xff1a; public class ConcurrentModificationException extends RuntimeException 某个线程在 Collection 上进行遍历时&#xff0c;通…

Spring入门-IOC/DI注解管理与整合mybatis及Junit(2)

1&#xff0c;核心容器 前面已经完成bean与依赖注入的相关知识学习&#xff0c;接下来我们主要学习的是IOC容器中的核心容器。 这里所说的核心容器&#xff0c;大家可以把它简单的理解为ApplicationContext&#xff0c;前面虽然已经用到过&#xff0c;但是并没有系统的学习&a…

1.15日报

完成font.css global.css login.vue request.js 今天完成了前端与后端的联通&#xff0c;并成功响应请求。返回登录成功欣喜。 遇到的问题&#xff1a; 我的body设置了&#xff1a; margin:0; padding:0; 但是页面四周还有白色留边。原因&#xff1a;body设置无边框了&a…

用Scipy理解Gamma函数

文章目录Gamma函数对数Gamma函数复数域的Gamma函数Gamma函数 Γ\GammaΓ函数是阶乘的解析延拓&#xff0c;在概率论中非常常见&#xff0c;例如Gamma分布表示某个事件在某个时刻发生第nnn次的概率&#xff1a;Gamma分布详解 Γ\GammaΓ函数显含在Γ\GammaΓ分布中&#xff0c;其…

linux基本功系列之pwd命令实战

本文目录 文章目录一. pwd命令介绍二. 语法格式及常用选项2.1 语法格式2.2 常用参数三. 参考案例3.1 显示所在目录的完整路径3.2 显示符号链接的路径 -P 参数3.3 查看上一次所在的工作目录3.4 查看PWD的版本四. pwd的命令类型总结前言&#x1f680;&#x1f680;&#x1f680; …

7、redis数据库jedis省份缓存案例

Redis 1. 概念&#xff1a; redis是一款高性能的NOSQL系列的非关系型数据库 1.1.什么是NOSQL NoSQL(NoSQL Not Only SQL)&#xff0c;意即“不仅仅是SQL”&#xff0c;是一项全新的数据库理念&#xff0c;泛指非关系型的数据库。 随着互联网web2.0网站的兴起…