Java-Day-11(项目零钱通 + 章节练习)

发布时间 2023-04-17 23:48:37作者: 朱呀朱~

Java-Day-11

项目零钱通

  • 功能

    • 搭建菜单显示
    • 完成零钱明细
    • 完成收益入账
    • 完成消费功能
    • 实现退出完善,进行 y / n 确认
    • 判断入账、消费金额的合理性
  • 面向过程的代码

    package com.hspJava;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Scanner;
    
    public class SmallChangeSys {
        public static void main(String[] args){
            boolean flag = true;
            Scanner scanner = new Scanner(System.in);
            String key = "";
    
            String details = "-------------钱财服务明细-------------";
    
            // 收益
            double money = 0;
            double balance = 0;
            // 消费
            String note = "";
    
            // java.util,Date
            Date date = null;
            // 日期格式化
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    
    
            do{
                System.out.println("========软件钱财服务========");
                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.println("请选择你所要做的操作: 1 ~ 4");
                key = scanner.next();
    
                switch (key){
                    case "1":
                        System.out.println(details);
                        break;
                    case "2":
                        System.out.println("收益入账金额:");
                        money = scanner.nextDouble();
                        // 找出不正确金额条件,提示后直接break ——> 一系列判断时,判不正确的更方便些,这样不仅可读性强,后期再添加不正确的情况时也更方便
                        if (money <= 0){
                            System.out.println("正确收入金额应大于0");
                            break;
                        }
                        balance += money;
                        // 获取当前日期
                        date = new Date();
                        details += "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t" + balance;
                        break;
                    case "3":
                        System.out.println("消费金额:");
                        money = scanner.nextInt();
                        if (money <= 0 || money > balance){
                            System.out.println("消费金额不能为0或者大于余额");
                        }
                        System.out.println("消费情况:");
                        note = scanner.next();
    
                        balance -= money;
                        date = new Date();
    
                        details += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t" + balance;
    
                        break;
                    case "4":
                        String choice = "";
                        while(true){
                            System.out.println("你确定要退出吗? y/n");
                            choice = scanner.next();
                            // 一段代码实现一个小功能,这样后期改善、拓展更方便更改,例:除了y、n外再加一个待定...
                            // 尽量不要混合在一起,这样代码耦合性低,提高可读性
                            if ("y".equals(choice) || "n".equals(choice)){
                                break;
                            }
                        }
                        if (choice.equals("y")){
                            flag = false;
                        }
                        // n 就是不退出,就是不做处理
                        break;
                    default:
                        System.out.println("选择有误,请重新选择");
                }
    
            }while(flag);
        }
    }
    
  • 修改为面向对象 OOP

    • SmallChangeSysOOP:完成各个功能
    // SmallChangeSysOOP:
    package com.hspJava.OOP;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Scanner;
    
    public class SmallChangeSysOOP {
        boolean flag = true;
        Scanner scanner = new Scanner(System.in);
        String key = "";
        String details = "-------------钱财服务明细-------------";
        // 收益
        double money = 0;
        double balance = 0;
        // 消费
        String note = "";
        // java.util,Date
        Date date = null;
        // 日期格式化
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    
    
        // 被调用的主方法:菜单
        public void mainMenu(){
            do{
                System.out.println("========软件钱财服务OOP========");
                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.println("请选择你所要做的操作: 1 ~ 4");
                key = scanner.next();
    
                switch (key){
                    case "1":
                        this.detail();
                        break;
                    case "2":
                        this.income();
                        break;
                    case "3":
                        this.pay();
                        break;
                    case "4":
                        this.exit();
                        // n 就是不退出,就是不做处理
                        break;
                    default:
                        System.out.println("选择有误,请重新选择");
                }
            }while(flag);
        }
    
    //    完成零钱明细
        public void detail(){
            System.out.println(details);
    //        此处采用简单的直接拼接的方法放进details里
        }
    
    //    完成收益入账
        public void income(){
            System.out.println("收益入账金额:");
            money = scanner.nextDouble();
            // 找出不正确金额条件,提示后直接break ——> 一系列判断时,判不正确的更方便些,这样不仅可读性强,后期再添加不正确的情况时也更方便
            if (money <= 0){
                System.out.println("正确收入金额应大于0");
    //            break; 在面向对象时选择退出方法
                return;
            }
            balance += money;
            // 获取当前日期
            date = new Date();
            details += "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t" + balance;
        }
    
    //    消费
        public void pay(){
            System.out.println("消费金额:");
            money = scanner.nextInt();
            if (money <= 0 || money > balance){
                System.out.println("消费金额不能为0或者大于余额");
                return;
            }
            System.out.println("消费情况:");
            note = scanner.next();
    
            balance -= money;
            date = new Date();
    
            details += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t" + balance;
        }
    
    //    退出
        public void exit(){
            String choice = "";
            while(true){
                System.out.println("你确定要退出吗? y/n");
                choice = scanner.next();
                // 一段代码实现一个小功能,这样后期改善、拓展更方便更改,例:除了y、n外再加一个待定...
                // 尽量不要混合在一起,这样代码耦合性低,提高可读性
                if ("y".equals(choice) || "n".equals(choice)){
                    break;
                }
            }
            if (choice.equals("y")){
                flag = false;
            }
        }
    }
    
    
    • SmallChangeSysApp:调用相关方法完成功能
    // 在这里直接调用SmallChangeSysOOP对象,显示主菜单即可
    public class SmallChangeSysApp {
        public static void main(String[] args) {
            new SmallChangeSysOOP().mainMenu();
        }
    }
    
    
    • 此面向对象的方式,不仅可以供方法给他人使用,在拓展优化功能时可以更方便写入

