Java前三次训练集总结
一、前言
通过pta的三次作业,使我了解了一些Java的语法,结构方面的知识,对Java语言没有那么陌生了。三次作业的难度是逐渐升高的,第一次pta作业的代码量并不大,一共十二道题,每道题的代码也就三十行左右,完成的还比较轻松。主要考察的是逻辑方面,这一部分与之前学的c语言基本是一样的,像循环,选择结构的使用等。第二次pta作业一共九道题,前五道题和第一次作业难度差不多,大多是对选择循环等结构的使用,后面几道题,会考察数据类型,以及在输入输出的形式上也有一定要求。但第二次作业更加突出的是对字符串的使用,还有自己去定义新方法。第三次作业对于我来说在难度上是提高了一大截的,虽然它只有四道题,但却是我三次作业用时最长的一次。第三次作业主要考察了对于类的创建,起初并不能理解怎样去创建类,创建类的过程中每句话都是什么作用,也不能明白为什么要去创建类感觉之前使用多种方法的形式已经很不错了。但深入学习后才明白,创建类可以让程序更加清晰明了。通过三次作业,也更加明白了,在做作业的过程中,有自己去看书,去网上找资料,看视频,这个过程也是非常必要的,不能只看懂更重要的是实践。
二、设计与分析
1、第一次作业的7-6题: 统计一个子串在整串中出现的次数 其代码如下
import java.util.Scanner; public class Main{ public static void main(String[] args){ Scanner put = new Scanner(System.in); String a=put.nextLine(); String b=put.nextLine(); int c=0; int d=0; while(a.length()!=0){ d=a.indexOf(b); if(d!=-1){ c=c+1; a=a.substring(d+b.length()); } else {break;} } System.out.print(""+c);}}
这里是输入了两个字符串a和b,这里的输入也是有讲究的以输入字符串a为例,它可以以String a=put.nextLine();形式输入也可以以String a=put.next();形式输入。两种方式的不同是String a=put.nextLine();是读取以按下回车结束的字符串而String a=put.next();是读取以空白字符结束的字符串。a.indexOf(b)是返回字符串a中出现的字符串b的下标如果没有匹配则返回负一。a.substring(d+b.length())是返回从d+b.length下标开始的a字符串,使程序可以继续运行直到减去字符串a中所有的字符串b。
2、第一次作业的7-11题:求定积分 其代码如下
import java.util.Scanner; public class Main{ public static void main(String[] args){ Scanner put=new Scanner(System.in); double a = put.nextDouble(); double b =put.nextDouble(); double s = 0.0; int c = put.nextInt(); double dx = (b-a)/c; for (double i = 1.0; i <= c; i++) { s=(b*b*b-a*a*a)/3.0; } System.out.print(""+String.format("%.4f",s)); }}
其实这道题并不难,但有一个点就是对于定积分输出结果的计算,如果按照题目给的方法计算多个小矩形的面积来计算定积分是与输入输出样例不一样的,按照数学公式来计算定积分就可以通过测试点,这个问题我一开始并没有意识到,直到后来多次查看确保代码没有逻辑错误才想到这一方面,进行代码设计的时候不能局限自己的思维。
3、第三次作业的7-2:创建账户类Account 其代码如下
import java.util.Scanner; import java.time.LocalDate; class Account{ private int id; private double balance;//余额 private double Rate;//当前利率,默认为零 private LocalDate dateCreated;//开户时间 public Account(int id,double balance){ this.id=id; this.balance=balance; } public int getterid(){ return id; } public double getterbalance(){ return balance; } public double getterRate(double Rate){ return Rate; } public double getMonthlyInterestRate(double balance,double Rate){ double b; b=balance*(Rate/1200); return b; } public boolean withDraw(double c){//提取 if(c<0||c>balance){ return false; } else { return true; } } public boolean deposit(double d){//存款 if(d>20000||d<0){ return false; } else { return true; } } public void getterdateCreated(){ System.out.println("The Account'dateCreated:2020-07-31"); } } public class Main { public static void main(String[] args) { Scanner put = new Scanner(System.in); int id=put.nextInt(); double balance=put.nextDouble(); double Rate=put.nextDouble(); Account a= new Account(id,balance); double c=put.nextDouble();//提取 double d=put.nextDouble();//存款 if(a.withDraw(c)==false){ System.out.print("WithDraw Amount Wrong\n");} else balance=balance-c; if(a.deposit(d)==false){ System.out.print("Deposit Amount Wrong\n");} else balance=balance+d; System.out.printf("The Account'balance:%.2f\n",balance); System.out.printf("The Monthly interest:%.2f\n",a.getMonthlyInterestRate(balance,Rate)); a.getterdateCreated(); }}
这里就涉及到类的创建和使用了,创建类的时候首先要先定义类的“数据成员”也就是它的属性,表明数据是私有(关键字private)还是公有(关键字public)设置访问权限,getter获取数据,setter更改数据。私有数据也是Java封装性的基本表现,更体现其面向对象的特点。同时类的与类之间尽量不要有关联,要做到高内聚低耦合,保证程序模块化,这样程序出错也便于修改。这道题里首先是定义了余额,利率,开户时间的私有数据类型。但是开户时间是LocalDate类型,在类前要加一个import java.time.LocalDate;包,不然程序是错的。然后创建了public Account(int id,double balance)方法,这个方法会在后面的主方法里被调用,要注意它是有参还是无参的形式,其作用是确定id和balance的值。接下来就比较简单了,主要是有对余额范围控制这里用到if语句,计算利率,这里有一点要注意b=balance*(Rate/1200);在等号两边要留空格,如果没有空格也不会报错,但是程序书写要规范,这个习惯很重要。在主方法里进行创建对象其语句是Account a= new Account(id,balance);(包含了声明对象引用变量,创建对象,将对象的引用赋值),既然创建了对象就要使用对象的数据和方法,对象的数据访问和方法调用使用操作符(.)来完成,例如主方法中a.withDraw(c)是调用对象a中的withDraw函数括号中的c是函数的参数。if(a.withDraw(c)==false)在主方法中对对象中方法的返回值进行判断,这一部分最开始是想放在account类当中的,后来发现走不通就放到主方法里了。其实创建新类后主方法能干的事情也很多,它的作用不仅仅是输入输出。主方法判断好后对余额量进行改变balance=balance-c;。最后的部分就是输出,输出也有许多的方式与不同,例如System.out.print()在显示完成后不会前进到下一行,而System.out.println()在显示完成后移到下一行开始处,这些细小的地方也是最后程序能否测试通过的关键。System.out.print(")输出的是引号中的内容引号外用加号相连接的也会输出,像上面定积分的输出模式就是有引号和加号相结合的System.out.print(""+String.format("%.4f",s))此处输出的是保留s的四位小数。统计子串在整串中出现的次数中System.out.print(""+c)输出的是c的值。
4、第三次作业的7-3 :定义日期类表示下一天 其代码如下
import java.util.Scanner; class Date{ private int year; private int month; private int day; int [] max=new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31}; public Date(int year,int month,int day){ this.year=year; this.month=month; this.day=day; } public int getteryear(){ return year; } public int gettermonth(){ return month; } public int getterday(){ return day; } public boolean isleapyear(int year){ if((year%4==0&&year%100!=0)||(year%400==0)) return true; else return false; } public boolean cpv(){ int [] b=new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31}; if(isleapyear(year)==true) b[2]=29; if((year>=1900&&year<=2000)&&(month>=1&&month<=12)&&(day>=1&&day<=b[month])) return true; return false;} public void getternextday(){ int [] m=new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31}; if(isleapyear(year)==true)//闰年 m[2]=29; if(day==m[month]&&month!=12){ day=1; month++; System.out.println("Next day is:"+year+"-"+month+"-"+day); return; } if(day!=m[month]){ day++; System.out.println("Next day is:"+year+"-"+month+"-"+day); return; } if(day==m[month]&&month==12){ day=1; month=1; year++; System.out.println("Next day is:"+year+"-"+month+"-"+day); return; } else System.out.print("Date Format is Wrong"); }} public class Main { public static void main(String[] args) { Scanner put = new Scanner(System.in); int year=put.nextInt(); int month=put.nextInt(); int day=put.nextInt(); Date d = new Date(year,month,day); if(d.cpv()==false){ System.out.print("Date Format is Wrong");} else {d.getternextday();} }}
这是三次作业中的重量级选手,首先是定义了一个Date类,定义了类中的数据类型private型的年月日以及一个赋值为一年中每个月最大日期的整形数组。其实第二次作业也有一样求下一天的题目,但那道题我并没有用数组,这道题的题目上给出了要用的类中的数据类型,所以我用到了数组。也更加清晰了两种方法各自的特点和难易程度。首先先来说这道题的数组方法,定义好数据类型后先是判断输入的日期是否合法,在这里就能体现数组的优点了,先是判断输入的年份是否是闰年,如果是闰年直接把二月所对应的数组值赋成29,如果不是就还是原来的恶28,然后进行比较输入的日期是否合法,这里不仅要比较输入的年份信息和月份信息还要比较输入的day信息(因为不同月份的最大日期值可能会不一样)所以比较day用数组就显得很方便(
if(isleapyear(year)==true)
b[2]=29;
if((year>=1900&&year<=2000)&&(month>=1&&month<=12)&&(day>=1&&day<=b[month]))
)。然后就是判断是否是闰年(其实我的程序先判断了闰年,但先判断日期是否合法可能更好),判断闰年就用到if else语句还是很容易理解的。接下来就是计算下一天,这里的重点就是对于是否是闰年的二月最后一天的计算以及跨年计算,其余的并难,语法也比较简单。day就直接跟数组比较就行,每个计算下一天的语句下都要有个输出,直接在主方法中调用方法输出就行。这一部分多用if return语句,真的感觉它好方便,return就相当于else,如果if中的语句为真就进行if下的语句遇到return就不再向下运行。return也可以避免有else没有if的错误,相比之下它的程序也更加清晰。下面看一下我之前没有使用数组的方法,相比之下真的显得很麻烦。
import java.util.Scanner; public class Main { public static void main(String[] args){ Scanner put = new Scanner(System.in); int year=put.nextInt(); int month=put.nextInt(); int day=put.nextInt(); String a=""; if(checkInputValidity(year,month,day)){ isLeapYear(year); nextDate(year,month,day); } else{ System.out.print("Wrong Format"); }} //判断是否合法 public static boolean checkInputValidity(int year,int month,int day){ if(year<1820||year>2020||month<1||month>12||day<1||day>31){ return false; } else return true;} //判断闰年 public static boolean isLeapYear(int year){ if((year%4==0&&year%100!=0)||(year%400==0)){ return true; } else return false; } //求下一天 public static void nextDate(int year,int month,int day){ if(isLeapYear(year)){ if(month==2&&day==28){ day=29; System.out.println("Next date is:"+year+"-"+month+"-"+day); return;} if(month==2&&day==29){ day=1; month=3; System.out.println("Next date is:"+year+"-"+month+"-"+day); return; } if(day==31&&month==1||month==3||month==5||month==7||month==8||month==10){ day=1;
month=month+1; System.out.println("Next date is:"+year+"-"+month+"-"+day); return; } if(day==30&&month==4||month==6||month==9||month==11){ day=1; month=month+1; System.out.println("Next date is:"+year+"-"+month+"-"+day); return; }
if(day==31&&month==12){
year=year+1; month=1;
day=1;
System.out.println("Next date is:"+year+"-"+month+"-"+day);return;
}
else day=day+1; System.out.println("Next date is:"+year+"-"+month+"-"+day); } else{ if(month==2&&day==28){ day=1;month=3; System.out.println("Next date is:"+year+"-"+month+"-"+day); return; } if(day==30&&month==1||month==3||month==5||month==7||month==8||month==10||month==12){ day=31; System.out.println("Next date is:"+year+"-"+month+"-"+day); return; } if(day==30&&month==4||month==6||month==9||month==11){ day=1; month=month+1; System.out.println("Next date is:"+year+"-"+month+"-"+day); return; } else day=day+1; System.out.println("Next date is:"+year+"-"+month+"-"+day); } if(day==31&&month==12){ year=year+1; month=1; day=1; System.out.println("Next date is:"+year+"-"+month+"-"+day);return; }}}
这种方法占用内存很多。
4、第三次作业的7-4:日期类设计 (主要测试:求下n天,求前n天,求两个日期间的差)其代码如下
import java.util.Scanner; class DateUtil{ private int year; private int month; private int day; int [] max=new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31}; public DateUtil(DateUtil g){ this.year=g.getYear(); this.month=g.getMonth(); this.day=g.getDay(); int [] m=new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31}; } public DateUtil(int year,int month,int day){ this.year=year; this.month=month; this.day=day; } public int getYear(){ return year; } public int getMonth(){ return month; } public int getDay(){ return day; } public void setYear(int year){ this.year=year; } public void setMonth(int month){ this.month=month; } public void setDay(int day){ this.day=day; } public boolean check(){//判断是否合法 int [] b=new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31}; if(isLeapYear(year)==true) b[2]=29; if((year>=1820&&year<=2020)&&(month>=1&&month<=12)&&(day>=1&&day<=b[month])){ return true; } else return false; } public boolean isLeapYear(int year){//判断闰年 if((year%4==0&&year%100!=0)||(year%400==0)) return true; else return false; } public DateUtil getNextNDays(int n){//取得year-month-day的下n天日期 int [] m=new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31}; int i,b=month+1,d; if(isLeapYear(year)) m[2]=29; m[2]=28; if(day+n<=m[month]){ day=day+n; } else{ if(month==12){ month=0; year=year+1; if(isLeapYear(year)) m[2]=29; m[2]=28; } n=n+day-m[month]; b=month+1; while(n>m[b]){ n=n-m[b]; b=b+1; if(b==13){ b=1; year=year+1; if(isLeapYear(year)) m[2]=29; } } day=n;month=b;year=year; } return this;} public DateUtil getPreviousNDays(int n){//取得year-month-day的前n天日期 int [] m=new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31}; int a; a=Math.abs(day-n); if(isLeapYear(year)) m[2]=29; if(day-n>0){ day=day-n; } else{ while(a-m[month-1]>0){ a=a-m[month-1]; month=month-1; if(month==0){ month=13; year=year-1; } } day=a+3;month=month-1;year=year; } return this; } public boolean compareDates(DateUtil date){//比较日期大小 if(this.year>date.getYear()) return true; else if(this.year==date.getYear()&&this.month>date.getMonth()) return true; else if(this.year==date.getYear()&&this.month==date.getMonth()&&this.day>date.getDay()) return true; return false; } public boolean equalTwoDates(DateUtil date){//比较日期是否相等 if(this.year==date.getYear()&&this.month==date.getMonth()&&this.day==date.getDay()) return true; else return false; } public int getDaysofDates(DateUtil date){//日期间天数差 int [] m=new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31}; DateUtil g=new DateUtil(this); int a=date.getYear()-g.getYear();int i,b=0,e=0; int c=date.getMonth()-g.getMonth(),d=date.getDay()-g.getDay(); if(a!=0){ for(i=1;i<=a;i++){ a--; if(isLeapYear(g.getYear()+i)){ b=b+366; } b=b+365; } } else if(c!=0){ if(isLeapYear(date.getYear())){ m[2]=29;} for(i=1;i<c;i++){ e=e+m[g.getMonth()+1]; } } else e=0; return b+e+d;} public String showDate(){ return year+"-"+month+"-"+day; } }//类括号 public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); int year = 0; int month = 0; int day = 0; int choice = input.nextInt(); if (choice == 1) { // test getNextNDays method int m = 0; year = Integer.parseInt(input.next()); month = Integer.parseInt(input.next()); day = Integer.parseInt(input.next()); DateUtil date = new DateUtil(year, month, day); if (!date.check()) { System.out.println("Wrong Format"); System.exit(0); } m = input.nextInt(); if (m < 0) { System.out.println("Wrong Format"); System.exit(0); } System.out.print(date.getYear() + "-" + date.getMonth() + "-" + date.getDay() + " next " + m + " days is:"); System.out.println(date.getNextNDays(m).showDate()); } else if (choice == 2) { // test getPreviousNDays method int n = 0; year = Integer.parseInt(input.next()); month = Integer.parseInt(input.next()); day = Integer.parseInt(input.next()); DateUtil date = new DateUtil(year, month, day); if (!date.check()) { System.out.println("Wrong Format"); System.exit(0); } n = input.nextInt(); if (n < 0) { System.out.println("Wrong Format"); System.exit(0); } System.out.print( date.getYear() + "-" + date.getMonth() + "-" + date.getDay() + " previous " + n + " days is:"); System.out.println(date.getPreviousNDays(n).showDate()); } else if (choice == 3) { //test getDaysofDates method year = Integer.parseInt(input.next()); month = Integer.parseInt(input.next()); day = Integer.parseInt(input.next()); int anotherYear = Integer.parseInt(input.next()); int anotherMonth = Integer.parseInt(input.next()); int anotherDay = Integer.parseInt(input.next()); DateUtil fromDate = new DateUtil(year, month, day); DateUtil toDate = new DateUtil(anotherYear, anotherMonth, anotherDay); if (fromDate.check() && toDate.check()) { System.out.println("The days between " + fromDate.showDate() + " and " + toDate.showDate() + " are:" + fromDate.getDaysofDates(toDate)); } else { System.out.println("Wrong Format"); System.exit(0); } } else{ System.out.println("Wrong Format"); System.exit(0); } } }
这道题是在上面那道题的基础上完成的,但是四十四分的题我只拿到了十八分。主要是求下n天和前n天的逻辑捋不明白。
三、踩坑心得
在提交第三次作业的7-3题时,我相当确定自己的代码没问题,但总是有三个测试点过不了,这三个测试点都是关于二月底测试的,这个时候我一直认为是我的代码的逻辑出了问题就不停的对测试输入信息是否合法的方法进行修改,看每一个方法间的逻辑。即使这样我依然没有通过那三个测试点,后来是同学看出了我代码的问题,原来是我判断闰年那一步,return的布尔型值写反了,改好后通过了那三个测试点。我真的很无语,所以说改代码的时候不能只顾大头,更要注重细节。还有就是前两次作业的输出格式,提交代码过不去很大一部分原因是因为输出格式不符合要求,所以当代码通不过测试点的时候要多进行测试代码,看输出格式是否有误。
四、改进建议
第一次作业的7-7题循环嵌套过多,还可以使程序运行的更快缩短时间。写程序要注重精简和效率,不能只图通过测试点。
五、总结
通过本阶段三次题目集的总结,我清晰明白了这段时间我学到了什么,还有哪些部分是不足的,像对字符串的使用,对类的的使用也不熟悉。像老师说的,学习Java要形成系统而不能点学习,我的基础知识还需要更近一步的提升,代码要多打,做到熟能生巧。希望老师上课有时间的时候可以讲一下我们pta的作业,或者给份正确代码。这样能知道自己哪里不足。
浙公网安备 33010602011771号