【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;
}
}
}
}
}