韩顺平Java-第八章:面向对象编程(中级)

一 IDEA使用

1 IDEA使用技巧和经验
(1)设置字体

​ 菜单 file -> settings

(2)字符编码设置
2 IDEA常用快捷键

​ (1)删除当前行,默认ctrl + y,可以改成ctrl + d;

​ (2)复制当前行到下一行,ctrl +alt + 向下光标;

​ (3)补全代码:alt + /;

​ (4)添加注释和取消注释:ctrl + /;

​ (5)导入该行需要的类,先配置 auto import ,然后使用alt + enter即可;

​ (6)快速格式化代码 ctrl + alt + L;

​ (7)快速运行程序,自己定义 alt + R。

​ (8)生成构造方法等 alt + insert; [提高开发效率]

​ (9)查看一个类的层次关系 ctrl + H;

​ (10)将光标放在一个方法上,输入ctrl + B,可以选择定位到哪个类的方法;

​ (11)自动的分配变量名,通过 在后面 .var;

3 模板/自定义模板
file -> setting -> editor -> Live templates ->查看有哪些模板快捷键/可以自己增加模板

二 包

1 包的三大作用

​ (1)区分相同名字的类;

​ (2)当类很多时,可以很好的管理类;

​ (3)控制访问范围。

2 包的基本语法

​ package com.lyxlearn;

​ 说明:

​ (1)package 关键字:表示打包;

​ (2)com.lyxlearn:表示包名。

3 包的本质分析(原理)

​ 实际上就是创建不同的文件夹/目录来保存类文件。

4 包的命名
(1)命名规则

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

(2)命名规范

​ 一般是小写字母 + 小圆点;一般是com.公司名.项目名.业务模块名。

5 常用的包

​ (1)java.lang.* //lang包是基本包,默认引入,不需要再引入;

​ (2)java.util.* //util包,是系统提供的工具包,工具类,例如使用Scanner;

​ (3)java.net.* //网络包,网络开发;

​ (4)java.awt.* //是做java的界面开发,GUI。

6 注意事项和使用细节
(1)package

​ 作用是声明当前类所在的包,需要在类的最上面,一个类中最多只有一句package;

(2)import指令

​ 位置放在package的下面,在类定义前面,可以有多句且没有顺序要求。

三 访问修饰符

1 基本介绍

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

​ (1)公开级别:用 public 修饰,对外公开;

​ (2)受保护级别:用 protected 修饰,对子类和同一个包中的类公开;

​ (3)默认级别:没有修饰符号,向同一个包的类公开;

​ (4)私有级别:用 private 修饰,只有类本身可以访问,不对外公开。

2 四种访问修饰符的访问范围
3 使用的注意事项

​ (1)修饰符可以用来修饰类中的属性,成员方法以及类;

​ (2)只有默认和public才能修饰类,并遵循上述访问权限的特点;

​ (3)讲完子类再说;

​ (4)成员方法的访问规则和属性完全一样。

四 OOP三大特征(封装,继承和多态)🚩

1 封装(encapsulation)
(1)介绍

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

(2)理解和好处

​ ① 隐藏实现细节;

​ ② 可以对数据进行验证,保证安全合理。

(3)实现步骤(三步)

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

​ ② 提供一个公共的(public)set方法,用于对属性判断并赋值;

java 复制代码
public void setXxx(类型 参数名){  //Xxx  表示某个属性
	//加入数据验证的业务逻辑
	属性 = 参数名;
}

​ ③ 提供一个公共的(public)get方法,用于获取属性的值;

java 复制代码
public 数据类型 getXxx(){  //权限判断,Xxx 某个属性
	return xx;
}

便捷方法:

摁alt + insert 键 弹出框,选择Getter and Setter即可。

(4)封装和构造器合用

为了避免构造器破环私有访问属性的隐私,可以将set方法在构造器内调用即可。

2 继承
(1)基本介绍和示意图

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

(2)基本语法
java 复制代码
class 子类 extends 父类{

}

​ ① 子类就会自动拥有父类定义的属性和方法;

​ ② 父类又叫超类,基类;

​ ③ 子类又叫派生类。

(3)细节问题

​ ① 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供的公共的方法区访问;

​ ② 子类必须调用父类的构造器,完成父类的初始化;

​ ③ 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器种用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过;

