就业培训学习记录-day010

前言

暑期集训开始了,天天忙着上课,什么时候闲下来了再接着写。2021.07.16

课堂任务

面向对象

参考资料:
Java 对象和类 | 菜鸟教程
面向对象系列教材 (一)- Java中的类和对象

Java作为一种面向对象语言。在解释面向对象前,先说一下面向过程。

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。可以简单的理解为:面向过程关心过程,面向对象不关心过程,只关心结果。举个面向对象的例子,让室友带饭。你不关心室友去的是哪个食堂或者店铺,中途发生什么事,花了多少时间,只关心饭能送到你手里。

拿生活中的实例来理解面向过程与面向对象。例如五子棋,面向过程的设计思路就是首先分析问题的步骤:1、开始游戏;2、黑子先走;3、绘制画面;4、判断输赢;5、轮到白子;6、绘制画面;7、判断输赢;8、返回步骤2;9、输出最后结果。把上面每个步骤用不同的方法来实现。

如果是面向对象的设计思想来解决问题。面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为1、黑白双方,这两方的行为是一模一样的,2、棋盘系统,负责绘制画面,3、规则系统,负责判定诸如犯规、输赢等。第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。

可以明显地看出,面向对象是以功能来划分问题,而不是步骤。同样是绘制棋局,这样的行为在面向过程的设计中分散在了多个步骤中,很可能出现不同的绘制版本,因为通常设计人员会考虑到实际情况进行各种各样的简化。而面向对象的设计中,绘图只可能在棋盘对象中出现,从而保证了绘图的统一。

类和对象

  • 类:类是一个模板,它描述一类对象的行为和状态。
  • 对象:对象是类的一个实例(对象不是找个女朋友),有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。

下图中男孩(boy)、女孩(girl)为类(class),而具体的每个人为该类的对象(object)。

下图中汽车为类(class),而具体的每辆车为该汽车类的对象(object),对象包含了汽车的颜色、品牌、名称等。

Java中的对象

现在让我们深入了解什么是对象。看看周围真实的世界,会发现身边有很多对象,车,狗,人等等。所有这些对象都有自己的状态和行为。
拿一条狗来举例,它的状态有:品种、名字、大小、颜色,行为有:叫、摇尾巴和跑。
对比现实对象和软件对象,它们之间十分相似。
软件对象也有状态和行为。软件对象的状态就是属性,行为通过方法体现。
在软件开发中,方法操作对象内部状态的改变,对象的相互调用也是通过方法来完成。

Java中的类

类可以看成是创建 Java 对象的模板。

通过上图创建一个简单的类来理解下 Java 中类的定义。

public class Dog {
    String breed;
    int size;
    String colour;
    int age;
 
    void eat() {
    }
 
    void run() {
    }
 
    void sleep(){
    }
 
    void name(){
    }
}

一个类可以包含以下类型变量:

  • 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
  • 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
  • 类变量:类变量也声明在类中,方法体之外,但必须声明为 static 类型。
    一个类可以拥有多个方法,在上面的例子中:eat()、run()、sleep() 和 name() 都是 Dog 类的方法。

类的创建使用

上面展示了新建class(类),也可以在同一个类里写其他的类。通过class关键字创建类,通过new关键字创建对象。

public class Test1 {
	public static void main(String[] args) {
		Person p;//此时的p,含有属性,但都是默认值
		p = new Person();//创建对象,p是引用,持有了对于person对象的地址值引用
		p.name = "张三";//设置属性name的值
		p.age = 18;//设置属性age的值
		p.eat();//调用方法
		p.sleep();
		p.sayHello();
	}
}
class Person{
	//属性,用变量来表示
	String name;//姓名
	int age;//年龄
	
	//行为,用方法来表示
	public void eat() {
		System.out.println("吃饭");
	}
	
	public void sleep() {
		System.out.println("睡觉");
	}
	
	public void sayHello() {
		System.out.println("我叫"+name+",今年"+age);
	}
}

构造方法(构造器)

每个类都有构造方法。如果没有显式地为类定义构造方法,Java 编译器将会为该类提供一个默认构造方法。
在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。

