我的第三次JAVA学习
1. 前言
经过了一个学期的java学习,我已经能做到熟练运用各种java的基础操作来解决一些偏向实际的问题。这几次的作业也考验这种能力的熟练运用。第七次的题目集主要是对于一种图形卡片排序游戏的分析和计算,以及输出格式的各种要求。第八第九次的作业都是与银行有关的系统构建与处理。这几次的作业都偏向实际,同样的难度也有一定的增加,但还不至于到做不出来的地步,接下来我将仔细介绍这几次作业的关注点和对于内容的着重分析。
2. 设计与分析
这几次作业主要分成两个部分,一部分是一种图形分拣游戏,另一种是银行系统的搭建,我将逐个介绍。
(1) 题目集7(7-1)(7-2)两种图形分拣游戏的比较
先是题目集7-1的程序功能需求
先简单分析一下这道题目的需求

一眼看上去这道题目需求极为复杂,复杂的点并不是在于单个操作有多难实现,而是对于每一个操作到最后聚合成一个完整的程序,最后这个程序的复用性和简洁性是否还能保持。所以可以很容易看出来这次需要我注意的是类的继承,多态性使用方法以及接口的运用。
接下来仔细分析整个过程。
先有一个整体的输入,很明显这里我们要先进行一个输入数据的分拣和统计储存,以方便后续计算,同时我们要对于每一个输入的数据进行判断,判断是否违规。如果违规则作出相应反应。这里要提醒一点,数据正误判断和数据存储还有数据输入应该在不同的类和方法之中,这样可以极大地提高代码的观赏和理解性,以及对于后续内容的添加和修改带来极大便利,具有较高的复用性。
接下来放上一些简单的代码:
public DealCardList(ArrayList<Integer> list) {
// TODO Auto-generated constructor stub
this.arr = list.stream().mapToInt(k->k).toArray();
// for(int i = 0;i<arr.length;i++) {
// System.out.println(arr[i]);
// }
for(int i = 0 ; i<arr.length;i++ ) {
if(arr[i] == 1) {
Circle C = new Circle(Main.input.nextDouble());
if(C.validate()) {
area[i] = C.getArea();
}
else {
System.out.println("Wrong Format");
System.exit(0);
}
}
if(arr[i] == 2) {
Rectangle R = new Rectangle(Main.input.nextDouble(),Main.input.nextDouble());
if(R.vaildate()) {
area[i] = R.getArea();
}
else {
System.out.println("Wrong Format");
System.exit(0);
}
}
if(arr[i] == 3) {
Triangle TR = new Triangle(Main.input.nextDouble(),Main.input.nextDouble(),Main.input.nextDouble());
if(TR.validate()) {
area[i] = TR.getArea();
}
else {
System.out.println("Wrong Format");
System.exit(0);
}
}
if(arr[i] == 4) {
Trapezoid T = new Trapezoid(Main.input.nextDouble(),Main.input.nextDouble(),Main.input.nextDouble());
if(T.validate()) {
area[i] = T.getArea();
}
else {
System.out.println("Wrong Format");
System.exit(0);
}
}
}
// for(int i = 0;i<arr.length;i++) {
// System.out.println(area[i]);
// }
}
这一段代码是进行每一个输入的内容的分拣和纠错,对于每一个不同图形的错误是不一样的,所以这里将每一个图形独特的纠错作为一个继承的方法在每一个图形中实现,例如:


