BLOG-1_JavaHomework_Summary


前言

虽说上学期为了助眠看了一点 Java 但是散而不精,看而不敲,所以这学期正式学 Java 并没有想象中那么容易...

javaC/C++区别的直观感受:最直观感受是运行速度, Java 的运行速度大约比C/C++ 要慢许多

前三次PTA题目集的小总结

总体来看,前两次的题目集偏基础一点,会使用简单的循环结构、条件判断、格式化输出和字符串操作便可轻松满分,题量难度都不算大,但是第三次的题目集便立马感受到了压力;

第三次的题目集不仅提高了对Java基础的要求,还要求从面向过程的思路转化到面向对象的思维上,在做题前看题便可以注意到,若不使用类和对象便会大大增加代码量且不易于维护,另外,第三次题目集还对输入格式的把控提出了苛刻的要求,还要求将数学知识运用到程序中(数学是硬伤啊),并时刻注意变量的存储类型和输出格式。最重要的是在动手写之前构思:如何分配对象的方法,如何联系对象与对象之间.....

设计与分析

题目集2_7-2

源码的生成报表

LFEt3t.png

代码类图

LFcxeI.png

分析 : 一眼丁真:纯纯的面向过程,代码简洁,充斥着if-else ,可读性不是很高

题目集3_7-1

源码的生成报表

LFmfzR.png

代码类图

LFeqvq.png

分析 : 相比之前,有了更多的方法,代码量也随之增加

题目集3_7-2

源码的生成报表

LFN9v8.png

代码类图

LFJiDJ.png

分析 : 使用了更多的类,并以对象进行数据计算代码清晰了很多

题目集3_7-3

源码的生成报表

LFN5ZQ.png

代码类图

LFNeCq.png

分析 : 使用了更多的类,并以对象进行数据计算代码清晰了很多

采坑心得

这波?这波是Java搞偷袭,是我大意了啊,没有闪。但没关系....\(%^&%\)!\(%\)'']$]!]@.~\·.!~@^[\//*';]/=/[/[\&/]]@=/]/!\-[/-'%/¥

1)数据存储与输出

浮点数保留位数问题

需求:若输出的数据小数点后超过6位,只保留小数点后6位,多余部分采用四舍五入规则进到最低位。小数点后若不足6位,按原始位数显示。

踩坑笔记

踩坑思路:printf会自动四舍五入,所以直接使用printf格式化输出

//假定要保留六位小数
public static void main(String[] args) {
  Scanner in = new Scanner(System.in);
  float a = in.nextFloat(), b = in.nextFloat();
  System.out.printf("a/b: %.6f a+b: %.6f", a / b, a + b);
}

发现问题:

a/b是确确实实按6位保留并四舍五入了,但是a+b本应输出5.0却也多了几个0

//输入
2 3
//输出
a/b: 0.666667 a+b: 5.000000

影响:PTA答案错误!

解决方法

问题分析:%f会一视同仁格式化所有浮点数,所以只要让其该根据数据位数来选择是否要格式化为6位即可

正确写法:

//假定要保留六位小数
public static void formatPrint(float n) {
  final int esp = (int)1e6;
  if(((n*esp)%10)!=0)            		//小数点后第六位数不为0
      System.out.printf("%.6f",n);    //格式化输出
  else
      System.out.print(n);
}

public static void main(String[] args) {
  Scanner in = new Scanner(System.in);
  float a = in.nextFloat(), b = in.nextFloat();
  System.out.print("a/b: ");
  formatPrint(a/b);
  System.out.print(" a+b: ");
  formatPrint(a+b);
}

问题解决:

//输入
2 3
//输出
a/b: 0.666667 a+b: 5.0

反思:若第6位恰好是0呢?是否可以用String.format()格式化输出?使用DecimalFormat 格式化?使用BigDecimal.setScale 格式化?

2)其他

忽略if( )中的判断顺序

需求:将字符串根据逗号,或者空格 来分割成若干部分字符串,并转化为实数

踩坑笔记

踩坑原因:将循环边界判断放在了charAt() 之后

public static void main(String[] args) {
  Scanner in = new Scanner(System.in);
  String str = in.nextLine();
  String[] nums = new String[10];
  int idx=0;
  for(int i=0,j=0;i<=str.length();i++){
      if(str.charAt(i)==',' || str.charAt(i)==' ' || i == str.length()){
          nums[idx++] = str.substring(j,i);
          j = ++i;
      }
  }
  System.out.println(nums[0] + " " + nums[1] + " " + nums[2] + " " + nums[3]);
}