章节练习

  • Person 数组存三个对象,按对象年龄从大到小冒泡排序

    // main
    Person[] persons = new Person[3];
    persons[0] = new Person(...);
    persons[1] = new Person(...);
    persons[2] = new Person(...);
    
    for(int i = 0; i < persons.length - 1; i++){ // 外部
        for(int j = 0; j < person.lenth - 1 - i, j++){ // 内部每次定最后一个
        	System.out.println();
        	// 用于交换的临时变量
        	Person tmp = null;
        	if(persons[i].getAge() < person[i + 1].getAge()){
            	tmp = person[i];
            	person[i] = person[i + 1];
            	person[i + 1] = tmp;
        	}
        }
    }
    
    • 若是看名字长度,就persons[i].getName().length
  • 编写对象时,若是姓名、固定工资等可以写进构造器中,但若是奖金等可能会变化的值就最好不要写进构造器里,而是用 get、set 方法,于需要时 get 获取,main 里 set 存入

    // 若是继承了父类的day属性,在计算工资时就可以在子类中使用get方法
    public void printSal(){
        System.out.println("管理层" + getname() + "的工资是" + (bonus + getDaySal() * getGrade()) + "。");
    }
    
    // 若是没有特殊加奖金等要求,就可以直接继承父类方法,无需像上述管理层那样重写
    public void printSal(){
        super.printSal();
    }
    
  • 设计父类——员工类,子类:农民类 ( Peasant ),教师类 ( Teaher ),科学家类 ( Scientist )

    • 其中农民只有基本工资
    • 教师除了基本工资外,还有课酬 ( 元/天 )
    • 科学家除基本工资外还有年终奖
    • 编写一个测试类,将各种类型的员工的全年工资打印出来
    // 父类——员工类
    public class Employee {
        private String name;
        private double sal;
    //    带薪月份:salMonth,这里假设是12个月
        private int salMonth = 12;
    
        public Employee(String name, double sal) {
            this.name = name;
            this.sal = sal;
        }
        public void printSal(){
            System.out.println(name + "的年工资是:" + (sal * salMonth));
        }
        public double printSaltest(){
            return sal * salMonth;
        }
        // 此处省略所有私有属性的get、set方法
    }
    
    // 农民类
    public class Peasant extends Employee{
    
        public Peasant(String name, double sal) {
            super(name, sal);
        }
        public void printSal(){
            System.out.print("农民");
            super.printSal();
        }
    }
    
    // 教师类
    public class Teacher extends Employee{
        private int classDays; // 一天课时
        private double classSal; // 课时费
    
        public Teacher(String name, double sal) {
            super(name, sal);
        }
        public void printSal() {
            System.out.print("老师");
            System.out.println(getName() + "的年工资是:" + (getSal() * getSalMonth() + classSal * classDays));
        }
        // 此处省略私有属性的get、set方法
    }
    
    // 科学家类
    public class Scientist extends Employee{
        private double bonus;
    
        public Scientist(String name, double sal) {
            super(name, sal);
        }
        @Override
        public void printSal() {
            System.out.println("科学家");
            System.out.println("*****用(getSal() * getSalMonth() + bonus)");
            System.out.println(getName() + "的年工资是:" + (getSal() * getSalMonth() + bonus));
            System.out.println("*****用(getSal() * getSalMonth() + getBonus())");
            System.out.println(getName() + "的年工资是:" + (getSal() * getSalMonth() + getBonus()));
            System.out.println("*****用(super.printSaltest() + bonus),前提是父类有一个方法printSaltest()是返回double类型的纯工资计算式结果");
            System.out.println(getName() + "的年工资是:" + super.printSaltest() + bonus);
            System.out.println("*****因为前面有字符串,所以上面输出的是字符串拼接后加bonus,所以要像下面括号括起来先");
            System.out.println(getName() + "的年工资是:" + (super.printSaltest() + bonus));
        }
        
        // 私有属性的get、set方法
        public double getBonus() {
            return bonus;
        }
    
        public void setBonus(double bonus) {
            this.bonus = bonus;
        }
    }
    
    // main所在
    public class SalTest {
        public static void main(String[] args) {
            Peasant p1 = new Peasant("小一", 1000);
    //        可随时更改带薪月份,从而更改了年薪
    //        p1.setSalMonth(10);
            p1.printSal();
    
            Teacher t1 = new Teacher("小二", 2000);
            t1.setClassDays(365);
            t1.setClassSal(100);
            t1.printSal();
    
            Scientist s1 = new Scientist("小四", 2000);
            s1.setBonus(5);
            s1.printSal();
        }
    }
    
    /* 输出为
    农民小一的年工资是:12000.0
    老师小二的年工资是:60500.0
    科学家
    *****用(getSal() * getSalMonth() + bonus)
    小四的年工资是:24005.0
    *****用(getSal() * getSalMonth() + getBonus())
    小四的年工资是:24005.0
    *****用(super.printSaltest() + bonus),前提是父类有一个方法printSaltest()是返回double类型的纯工资计算式结果
    小四的年工资是:24000.05.0
    *****因为前面有字符串,所以上面输出的是字符串拼接后加bonus,所以要像下面括号括起来先
    小四的年工资是:24005.0
    
    Process finished with exit code 0
    */
    
    
  • 勿混淆 this、super

    • this:当前对象开始,向上到父类、超类 ...

    • super:子类中访问的父类对象开始,向上访问到超类 ...

    • 不管 this 还是 super 都要遵守访问权限、就近原则

    class Test{
        String name = "ZHANG";
        Test(String name){
            this.name = name;
        }
    }
    
    class Demo extends Test{
        String name = "ZHU";
        Demo(String s){
            super(s);
        }
        public void test(){
            System.out.print(super.name);
            System.out.print(this.name);
        }
        public static void main(String[] args){
            new Demo("jojo").test();
            // 输出为 jojo ZHU
        }
    }
    
  • 银行存款取款 — BankAccount,

    • BankAccount 基础上扩展,建一个新类 CheckingAccount,每次存、取款都要加一块钱的手续费
    • BankAccount 基础上扩展,建一个新类 SavingsAccount,每个月都有利息产生,并且每月三次免手续费的存或取款
    // 父类
    public class BankAccout {
        private double balance;
        public BankAccout(double initiaBalance){
            this.balance = initiaBalance;
        }
    //    存款
        public void deposit(double amount){
            balance += amount;
        }
    //    取款
        public void withdraw(double amount){
            balance -= amount;
        }
    // set、get 方法 
    }
    
    // 子CheckingAccount
    public class CheckingAccount extends BankAccout{
    
        public CheckingAccount(double initiaBalance) {
            super(initiaBalance);
        }
        public void deposit(double amount){
    //        这样就无需重写,巧用父类
            super.deposit(amount - 1);
        }
        public void withdraw(double amount){
    //        父类减钱减的是amount,所以这里是加1
            super.withdraw(amount + 1);
        }
    }
    
    // 子SavingsAccount
    public class SavingsAccount extends BankAccout{
        private int count = 3;
        private double rate = 0.1;
    
        public SavingsAccount(double initiaBalance) {
            super(initiaBalance);
        }
        
        @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.withdraw(amount);
            } else {
                super.withdraw(amount + 1);
            }
            count--;
        }
    
        //    每个月初,统计上个月的利息,同时将count=3
        public void earnMonthlyInterest(){
            count = 3;
            super.deposit(getBalance() * rate);
        }
    // set、get 方法 
    }
    
    // test测试
    public class SalTest {
        public static void main(String[] args) {
            CheckingAccount checkingAccount = new CheckingAccount(1000);
            checkingAccount.deposit(10);
            System.out.println(checkingAccount.getBalance()); // 1009.0
    
            SavingsAccount savingsAccount = new SavingsAccount(100);
            savingsAccount.deposit(10);
            savingsAccount.deposit(10);
            savingsAccount.deposit(10);
            System.out.println(savingsAccount.getBalance()); // 130
            savingsAccount.deposit(10);
            System.out.println(savingsAccount.getBalance()); // 130 + 10 - 1 = 139
    //        假设月初了
            savingsAccount.earnMonthlyInterest();
            System.out.println(savingsAccount.getBalance()); // 139 + 139 * 0.1 = 152.9
        }
    }
    
  • 向上转型和向下转型

    class Person{
        public void sing(){
            System.out.println("person sing");
        }
        public void dance(){
            System.out.println("person dance");
        }
    }
    
    class Student extends Person{
        public void sing(){
            System.out.println("Student sing");
        }
        public void rap(){
            System.out.println("Student rap");
        }
    }
    
    // 向下转型:编译类型是p,
    Person p = new Student();
    p.sing(); // 调用就动态绑定机制,运行是从子类开始找实现内容——Student sing
    p.dance(); // 子类没有再去父类找——person dance
    // p.rap 用不了,只有编译类型的方法能用
    
    //向下转型:把指向子类对象的父类引用,转成指向子类的子类引用,编译类型变成子类,此时一个指向堆的p一个s
    Student s = (Student)p;
    s.sing(); // Student sing
    s.rap(); // Student rap
    s.dance(); // person dance
    
  • 定义方法,形参为父类 Person 类型,功能:调用 Student 的 study 方法或者老师 Teacher 的 teach 方法

    public void test(Person p) {
        if(p instanceof Student) {
            ((Student) p).study();
        } else if(p instanceof Teacher) {
            ((Teacher) p).teach();
        } else {
            System...
        }
    }
    
  • 区分 == 与 equals

    名称 概念 用于基本数据类型 用于引用类型
    == 比较运算符 可以,判断值是否相等 可以,判断是否是同一个对象
    equals Object 类的方法,Java 类都可以使用 不可以 可以,默认判断两个对象是否相等,但是子类往往重写为比较对象的属性是否相同
  • 多态:方法或对象具有多种状态,是 OOP 的第三大特征,是建立在封装和继承基础之上的

    • 体现
      • 方法:重载、重写
      • 对象:编译类型可以与运行类型不同,编译类型不可改变,但运行类型可以变化,可以用 .getClass() 来查看运行类型
    • 向上转型:父 = new 子;超 = new 父 / 子 ;超 = 父 / 子切换
    • 向下转型:( 父 = new 子 后 ) 子 = ( 子 ) 父