​ ④ 如果希望指定去调用父类的某个构造器,则显示的调用一下:super(参数列表);(若是调用父类的无参构造器,可以写super();或者说明都不写。)

​ ⑤ super在使用时,必须放在构造器的第一行;(super只能在构造器中使用)

​ ⑥ super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器;

​ ⑦ java所有类都是Object类的子类,Object是所有类的基类;

​ ⑧ 父类构造器的调用不限于直接父类,将一直往上追溯知道Object类(顶级父类),从上向下依次调用;

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

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

​ 答:可以让A类继承B类,B类继承C类,与题目同样的效果;

​ ⑩ 不能滥用继承,子类和父类之间必须满足is - a 的逻辑关系;

(4)继承的本质分析🚩

​ 访问时:

​ ① 首先看子类是否有该属性;

​ ② 如果子类有这个属性,并且可以访问,则返回信息;

​ ③ 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息...);

​ ④ 如果父类没有就按照 ③ 的规则,继续找上级父类,直到Object...。

3 多态
(1)基本介绍

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

(2)具体表现

​ ① 方法的多态

​ ② 对象的多态🚩

​ a 一个对象的编译类型和运行类型可以不一致;

​ 例如:Animal animal = new Dog(); //animal 编译类型是Animal,运行类型Dog(其中Animal是Dog的父类)

​ b 编译类型在定义对象时,就确定了,不能改变;

​ c 运行类型是可以改变的;

​ 例如:animal = new Cat();

​ d 编译类型看定义时 = 号的左边,运行类型看 = 号的右边

(3)多态注意事项和使用细节

​ 多态的前提是:两个对象(类)存在继承关系;

​ ① 多态的向上转型

​ a 本质:父类的引用指向(接收)了子类的对象;

​ b 语法:父类类型 引用名 = new 子类类型();

​ c 特点如下:

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

​ (b)不能调用子类的特有的成员,因为在编译阶段,能够调用哪些成员,是由编译类型来决定的;

​ (c)最终运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法,然后调用,规则和前面讲的方法调用规则一致。

​ ② 多态的向下转型

​ a 语法:子类类型 引用名 = (子类类型)父类引用;

​ b 只能强转父类的引用,不能强转父类的对象;

​ c 要求父类的引用必须指向的是当前目标类型(即等号前的子类类型)的对象;

​ d 当向下转型后,可以调用子类类型中所有的成员。

​ ③ 属性没有重写之说,属性的值看编译类型;

​ ④ instanceof 比较操作符:用于判断对象xx的运行类型是否为XX类型或者XX类型的子类型。例如 xx instanceof XX;

(4)java的动态绑定机制🚩🚩

​ ① 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定;

​ ② 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用。

java 复制代码
public class DynamicBinding{
    public static void main(String[] args){
    	A a = new B();//a的编译类型是 A,运行类型是B
    	System.out.println(a.sum());
    	System.out.println(a.sum1());
    }

}
class A{  //父类
    public int i = 10;

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

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

	public int getI(){
    	return i;
	}
}
class B extends A{  //子类
    public int i = 20;
    
	public int sum(){
    	return i + 20;
	}

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

	public int getI(){
    	return i;
	}
}

​ 此时结果为40,30。

java 复制代码
public class DynamicBinding{
    public static void main(String[] args){
    	A a = new B();//a的编译类型是 A,运行类型是B
    	System.out.println(a.sum());
    	System.out.println(a.sum1());
    }
}
class A{  //父类
    public int i = 10;

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

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

	public int getI(){
    	return i;
	}

}
class B extends A{  //子类
    public int i = 20;
    

	//public int sum(){
		//return i + 20;
	//}

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

	public int getI(){
    	return i;
	}
}

​ 此时结果为30,20。

​ 对于30,因为子类中没有sum()方法,所以会去父类中调用sum()方法,调用方法中的return有getI()方法,因为当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定,此时对象的运行类型是B,所以调用B类中的getI()方法,返回20,加上10,得到30。

​ 对于20,因为子类中没有sum1()方法,所以会去父类中调用sum1()方法,调用方法中有变量i,因为当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用,所以使用A类中声明的i=10,加上10,等于20。

(5)多态的应用

​ ① 多态数组

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

​ 实例:现有一个继承结构如下,要求创建1个Person对象,2个Student对象和2个Teacher对象,统一放在数组中,并调用say方法。

java 复制代码
//Person类
public class Person {
    private String name = "li";
    private int age = 23;

