22201233-徐东OOP第一阶段作业总结
OOP第一阶段作业总结
一、前言
经过几周的题目训练之后,我也算是初步窥探到了Java的世界,摆脱了门外汉的身份,但还是知之甚少,本文应当会有许多不足之处,还请读者见谅🫡。
在第一次作业里,前面几题简单考察了一下变量定义、赋值、运算、选择分支语句和基本的输入输出等基础知识点,熟悉了一下Java的编程环境之后,就开始考察字符串运算的相关知识点,整体难度不大。
第二次作业前面的题比较简单,考察不同类型之间的转换,除了有些题的输出格式要控制的比较精细之外,其他大多数题的代码格式都和上学期的c语言差不多,可以说是c风格的Java。最后两道是考察逻辑思维的题,且最后一题涉及到了方法的设计,难度适中。
第三次作业,重点考察了类的定义、设计和使用,题量少,但是难度上升了。一开始看到类的时候,还觉得跟c语言里面的结构体数组挺像,但在深入学习之后,发现两者还是差别挺大。
二、设计与分析
接下来就从三次作业里面挑几题来进行分析
第一次作业:
1.从一个字符串中移除包含在另一个字符串中的字符。
输入两行字符串,将第一行字符串中包括第二行字符串中的所有字母去除。输出去除后保留的字符串。
输入格式:
第一行输入一个字符串
第二行输入一个字符串
输出格式:
输出移除后的字符串
输入样例:
在这里给出一组输入。例如:
abcdefghijklmnopqrstuvwxyz
secret
输出样例:
在这里给出相应的输出。例如:
abdfghijklmnopquvwxyz
代码长度限制 16kb
时间限制 400ms
内存限制 64MB
我的源码如下:
import java.util.*;
public class Main {
public static void main(String[] args)
{
Scanner n = new Scanner(System.in);
int m,i = 0;
String a,b;
a = n.nextLine();
b = n.nextLine();
char[] j = a.toCharArray();
char[] k = b.toCharArray();
char[] l = new char[j.length];//定义新的字符数组来存放不相同的字符
for (char c1:j) {//定义字符c1来遍历字符串数组j
boolean flag = false;
for (char c2:k) {//定义字符c2来遍历字符串数组k
if (c1==c2) {
flag = true;//j,k中有相同字符则返回true
}
}
if(!flag){//遍历完都无相同字符,将当前c1存入l
l[i] = c1;
i++;
}
}
for(m=0;m<i;m++){
System.out.print(l[m]);
}
}
}
解释和心得:
本题难度较易,主要考察的是字符串、字符数组的相关知识。
首先就直接定义并输入a,b两个字符串,由于要对字符串进行操作,因此我使用了toCharArray函数将字符串的内容放进字符数组,考虑到最后会产生一条新的字符来输出,所以又定义了一个char数组来存放前两个字符串中不相同的字符。这里把该数组的长度定义为j数组的长度,是因为最后得到的数组长度不可能比原来的数组长。
既然要去除重复的,那反过来想,就是保留不重复的,那么就要将两个数组的内容挨个遍历一次,每次都进行比对。于是写一个双重嵌套循环,并且两个循环都定义一个字符来得到当前遍历的值,也就是

使用flag标志,来标记是否相同,相同为true,不相同为false。前面说到是保留不重复的,所以if(!flag),把当前的c1存入准备好的新字符数组。
(需要注意的是,这个if条件语句应当写到第二重循环的外面,在遍历完第二个字符数组的所有内容之后才进行存放操作)
第二次作业:
1.二进制数值提取
在一个字符串中提取出其中的二进制数值序列
输入格式:
一个由0、1构成的序列,以-1为结束符,非0、1字符视为正常输入,但忽略不计,未包含结束符的序列视为非法输入。例如:abc00aj014421-1
输出格式:
将输入的序列去掉非0、1字符以及结尾符的数据内容,
注:结束符-1之后的0\1字符忽略不计。
例如:00011。
输入样例1:
在这里给出一组输入。例如:
abc00aj014421-1
输出样例1:
在这里给出相应的输出。例如:
00011
输入样例2:
在这里给出一组输入。例如:
a0571-1k001y
输出样例2:
在这里给出一组输入。例如:
01
代码长度限制 16kb
时间限制 400ms
内存限制 64MB
我的源码如下:
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
int i;
String a = s.nextLine();
StringBuilder b = new StringBuilder();//可改变的字符序列
if (a.indexOf("-1")==-1){//未查找到-1,非法输入
System.out.print("Wrong Format");
} else {//合法输入
for (i = 0; i < a.length(); i++) {
char c = a.charAt(i);//提取字符串a的第i个字符
if (a.charAt(i) == '0' || a.charAt(i) == '1') {
b.append(c);
} else if (a.charAt(i) == '-' && a.charAt(i + 1) == '1') {
System.out.print(b);
break;
}
}
}
}
}
解释和心得
这道题比较简单,代码量少,我拉它出来展示是因为它涉及到了StringBuilder的相关知识。
读题可知,这道题是要把自己输入的字符串里面的'0'和'1'提取出来,并且字符串中-1之后的内容忽略不计。
那么我们就先输入一串字符串,然后仍然是会产生一串新的字符,所以定义一个字符数组。
此处我使用的是StringBuilder类来定义,它的特点是定义得到的字符数组是可变的,能非常便捷地进行增删改查等操作

