【Java复健指南08】OOP中级03【完结】-Object类和一些练习

前情回顾:https://www.cnblogs.com/DAYceng/category/2227185.html

Object类

equals方法

"=="与equals的区别

"=="是一个比较运算符

  • 双等号既可以判断基本类型,又可以判断引用类型
  • 判断基本类型时是判断值是否相等(如int i= 10)
  • 判断引用类型时是判断地址是否相等,即判定是否为同一个对象

练习题

1.记账软件

面向过程版
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

public class SmallChangeSys {
    //化繁为简
    //1、先完成显示菜单,并可以选择菜单给出对应提示
    //2、完成资金明细
    //3、完成收益入账
    //4、消费
    //5、退出
    //6、用户退出时给提示是否要真的退出
    //7、金额校验
    public static void main(String[] args) {
        //定义相关变量
        boolean loop = true;
        Scanner scanner = new Scanner(System.in);
        String key = "";
        //资金明细
        //可以用数组存;可以使用对象;可以直接用字符串拼接;
        String detail = "----------------明细----------------";
        //入账
        //需要新的变量
        double money = 0;
        double balance = 0;
        Date date = null;
        //用于日期格式化
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        //消费
        String note = "";
        do{
            System.out.println("\n================记账菜单================");
            System.out.println("\t\t\t\t1 明细");
            System.out.println("\t\t\t\t2 入账");
            System.out.println("\t\t\t\t3 消费");
            System.out.println("\t\t\t\t4 退出");

            System.out.print("输入选项1~4: ");
            key = scanner.next();
            //switch判断
            switch (key){
                case "1":
                    System.out.println(detail);
                    break;
                case "2":
                    System.out.println("收益入账金额:");//输入值校验
                    money = scanner.nextDouble();
                    //找不正确的条件,给出提示,直接 break
                    if(money <= 0){
                        System.out.println("收益金额需要大于0");
                        break;
                    }
                    balance += money;
                    //获取当前日期
                    date = new Date();
                    detail += "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t" + + balance;
                    break;
                case "3":
                    System.out.println("消费金额:");
                    money = scanner.nextDouble();
                    //找不正确的条件,给出提示,直接 break
                    if(money <= 0){
                        System.out.println("消费金额需要大于0");
                        break;
                    }
                    System.out.println("消费说明:");
                    note = scanner.next();
                    balance -= money;
                    //获取当前日期
                    date = new Date();
                    detail += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t" + + balance;
                    break;
                case "4":
                    //定义一个变量choice,接受输入
                    //使用while-break,处理接受到的输入是y or n
                    //退出while循环后再判断输入的是什么
                    String choice = "";
                    while (true){
                        System.out.println("你确定要退出?y/n");
                        choice = scanner.next();
                        if("y".equals(choice)||"n".equals(choice)){
                            break;
                        }
                    }
                    if (choice.equals("y")){
                        loop = false;
                    }else {
                        loop = true;
                    }
                    break;
                default:
                    System.out.println("选择有误请重试");
            }
        }while (loop);
        System.out.println("----------------退出软件----------------");
    }
}
OOP版

SmallChangeSysOOP,功能类。

实际上就是将之前的各个部分的功能代码封装到类方法中,这样mainMenu()主菜单类只需要去调用本类的方法即可实现所有功能

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
/*
* 完成记账软件的各个功能
* 将各个功能对应一个方法
* */
public class SmallChangeSysOOP {
    //属性
    boolean loop = true;
    Scanner scanner = new Scanner(System.in);
    String key = "";

    //资金明细
    //可以用数组存;可以使用对象;可以直接用字符串拼接;
    String detail = "----------------明细----------------";

    //入账
    //需要新的变量
    double money = 0;
    double balance = 0;
    Date date = null;
    //用于日期格式化
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");

    //消费
    String note = "";