	public Person() {
	}

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

	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 say(){
    	return name + "\t" + age;
	}
}
java 复制代码
//Student类
public class Student extends Person {
    private double score;

	public Student(String name, int age, double score) {
    	super(name, age);
    	this.score = score;
	}

	public double getScore() {
    	return score;
	}

	public void setScore(double score) {
    	this.score = score;
	}

	@Override
	public String say() {
    	return super.say() + "\t" + score;
	}
	public void study(){
    	System.out.println("学生" + getName() + "正在听课...");
	}
}
java 复制代码
//Teacher类
public class Teacher extends Person{
    private double sal;

	public Teacher(String name, int age, double sal) {
    	super(name, age);
    	this.sal = sal;
	}

	public double getSal() {
    	return sal;
	}

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

	@Override
	public String say() {
    	return super.say() + "\t" + sal;
	}
	
	public void teach(){
    	System.out.println("老师" + getName() + "正在讲课...");
	}
}
java 复制代码
//主类
public class PolyArray {
    public static void main(String[] args) {
        Person[] persons = new Person[5];
        persons[0] = new Person("hack",30);
        persons[1] = new Student("jack1",31,60);
        persons[2] = new Student("jack2",35,80);
        persons[3] = new Teacher("mark1",50,9000);
        persons[4] = new Teacher("mark2",55,10000);
        for (int i = 1; i < persons.length; i++) {
            System.out.println(persons[i].say());
        }
    }
}

​ 如何调用子类特有的方法,比如Teacher有一个teach,Student有一个study,怎么调用?

使用向下转型即可。

java 复制代码
//在主类for循环内加如下代码即可
if (persons[i] instanceof Student) {
    Student student = (Student)persons[i];
    student.study();
    //也可以写成  ((Student)persons[i]).study();
}else if (persons[i] instanceof Teacher) {
    ((Teacher) persons[i]).teach();
}

​ ② 多态参数

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

​ a 定义员工类Employee,包含姓名和月工资【private】,以及计算年工资getAnnual的方法。普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法;

​ b 测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法;

​ c 测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法。

java 复制代码
//Employee类
public class Employee {
    private String name;
    private double sal;

	public Employee(String name, double sal) {
    	this.name = name;
    	this.sal = sal;
	}

	public String getName() {
    	return name;
	}

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

	public double getSal() {
    	return sal;
	}

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

	public String getAnnual(){
    	return "姓名:" + name + " 薪水:" + 12*sal ;
	}
}
java 复制代码
//Manager类
public class Manager extends Employee{
    private double bonus;

	public Manager(String name, double sal, double bonus) {
    	super(name, sal);
    	this.bonus = bonus;
	}

	public double getBonus() {
    	return bonus;
	}

	public void setBonus(double bonus) {
    	this.bonus = bonus;
	}
	public void manage(){
    	System.out.println("经理:" + getName() + "管理工作.");
	}

	@Override
	public String getAnnual() {
    	return "经理" + super.getAnnual() + " 奖金" + bonus;
	}
}
java 复制代码
//OrdEmployee类
public class OrdEmployee extends Employee{
    public OrdEmployee(String name, double sal) {
        super(name, sal);
    }

	public void work(){
    	System.out.println("普通员工:" + getName() + "从事工作.");
	}

	@Override
	public String getAnnual() {
    	return "普通员工" + super.getAnnual();
	}
}
java 复制代码
//主类
public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        Manager manager = new Manager("lili",10000,20000);
        OrdEmployee ordEmployee = new OrdEmployee("xiaoli",7000);
        Employee employee1 = new Manager("haha",15000,25000);
        Employee employee2 = new OrdEmployee("xiaoha",8000);
        System.out.println(test.showEmpAnnual(manager));
        System.out.println(test.showEmpAnnual(ordEmployee));
        System.out.println(test.showEmpAnnual(employee1));
        System.out.println(test.showEmpAnnual(employee2));
        test.testWork(manager);
        test.testWork(ordEmployee);
        test.testWork(employee1);
        test.testWork(employee2);
    }
    public String showEmpAnnual(Employee e){
        return e.getAnnual();
    }
    public void testWork(Employee e) {
        if (e instanceof Manager) {
            Manager manager = (Manager) e;
            manager.manage();
        } else if (e instanceof OrdEmployee) {
            OrdEmployee ordEmployee = (OrdEmployee) e;
            ordEmployee.work();
        } else {
            System.out.println("错误");
        }
    }
}