首先写if条件语句判断输入是否合法,就是要查找输入的字符串里有没有-1,使用indexOf函数,没查找到的话会返回-1,那么就if该函数==-1,输出Wrong Format。

输入合法,则遍历字符串,定义一个字符来存储当前次遍历得到的字符,如果字符为0或1,则使用StringBuilder类里面的append函数,将该字符添加入准备好的字符数组。

查找到-1则输出字符数组,结束循环。
需要注意的是:在字符串中,'-'也算一个字符,那么-1其实是两个字符,所以写的是:

第三次作业:
1.定义日期类
定义一个类Date,包含三个私有属性年(year)、月(month)、日(day),均为整型数,其中:年份的合法取值范围为[1900,2000] ,月份合法取值范围为[1,12] ,日期合法取值范围为[1,31] 。
注意:不允许使用Java中和日期相关的类和方法,否则按0分处理。
要求:Date类结构如下图所示:

输入格式:
在一行内输入年月日的值,均为整型数,可以用一到多个空格或回车分隔。
输出格式:
当输入数据非法及输入日期不存在时,输出“Date Format is Wrong”;
当输入日期合法,输出下一天,格式如下:Next day is:年-月-日
输入样例1:
在这里给出一组输入。例如:
1912 12 25
输出样例1:
在这里给出相应的输出。例如:
Next day is:1912-12-26
输入样例2:
在这里给出一组输入。例如:
2001 2 30
输出样例2:
在这里给出相应的输出。例如:
Date Format is Wrong
代码长度限制 16kb
时间限制 400ms
内存限制 64MB
我的源码如下:
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
Date date = new Date();
int year,month,day;
year = s.nextInt();
month = s.nextInt();
day = s.nextInt();
date.date0();
date.date1(year,month,day);
date.setYear(year);
date.setMonth(month);
date.setDay(day);
date.getNextDate(year,month,day);
}
}
class Date{
private int year;
private int month;
private int day;
public void date0(){
}
public void date1(int year,int month,int day){
this.year = year;
this.month = month;
this.day = day;
}
public void setYear(int year){
this.year = year;
}
public int getYear(){
return year;
}
public void setMonth(int month){
this.month = month;
}
public int getMonth(){
return month;
}
public void setDay(int day){
this.day = day;
}
public int getDay(){
return day;
}
boolean isLeapYear(int year){
if(year%4==0&&year%100!=0||year%400==0){
return true;
}
else{
return false;
}
}
boolean checkInputValidity(){
int m[] = new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31};
if(isLeapYear(year)){
m[2]=29;
}
if (year>=1900&&year<=2000&&month>=1&&month<=12&&day>=1&&day<=m[month]){
return true;//day根据实际情况小于当前月份的天数
}else {
return false;
}
}
public void getNextDate(int year,int month,int day){
int m[] = new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31};
if(isLeapYear(year)){//闰年则2月份为29天
m[2]=29;
}
if (checkInputValidity()){
if (month<12){
if(day>=1&&day<m[month]){
day +=1;
}
else{
month +=1;
day = 1;
}
}
else {//12月份
if (day==m[month]){
year +=1;
month =1;
day =1;
}
else {
day +=1;
}
}
System.out.print("Next day is:"+year+"-"+month+"-"+day);
}
else {
System.out.print("Date Format is Wrong");
}
}
}
本题涉及到了类的定义与使用。代码看起来长度适中,但是大部分都是固定格式的getter和setter方法,实际需要设计的方法并不多。
首先读题,题目已经给出了Date类的结构,那么就先把结构里的函数的框架全部写出来,内容后续一个个补充。Main函数里面只用来键入年月日及调用方法,具体功能都在Date类里面实现。
写完了一系列的set和get函数后,第一个要设计的方法是判断闰年的isLeapYear函数,内容简单,定义为boolean类型的函数返回true或false。
然后就是判断输入数据是否合法的checkInputValidity函数。在给每个月份的天数赋值时,我定义了int数组来赋值。有的同学会用switch语句来写12个选择分支,一个case语句赋一次值,这样也可以,但是有点冗长,不如使用int数组来的方便。
这一函数里面使用isLeapYear函数判断闰年,是闰年则数组的第三个值赋为29,表示闰二月有29天,然后根据题目要求判断输入的年月日数据是否合法,也是定义为boolean函数返回true或false。