    //先完成显示菜单
    public void mainMenu(){
        do{
            System.out.println("\n================记账菜单OOP================");
            System.out.println("\t\t\t\t1 明细");
            System.out.println("\t\t\t\t2 入账");
            System.out.println("\t\t\t\t3 消费");
            System.out.println("\t\t\t\t4 退出");

            System.out.print("输入选项1~4: ");
            key = scanner.next();
            //switch判断
            switch (key){
                case "1":
                    this.detail();
                    break;
                case "2":
                    this.income();
                    break;
                case "3":
                    this.pay();
                    break;
                case "4":
                    this.exit();
                    break;
                default:
                    System.out.println("选择有误请重试");
            }
        }while (loop);
    }
    //明细
    public void detail(){
        System.out.println(detail);
    }
    //入账
    public void income(){
        System.out.println("收益入账金额:");//输入值校验
        money = scanner.nextDouble();
        //找不正确的条件,给出提示,直接 return
        if(money <= 0){
            System.out.println("收益金额需要大于0");
            return;
        }
        balance += money;
        //获取当前日期
        date = new Date();
        detail += "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t" + + balance;
    }
    //消费
    public void pay(){
        System.out.println("消费金额:");
        money = scanner.nextDouble();
        //找不正确的条件,给出提示,直接 break
        if(money <= 0){
            System.out.println("消费金额需要大于0");
            return;
        }

        System.out.println("消费说明:");
        note = scanner.next();
        balance -= money;
        //获取当前日期
        date = new Date();
        detail += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t" + + balance;
    }
    //退出
    public void exit(){
        //定义一个变量choice,接受输入
        //使用while-break,处理接受到的输入是y or n
        //退出while循环后再判断输入的是什么
        String choice = "";
        while (true){
            System.out.println("你确定要退出?y/n");
            choice = scanner.next();
            if("y".equals(choice)||"n".equals(choice)){
                break;
            }
        }
        if (choice.equals("y")){
            loop = false;
        }else {
            loop = true;
        }
    }
}

SmallChangeSysApp,调用功能类实现软件功能

/*
* 调用SmallChangeSysOOP对象,显示主菜单即可*/
public class SmallChangeSysApp {
    public static void main(String[] args) {
        new SmallChangeSysOOP().mainMenu();
    }
}

2.类方法复用(扩展银行类的练习)

扩展如下的BankAccount类

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;
    }
}
  • 要求:
    (1)在上面类的基础上扩展新类CheckingAccount对每次存款和取款都收取1美元的手续费
    (2)扩展前一个练习的BankAccount类,新类SavingsAccount每个月都有利息产生
    (earnMonthlylnterest方法被调用),并且有每月三次免手续费的存款或取款。在earnMonthlylnterest方法中重置交易计数

需求一

扩展后的新类CheckingAccount,只需充分利用父类方法即可

//在上面类的基础上扩展新类CheckingAccount对每次存款和取款都收取1美元的手续费
public class CheckingAccount extends BankAccount{
    //属性
    public CheckingAccount(double initialBalance) {
        super(initialBalance);
    }

    @Override
    public void deposit(double amount) {//存款
        super.deposit(amount - 1);//利用父类的deposit
        //继承父类的存款方法deposit,但是每次存款都少记账1¥,相当于扣除手续费
    }

    @Override
    public void withdraw(double amount) {
        super.withdraw(amount + 1);//同理,每次取款多扣1¥
    }
}

需求二

扩展后的新类SavingsAccount

/*
* 新类SavingsAccount每个月都有利息产生(earnNonthlyInterest方法被调用),
* 并且有每月三次免手续费的存款或取款。
* 在earnMonthlyInterest方法中重置交易计数
 */
public class SavingsAccount extends BankAccount{
    private int count = 3;
    private double rate = 0.01;//利率

    //正经写项目时,新增方法写在最后
    public void earnMonthlylnterest(){//每个月初统计上个月的利息,同时将count重置
        count = 3;
        super.deposit(getBalance() * rate);//复用父类方法
    }

    @Override
    public void deposit(double amount) {
        //判断是否还可以免手续费
        if(count > 0){
            super.deposit(amount);
        }else {
            super.deposit(amount - 1);//扣手续费
        }
        count--;//减去一次机会
    }

    public SavingsAccount(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;
    }
}

测试类

待拓展的BankAccount类我写在了测试类中,当做父类方法供拓展的子类继承

public class Homework08 {
    public static void main(String[] args) {
////        CheckingAccount checkingAccount = new CheckingAccount(1000);
////        System.out.println(checkingAccount.Accountinfo());
////        System.out.println(checkingAccount.deposit(500));
////        System.out.println(checkingAccount.withdraw(100));
//        SavingsAccount savingsAccount = new SavingsAccount(1000);
//
//        for (int i = 0; i < 5; i++) {
//            int date = i+1;
//            System.out.println("============第"+date+"月============");
//            System.out.println(savingsAccount.Accountinfo());
//            System.out.println(savingsAccount.earnMonthlylnterest(savingsAccount.getBalance()));
//            System.out.println(savingsAccount.deposit(500));
//            System.out.println(savingsAccount.withdraw(100));
//        }

//        CheckingAccount checkingAccount = new CheckingAccount(1000);
//        checkingAccount.deposit(10);//1010-1=1009
//        System.out.println(checkingAccount.getBalance());
        SavingsAccount savingsAccount = new SavingsAccount(1000);
        savingsAccount.deposit(100);
        savingsAccount.deposit(100);
        savingsAccount.deposit(100);
        System.out.println(savingsAccount.getBalance());//1300
        savingsAccount.deposit(100);
        System.out.println(savingsAccount.getBalance());//1400-1 = 1399

        //月初,定时器自动调用earnMonthlylnterest
        savingsAccount.earnMonthlylnterest();
        System.out.println(savingsAccount.getBalance());//1399+13.99
        savingsAccount.withdraw(100);//免手续费,1412.99-100
        System.out.println(savingsAccount.getBalance());
        savingsAccount.withdraw(100);
        savingsAccount.withdraw(100);
        System.out.println(savingsAccount.getBalance());//1412.99-200
        savingsAccount.deposit(100);//扣手续费
        System.out.println(savingsAccount.getBalance());//1211.99
    }
}