发现问题:

数组越界?

//输入
12,23 34.1,45
//报错
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 13
	at java.base/java.lang.StringLatin1.charAt(StringLatin1.java:47)
	at java.base/java.lang.String.charAt(String.java:693)
	at Main.main(Main.java:24)

影响:程序运行异常终止

解决方法

问题分析:根据调试发现,if()括号中的判断遵循从左到右顺序,由于i == str.length() 放在最后,所以先判断str.charAt(i) 但是循环的条件是i<=str.length() 会使str.charAt(i)越界

正确写法:

public static void main(String[] args) {
  Scanner in = new Scanner(System.in);
  String str = in .nextLine();
  String[] nums = new String[10];
  int idx=0;
  for(int i=0,j=0;i<=str.length();i++){
      //先判断i是否为末位
      if(i==str.length() || str.charAt(i)==',' || str.charAt(i)==' '){	
          nums[idx++] = str.substring(j,i);
          j = ++i;
      }
  }
  System.out.println(nums[0] + " " + nums[1] + " " + nums[2] + " " + nums[3]);
}

问题解决:

//输入
12,23 34.1,45
//输出
12 23 34.1 45

反思:挺完美了吧(指按格式正常输入的话)

C/C++带来的习惯

需求:简化题目中多组数据的输入,

踩坑笔记

踩坑原因:不知 Java 的类型一定要完全匹配

//输入T为组数,每组输入一个数并将其输出
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int T = in.nextInt();
int t;
while(T--){
  t = in.nextInt();
  System.out.println(t);
}
}

影响:直接error

解决方法

问题分析:C/C++中,0false,非0的数便是true,但Java不这么认为,所以只要将int转化为boolean就好

正确写法:

//输入T为组数,每组输入一个数并将其输出
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int T = in.nextInt();
int t;
while(T-->0){
  t = in.nextInt();
  System.out.println(t);
}
}

问题解决:

3	//输入
111	//输入
111
222	//输入
222
333	//输入
333

//进程已结束,退出代码0

反思:若每组数据要输入String,怎么使其不读入组数后的回车?

改进建议

由于之前的小题不需要有整体的构思,导致第三次作业潦草起手,使得后期维护难上加难,所以动手之前应该考虑工程大小、各部分的责任范围、

引言:贯穿始终的一条原则就是“想好再做”。如果实在“想不好”,那就“做一点”,出了问题再“好好想”。在实践中,“做一点”的度很难把握,常常是开了闸就一泻千里。怎么判断这个度呢?我觉得说到底是你对程序的掌控感,要保持程序始终在你的控制之下。比如如果一个函数超过了50行,就是一个危险的信号。
————————————————原文链接:https://blog.csdn.net/RiderOnStorm/article/details/594775

代码复制问题

在第三次作业最开始的版本中,有很严重的代码复制情况:

//比如这段	作用是将字符串从第二位下标开始,根据空格和逗号分割为若干部分字符串,并将其转化为实数存于数组
public static void main(String[] args) {
	Scanner in = new Scanner(System.in);
	String str = in.nextLine();
	int choice = str.charAt(0) - '0';
	str = str.substring(2);	//跳过开始的两个字符
	String[][] nums = new String[4][];

	String[] location = str.split(" ");		//按空格分为若干个字符串数组
	for (int i = 0; i < 4; i++)
	    nums[i] = location[i].split(",");	//按逗号分为若干个字符串数组

	float[] fls = new float[8];
	fls[0] = Float.parseFloat(nums[0][0]);
	fls[1] = Float.parseFloat(nums[0][1]);
	fls[2] = Float.parseFloat(nums[1][0]);
	fls[3] = Float.parseFloat(nums[1][1]);
	fls[4] = Float.parseFloat(nums[2][0]);
	fls[5] = Float.parseFloat(nums[2][1]);
	fls[6] = Float.parseFloat(nums[3][0]);
	fls[7] = Float.parseFloat(nums[3][1]);

	for (float j : fls) System.out.println(j);	//仅供测试功能
}
////////////////////////////////////////////////////
//输入
1:1,2 3,4 5,6 7,8			
//输出
1.0
2.0
3.0
4.0
5.0
6.0
7.0
8.0
////////////////////////////////////////////////////

其他地方或许只是数量不同