接下来就是本题的核心代码,getNextDate函数,求当前输入日期的下一天。在函数开头就要先使用isLeapYear函数判断当前是否为闰年,然后用if条件语句,分为输入合法和输入不合法两种情况。
合法输入情况下,细分为输入月份小于或等于12两种情况
小于十二月份情况下,再细分为跨月和不跨月两种情况:

等于12月份,细分为跨年和不跨年两种情况:

最后输出得到的下一天
非法输入情况则提示数据错误:

2.日期类设计
设计一个类DateUtil,该类有三个私有属性year、month、day(均为整型数),其中,year∈[1820,2020] ,month∈[1,12] ,day∈[1,31] , 除了创建该类的构造方法、属性的getter及setter方法外,需要编写如下方法:
public boolean checkInputValidity();//检测输入的年、月、日是否合法
public boolean isLeapYear(int year);//判断year是否为闰年
public DateUtil getNextNDays(int n);//取得year-month-day的下n天日期
public DateUtil getPreviousNDays(int n);//取得year-month-day的前n天日期
public boolean compareDates(DateUtil date);//比较当前日期与date的大小(先后)
public boolean equalTwoDates(DateUtil date);//判断两个日期是否相等
public int getDaysofDates(DateUtil date);//求当前日期与date之间相差的天数
public String showDate();//以“year-month-day”格式返回日期值
应用程序共测试三个功能:
1.求下n天
2.求前n天
3.求两个日期相差的天数
程序主方法如下:
import java.util.Scanner;
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.checkInputValidity()) {
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.checkInputValidity()) {
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.checkInputValidity() && toDate.checkInputValidity()) {
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);
}
}
}
输入格式:
有三种输入方式(以输入的第一个数字划分[1,3]):
1 year month day n //测试输入日期的下n天
2 year month day n //测试输入日期的前n天
3 year1 month1 day1 year2 month2 day2 //测试两个日期之间相差的天数
输出格式:
当输入有误时,输出格式如下:
Wrong Format
当第一个数字为1且输入均有效,输出格式如下:
year1-month1-day1 next n days is:year2-month2-day2
当第一个数字为2且输入均有效,输出格式如下:
year1-month1-day1 previous n days is:year2-month2-day2
当第一个数字为3且输入均有效,输出格式如下:
The days between year1-month1-day1 and year2-month2-day2 are:值
输入样例1:
在这里给出一组输入。例如:
3 2014 2 14 2020 6 14
输出样例1:
在这里给出相应的输出。例如:
The days between 2014-2-14 and 2020-6-14 are:2312
输入样例2:
在这里给出一组输入。例如:
2 1834 2 17 7821
输出样例2:
在这里给出相应的输出。例如:
1834-2-17 previous 7821 days is:1812-9-19
输入样例3:
在这里给出一组输入。例如:
1 1999 3 28 6543
输出样例3:
在这里给出相应的输出。例如:
1999-3-28 next 6543 days is:2017-2-24
输入样例4:
在这里给出一组输入。例如:
0 2000 5 12 30
输出样例4:
在这里给出相应的输出。例如:
Wrong Format
代码长度限制 12kb
时间限制 10000ms
内存限制 64MB
我的源码如下:
class DateUtil{
private int year;
private int month;
private int day;
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 void setYear(int year){
this.year = year;
}
public int getYear(){
return year;
}
public void setMonth(int month){
this.month = month;
}
public int getMonth(){
return month;
}
public void setDay(int day){
this.day = day;
}
public int getDay(){
return day;
}
public boolean isLeapYear(int year){
if(year%4==0&&year%100!=0||year%400==0){
return true;
}
else{
return false;
}
}
public boolean checkInputValidity(){
if(isLeapYear(year)){
m[2]=29;
}
if (year>=1820&&year<=2020&&month>=1&&month<=12&&day>=1&&day<=m[month]){
return true;//day根据实际情况小于当前月份的天数
}else {
return false;
}
}
public boolean compareDates(DateUtil date){
if (year<date.year){
return false;
} else if (year==date.year) {
if (month<date.month){
return false;
} else if (month==date.month) {
if (day<date.day){
return false;
}
}
}
return true;
}
public boolean equalTwoDates(DateUtil date){
if (year==date.year&&month==date.month&&day==date.day){
return true;
}
return false;
}
public DateUtil getNextNDays(int n){
int i;
for (i=0;i<n;i++){
if(isLeapYear(year)) {
m[2] = 29;
}else{
m[2] = 28;
}
if (isLeapYear(year)&&month!=12&&day==m[month]){//闰年跨月不跨年
month += 1;
day = 1;
} else if (isLeapYear(year)&&month==12&&day==m[month]) {//闰年跨年
year += 1;
month = 1;
day = 1;
} else if (isLeapYear(year)&&day!=m[month]) {//闰年不跨月
day ++;
}
else if (!isLeapYear(year)&&month!=12&&day==m[month]) {//非闰年跨月不跨年
month += 1;
day = 1;
}else if (!isLeapYear(year)&&month==12&&day==m[month]) {//非闰年跨年
year += 1;
month = 1;
day = 1;
} else if (!isLeapYear(year)&&day!=m[month]) {//非闰年不跨月
day ++;
}
}
return this;
}
public DateUtil getPreviousNDays(int n){
int i;
for (i=0;i<n;i++){
if(isLeapYear(year)) {
m[2] = 29;
}else {
m[2] = 28;
}
if (isLeapYear(year)&&month!=1&&day==1){//闰年退月不退年
month -= 1;
day = m[month];
} else if (isLeapYear(year)&&month==1&&day==1) {//闰年退年
year -= 1;
month = 12;
day = 31;
} else if (isLeapYear(year)&&day!=1) {//闰年不退月
day --;
} else if (!isLeapYear(year)&&month!=1&&day==1) {//非闰年退月不退年
month -= 1;
day = m[month];
}else if (!isLeapYear(year)&&month==1&&day==1) {//非闰年退年
year -= 1;
month = 12;
day = 31;
} else if (!isLeapYear(year)&&day!=1) {//非闰年不跨月
day --;
}
}
return this;
}
public int getDaysofDates(DateUtil date){
if(isLeapYear(year)) {
m[2] = 29;
}else{
m[2] = 28;
}
if(isLeapYear(date.year)) {
m[2] = 29;
}else{
m[2] = 28;
}
int i,sum=0,day1=0,day2=0;
if (equalTwoDates(date)){
return sum;
}else {
if (compareDates(date)){//前大于后
if (year==date.year){//同一年
day1 = m[date.month]-date.day;//小月剩余的天数
for (i=date.month+1;i<month;i++){
day2 += m[date.month];
}
day2 += day;//整月以及大月当月已走过天数之和
} else if (year>date.year) {//非同一年
for (i = date.year + 1; i < year; i++) {//计算相隔的完整年的天数
if (isLeapYear(i)) {
sum += 366;
} else {
sum += 365;
}
}
for (i = 1; i < date.month; i++) {
day1 += m[i];//已走过的整月的天数
}
for (i = 1; i < month; i++) {
day2 += m[i];//同上
}
if (isLeapYear(date.year)) {
day1 = 366 - day1 - date.day;//小年当年剩余的天数
} else {
day1 = 365 - day1 - date.day;
}
day2 += day;//大年当年已走过的天数
}
} else {//后大于前
if (year == date.year) {//同一年
day2 = m[month] - day;//小月剩余的天数
for (i = month + 1; i < date.month; i++) {
day1 += m[month];
}
day1 += date.day;//整月以及大月当月已走过天数之和
} else if (year < date.year) {//非同一年
for (i = year + 1; i < date.year; i++) {//计算相隔的完整年的天数
if (isLeapYear(i)) {
sum += 366;
} else {
sum += 365;
}
}
for (i = 1; i < date.month; i++) {
day1 += m[i];//已走过的整月的天数
}
for (i = 1; i < month; i++) {
day2 += m[i];//同上
}
if (isLeapYear(year)) {
day2 = 366 - day2 - day;//小年当年剩余的天数
} else {
day2 = 365 - day2 - day;
}
day1 += date.day;//大年当年已走过的天数
}
}
}
return sum+day1+day2;
}
public String showDate(){
return (this.year+"-"+this.month+"-"+this.day);
}
}
本题是在上一题的基础上,增加了更复杂的要求,求当前输入日期的下n天,前n天,和两个日期之间的总天数。
本题的类图结构如下:

首先照例把格式固定的getter和setter函数写好,再去设计方法。题目需求的isLeapYear函数、checkInputValidity函数都与上一题无异,完成以上的操作后,就设计相对比较简单的compareDates函数、equalTwoDates函数与showDate函数。
compareDates函数是用来比较当前日期和date日期的先后,那么就用多重if条件语句来判断两个日期之间的大小,返回true或false。
equalTwoDates函数比较当前日期和date日期是否相等,同样也是用if条件语句判断,返回true或false。
接下来,就开始设计本题的三大核心代码。
getNextNDays函数:
这个函数我的思想是用for循环,一天一天的从当前日期开始加下去,那么在for循环里面就要分好多种情况来设计代码。
首先考虑的是当前年份为闰年的情况,经过仔细思考,可细分为三种情况,跨年、跨月不跨年、不跨月。
跨年的情况就是当前月份为12月且当前天数为12月的最后一天,那么就年份加一,月份和天数重置为1。

跨月不跨年的情况则是月份不为12且当前天数为当前月的最后一天,月份加一,天数重置为1。

不跨月的情况是月份不为12且当前天数不为当前月的最后一天,天数+1。

其次是当前年份为非闰年的情况,这部分的代码和闰年情况差不多,就不过多赘述。
getPreviousNDays函数:
这个函数的主要思想也是for循环,一天一天从当前日期往前减去,同样分为闰年非闰年情况,进一步的细分情况也和getNextDays函数中的细分情况一样。
具体代码则是当前天数为当月第一天或当前为1月第一天,进行减天数,减月份或减年份等操作。
getDaysofDates函数:
这个函数,我的思路是:分别求出小的日期在当年还未走过的天数,大的日期在当年已走过的天数,以及两个年份之间整年的天数,三者相加就可得到两个日期相隔的天数。
首先分为两种大情况,两个日期相等和不相等,相等则直接返回sum=0;不相等则继续讨论。
接着使用compareDates函数判断两个日期之间的大小,然后再细分为两个日期为同一年与非同一年两种情况:
两个日期在同一年,则只用求较小的那个月的天数,加上大月已走过的天数和两者之间的整月天数,代码如下:

非同一年,就先求出两个日期之间相隔的整年天数sum,这里需要判断每一年是不是闰年,是则加366,否则加365
然后就求小年已走过的天数和大年已走过的天数day2,求出前者之后,用当前一整年的天数减去已走过的天数,就可得到小年剩余的天数day1。

最后return 三者之和,本题结束。
三、踩坑心得
首先是写判断三角形那道题的时候,如果判断等腰直角三角形的那里写b2+a2-c^2==0,会出现不能通过测试点的情况,后来经过老师提醒和同学的解释,了解到浮点数运算时会有误差,所以不能直接写等于0,而是使用近似值,写<0.1,才能通过测试点。

然后是写第三次作业第四题那里,最初写的时候,算出来的日期总会有几十天的误差,经过多种数据反复测试,发现了问题所在:
一开始求整年天数的时候,我的判断闰年是这样写的:

这样写就会出现二月份赋值为29天之后,遇到非闰年,二月份还是29天,因此最终结果会有几十天的误差,于是就修改成了这样:

判断为非闰年再把二月份赋值回28天,即可解决这个问题。
四、改进建议
改进代码方面,第三次作业第四题的代码还有非常大的改进空间,因为我的方法是从1开始一天一天的运算,所以效率非常慢,耗时多,如果输入数据为int类型的最大值2147483647,虽然最终结果也是对的,但耗时已经超过了题目的限制时间,因此这里还可以改进。


浙公网安备 33010602011771号