五 Super关键字

1 基本介绍

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

2 基本语法

​ ① 访问父类的属性,但不能访问父类的private属性;

​ 例如:super.属性名;

​ ② 访问父类的方法,不能访问父类的private方法;

​ 例如:super.方法名(参数列表);

​ ③ 访问父类的构造器;

​ 例如:super(参数列表);只能放在构造器的第一句,只能出现一句。

3 super给编程带来的便利/细节

​ (1)调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化);

​ (2)当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果(从子类逐层向上【父类】查找)!

​ (3)super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员,如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A(子类) -> B(父类) -> C(爷爷类)(先找父类,再找爷爷类,若都没有再找子类);

4 super和this的比较

六 方法重写(overwrite)

1 基本介绍

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

2 注意事项和使用细节

​ (1)子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样;

​ (2)子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类;

​ 比如:父类返回类型是Object,子类方法返回类型是String

​ (3)子类不能缩小父类方法的访问权限,但可以扩大。

3 方法重载和方法重写的区别和联系

七 Object类详解,垃圾回收机制

1 equals方法
(1)==和equals的对比

​ ==是一个比较运算符:

​ ① ==:既可以判断基本类型,又可以判断引用类型;

​ ② ==:如果判断基本类型,判断的是值是否相等。例如:int i = 10; double d = 10.0;true

​ ③ ==:如果判断引用类型,判断的是地址是否相等,及判断是不是同一个对象;

​ ④ equals():是Object类中的方法,只能判断引用类型;

​ ⑤ 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如Integer,String。

java 复制代码
//Object类中的equals方法
//判断对象/地址是否相同
public boolean equals(Object obj) {
        return (this == obj);
}
java 复制代码
//String类中的equals方法
//判断字符串的值是否一致
public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
}
java 复制代码
//Integer类的equals方法
//判断传入的值是否相等
public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}
(2)equals()方法练习题
java 复制代码
Person p1 = new Person();
p1.name = "lyxlearn";

Person p2 = new Person();
p2.name = "lyxlearn";

system.out.println(p1 == p2);  // ==在此判断的是对象(地址),不同,false
system.out.println(p1.name.equals(p2.name));  //true,因为p1.name是字符串,
//在Person类中重写了equals,判断的是字符串是否一样,同,true
system.out.println(p1.equals(p2));  //因为person没有重写equals方法,
//所以用的还是Object父类的该方法,所以判断的是地址,不同,false

String s1 = new String("asdf");

String s2 = new String("asdf");

system.out.println(s1.equals(s2)); //同上第二个
system.out.println(s1 == s2);//同上第一个
java 复制代码
int it =65;
float fl = 65.0f;
System.out.println(it == fl);  //true 值一样
char ch1 = 'A'; char ch2 = 12;
System.out.println(it == ch1);  //字符的本质是数字,A字符对应的数字为65, true
System.out.println(12 == ch2);  //同上

String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2);  //判断地址,不同 f
System.out.println(str1.equals(str2));  //判断字符串是否一样,true
System.out.println("hello" == new java.sql.Date());  //类型不一致,直接false  
2 hashCode方法
(1)语法

​ 对象名.hashCode();

​ 一般通过将调用对象的内部地址转换成一个整数来实现的,注意不能等价于地址。

(2)注意细节

​ ① 提高具有哈希结构的容器的效率;

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

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

​ ④ 哈希值主要是根据地址来的,不能完全将哈希值等价于地址;

​ ⑤ 在集合中,hashCode如果需要的话,也会重写。

3 toString方法

(1)基本介绍

​ 默认返回:全类名 + @ +哈希值的十六进制

java 复制代码
//Object类中的toString方法
//getClass().getName():全类名
//Integer.toHexString(hashCode()): 哈希值的十六进制

