有关java的第四五次大作业与期中考试总结
一、前言
本次博客主是针对java学习第二阶段中的PTA题目与期中考试题目的总结性博客,第二阶段的作业难度与第一阶段想必有所提高,对java的知识点考察主要集中在类的设计,正则表达式的运用,类的继承,多态,抽象类与接口。
期中考试代码情况:
二、设计与分析
1、java期中考试的三四题
完成情况:
![]()
这里重点来分析第三题和第四题。
—— 1.1测验3-继承与多态
1)类图:
2)生成报表:
![]()
(绿色环形区域即被测量维度的期望值,维度上的点则是测量得到的实际值)
据报表分析:平均圈复杂度和函数深度都比较低,说明代码简洁明了。
3)类设计:
✔️ 继承关系:Shape做抽象形状类,有getarea方法,三角形类和矩形类继承Shape类,并覆盖其getarea方法。
✔️ 组合关系:矩形有四个点,故点类是矩形类为组合关系
4)代码如下:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner input = new Scanner(System.in);
int choice = input.nextInt();
switch(choice) {
case 1://Circle
double r=input.nextDouble();
Shape circle = new Circle(r);
if(r<=0) {
System.out.println("Wrong Format");
}
if(r>0) {
System.out.println(format((circle.getArea())));
}
break;
case 2://Rectangle
double x1 = input.nextDouble();
double y1 = input.nextDouble();
double x2 = input.nextDouble();
double y2 = input.nextDouble();
点1 leftTopPoint = new 点1(x1,y1);
点1 lowerRightPoint = new 点1(x2,y2);
Rectangle rectangle = new Rectangle(leftTopPoint,lowerRightPoint);
printArea(rectangle);
break;
}
}
public static void printArea(Shape circle){
System.out.println(format(circle.getArea()));
}
public static String format(double value) {
return String.format("%.2f", value).toString();
}
}
class 点1{
private double x;
private double y;
public 点1(double x, double y) {
super();
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public 点1() {
super();
}
}
class Shape{
public Shape() {
}
public double getArea(){
return getArea();
}
}
class Rectangle extends Shape{
private 点1 左上角点;
private 点1 右下角点;
public Rectangle(点1 左上角点, 点1 右下角点) {
super();
this.左上角点 = 左上角点;
this.右下角点 = 右下角点;
}
public 点1 get左上角点() {
return 左上角点;
}
public void set左上角点(点1 左上角点) {
this.左上角点 = 左上角点;
}
public 点1 get右下角点() {
return 右下角点;
}
public void set右下角点(点1 右下角点) {
this.右下角点 = 右下角点;
}
public double get长() {
return Math.abs(左上角点.getX()-右下角点.getX());
}
public double get宽() {
return Math.abs(左上角点.getY()-右下角点.getY());
}
public double getArea() {
return get长()*get宽();
}
}
class Circle extends Shape{
private double 半径=-1;
public Circle(double 半径) {
super();
this.半径 = 半径;
}
public double get半径() {
return 半径;
}
public void set半径(double r) {
if(r<=0)
System.out.println("Wrong Format");
else
this.半径 = r;
}
public double getArea() {
double s;
s=Math.PI*半径*半径;
return s;
}
}
|
——1.2测验4-抽象类与接口
1)类图
2)生成报表
![]()
(绿色环形区域即被测量维度的期望值,维度上的点则是测量得到的实际值)
据报表分析:平均圈复杂度和函数深度都在合理范围内,代码质量中等。且平均每个类的方法数(Methods/Class)也在期望范围内,说明对类中方法的分配也较为妥当。
3)类设计
✔️ 继承关系:Shape做抽象形状类,有getarea方法,三角形类和矩形类继承Shape类,并覆盖其getarea方法。
✔️ 组合关系:矩形有四个点,故点类是矩形类为组合关系
✔️ Shape类实现Comparable接口:实现了对按面积大小进行对象数组的排列。
相关知识点如下:
接口 Comparable
此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。
Comparable的compareTo方法
比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
故可写Shape类
class Shape implements Comparable<Shape>{
public Shape() {
}
public double getArea(){
return getArea();
}
@Override
public int compareTo(Shape o) {
// TODO Auto-generated method stub
if(this.getArea()>o.getArea()){
return 1;
}else if(this.getArea()<o.getArea()){
return -1;
}
return 0;
}
}
|
4)代码如下
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
int flag=0;
ArrayList<Shape> shape=new ArrayList();
Scanner input = new Scanner(System.in);
while(flag==0) {
int choice = input.nextInt();
switch(choice) {
case 1://Circle
double r=input.nextDouble();
Shape circle = new Circle(r);
if(r<=0) {
System.out.println("Wrong Format");
}
if(r>0) {
shape.add(circle);
//printArea(circle);
}
break;
case 2://Rectangle
double x1 = input.nextDouble();
double y1 = input.nextDouble();
double x2 = input.nextDouble();
double y2 = input.nextDouble();
点1 leftTopPoint = new 点1(x1,y1);
点1 lowerRightPoint = new 点1(x2,y2);
Rectangle rectangle = new Rectangle(leftTopPoint,lowerRightPoint);
shape.add(rectangle);
//printArea(rectangle);
break;
case 0:
flag=1;break;
}
}
Collections.sort(shape);
for(int i=0;i<shape.size();i++) {
if(shape.get(i)!=null)
printArea(shape.get(i));
}
}
public static void printArea(Shape circle){
System.out.print(format(circle.getArea())+" ");
}
public static String format(double value) {
return String.format("%.2f", value).toString();
}
}
class 点1{
private double x;
private double y;
public 点1(double x, double y) {
super();
this.x = x;
this.y = y;
}
}
class Shape implements Comparable<Shape>{
public Shape() {
}
public double getArea(){
return getArea();
}
@Override
public int compareTo(Shape o) {
// TODO Auto-generated method stub
if(this.getArea()>o.getArea()){
return 1;
}else if(this.getArea()<o.getArea()){
return -1;
}
return 0;
}
}
class Rectangle extends Shape{
private 点1 左上角点;
private 点1 右下角点;
public Rectangle(点1 左上角点, 点1 右下角点) {
super();
this.左上角点 = 左上角点;
this.右下角点 = 右下角点;
}
public double get长() {
return Math.abs(左上角点.getX()-右下角点.getX());
}
public double get宽() {
return Math.abs(左上角点.getY()-右下角点.getY());
}
public double getArea() {
return get长()*get宽();
}
}
class Circle extends Shape{
private double 半径=-1;
public Circle(double 半径) {
super();
this.半径 = 半径;
}
public double get半径() {
return 半径;
}
public void set半径(double r) {
if(r<=0)
System.out.println("Wrong Format");
else
this.半径 = r;
}
public double getArea() {
double s;
s=Math.PI*半径*半径;
return s;
}
}
|
2、菜单计价程序-5
1)完成情况
![]()
2)类图
![]()
3)生成报表
![]()
(绿色环形区域即被测量维度的期望值,维度上的点则是测量得到的实际值)
据报表分析:
①平均圈复杂度和函数深度超标,圈复杂度大,说明程序代码的判断逻辑复杂,可能质量低且难于测试和维护。
②耦合度过高:类与类间分支嵌套过多
4)类设计
✔️ 判断输入格式:
利用正则表达式判断格式,这里给出我写的部分正则表达式作为例子
String regex4 = "[\\u4e00-\\u9fa5]{0,}[ ]\\d{0,}";//非特色菜菜谱
String regex3 = "[\\u4e00-\\u9fa5]{0,}[ ][\\u4e00-\\u9fa5]{0,}[ ]\\d{0,}[ ][T]";//特色菜谱
String regex6 = "\\d[ ](delete)";//删除情况
✔️ 菜单里新增特色菜的处理方法:
在dish类里增加特色菜(boolean)属性(默认是false),输入菜单时用正则表达式判断,如果输入的是特色菜就把特色菜属性改为true。
dish类代码实现如下:
class Dish {
String name;//菜品名称
int unit_price; //单价
boolean 特色菜=false;
String 口味;
public void set口味(String 口味) {
this.口味 = 口味;
}
public Dish(String name,int unit_price) {
this.name=name;
this.unit_price=unit_price;
}
public int getPrice(int portion){//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份)
double p;
if(portion==1)
p=unit_price;
else if(portion==2)
p=unit_price*1.5;
else if(portion==3)
p=unit_price*2;
else return 0;
return (int)Math.round(p);
}
}
|
✔️ 输入订单时对用户电话号和姓名的判断与记录:
用正则表达式判断输入是否正确。
在Table类((记录输出时所需每桌订单的信息))里增加name属性,number属性。
✔️ 将输出以用户姓名首字母从小到大的顺序排序:
处理方法:
Table类要实现Comparable接口,Comparable的compareTo方法比较此对象与指定对象的顺序。
class Table implements Comparable<Table>{
.....
}
|
Table类的comeparable方法代码入下:
@Override
public int compareTo(Table o) {
// TODO Auto-generated method stub
if(this.s.charAt(0)>o.s.charAt(0)){
return 1;
}else if(this.s.charAt(0)<o.s.charAt(0)){
return -1;
}
return 0;
}
|
:heavy_check_mark:新增了记录每桌的特色菜点菜情况的类(用于输出每桌特色菜点菜情况):
该类里最主要有川菜口味度数组,晋菜口味度数组,川菜口味度数组等属性。如果该桌没有点特色菜,则该几个数组都为null;如果该桌点特色菜了,则记录下来其口味度,通过动态数组的add方法将口味度添加到其对应的口味度数组。
这样下来,数组的大小为点该口味的特色菜的份数,数组里的值为该口味
的特色菜的口味度。
该类具体代码体现如下:
class 特色菜记录{
String 级别;
String 种类;
int portion;
ArrayList<Integer> 川菜口味度=new ArrayList();
ArrayList<Integer> 浙菜口味度=new ArrayList();
ArrayList<Integer> 晋菜口味度=new ArrayList();
public 特色菜记录(ArrayList<Integer> 川菜口味度, ArrayList<Integer> 浙菜口味度,ArrayList<Integer> 晋菜口味度) {
super();
this.川菜口味度 = 川菜口味度;
this.浙菜口味度 = 浙菜口味度;
this.晋菜口味度 = 晋菜口味度;
}
public void get种类() {//该方法用来输出每桌点的特色菜的平均口味度
int sum7=0,sum8=0,sum9=0;
int flag7=0,flag8=0,flag9=0;
int i=0,j=0,k=0;
for(i=0;i<川菜口味度.size();i++) {
if(川菜口味度.get(i)!=null) {
sum7=sum7+川菜口味度.get(i);
flag7=1;
}
}
for(j=0;j<晋菜口味度.size();j++) {
if(晋菜口味度.get(j)!=null) {
sum8=sum8+晋菜口味度.get(j);
flag8=1;
}
}
for(k=0;k<浙菜口味度.size();k++) {
if(浙菜口味度.get(k)!=null) {
sum9=sum9+浙菜口味度.get(k);
flag9=1;
}
}
if(flag7==1) {
int tem=Math.round(sum7/((float)i));
String 级别 = null;
switch(tem) {
case 0:级别="不辣";break;
case 1:级别="微辣";break;
case 2:级别="稍辣";break;
case 3:级别="辣";break;
case 4:级别="很辣";break;
case 5:级别="爆辣";break;
}
System.out.print(" "+"川菜"+" "+i+" "+级别);
}
if(flag8==1) {
int tem=(int)Math.round(sum8/(float)j);
String 级别 = null;
switch(tem) {
case 0:级别="不酸";break;
case 1:级别="微酸";break;
case 2:级别="稍酸";break;
case 3:级别="酸";break;
case 4:级别="很酸";break;
}
System.out.print(" "+"晋菜"+" "+j+" "+级别);
}
if(flag9==1) {
int tem=(int)Math.round(sum9/(float)k);
String 级别 = null;
switch(tem) {
case 0:级别="不甜";break;
case 1:级别="微甜";break;
case 2:级别="稍甜";break;
case 3:级别="甜";break;
}
System.out.print(" "+"浙菜"+" "+k+" "+级别);
}
}
}
|
**2、菜单计价程序-4**
1)完成情况
2)类图
3)生成报表
(绿色环形区域即被测量维度的期望值,维度上的点则是测量得到的实际值)
据报表分析:
①平均圈复杂度和函数深度超标,圈复杂度大说明程序代码的判断逻辑复杂,代码质量较低(其实是因为我把判断输入是否正确的代码大部分都放在了主函数,所以主函数写的过于冗长)
②耦合度过高:类与类间分支嵌套过多。
4)代码分析与类设计
✔️ 本题难点在于判断输入格式是否正确:
比较考验正则表达式的设计和正确书写,这里给出我写的部分正则表达式作为例子:
String regex = "[\\u4e00-\\u9fa5]{0,}[ ]\\d{0,}";//非特色菜菜谱
String regex1 = "[\\u4e00-\\u9fa5]{0,}[ ]\\d{0,}[ ]\\d{0,4}[ ][T]";//特色菜谱
String regex3 = "(table)[ ]([1-4][0-9]*|[5][0-5]*)[ ]\\d{4}[/]\\d{1,}[/]\\d{1,2}[ ]([0-1]?[0-9]|2[0-3])/([0-5][0-9])/([0-5][0-9])";//订单信息
|
除了判断格式,还有日期是否合理且在[2022.1.1-2023.12.31]范围内的判断,这里我的处理方法先判断是专门写一个类。该类代码如下:
class 判断table行是否合法{
String tian;
public 判断table行是否合法(String tian, String zhuo) {
super();
this.tian = tian;
this.zhuo = zhuo;
}
String zhuo;
boolean 日期合法() {//判断日期是否合法且在范围内
String[]arr=tian.split(" ");//2023/2/1 14/20/00
String[]brr=arr[0].split("/");
boolean judge=false;
int nian=brr[0].length();
int yue=brr[1].length();
int ri=brr[2].length();
String[]crr=arr[1].split("/");
int shi=crr[0].length();
int fen=crr[1].length();
int miao=crr[2].length();
int year=Integer.parseInt(brr[0]);
int month=Integer.parseInt(brr[1]);
int day=Integer.parseInt(brr[2]);
judge=judgeday(month,day,year);
if(judge&&nian==4&&(yue==1||yue==2)&&(ri==1||ri==2)&&(shi==1||shi==2)&&(fen==1||fen==2)&&(miao==1||miao==2)) {
return true;
}
else
return false;
}
public int tday(int year){//判断是否为闰年
if((year%4==0&&year%100!=0)||year%400==0) {
return 29;
}
else
return 28;
}
public boolean judgeday(int month,int day,int year){//判断日和月是否合法
switch(month) {
case 1:if(day<=31&&day>0) return true;else return false;
case 2:int d=tday(year);if(day<=d&&day>0) return true;else return false;
case 3:if(day<=31&&day>0) return true;else return false;
case 4:if(day<=30&&day>0) return true;else return false;
case 5:if(day<=31&&day>0) return true;else return false;
case 6:if(day<=30&&day>0) return true;else return false;
case 7:if(day<=31&&day>0) return true;else return false;
case 8:if(day<=31&&day>0) return true;else return false;
case 9:if(day<=30&&day>0) return true;else return false;
case 10:if(day<=31&&day>0) return true;else return false;
case 11:if(day<=30&&day>0) return true;else return false;
case 12:if(day<=31&&day>0) return true;else return false;
default:return false;
}
}
}
|
:heavy_check_mark: 新增特色菜种类:
在Dish类里新增(boolean)特色菜属性,赋值为false,如果输入菜单时有‘T’则把该属性改为true。
三、踩坑心得
- 写正则表达式
由于正则表达式过于不熟练,导致第四次大作业好多测试点都没有过,第五次大作业出了后,我上网看了有关正则表达式的文章,学习了一下后运用到代码里,发现真的很方便。
这里给出我当时学习正则表达式用的文章:
[]:http://t.csdn.cn/ZmH67
- Comparable接口
期中考试考到了接口,Comparable的comepareTo方法搭配Collection.sort()方法可以实现对对象数组的排序,十分方便。
- 格式错误的踩坑
错误格式是因为漏了空格或多了回车,这需要不断调试代码并且对比题干才可以发现这类错误的原因。在拿到题目的时候。
- 审题与类设计
java是面向对象的语言,在题目给出后,应该先认真审题,将类都设计好,不然到后续发现漏了题目要求的时候,改起来很复杂。
同样,为了方便修改代码内容,应该降低类之间耦合性,熟悉类间关系进行设计。
四、主要困难与改进建议
①主要困难:
基础知识薄弱,对于很多新的语法不会用不敢用,如最近接触的哈希,Hashmap,Hashset。
②改进建议:
希望留大作业时不要只留一道菜单题,应该多留几道不算难但能锻炼学生灵活掌握新知识的题。
五、总结
经历了java第二个学习阶段,我深刻认识到在面向对象过程中对类的设计是多么重要,类与类间关系,接口和抽象类的设计等等,都是对我的考验。代码越好,代码的圈复杂度就越低,类与类之间关系明确,代码的可维护性也高。
再回归具体知识点,在本阶段学习中我训练了继承和接口的使用,也自主设计了面向对象的代码,并逐步接触了一些比较好用的类与方法,也对动态数组的使用逐渐熟悉。希望在学好目前的知识的基础上,能学到更深的知识。