class BankAccount {
    private double balance;
//    public int fee = 1;
    //    Scanner scanner = new Scanner(System.in);
    public BankAccount(double initialBalance){
        this.balance = initialBalance;
    }

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

    public void withdraw(double amount){//取出
        balance -= amount;
    }

    public String Accountinfo(){
        return "账户当前金额: "+balance;
    }

    public double getBalance() {
        return balance;
    }

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

3.重写equals方法

编写Doctor类{name, age. job, gender, sal}以及相应的getter()和setter()方法,5个参数的构造器,

重写父类的equals()

方法:public boolean equals(Object obj),并判断测试类中创建的两个对象是否相等。

相等就是判断属性是否相同。

根据题意写出Doctor类

class Doctor{
    private String name;
    private int age;
    private String job;
    private char gender;
    private double sal;

    public Doctor(String name, int age, String job, char gender, double sal) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.gender = gender;
        this.sal = sal;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    public double getSal() {
        return sal;
    }

    public void setSal(double sal) {
        this.sal = sal;
    }

    @Override//重写equals,所有Object的子类都需要重写以满足特定的判定需求
    public boolean equals(Object obj) {
        //判断两个比较对象是否相同
        if(this == obj){
            return true;
        }
        //判断obj是否为Doctor类型或其子类
        if(!(obj instanceof Doctor)){//不是的话
            return false;
        }
        //如果相等就向下转型,因为obj的运行类型是Doctor或者其子类型
        //要从需要比较的子类中获取所有属性并进行比较
        Doctor doctor = (Doctor) obj;
        return this.name.equals(doctor.name)&&this.age ==doctor.age&&
                this.gender == doctor.gender&&this.job.equals(doctor.job)&&this.sal==doctor.sal;
    }
}

注意,为什么要重写equals类呢?

实际上这是一个很常规的操作,java提供的父类Object中定义的equals仅仅能判断输入的两个对象的地址是否相同,即两个对象是否相同,并不能判断对象的具体类型(详见java源码)

因此开发者需要根据具体判断需求重写equals类,实际上java提供的数据类型在使用equals进行比较时,已经使用了针对相应类型重写的equals类了(例如在比较String时,其使用的equals类就不是Object中定义的equals)

测试类

public class Homework10 {
    public static void main(String[] args) {
        Doctor doctor1 = new Doctor("jk",20,"牙医",'男',2000);
        Doctor doctor2 = new Doctor("jk",20,"牙医",'男',2000);

        System.out.println(doctor1.equals(doctor2));//ture
    }
}

4.综合练习

题目
打印效果如下
/*
老师的信息:
姓名:王飞
年龄:30
性别:男
工龄:5
我承诺,我会认真教课。
王飞爱玩象棋
--------------------------
学生的信息:
姓名:小明
年龄:15
性别:男
学号:00023102
我承诺,我会好好学习。
小明爱玩足球。
*/
/*
* 案例题目描述;
(1)做一个Student类,Student类有名称(name),性别(sex),年龄(age).学号(stu_id),
* 做合理封装,通过构造器在创建对象时将4个属性赋值。
(2)写一个Teacher类,Teacher类有名称(name),性别(sex),年龄(age),工龄(work_age).
* 做合理封装,通过构造器在创建对象时将4个属性赋值。
(3)抽取一个父类Person类,将共同属性和方法放到Person类
(4)学生需要有学习的方法(study),在方法里写生“我承诺,我会好好学习。”。
(5)教师需要有教学的方法(teach),在方法里写上“我承诺,我会认真教学。“.
(6)学生和教师都有玩的方法(play),学会玩的是足球,老师玩的是象棋,此方法是返回字符串的,
* 分别返回“xx爱玩足球”和“双爱玩象棋”(其中xXx分别代表学生和老师的姓名)。
* 因为玩的方法名称都一样,听以要求此方法定义在父类中,子类实现重写。
* 应当分析出需要打印信息的方法
(7)定义多态数组,里面保存2个学生和2个教师,要求按年龄从高到低排序,
(8)定义方法,形参为Person类型,功能:调用学生的study或教师的teach方法
学生类

没什么好说的,就是按属性-构造器-get/set方法的流程写就行

public class Student extends Person{
    //private String name;
    //private char gender;
    //private int age;
    private String stu_id;

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

    public String getStu_id() {
        return stu_id;
    }

    public void setStu_id(String stu_id) {
        this.stu_id = stu_id;
    }

    public void study(){
        System.out.println(getName()+"说,要好好学习");
    }

    @Override
    public String play() {
        return super.play()+"足球";
    }
    //编写一个输出信息的方法,体现封装
    public void printInfo(){
        System.out.println("学生信息:");
        System.out.println(super.basicInfo());
        System.out.println("学号:"+stu_id);
        study();
        System.out.println(play());
    }

    @Override
    public String toString() {
        return "Student{" +
                "stu_id='" + stu_id + '\'' +
                '}'+super.toString();
    }
}
教师类
public class Teacher extends Person{
	//private String name;
    //private char gender;
    //private int age;
    private int work_age;

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

    public int getWork_age() {
        return work_age;
    }

    public void setWork_age(int work_age) {
        this.work_age = work_age;
    }

    public void teach(){
        System.out.println(getName()+"说,要认真教学");
    }

    @Override
    public String play() {
        return super.play()+"象棋";
    }

    //输出信息的方法
    public void printInfo(){
        System.out.println("老师的信息:");
        System.out.println(super.basicInfo());
        System.out.println("工龄:"+work_age);
        teach();
        System.out.println(play());
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "work_age=" + work_age +
                '}'+super.toString();
    }
}

注意,按正常的逻辑来说,拿到需求后都是先把子类写出来(例如这里的学生和教师类),然后再去将他们共同的属性抽离写出父类(这里是Person)

父类Person

学生和教师类中共有的属性是姓名、年龄和性别,拿出来构成父类

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

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

    public String getName() {
        return name;
    }

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

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    //编写play方法,把共有的输出内容写在这里
    public String play(){
        return name + "爱玩";
    }

    //返回一个基本信息
    /*
    姓名:xx
    年龄:x
    性别:x
    **/
    public String basicInfo(){
        return "姓名: "+name+"\n年龄: "+age+"\n性别: "+gender;
    }

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

细节说明

1、学生的特殊方法是study,老师的是teach,然后他们都有一个爱好类play,只是打印的爱好不同,因此该类可以写在父类中,通过继承去使用,不同类在复用时有自定义内容

2、打印信息肯定需要一个打印类,该类也需要进行抽离,基本信息在父类打印【basicInfo()】,特殊信息在子类通过继承复用的方式打印【printInfo()】

主类

(7)定义多态数组,里面保存2个学生和2个教师,要求按年龄从高到低排序

​ 没什么好说的,排序就用冒泡就行【bubbleSort()】

(8)定义方法test,形参为Person类型,功能:调用学生的study或教师的teach方法

​ "形参为Person类型"又要调用子类方法,联想到对象类型判断【instanceof】和向下转型

以上两个需求在主类中编写方法实现

public class Homework13 {
    public static void main(String[] args) {
        //测试学生
        Student student = new Student("xm", '男', 15, "00328");
        student.printInfo();
        //测试老师
        Teacher teacher = new Teacher("zf", '男', 30, 15);
        System.out.println("--------------------------------------------");
        teacher.printInfo();
        //定义多态数组,里面保存2个学生和2个老师,要求年龄从高到低
        Person[] people = new Person[4];
        people[0] = new Student("jk",'女',10,"001");
        people[1] = new Student("km",'男',12,"002");
        people[2] = new Teacher("SM",'男',35,5);
        people[3] = new Teacher("xx",'男',23,1);
        //创建对象
        Homework13 homework13 = new Homework13();
        homework13.bubbleSort(people);
        //输出数组
        System.out.println("-----------排序后的数组-----------");
        for (int i = 0; i < people.length; i++) {
            System.out.println(people[i]);
        }
        //遍历数组,调用test方法
        System.out.println("=========");
        for (int i = 0; i < people.length; i++) {
            homework13.test(people[i]);
        }
    }
    //定义方法,形参为Person类型,功能:调用学生的study和老师的teach方法
    //向下转型和类型判断
    public void test(Person p){
        if(p instanceof Student){
            ((Student)p).study();
        } else if (p instanceof Teacher) {
            ((Teacher)p).teach();
        }else {
            System.out.println("do nothing");
        }
    }
    //方法,完成年龄从高到低排序
    public void bubbleSort(Person[] people){
        Person temp = null;
        for (int i = 0; i < people.length-1; i++) {
            for (int j = 0; j < people.length-1-i; j++) {
                //判断条件
                if(people[j].getAge()<people[j+1].getAge()){
                    temp = people[j];
                    people[j] = people[j+1];
                    people[j+1] = temp;
                }
            }
        }
    }
}
posted @ 2022-11-01 12:46  dayceng  阅读(63)  评论(0编辑  收藏  举报