public String toString() {
	return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

​ 子类中往往重写toString方法(快捷键 alt + insert 选择toString后勾选即可),用于返回对象的属性信息,在重写了toString方法后,打印对象或者拼接对象时,都会自动调用该对象的toString形式。

​ 当直接输出一个对象时,toString方法会被默认的调用。

java 复制代码
public class ToString_ {
    public static void main(String[] args) {
        Monster monster = new Monster("小银角", 200, "巡山的");
        System.out.println(monster.toString());
        System.out.println(monster);
    }
}
class Monster{
    private String name;
    private int age;
    private String job;

	public Monster(String name, int age, String job) {
    	this.name = name;
    	this.age = age;
    	this.job = job;
	}

	@Override
	public String toString() {
    	return "Monster{" +
            	"name='" + name + '\'' +
            	", age=" + age +
            	", job='" + job + '\'' +
            	'}';
	}
}
4 finalize方法

​ (1)当对象被回收时,系统自动调用该对象的finalize方法,子类可以重写该方法(比如释放资源,数据库链接等,若不重写则直接调用父类Object的finalize方法),做一些释放资源的操作;

​ (2)什么时候被回收:当某一个对象没有任何引用时,则jvm就认定这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,就先调用finalize方法;

​ (3)垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),也可以通过System.gc()主动除法垃圾回收机制(不能保证一定,只是很大概率,因为垃圾回收机制可能在忙别的)。

八 断点调试(debug)

1 一个实际需求

​ (1)断点调试可以在一步一步的看源码执行的过程,从而发现错误所在;

​ (2)在断点调试过程中是运行状态,是以对象的运行类型来执行的。

2 介绍

​ (1)断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看到各个变量当前的值,如果出错的话,调试到出错的代码行即显示错误,停下,进行分析从而找到这个Bug;

​ (2)断点调试是程序员必须掌握的技能;

​ (3)断点调试也能帮助我们查看java底层源代码的执行过程,提高程序员的java水平。

3 快捷键
(1)F7(跳入:跳入方法内)

​ ① 例子:Array.sort(arr);//其中arr是一个数组,对数组进行排序

​ 就可以通过F7进入sort方法内,查看相关源码

​ ② 若无法跳入有以下两种解决方法

​ a 使用alt + shift +F7进行强制跳入

​ b 通过设置(推荐)

(2)F8(跳过:逐行执行代码)
(3)shift + F8(跳出方法)

​ 通常和F7跳入方法内连用,用于跳出方法。

(4)F9(resume:执行到下一个断点)

​ 注意:断点支持在运行过程中动态地下断点。

​ F9尝使用于不在于两断点之间地内容,直接执行到下一个断点,不再逐行执行。

4 运行模块图

九 项目

1 零钱通(面向过程编程)

(1)完成显示菜单,并可以选择菜单,给出相应的提示;

(2)完成零钱通明细

三种方案①数组 ②对象 ③String拼接

(3)完成收益入账

(4)完成消费

(5)完成退出,改进代码

① 用户输入4退出时,给出提示"是否退出(y/n)",必须输入正确的y/n,否则循环输入指令,直到输入y或者n。

②在收益入账和消费时,判断金额是否合理,并给出相应的提示。

java 复制代码
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