到这里就已经完成了输入的数据的基本分拣纠错和处理,我们可以看到这里输出的数据已经是每一个图形的面积了,而计算面积的方法依然在每个图形类中,这样代码的复用性和简洁性就会强很多。
接下来是对于这些输入数据的处理,我在这里使用了List,可以视作一种对象数组,方便后续的比较排序和结果输出,比较的操作很简单,就是进行需要比较的数据进行读取,然后再排序。再贴一些简单的代码。
public void showResult() {
double sum = 0;
// TODO Auto-generated method stub
System.out.println("The original list:");
for(int i= 0;i<arr.length;i++) {
switch (arr[i]) {
case 1:
System.out.printf("Circle:%.2f ",area[i]);
break;
case 2:
System.out.printf("Rectangle:%.2f ",area[i]);
break;
case 3:
System.out.printf("Triangle:%.2f ",area[i]);
break;
case 4:
System.out.printf("Trapezoid:%.2f ",area[i]);
break;
}
}
System.out.println();
System.out.println("The sorted list:");
for(int i = 0;i<arr.length;i++) {
for(int j = i;j<arr.length;j++) {
if(area[i]<area[j]) {
double temp = area[j];
area[j] = area[i];
area[i] = temp;
int temp1 = arr[j];
arr[j] = arr[i];
arr[i] = temp1;
}
}
}
for(int i= 0;i<arr.length;i++) {
switch (arr[i]) {
case 1:
System.out.printf("Circle:%.2f ",area[i]);
break;
case 2:
System.out.printf("Rectangle:%.2f ",area[i]);
break;
case 3:
System.out.printf("Triangle:%.2f ",area[i]);
break;
case 4:
System.out.printf("Trapezoid:%.2f ",area[i]);
break;
}
}
System.out.println();
System.out.print("Sum of area:");
for(int i = 0;i<arr.length;i++) {
sum+=area[i];
}
System.out.printf("%.2f",sum);
}
public double getAllArea() {
return 0;
}
}
我们可以看到,这里我把排序和输出放到了一起进行一个处理。这里是有问题的,我应该把这两个功能分开来,这样的话排序的我可以单独修改和进行复用,输出格式我也可以更改复用。而我这里的操作像是把两个无关的内容放到一起操作,没有体现JAVA面向对象的设计理念。
题目集7-1就先分析到这里,再是题目集7-2的程序功能需求:


也先对题目进行分析,我们可以看到这里比上一次多了一个分组的要求,排序的操作也改变成了降序,以及输出规则也更加细化。我们上次对于不同数据分拣的操作在这里依旧能用到,如果上次的排序和输出是分开来的画,这里对于这两处的修改会变得容易的多,代码会更简洁美观。
也先是对于数据的处理这里的数据处理和上次几乎一模一样,这里就不再放出,以免浪费空间。直接跳到分拣处理。我在当时输入的时候,就已经根据他输入数据的不同进行了不同的对象创建,这里我们分拣只需要遍历对象判断就可以得到结果。
先贴上代码:
public void showResult() {
double sum = 0;
// TODO Auto-generated method stub
System.out.println("The original list:");
System.out.print("[");
for(int i= 0;i<arr.length;i++) {
switch (arr[i]) {
case 1:
System.out.printf("Circle:%.2f ",area[i]);
break;
case 2:
System.out.printf("Rectangle:%.2f ",area[i]);
break;
case 3:
System.out.printf("Triangle:%.2f ",area[i]);
break;
case 4:
System.out.printf("Trapezoid:%.2f ",area[i]);
break;
}
}
System.out.println("]");
System.out.println("The Separated List:");
for(int i = 1;i<=4;i++) {
System.out.print("[");
for(int j = 0;j<arr.length;j++) {
if(arr[j] == i && i==1) {
System.out.printf("Circle:%.2f ",area[i]);
}
if(arr[j] == i && i==2) {
System.out.printf("Rectangle:%.2f ",area[i]);
}
if(arr[j] == i && i==3) {
System.out.printf("Triangle:%.2f ",area[i]);
}
if(arr[j] == i && i==4) {
System.out.printf("Trapezoid:%.2f ",area[i]);
}
}
System.out.print("]");
}
for(int i = 0;i<arr.length;i++) {
for(int j = i;j<arr.length;j++) {
if(area[i]<area[j]) {
double temp = area[j];
area[j] = area[i];
area[i] = temp;
int temp1 = arr[j];
arr[j] = arr[i];
arr[i] = temp1;
}
}
}
System.out.println();
System.out.print("The Separated sorted List:");
for(int i = 1;i<=4;i++) {
System.out.print("[");
for(int j = 0;j<arr.length;j++) {
if(arr[j] == i && i==1) {
System.out.printf("Circle:%.2f ",area[i]);
}
if(arr[j] == i && i==2) {
System.out.printf("Rectangle:%.2f ",area[i]);
}
if(arr[j] == i && i==3) {
System.out.printf("Triangle:%.2f ",area[i]);
}
if(arr[j] == i && i==4) {
System.out.printf("Trapezoid:%.2f ",area[i]);
}
}
System.out.print("]");
}
System.out.println();
System.out.print("The max area:");
System.out.printf("%.2f",area[0]);
}
public double getAllArea() {
return 0;
}
}
接下来对这段代码进行一个分析,我在这里依旧把这两个进行了一个集中处理,这就是我直接套用上次的代码带来的后果,因为我上次把这两个毫无相关的处理放在了一起,导致这次直接复用的时候也犯下了这种错误。这里稍微做出的修改是我没有对于每一个进行分组挑选了,而是在输出的时候进行直接的判断。
这是题目集7的各个题目的单独分析已经最后的综合分析:
首先这是一个图形分拣系列程序,其中重要的是对于输入的处理,中间对于数据的处理,最后输出的处理。这里先是学到了这些关联性不强的操作一个独立出来单独处理,而不是混为一团,再就是最后输出的处理一定要看清题目,尽量不带更多判断的纯粹输出。
(2) 题目集8和题目集9对于银行系统的模拟操作程序的递进式比较:
先是题目集8的功能需求:
因为这次的题目比较复杂,我将从两层面分析题目,先是银行系统的构建,再是各种方法需求的实现。
先介绍银行系统的需求:

我们可以看到,这里银行隶属一个同一机构中国银联,这个可以成为一种父子类关系的体现,同样的,每一个银行都有其ATM机子,这里对于银行和其ATM关系的处理,我的想法是每一个ATM机子都是独立的对象,其中有一个数据标识其隶属于哪一个银行,并且在后面对那个银行对象进行操作。这样每个ATM是各自独立的,同时与隶属的银行也可以操作。接下来是每一个用户的信息,我倾向于每一个用户是隶属于银行系统中的中数据,而不是用一个独立的存在,这里就有一种分层的感觉,但是我目前还解决不了,所以只是简单的把每个人的数据放在银行类中。
这里简单的处理了各类数据的存储,接下来看每一个操作的处理

可以看到先有的是存取款的操作,这是最基础的操作,这里采用的是多行输入最后#终止的方式。所以应该是先把输入的内容储存起来,同时只进行停止输入的判断,到后面再来处理输入的数据。接下来是贴一些处理的代码:
//处理输入数据
Scanner sc = new Scanner(System.in);
ArrayList<String> in = new ArrayList<String>();
String value = sc.nextLine();
in.add(value);
while (value.equals("#")==false){
value = sc.nextLine();
in.add(value);
}
int a = 0;
ArrayList<String> CardNumber = new ArrayList<String>();
ArrayList<String> Password = new ArrayList<String>();
ArrayList<String> ATMNumber = new ArrayList<String>();
ArrayList<Double> need = new ArrayList<Double>();
Pattern s = Pattern.compile("\\d+ +\\d+ +\\d+ +[+-]?\\d+.\\d\\d");
Pattern ss = Pattern.compile("\\d+");
while (in.get(a).equals("#")==false){
Matcher x = s.matcher(in.get(a));
Matcher xx = ss.matcher(in.get(a));
boolean y = x.matches(); // 返回 true
boolean yy = xx.matches(); // 返回 true
if (y) {
String split[] = in.get(a).split(" +");
CardNumber.add(split[0]);
Password.add(split[1]);
ATMNumber.add(split[2]);
need.add(Double.parseDouble(split[3]));
}
if(yy){
CardNumber.add(in.get(a));
Password.add("");
ATMNumber.add("01");
need.add(0.00);
}
a++;
}
我们可以看到这里运用了正则表达式,方便对于输入数据的匹配和判断,但是这里只是一个匹配,判断的东西有很多,比如这个ATM机子是否存在,输入的账号是否存在,能不能在这台ATM机子上面存取钱,输入的账户密码是否匹配,账户里面有没有这么多钱。这些都是要考虑到的问题。解决方法就是比较繁琐依次判断处理,但没有什么难度,接下来贴一些处理的代码:
//卡号与密码与现金校验
int c = 0;
while (c<CardNumber.size()){
boolean flagN = false;
boolean flagP = false;
boolean flagB = false;
boolean flagX = false;
Pattern ccb = Pattern.compile("0[1-4]");
Pattern icbc = Pattern.compile("0[5-6]");
for (int j = 0; j < chinaUnionPay.CCB.getAccountList().size(); j++) {
for (int jj = 0; jj < chinaUnionPay.CCB.getAccountList().get(j).getCardNumber().size(); jj++) {
if(chinaUnionPay.CCB.getAccountList().get(j).getCardNumber().get(jj).equals(CardNumber.get(c))){
if(Password.get(c).equals("")){
flagP=true;
}
else {
flagP = chinaUnionPay.CCB.getAccountList().get(j).getCardList().get(jj).cardMatcher(Password.get(c));
}
flagB=chinaUnionPay.CCB.getAccountList().get(j).getBlance()>= need.get(c);
Matcher m = ccb.matcher(ATMNumber.get(c));
flagX = m.matches(); // 返回 true
flagN = true;
}
}
}
for (int ii = 0; ii < chinaUnionPay.ICBC.getAccountList().size(); ii++) {
for (int iii = 0; iii < chinaUnionPay.ICBC.getAccountList().get(ii).getCardNumber().size(); iii++) {
if(chinaUnionPay.ICBC.getAccountList().get(ii).getCardNumber().get(iii).equals(CardNumber.get(c))){
if(Password.get(c).equals("")){
flagP=true;
}
else {
flagP = chinaUnionPay.ICBC.getAccountList().get(ii).getCardList().get(iii).cardMatcher(Password.get(c));
}
flagB=chinaUnionPay.ICBC.getAccountList().get(ii).getBlance()>= need.get(c);
Matcher m = icbc.matcher(ATMNumber.get(c));
flagX = m.matches(); // 返回 true
flagN = true;
}
}
}
if (flagN == false) {
System.out.println("Sorry,this card does not exist.");
System.exit(0);
}
if (flagP == false) {
System.out.println("Sorry,your password is wrong.");
System.exit(0);
}
if (flagB == false) {
System.out.println("Sorry,your account balance is insufficient.");
System.exit(0);
}
if (flagX == false) {
System.out.println("Sorry,cross-bank withdrawal is not supported.");
System.exit(0);
}
c++;
}
到这里题目集8的介绍基本结束了,我们再来看一下题目集9的各种处理。先看数据的存储方面有什么不同:

我们看到这里有着一些内容的添加,先是加入了透支这么一个操作,换句话说就是临时的一个借贷,同样的这个借贷的利息等处理看这个样子应该是隶属于中国银联之下的。第二就是加入了跨行取款功能,可以用别人的取款机取不是他家的钱。同时增加了银行和相应的账户,有一个新的账户类型,这类账户类型可以做不同的事情这里就需要一波处理了。
首先是不同账户这边,因为我基本套用的是老师发过来的代码,在此基础上进行的修改。我可以了解到的是账户是独立的,其中存储了这个账户属于那一类,其所有人是谁,隶属于哪个银行,等等数据。这个还只是简单的存储,代码就不贴上来了,这里简单的处理了各类数据的存储,接下来看每一个操作的处理:

这里相对于上一个题目集添加了一些问题,比如手续费的计算,如果取完钱了后发现卡里没钱了,这个手续费应该扣在那里?经过实践和调查之后发现手续费是扣在剩余金额中的,如果剩余金额不足,再扣得交易金额。跨行业务怎么操作,我们之前的操作是ATM对应的银行进行查找,那这里可以跨行办业务了,ATM机又该去哪里找数据?这里的处理就是看这个账户的隶属,再在相应的银行调用相应的数据。这里就有一个问题,这样的话每一个ATM机子都有调用银行数据的能力,这个很明显是和常识违背的,如果是这样的话只要抢一个ATM机子所有和他数据互通的银行都可以调用,这很危险。这里我的想法是提供一个调用的操作,然后每一个ATM机子只是接口而已。等等计算和一些细节的处理。话不多说,直接贴上部分代码:
public void withdraw() {
/**
* 校验该卡是否存在
*/
Card card = ValidateData.getCardbyCardNO(unionPay, cardNO);
if(card == null) {
System.out.println("Sorry,this card does not exist.");
System.exit(0);
}
/**
* 校验ATM是否存在
*/
ATM aTM = ValidateData.getATMbyATMID(unionPay, ATMID);
if(aTM == null) {
System.out.println("Sorry,the ATM's id is wrong.");
System.exit(0);
}
Account account = Account.getAmountbyCardNO(cardNO);
double balance = account.getBalance();
charge = aTM.getBank().getCharge();
double cost1 = 0.0;
double cost2 = 0.0;
/**
* 校验卡密码是否正确
*/
if(!card.getCardPassword().equals(cardPassword)) {
System.out.println("Sorry,your password is wrong.");
System.exit(0);
}
/**
* 校验取款金额是否大于余额
*/
if (amount > balance) {
if(account.isCla()==0) {
System.out.println("Sorry,your account balance is insufficient.");
System.exit(0);
}else {
if(balance>0) {
cost2 = Math.abs(balance-amount)*0.05;
}else {
cost2 = Math.abs(amount)*0.05;
}
if((balance - amount - cost2) < -50000) {
System.out.println("Sorry,your account balance is insufficient.");
System.exit(0);
}
}
}
/**
* 校验是否为跨行取款
*/
if (account.getBank().getBankNO() != aTM.getBank().getBankNO()) {
if(account.isCla() == 0) {
if((amount+charge) > balance) {
System.out.println("Sorry,your account balance is insufficient.");
System.exit(0);
}else {
cost1 = Math.abs(amount)*charge;
}
}else {
cost1 = Math.abs(amount)*charge;
}
}
account.setBalance(balance - amount - cost1 - cost2);//取款更新余额操作
if(amount >= 0) {
showResult(account,1);
}else {
showResult(account,0);
}
}
这里就是各个操作的合集了,其实要实现还是不叫简单的,这里主要复杂的操作是把这些集合起来。
3. 踩坑心得
我发现刚开始主要的问题是一股脑写下去,然后当有各种之间的累加操作的时候,就会停顿下来,然后发现解决不了,只能推倒重写。这里学到的是在写这些东西前要先想好才能动笔,不然的话就会两手空空走一步算一步,最后跌跌撞撞什么也不剩。所以很重要的就是事先的架构,只有先构造好后面的内容才能动笔。然后对于里面的各种处理其实问题不大,主要是后面银行系统的各种模拟的方式,这个要结合现实的同时要弄出一个适合我们实现的方式十分困难。
4. 改进建议
这几次的作业,主要的问题就是我自己写的代码再看的时候就看不懂了,这里主要的问题就是代码的可读性很差,虽然自己当时边写的时候熟悉各种操作,但是隔了一段时间再看就看不懂了。这个问题主要的解决方式就是填写各种注释,同时把代码尽量写的简洁明了。
5. 总结
到了最后来总结一下这两次作业,首先是一类游戏,这里主要考验的就是各种类与类之间的关系的操作,这考验的其实是对于JAVA面向对象这一特性的理解。第二个则是银行的模拟,这里的主要的内容就是对于这个系统模拟的理解和实现,这里我走了弯路所幸最后成果很不错。到这里就应该是最后一次JAVA博客了,这一个学期我学习到了很多,从刚开始的完全不懂JAVA,到现在实现一些简单的程序,这是一个很大的跨度,但是距离面向社会,距离去实现各种项目,还差的很远。
浙公网安备 33010602011771号