public static void main(String[] args) {
	Scanner in = new Scanner(System.in);
	String str = in.nextLine();
	int choice = str.charAt(0) - '0';
	str = str.substring(2);	
	String[][] nums = new String[4][];

	String[] location = str.split(" ");		
	for (int i = 0; i < 3; i++)
	    nums[i] = location[i].split(",");	

	float[] fls = new float[6];
	fls[0] = Float.parseFloat(nums[0][0]);
	fls[1] = Float.parseFloat(nums[0][1]);
	fls[2] = Float.parseFloat(nums[1][0]);
	fls[3] = Float.parseFloat(nums[1][1]);
	fls[4] = Float.parseFloat(nums[2][0]);
	fls[5] = Float.parseFloat(nums[2][1]);

	for (float j : fls) System.out.println(j);
}
////////////////////////////////////////////////////
//输入
1:1,2 3,4 5,6			
//输出
1.0
2.0
3.0
4.0
5.0
6.0
////////////////////////////////////////////////////

还有好几段这样类似的代码,因为写的过程中为了缩短工期而写出的屎山

明显看到,可以对其进行优化,大大提升可维护性,并使代码更简洁

改进

public static void main(String[] args) {
	Scanner in = new Scanner(System.in);
	String str = in.nextLine();
	int choice = str.charAt(0) - '0';
	str = str.substring(2);
	String[][] nums = new String[4][];
	int len = 4;
	float[] fls = transform(len, str, nums);        //传入数据

	for (float j : fls) System.out.println(j);
}

public static float[] transform(int size, String str, String[][] num) { //将字符串处理为数组
	float[] res = new float[2 * size];
	String[] location = str.split(" ");
	for (int i = 0; i < size; i++)
	   num[i] = location[i].split(",");

	for (int i = 0, j = 0, k = 0; i < 2 * size; i++, k++, j += k / 2, k %= 2) {
	   res[i] = Float.parseFloat(num[j][k]);
}

return res;
}
////////////////////////////////////////////////////
//输入
1:1,2 3,4 5,6 7,8			
//输出
1.0
2.0
3.0
4.0
5.0
6.0
7.0
8.0
////////////////////////////////////////////////////

改进2.0

public static void main(String[] args) {
	Scanner in = new Scanner(System.in);
	String str = in.nextLine();
	int choice = str.charAt(0) - '0';
	str = str.substring(2);				
	int pointCnt = 4;
	float[] fls = transform(pointCnt, str);        //传入数据
	for (float j : fls) System.out.println(j);
}

public static float[] transform(int size, String str) {	//将字符串处理为数组
	String[][] nums = new String[2 * size][];
	float[] res = new float[2 * size];
	String[] location = str.split(" ");
	for (int i = 0; i < size; i++)
	   nums[i] = location[i].split(",");

	for (int i = 0, k = 0; i < 2 * size; i++, k++, k %= 2) {	//并不需要 j 
	   res[i] = Float.parseFloat(nums[i / 2][k]);
}

return res;
}
////////////////////////////////////////////////////
//输入
1:1,2 3,4 5,6 7,8			
//输出
1.0
2.0
3.0
4.0
5.0
6.0
7.0
8.0
////////////////////////////////////////////////////

除数为0不一定报错问题

之前遇到过int的0作为除数会报错的情况,便没有再深究

public static void main(String[] args) {
Scanner in = new Scanner(System.in);
a=in.nextInt();b=in.nextInt();
c=in.nextInt();d=in.nextInt();
System.out.println(getSlope());
}

public static int getSlope() {
return (a - b) / (c - d);
}

结果异常

//输入
2 2 3 3
//输出
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at Main.getSlope(Main.java:14)
	at Main.main(Main.java:10)

//进程已结束,退出代码1

但是如果是double类型,结果就变得有趣了起来

public static void main(String[] args) {
Scanner in = new Scanner(System.in);
a=in.nextInt();b=in.nextInt();
c=in.nextInt();d=in.nextInt();
System.out.println(getSlope());
}

public static double getSlope() {
return (a - b) / (c - d);
}

Not a Number

//输入
2 2 3 3 
//输出 	
NaN

//进程已结束,退出代码0

正无穷

//输入
3 2 2 2 
//输出 	
Infinity

//进程已结束,退出代码0

负无穷

//输入
2 3 3 3
//输出 	
-Infinity

//进程已结束,退出代码0

总结

通过该阶段的学习,学到了程序基本的整体构思思路、类与类的分工。但对于输入数据格式问题,仍存在考虑不全不周的问题,需要在今后的学习中着重注意。

对作业的感觉:挺有挑战性的,值得一做!

对今后学习方法的建议:不要只看不敲,看懂不等于学会,当然,若能用最精简的语言点通不懂的人,那便证明你真正学到点东西了。

相关