public class SmallChangeSys {
    //1.完成显示菜单,并可以选择菜单,给出相应的提示;
    //2.完成零钱通明细
    //三种方案(1):数组(2):对象(3)String拼接
    //3.完成收益入账
    //4.完成消费
    //5.完成退出,改进代码
    //(1)用户输入4退出时,给出提示"是否退出(y/n)",必须输入正确的y/n,
    // 否则循环输入指令,直到输入y或者n。
    //(2)在收益入账和消费时,判断金额是否合理,并给出相应的提示。
    //(3)将面向过过程的代码改成面向对象的方法,编写SmallChangeSysOOP.java,
    //完成改进后的1,2,3,4,5各个功能
    //并使用SmallChangeSysApp.java,创建对象,调用方法,完成测试。
    public static void main(String[] args) {
        //定义相关变量
        boolean loop = true;
        Scanner scanner = new Scanner(System.in);
        String choseNum = "";

​    //2.完成零钱通明细
​    String details ="--------------零钱通明细--------------";

​    //3.完成收益入账
​    //定义新的变量(根据功能),完成功能驱动程序员增加新的变化和代码
​    double money = 0;//收入
​    double balance = 0;//余额
​    Date date = null;// date 是 Java.util.Date 的类,表示日期
​    //将date转换为中国常用的格式
​    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

​    //4.完成消费
​    //定义新变量,保存消费的原因
​    String note ="";

​    //5.退出
​    //定义新变量,保存退出的标记
​    String choice = "";
​    do {
​        System.out.println("\n==============零钱通菜单==============");
​        System.out.println("\t\t\t" + "1 零钱通明细");
​        System.out.println("\t\t\t" + "2 收益入账");
​        System.out.println("\t\t\t" + "3 消费");
​        System.out.println("\t\t\t" + "4 退\t出");

​        System.out.print("请选择(1-4):");
​        choseNum = scanner.next();

​        //使用switch分支控制
​        switch (choseNum){
​            case "1":
​                System.out.println(details);
​                break;
​            case "2":
​                System.out.print("收益入账金额:");
​                money = scanner.nextDouble();
​                //money的范围应该校验,不可能为负数
​                if (money <= 0){
​                    System.out.println("收益入账金额输入有误(小于等于0)");
​                    break;
​                }
​                balance +=money;
​                date = new Date();//获取当前时间,需要SimpleDateFormat转换格式
​                //拼接收益入账信息到 details
​                details += "\n收益入账\t+" + money +"  "+ sdf.format(date) +"   余额:" + balance;
​                break;
​            case "3":
​                if (balance == 0){
​                    System.out.println("余额为0");
​                    break;
​                }
​                System.out.print("消费金额:");
​                money = scanner.nextDouble();
​                //money的范围应该校验,不能超过余额balance
​                if (money <= 0 || money > balance){
​                    System.out.println("消费金额有误(未在0-" + balance + "之间)");
​                    break;
​                }
​                System.out.println("消费说明:");
​                note = scanner.next();
​                balance -= money;
​                date = new Date();
​                //拼接消费信息到 details
​                details += "\n"+ note + "\t-" + money +"  "+ sdf.format(date) +"   余额:" + balance;
​                break;
​            case "4":
​                while (true){
​                    //y表示是,n表示否
​                    System.out.print("是否退出(y/n):");
​                    choice = scanner.next();
​                    if ("y".equals(choice) ||"n".equals(choice)){
​                        break;
​                    } else {
​                        System.out.println("输入有误,请重新输入(y/n)");
​                    }
​                    //第二个方案,将两个功能写在一段代码里,即判断①是否输入的为y/n;②输入的是y还是n

//                        if ("y".equals(choice)){
//                            loop = false;
//                            break;
//                        }else if ("n".equals(choice)){
//                            System.out.println("程序继续");
//                            break;
//                        }else {
//                            System.out.println("输入有误,请重新输入(y/n)");
//                        }
                    if ("y".equals(choice)){
                        loop = false;
                    }else {
                        System.out.println("程序继续");
                    }
                    break;
                default:
                    System.out.println("输入号码有误,请重新输入(1-4).");
            }
        }while (loop);
        System.out.println("使用愉快,欢迎下次登陆~");
    }
}
2 零钱通(面向对象编程)

将面向过过程的代码改成面向对象的方法,编写SmallChangeSysOOP.java,完成改进后的1,2,3,4,5各个功能,并使用SmallChangeSysApp.java,创建对象,调用方法,完成测试。

java 复制代码
//SmallChangeSysOOP类
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

public class SmallChangeSysOOP {
    //定义相关变量
    private boolean loop = true;
    private Scanner scanner = new Scanner(System.in);
    private String choseNum;

	//2.完成零钱通明细
	private String details ="--------------零钱通明细--------------";

	//3.完成收益入账
	//定义新的变量(根据功能),完成功能驱动程序员增加新的变化和代码
	private double money;//收入
	double balance;//余额
	private Date date;// date 是 Java.util.Date 的类,表示日期
	//将date转换为中国常用的格式
	private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

	//4.完成消费
	//定义新变量,保存消费的原因
	private String note;

	//5.退出
	//定义新变量,保存退出的标记
	private String choice;

	public boolean isLoop() {
    	return loop;
	}

	public void setLoop(boolean loop) {
    	this.loop = loop;
	}

	//1.完成显示菜单,并可以选择菜单,给出相应的提示;
	public void ShowMenu(){
    	do {
        	System.out.println("\n==============零钱通菜单(OOP)==============");
        	System.out.println("\t\t\t" + "1 零钱通明细");
        	System.out.println("\t\t\t" + "2 收益入账");
        	System.out.println("\t\t\t" + "3 消费");
        	System.out.println("\t\t\t" + "4 退\t出");
        	System.out.print("请选择(1-4):");
        	choseNum = scanner.next();
        	switch (choseNum) {
            	case "1":
                	ShowDetails();
                	break;
            	case "2":
                	CompleteEntry();
                	break;
            	case "3":
                	CompleteConsume();
                	break;
            	case "4":
                	ExitSys();
                	break;
            	default:
                	System.out.println("输入号码有误,请重新输入(1-4).");
        	}
    	}while (loop);
	}