public class Test1 {
	public static void main(String[] args) {
		Person p = new Person("张三", 18);//创建对象并赋值
		p.eat();//调用方法
		p.sleep();
		p.sayHello();
	}
}
class Person{
	//属性,用变量来表示
	String name;//姓名
	int age;//年龄
	// 无参构造器,如果类里没有写构造器,Java会隐式自动提供一个
	Person(){
		
	}
	// 有参构造器
	Person(String name){
		this.name = name; //this.name指的是类里的name属性,this后面会单独讲
	}
	// 全参构造器
	Person(String name, int age){
		this.name = name;
		this.age = age;
	}
	//行为,用方法来表示
	public void eat() {
		System.out.println("吃饭");
	}
	
	public void sleep() {
		System.out.println("睡觉");
	}
	
	public void sayHello() {
		System.out.println("我叫"+name+",今年"+age);
	}
}

访问实例变量和方法

通过已创建的对象来访问成员变量和成员方法,如下所示:

/* 实例化对象 */
Object referenceVariable = new Constructor();
/* 访问类中的变量 */
referenceVariable.variableName;
/* 访问类中的方法 */
referenceVariable.methodName();

继续拿前面的 Person 类来举例子:

public class Test1 {
	public static void main(String[] args) {
		Person p = new Person("张三", 18);//创建对象并赋值
		p.sayHello();//我叫张三,今年18
		System.out.println("我叫"+p.name+",今年"+p.age);//我叫张三,今年18
	}
}
class Person{
	//属性,用变量来表示
	String name;//姓名
	int age;//年龄
	// 无参构造器,如果类里没有写构造器,Java会隐世自动提供一个
	Person(){
		
	}
	// 有参构造器
	Person(String name){
		this.name = name; //this.name指的是类里的name属性,this后面会讲
	}
	// 全参构造器
	Person(String name, int age){
		this.name = name;
		this.age = age;
	}
	//行为,用方法来表示
	public void eat() {
		System.out.println("吃饭");
	}
	
	public void sleep() {
		System.out.println("睡觉");
	}
	
	public void sayHello() {
		System.out.println("我叫"+name+",今年"+age);
	}
}

源文件声明规则

在本节的最后部分,我们将学习源文件的声明规则。当在一个源文件中定义多个类,并且还有import语句和package语句时,要特别注意这些规则。

  • 一个源文件中只能有一个 public 类
  • 一个源文件可以有多个非 public 类
  • 源文件的名称应该和 public 类的类名保持一致。例如:源文件中 public 类的类名是 Employee,那么源文件应该命名为Employee.java。
  • 如果一个类定义在某个包中,那么 package 语句应该在源文件的首行。
  • 如果源文件包含 import 语句,那么应该放在 package 语句和类定义之间。如果没有 package 语句,那么 import 语句应该在源文件中最前面。
  • import 语句和 package 语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。

类有若干种访问级别,并且类也分不同的类型:抽象类和 final 类等。这些将在后面介绍。除了上面提到的几种类型,Java 还有一些特殊的类,如:内部类匿名类

对象在内存中的存储

Java把内存分成5大区域,我们重点关注栈和堆。

  1. 一般来讲局部变量存在栈中,方法执行完毕内存就被释放
  2. 对象(new出来的东西)存在堆中,对象不再被使用时,内存才会被释放
  3. 每个堆内存的元素都有地址值
  4. 对象中的属性都是有默认值的

  1. 变量p和变量p1不是同一片空间,p1需要开辟新的空间
  2. Person p1=new Person,这时只要有new,就会新开辟空间在堆内存中存入对象。

封装

在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
要访问该类的代码和数据,必须通过严格的接口控制。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。

封装的优点

  1. 良好的封装能够减少耦合。
  2. 类内部的结构可以自由修改。
  3. 可以对成员变量进行更精确的控制。
  4. 隐藏信息,实现细节。

实现Java封装的步骤

  1. 修改属性的可见性来限制对属性的访问(一般限制为private),例如:
public class Person {
    private String name;
    private int age;
}

这段代码中,将 name 和 age 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。

  1. 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问,例如:
public class Person{
    private String name;
    private int age;
​
    public int getAge(){
      return age;
    }
​
    public String getName(){
      return name;
    }
​
    public void setAge(int age){
      this.age = age;
    }
​
    public void setName(String name){
      this.name = name;
    }
}

使用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。
来看一个java封装类的例子:

EncapTest.java 文件代码:
/* 文件名: EncapTest.java */
public class EncapTest{
 
   private String name;
   private String idNum;
   private int age;
 
   public int getAge(){
      return age;
   }
 
   public String getName(){
      return name;
   }
 
   public String getIdNum(){
      return idNum;
   }
 
   public void setAge( int newAge){
      age = newAge;
   }
 
   public void setName(String newName){
      name = newName;
   }
 
   public void setIdNum( String newId){
      idNum = newId;
   }
}

以上实例中public方法是外部类访问该类成员变量的入口。
通常情况下,这些方法被称为getter和setter方法。
因此,任何要访问类中私有成员变量的类都要通过这些getter和setter方法。

访问控制修饰符

参考资料Java 修饰符 | 菜鸟教程

Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。

  • default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
  • private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
  • public : 对所有类可见。使用对象:类、接口、变量、方法
  • protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
修饰符 当前类 同一包内 子孙类(同一包) 子孙类(不同包) 其他包
public Y Y Y Y Y
protected Y Y Y Y/N(说明 N
default Y Y Y N N
private Y N N N N

JavaBean

JavaBean就是一个java的类,只不过这个类你要按一些规则来写来变得规范,方便复用:

  1. 类必须是公共(public)的
  2. 所有属性必须为 private
  3. 必须提供无参构造方法
  4. 必须提供 getter 和 setter
  5. 必须实现 Serializable 接口

看一个例子

// 必须实现Serializable接口
public class Car implements java.io.Serializable{
	// 属性私有
	private String color;
	private String type;
	private String brand;
	// 方法公有,构造方法也是方法
	// 必须提供无参构造方法
	public Car() {
		
	}
	// 推荐提供全参构造方法
	public Car(String color, String type, String brand) {
		this.color = color;
		this.type = type;
		this.brand = brand;
	}
	// 必须提供getter
	public String getColor() {
		return color;
	}
	// 必须提供setter
	public void setColor(String color) {
		this.color = color;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public String getBrand() {
		return brand;
	}

	public void setBrand(String brand) {
		this.brand = brand;
	}
}

this关键字

参考Java语言this关键字用法全面总结

代码块

参考Java中静态代码块、构造代码块、构造函数、普通代码块

继承

参考Java 继承

this和super的区别

  1. this代表本类对象的引用,super代表父类对象的引用。
  2. this用于区分局部变量和成员变量。
  3. super用于区分本类变量和父类变量。
  4. this.成员变量 this.成员方法() this([参数]),代表调用本类构造方法,若无参则调用无参构造方法,有参则调用对应的有参构造方法。
  5. super.成员变量 super.成员方法() super([参数]),代表调用父类构造方法,若无参则调用无参构造方法,有参则调用对应的有参构造方法。
  6. this([参数])和super([参数])不能同时出现在同一个构造方法里。两者若出现在构造方法中,必须放在构造方法的第一行。

重写与重载的区别(Overload和Override的区别)

重写(Override)
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,内容重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要,重写父类的方法。
重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,只能抛出 IOException 的子类异常。

方法的重写规则

  • 参数列表与被重写方法的参数列表必须完全相同。
  • 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
  • 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
  • 父类的成员方法只能被它的子类重写。
  • 声明为 final 的方法不能被重写。
  • 声明为 static 的方法不能被重写,但是能够被再次声明。
  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
  • 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
  • 构造方法不能被重写。
  • 如果不能继承一个类,则不能重写该类的方法。

重载(Overload)
重载是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。

方法的重载规则

  • 被重载的方法必须改变参数列表(参数个数或类型不一样)。
  • 被重载的方法可以改变返回类型。
  • 被重载的方法可以改变访问修饰符。
  • 被重载的方法可以声明新的或更广的检查异常。
  • 方法能够在同一个类中或者在一个子类中被重载。
  • 无法以返回值类型作为重载函数的区分标准。

static

参考详解Java 中 static 的作用
static 是静态的,不需要实例化即可使用的。static 关键字有 4 种使用场景,下面分别进行介绍:

  1. 静态成员变量

在类中一个成员变量可用 static 关键字来修饰,这样的成员变量称为 static 成员变量,或静态成员变量。而没有用 static 关键字修饰的成员变量称为非静态成员变量。

静态成员变量是属于类的,也就是说,该成员变量并不属于某个对象,即使有多个该类的对象实例,静态成员变量也只有一个。只要静态成员变量所在的类被加载,这个静态成员变量就会被分配内存空间。因此在引用该静态成员变量时,通常不需要生成该类的对象,而是通过类名直接引用。引用的方法是“类名 . 静态变量名”。当然仍然可以通过“对象名 . 静态变量名”的方式引用该静态成员变量。相对应的非静态成员变量则属于对象而非类,只有在内存中构建该类对象时,非静态成员变量才被分配内存空间。

public class Test1 {
	public static void main(String[] args) {
		System.out.println(Student.SchoolName); // 清华北大
		System.out.println(Student.score); // 100
//		System.out.println(Student.name); // 报错,因为name不是静态变量,在调用时,name可能(因为没被实例化)不存在
		Student student = new Student();
		System.out.println(student.name); // 张三
		System.out.println(student.age); // 18
		System.out.println(student.SchoolName); // 清华北大
	}
}
class Student {
    // 静态成员变量
    static String SchoolName = "清华北大";
    static int score = 100;
    
    // 非静态成员变量
    String name = "张三";
    int age = 18;
}

上述代码运行结果如下:

清华北大
100
张三
18
清华北大

  1. 静态成员方法

Java 中也支持用 static 关键字修饰的成员方法,即静态成员方法。与此相对应的没有用 static 修饰的成员方法称为非静态成员方法。

与静态成员变量类似,静态成员方法是类方法,它属于类本身而不属于某个对象。因此静态成员方法不需要创建对象就可以被调用,而非静态成员方法则需要通过对象来调用。

特别需要注意的是,在静态成员方法中不能使用 this、super 关键字,也不能调用非静态成员方法,同时不能引用非静态成员变量。这个道理是显而易见的,因为静态成员方法属于类而不属于某个对象,而 this、super 都是对象的引用,非静态成员方法和成员变量也都属于对象。所以当某个静态成员方法被调用时,该类的对象可能还没有被创建,那么在静态成员方法中调用对象属性的方法或成员变量显然是不合适的。即使该类的对象已经被创建,也是无法确定它究意是调用哪个对象的方法,或是哪个对象中的成员变量的。所以在这里特别强调这一点。

public class Test1 {
	public static void main(String[] args) {
		System.out.println(Student.SchoolName); // 清华北大
		System.out.println(Student.score); // 100
//		System.out.println(Student.name); // 报错,因为name不是静态变量,在调用时,name可能(因为没被实例化)不存在
		Student student = new Student();
		System.out.println(student.name); // 张三
		System.out.println(student.age); // 18
		System.out.println(student.SchoolName); // 清华北大
		Student.sayHello(); // 我考了100分,上了清华北大
		student.sayHello(); // 我考了100分,上了清华北大
	}
}
class Student {
    // 静态成员变量
    static String SchoolName = "清华北大";
    static int score = 100;
    
    // 非静态成员变量
    String name = "张三";
    int age = 18;
    
    static void sayHello() {
    	System.out.println("我考了"+score+"分,上了"+SchoolName); // 我考了100分,上了清华北大
//    	System.out.println(name); // 报错,因为name不是静态变量,在调用时,name可能(因为没被实例化)不存在
    }
}

上述代码运行结果如下:

清华北大
100
张三
18
清华北大
我考了100分,上了清华北大
我考了100分,上了清华北大

  1. 静态代码块

static 代码块又称为静态代码块,或静态初始化器。它是在类中独立于成员函数的代码块。static 代码块不需要程序主动调用,在JVM加载类时系统会执行 static 代码块,因此在static 代码块中可以做一些类成员变量的初始化工作。如果一个类中有多个 static 代码块,JVM将会按顺序依次执行。需要注意的是,所有的static 代码块只能在JVM加载类时被执行一次。

public class Test1 {
	public static void main(String[] args) {
		Student student = new Student();
		System.out.println(student.name); // 张三
		Student student2 = new Student("李四");
		System.out.println(student2.name); // 李四
	}
}

class Student {
	String name = "张三";
	int age = 18;

	static {
		System.out.println("static 1");
	}
	static {
		System.out.println("static 2");
	}

	public Student() {
		System.out.println("Student()");
	}

	static {
		System.out.println("static 3");
	}

	public Student(String name) {
		System.out.println("Student(String name)");
		this.name = name;
	}

	static {
		System.out.println("static 4");
	}
}

上述代码运行结果如下:

static 1
static 2
static 3
static 4
Student()
张三
Student(String name)
李四

  1. 静态内部类
public class Test1 {
	public static void main(String[] args) {
		Student student = new Student();
		System.out.println(student.name); // 张三
		Student.test st = new Student.test();
	}
}

class Student {
    // 静态成员变量
    static String SchoolName = "清华北大";
    static int score = 100;
    
    // 非静态成员变量
    String name = "张三";
    int age = 18;

	static class test{
		public test() {
        		System.out.println("我考了"+score+"分,上了"+SchoolName); // 我考了100分,上了清华北大
//      		System.out.println(name); // 报错,因为name不是静态变量,在调用时,name可能(因为没被实例化)不存在
		}		
	}
}

上述代码运行结果如下:

张三
我考了100分,上了清华北大

final

参考JAVA中final关键字的作用

课后任务

利用所学知识,尝试设计一个银联取款系统,具体要求如下:

  • Account账户类,具备的功能,查询余额,存钱,取钱
  • UnionPay银联类,统一银行卡功能
  • 银行卡类(可能有多个,例如工商,农业,交通,建行)
  • ATM取款机类,支持各种银行卡取款
import java.io.Serializable;
import java.util.Arrays;

/*
	利用所学知识,设计一个银联取款系统,具体要求如下: Account账户类,具备的功能,查询余额,存钱,取钱
	UnionPay银联类,统一银行卡功能
	BankCard银行卡类(可能有多个,例如工商,农业,交通,建行) 
	ATM取款机类,支持各种银行卡取款
*/
public class UnionPaySystem {
	public static void main(String[] args) throws Exception {
		String userName = "张三";
		String password = "zhangsan";
		String phone = "12345";
		BankCard bankCard1 = new BankCard(0, "工商银行", 3000);
		BankCard bankCard2 = new BankCard(1, "工商银行", 2000);
		BankCard bankCard3 = new BankCard(0, "农业银行", 1000);
		BankCard bankCard4 = new BankCard(0, "建设银行", 500);
		Account account = new Account(null, userName, phone);
//		account.delBankCard(bankCard1); // 银行卡为空,无法移除!
		account.AddBankCard(bankCard1);
//		account.AddBankCard(bankCard1);	// 银行卡重复,无法添加!
		account.AddBankCard(bankCard2);
		account.AddBankCard(bankCard3);
		account.AddBankCard(bankCard4);
		UnionPay unionPay = new UnionPay(userName, password, account);
//		System.out.println(unionPay);
		ATM atm = new ATM(4000);
//		atm.checkBalance(unionPay, userName, "lisi"); // 用户名或密码错误
		atm.checkBalance(unionPay, userName, password); // 查余额
		atm.withdraw(unionPay, userName, password, bankCard1, 3000); // 卡1取3000
		atm.checkBalance(unionPay, userName, password); // 查余额
//		atm.withdraw(unionPay, userName, password, bankCard1, 1000); // 卡1里没钱了,报错:卡不存在或卡内余额不足!
		unionPay.getAccount().delBankCard(bankCard4); // 移除卡4
		atm.checkBalance(unionPay, userName, password); // 查余额
//		atm.withdraw(unionPay, userName, password, bankCard2, 2000); // 卡2取2000,机内现金余额不足!
//		atm.deposit(unionPay, userName, password, bankCard4, 100); // 卡4存100,但是卡4已经被移除了,报错:卡不存在或卡内余额不足!
		atm.deposit(unionPay, userName, password, bankCard1, 100); // 卡1存100
		atm.checkBalance(unionPay, userName, password); // 查余额
	}
}

/**
 * 银行卡类,包含银行卡Id(每个银行有独立的cardId,即银行A可以有cardId为1的卡,银行B也可以有cardId为1的卡), 银行种类和卡余额
 * 
 * @author IceCream - 吃猫的鱼℘, 935478677@qq.com
 */
@SuppressWarnings("serial")
class BankCard implements Serializable {
	private int cardId;
	private String bank; // 银行种类
	private double money = 0; // 卡余额

	public BankCard() {

	}

	public BankCard(int cardId, String bank, double money) {
		this.cardId = cardId;
		this.bank = bank;
		this.money = money;
	}

	public int getcardId() {
		return cardId;
	}

	public void setcardId(int id) {
		cardId = id;
	}

	public void setBank(String bank) {
		this.bank = bank;
	}

	public String getBank() {
		return bank;
	}

	public double getMoney() {
		return money;
	}

	public void setMoney(double money) {
		this.money = money;
	}

	@Override
	public String toString() {
		return "BankCard [cardId=" + cardId + ", bank=" + bank + ", money=" + money + "]";
	}
}

/**
 * 账户类,一个账户可以绑定多张银行卡,账户内包含了用户的真实姓名和电话号码
 * 
 * @author IceCream - 吃猫的鱼℘, 935478677@qq.com
 */
@SuppressWarnings("serial")
class Account implements Serializable {
	private BankCard[] bankCards;
	private String trueName;
	private String phone;

	public Account() {

	}

	public Account(BankCard[] bankCards, String trueName, String phone) {
		this.bankCards = bankCards;
		this.trueName = trueName;
		this.phone = phone;
	}

	public BankCard[] getBankCards() {
		return bankCards;
	}

	public void setBankCards(BankCard[] bankCards) {
		this.bankCards = bankCards;
	}

	public String getTrueName() {
		return trueName;
	}

	public void setTrueName(String trueName) {
		this.trueName = trueName;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	/**
	 * 添加银行卡
	 * 
	 * @param bankCard 银行卡
	 * @return
	 * @throws Exception
	 */
	public boolean AddBankCard(BankCard bankCard) throws Exception {
		BankCard[] newBankCards;
		if (bankCards == null) {
			newBankCards = new BankCard[1];
			newBankCards[0] = bankCard;
			this.bankCards = newBankCards;
			return true;
		} else {
			newBankCards = new BankCard[bankCards.length + 1];
			// 如果新增的银行卡和之前的有重复,则不新增
			for (int i = 0; i < bankCards.length; i++) {
				if (bankCard.getBank().equals(bankCards[i].getBank())
						&& bankCard.getcardId() == bankCards[i].getcardId()) {
					throw new Exception("银行卡重复,无法添加!");
				}
			}
			int i = 0;
			for (i = 0; i < bankCards.length; i++) {
				newBankCards[i] = bankCards[i];
			}
			newBankCards[i] = bankCard;
			this.bankCards = newBankCards;
			return true;
		}
	}

	/**
	 * 移除银行卡
	 * 
	 * @param bankCard 银行卡
	 * @return
	 * @throws Exception
	 */
	public boolean delBankCard(BankCard bankCard) throws Exception {
		BankCard[] newBankCards;
		if (bankCards == null) {
			// 没有银行卡时,移除失败
			throw new Exception("银行卡为空,无法移除!");
		} else if (bankCards.length == 1) {
			// 只有一张银行卡时,再移除就没有银行卡了,设卡为空
			this.bankCards = null;
			return false;
		} else {
			// 有一张以上的银行卡时,移除后的银行卡数量是之前数量-1
			newBankCards = new BankCard[bankCards.length - 1];
		}
		int i = 0, counts = 0;
		for (i = 0; i < bankCards.length; i++) {
			// 逻辑上理解:当银行卡中存在银行种类一致并且cardId相同时,移除这张银行卡
			// 实际上操作:当银行卡中存在的银行种类或cardId不一致时,保留这张银行卡
			if (!(bankCards[i].getBank().equals(bankCard.getBank()))
					|| !(bankCards[i].getcardId() == bankCard.getcardId())) {
				newBankCards[counts++] = bankCards[i];
			}
		}
		if (counts == bankCards.length - 1) {
			this.bankCards = newBankCards;
			return true;
		} else {
			throw new Exception("移除银行卡出错!");
		}
	}

	/**
	 * 查询账户余额
	 * 
	 * @return double
	 */
	public double checkBalance() {
		double money = 0;
		for (int i = 0; i < bankCards.length; i++) {
			money += bankCards[i].getMoney();
		}
		return money;
	}

	/**
	 * 存钱
	 * 
	 * @param cardId 银行卡Id
	 * @param bank   银行种类
	 * @param money  存款金额
	 * @return boolean
	 */
	public boolean deposit(BankCard bankCard, double money) {
		// 判断特殊情况
		if (bankCard.getcardId() < 0 || bankCard.getBank() == null || bankCard.getBank().length() == 0 || money <= 0) {
			return false;
		}
		boolean success = false;
		for (int i = 0; i < bankCards.length; i++) {
			// 银行种类一致并且cardId相同,则进行下一步操作
			if (bankCards[i].getBank().equals(bankCard.getBank()) && bankCards[i].getcardId() == bankCard.getcardId()) {
				double newMoney = bankCards[i].getMoney() + money;
				bankCards[i].setMoney(newMoney);
				success = true;
				break;
			}
		}
		return success;
	}

	/**
	 * 取钱
	 * 
	 * @param cardId 银行卡Id
	 * @param bank   银行种类
	 * @param money  取款金额
	 * @return boolean
	 */
	public boolean withdraw(BankCard bankCard, double money) {
		// 判断特殊情况
		if (bankCard.getcardId() < 0 || bankCard.getBank() == null || bankCard.getBank().length() == 0 || money <= 0) {
			return false;
		}
		boolean success = false;
		for (int i = 0; i < bankCards.length; i++) {
			// 银行种类一致并且cardId相同,则进行下一步操作
			if (bankCards[i].getBank().equals(bankCard.getBank()) && bankCards[i].getcardId() == bankCard.getcardId()) {
				// 取款金额要大于卡内余额才能取
				if (money <= bankCards[i].getMoney()) {
					double newMoney = bankCards[i].getMoney() - money;
					bankCards[i].setMoney(newMoney);
					success = true;
				}
				break;
			}
		}
		return success;
	}

	@Override
	public String toString() {
		return "Account [bankCards=" + Arrays.toString(bankCards) + ", trueName=" + trueName + ", phone=" + phone + "]";
	}
}

/**
 * 银联类,包含账号、密码和对应的账户
 * 
 * @see UnionPaySystem.Account.Class#Account()
 * @author IceCream - 吃猫的鱼℘, 935478677@qq.com
 */
@SuppressWarnings("serial")
class UnionPay implements Serializable {
	private String userName;
	private String password;
	private Account account;

	public UnionPay() {

	}

	public UnionPay(String userName, String password, Account account) {
		this.userName = userName;
		this.password = password;
		this.account = account;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public Account getAccount() {
		return account;
	}

	public void setAccount(Account account) {
		this.account = account;
	}

	@Override
	public String toString() {
		return "UnionPay [userName=" + userName + ", password=" + password + ", account=" + account + "]";
	}
}

/**
 * 取款机类,实现查看余额、存款和取款,包含机内现金余额
 * 
 * @author IceCream
 */
@SuppressWarnings("serial")
class ATM implements Serializable {
	// 取款机内的现金余额,查询余额的次数
	private double money = 100000;
	public static int checkBalanceTimes = 0;

	{
		// 构造代码块,初始化查余额次数
		checkBalanceTimes++;
	}

	public ATM() {

	}

	public ATM(double money) {
		this.money = money;
	}

	public double getMoney() {
		return money;
	}

	public void setMoney(double money) {
		this.money = money;
	}

	/**
	 * 查询余额,显示
	 * 
	 * @param unionPay
	 * @param userName
	 * @param password
	 * @throws Exception
	 */
	public void checkBalance(UnionPay unionPay, String userName, String password) throws Exception {
		double money = 0;
		// 用户名和密码正确
		if (userName.equals(unionPay.getUserName()) && password.equals(unionPay.getPassword())) {
			System.out.println("第" + checkBalanceTimes + "次查余额:");
			for (int i = 0; i < unionPay.getAccount().getBankCards().length; i++) {
				BankCard bankCard = unionPay.getAccount().getBankCards()[i];
				System.out.println(bankCard.getBank() + "卡" + bankCard.getcardId() + "里还有" + bankCard.getMoney() + "元");
				money += bankCard.getMoney();
			}
			System.out.println("总余额" + money);
			checkBalanceTimes++;
		} else {
			throw new Exception("用户名或密码错误!");
		}
	}

	/**
	 * 存钱,unionPay模拟登录银联,输入账号密码以登录,银行卡Id是区分取哪张卡里的钱,bank区分是哪家银行,money操作金额
	 * 
	 * @param unionPay
	 * @param userName
	 * @param password
	 * @param cardId
	 * @param bank
	 * @param money
	 * @return boolean
	 * @throws Exception
	 */
	public boolean deposit(UnionPay unionPay, String userName, String password, BankCard bankCard, double money)
			throws Exception {
		boolean success = false;
		// 用户名和密码正确
		if (userName.equals(unionPay.getUserName()) && password.equals(unionPay.getPassword())) {
			// 账户存钱成功后,机器内现金余额增加
			if (unionPay.getAccount().deposit(bankCard, money)) {
				this.money += money;
				success = true;
			} else {
				throw new Exception("卡不存在或卡内余额不足!");
			}
		} else {
			throw new Exception("用户名或密码错误!");
		}
		return success;
	}

	/**
	 * 取钱,unionPay模拟登录银联,输入账号密码以登录,银行卡Id是区分取哪张卡里的钱,bank区分是哪家银行,money操作金额
	 * 
	 * @param unionPay
	 * @param userName
	 * @param password
	 * @param cardId
	 * @param bank
	 * @param money
	 * @return boolean
	 * @throws Exception
	 */
	public boolean withdraw(UnionPay unionPay, String userName, String password, BankCard bankCard, double money)
			throws Exception {
		boolean success = false;
		// 用户名和密码正确
		if (userName.equals(unionPay.getUserName()) && password.equals(unionPay.getPassword())) {
			if (this.money < money) {
				// 如果取款机现金余额不足,则无法取出
				throw new Exception("机内现金余额不足!");
			}
			// 账户取钱成功后,机器内现金余额减少
			if (unionPay.getAccount().withdraw(bankCard, money)) {
				this.money -= money;
				success = true;
			} else {
				throw new Exception("卡不存在或卡内余额不足!");
			}
		} else {
			throw new Exception("用户名或密码错误!");
		}
		return success;
	}

	@Override
	public String toString() {
		return "ATM [money=" + money + "]";
	}
}

上述代码运行结果如下:

第1次查余额:
工商银行卡0里还有3000.0元
工商银行卡1里还有2000.0元
农业银行卡0里还有1000.0元
建设银行卡0里还有500.0元
总余额6500.0
第2次查余额:
工商银行卡0里还有0.0元
工商银行卡1里还有2000.0元
农业银行卡0里还有1000.0元
建设银行卡0里还有500.0元
总余额3500.0
第3次查余额:
工商银行卡0里还有0.0元
工商银行卡1里还有2000.0元
农业银行卡0里还有1000.0元
总余额3000.0
第4次查余额:
工商银行卡0里还有100.0元
工商银行卡1里还有2000.0元
农业银行卡0里还有1000.0元
总余额3100.0

posted @ 2021-07-16 10:58  吃猫的鱼℘  阅读(115)  评论(0编辑  收藏  举报
Document