韩顺平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);即可转换成功。

相关推荐
计算机程序员小杨21 分钟前
计算机专业的你懂的:大数据毕设就选贵州茅台股票分析系统准没错|计算机毕业设计|数据可视化|数据分析
java·大数据
y1y1z25 分钟前
EasyExcel篇
java·excel
DokiDoki之父1 小时前
多线程—飞机大战排行榜功能(2.0版本)
android·java·开发语言
高山上有一只小老虎1 小时前
走方格的方案数
java·算法
whatever who cares1 小时前
Java 中表示数据集的常用集合类
java·开发语言
JavaArchJourney2 小时前
TreeMap 源码分析
java
whitepure2 小时前
万字详解Java中的IO及序列化
java·后端
还梦呦2 小时前
2025年09月计算机二级Java选择题每日一练——第一期
java·开发语言
与火星的孩子对话2 小时前
Unity高级开发:反射原理深入解析与实践指南 C#
java·unity·c#·游戏引擎·lucene·反射
花开富贵ii2 小时前
代码随想录算法训练营四十六天|图论part04
java·数据结构·算法·图论