	//2.完成零钱通明细
	public void ShowDetails(){
    	System.out.println(details);
	}

	//3.完成收益入账
	public void CompleteEntry(){
    	System.out.print("收益入账金额:");
    	money = scanner.nextDouble();
    	//money的范围应该校验,不可能为负数
    	if (money <= 0){
        	System.out.println("收益入账金额输入有误(小于等于0)");
        	return;
    	}
    	balance +=money;
    	date = new Date();//获取当前时间,需要SimpleDateFormat转换格式
    	//拼接收益入账信息到 details
    	details += "\n收益入账\t+" + money +"  "+ sdf.format(date) +"   余额:" + balance;
	}

	//4.完成消费
	public void CompleteConsume(){
    	if (balance == 0){
        	System.out.println("余额为0");
        	return;
    	}
    	System.out.print("消费金额:");
    	money = scanner.nextDouble();
    	//money的范围应该校验,不能超过余额balance
    	if (money <= 0 || money > balance){
        	System.out.println("消费金额有误(未在0-" + balance + "之间)");
        	return;
    	}
    	System.out.println("消费说明:");
    	note = scanner.next();
    	balance -= money;
    	date = new Date();
    	//拼接消费信息到 details
    	details += "\n"+ note + "\t-" + money +"  "+ sdf.format(date) +"   余额:" + balance;
	}
	//5.完成退出
	public void ExitSys(){
    	while (true){
        	//y表示是,n表示否
        	System.out.print("是否退出(y/n):");
        	choice = scanner.next();
        	if ("y".equals(choice) ||"n".equals(choice)){
            	break;
        	} else {
            	System.out.println("输入有误,请重新输入(y/n)");
        	}
        	//第二个方案,将两个功能写在一段代码里,即判断①是否输入的为y/n;②输入的是y还是n

//                        if ("y".equals(choice)){
//                            loop = false;
//                            break;
//                        }else if ("n".equals(choice)){
//                            System.out.println("程序继续");
//                            break;
//                        }else {
//                            System.out.println("输入有误,请重新输入(y/n)");
//                        }
        }
        if ("y".equals(choice)){
            loop = false;
        }else {
            System.out.println("程序继续");
        }
    }
}
java 复制代码
//SmallChangeSysApp测试类

public class SmallChangeSysApp {
    public static void main(String[] args) {
        SmallChangeSysOOP smallChangeSysOOP = new SmallChangeSysOOP();

​        smallChangeSysOOP.ShowMenu();

​    	System.out.println("使用愉快,欢迎下次登陆~");
	}
}

知识点

1 继承设计的基本思想

​ 父类的构造器完成父类属性的初始化,子类的构造器完成子类属性的初始化。

2 查看JDK源码

​ 放在想要查看的方法上面直接ctrl+B即可(这是我的快捷键)无快捷键可以右击,选择GO TO -> Declaration or Usages即可查看源码。

​ 若无法查看,可能是因为jdk包没有导入,可以File -> Project Structure -> SDKs -> Sourcepath,点击右侧的加号,选择自己安装jdk的路径,导入src.zip和javafx-src.zip即可。

3 得到当前日期

​先导入包:import java.util.Date

Date date = new Date; //此时得到的日期格式是外国格式

导入包:import java.text.SimpleDateFormat

然后创建对象、转换日期格式:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//注意大小写

sdf.format(date);即可转换成功。

相关推荐
禁默22 分钟前
深入浅出:AWT的基本组件及其应用
java·开发语言·界面编程
Cachel wood28 分钟前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Code哈哈笑31 分钟前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
gb421528734 分钟前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端
程序猿进阶34 分钟前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
zfoo-framework42 分钟前
【jenkins插件】
java
风_流沙1 小时前
java 对ElasticSearch数据库操作封装工具类(对你是否适用嘞)
java·数据库·elasticsearch
ProtonBase1 小时前
如何从 0 到 1 ,打造全新一代分布式数据架构
java·网络·数据库·数据仓库·分布式·云原生·架构
乐之者v1 小时前
leetCode43.字符串相乘
java·数据结构·算法