java总结性Blog-2
一、前言:
【PTA第四次题目集、慕课作业、实验题以及期中考试】
知识点:主要涉及到了类的设计;面向对象程序设计的三大特性:封装、继承、多态;正则表达式;泛型等等。
题量:PTA第四次题目集只有三题,题量不大,但是第二题的题目很长,写PTA的绝大部分时间都花在了第二题上;慕课作业和期中考试的题量均较小。
难度:PTA作业的第二题较难,要用到很多的计算几何的知识,其余题目都比较基础;慕课作业和期中考试的题目均比较基础,主要考察我们的java基础有没有打牢,基础教好的同学写起来还是没有难度的。
--------------------------------------------------------------------------------------------------------------------------------------------------------
二、设计与分析:
【PTA第四次题目】
第一题:
这一题主要是考察我们正则表达式的使用,要在一段文字中提取出数字。
关于如何提取数字,我刚开始的思路是按照汉字和标点符号以及空格等等把字符串分开,但是这样就会随之产生三个问题:
1.怎么去掉汉字?2.怎么去掉标点符号?3.怎么去掉空格?
在网上查阅正则表达式之后,我找到了一个可以去掉双字节的正则表达式,我们知道,汉字和绝大部分的中文标点是双字节的,但仍有部分标点是单字节的,所以行不通。
那么这时我换了一种思路,即“正难则反”:
去掉不是数字的字符,剩下的就是我们想要的数字了,这个正则表达式也很简单:
[^\\d+],也可以写成"\\D+",二者是等价的,都指的是:按非数字的字符为分隔符
核心代码:
String[] split = dataLine.split("[^\\d+]");
int sum = 0;
for(int i = 0;i < split.length;i++){
if(!split[i].equals(""))
{
int digit=Integer.parseInt(split[i]);
sum+=digit;
}
}
踩坑心得:
1、编译报错“ 解析时已到达文件结尾”
原因:大括号未对齐,少了一半
此类错误通常是括号没对齐。
2.运行时报错“NumberFormatException”
java.lang.NumberFormatException指数字格式异常。当试图将一个String转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常
原因:将String转换成int型时,String中有中文符号。
3、分组之后的字符串中出现空格
spilt是正则表达式中的一种,用来切分字段,如果切分的字段连续出现,就会生成空值。
因此,我们在将String转换成int时,要加上判断语句“if(!split[i].equals(""))”
4.运行时异常:IndexOutOfBoundsException
数组角标越界
原因:对ArrayList<>()还未有元素时使用set()方法
应把set()改成add()进行元素的添加
5.控制读取字符串的相关错误:
正则表达式正确当结果仍然是“Wrong Format”
原因:input.next(),只读了一个字符串,应该将代码改为是input.nextLine();
---------------------------------------------------------------------------------------------------------------------
第二题:
本题延续了前几次的PTA题目风格,仍然是与计算几何密切相关的题目,不过从三角形升级到了四边形,很多在三角形题目的代码也可以在本题中使用。
写这题的时候,我们应该把点类,线类,三角形类都先准备好,这些类的又可以为四边形类服务,面向对象编程的知识点考察的不多,主要是数学计算。
根据题意,我们首先进行基本格式校验,这里仍然用到的是正则表达式:
if(!data.matches("[1-4]:[+-]?\\d+,[+-]?\\d+(\\s[+-]?\\d+,[+-]?\\d+)*")){
System.out.println("Wrong Format");
}
这个正则表达式虽然看起来长,但它是由很多简单的正则组成的,[+-]? 表示的是是否出现+、-,如果有,只能出现一次,\\d+表示至少出现一次数字,\\s想必大家都不陌生,表示对空格、tab等等的校验,最后的“*”指出现0次或多次。
其次,我们根据用户输入的choice判断执行哪段代码
①:choice ==1:
首先对坐标个数进行校验:
if(xy.length != 9){
System.out.println("wrong number of points");
}
(在本题中我并没有定义自定义的Point类,这导致我之后的写起来代码比较麻烦,当然也可以不用自定义,API中有Point2D.Double类也是一个点类)
-------------------------------------------------------------------------------------------------------------------------------------------------------------
其次,调用在自定义类中“class CalculationOfQuadrilaterals”的方法:
if(!calculation.isFourCoincide(point)){
boolean isQuad = calculation.isQuadrilateral(point);
boolean[] judge = calculation.judgeQuadrilateral(point);
System.out.println(judge[0] + " " + judge[1]);
}else{
System.out.println("points coincide");
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------
这里的isFourCoincide()方法用来判断四个点是否重合:
public boolean isFourCoincide(Integer[] points){
return isTwoCoincide(points[0], points[1], points[2], points[3]) || isTwoCoincide(points[0], points[1], points[4], points[5])
|| isTwoCoincide(points[0], points[1], points[6], points[7]) || isTwoCoincide(points[2], points[3], points[4], points[5])
|| isTwoCoincide(points[2], points[3], points[6], points[7]) || isTwoCoincide(points[4], points[5], points[6], points[7]);
}
isQuadrilateral(Integer[] points)方法判断是否为四边形:
我使用的算法是:使用向量积,如果两个两条边共线的话是构不成四边形的,此时他们的向量积为0,使用此方法要注意向量的方向,不要弄反了。
public boolean isQuadrilateral(Integer[] points){//判断是否为四边形
double xAB, yAB, xBC, yBC, xCD, yCD, xDA, yDA;
xAB = points[2] - points[0];
yAB = points[3] - points[1];
xBC = points[4] - points[2];
yBC = points[5] - points[3];
xCD = points[6] - points[4];
yCD = points[7] - points[5];
xDA = points[6] - points[0];
yDA = points[7] - points[1];
return Math.abs(xAB * yBC - xBC * yAB) > 0.001 && Math.abs(xBC * yCD - xCD * yBC) > 0.001 && Math.abs(xCD * yDA - xDA * yCD) > 0.001
&& Math.abs(xAB * yDA - xDA * yAB) > 0.001;
}
---------------------------------------------------------------------------------------------------------------------------------------------------
踩坑心得:
关于浮点数的比较,我们应尽量少的使用“==”和“!=”,因为浮点数在实际储存中是由精度缺失的。
因此,为了得到我们想要的结果,应用“>=”或者"<="比较:
例如:1)比较一个浮点数是否为0:a <= 0.001;一般来说我们用0.001就可以了,如果是十分精细的计算,我们也可以用科学计数法,aE-6
2)判断两个浮点数是否相等:abs( fa - fb) < 0.001,要加上绝对值
-------------------------------------------------------------------------------------------------------------------------------------------------------------
接着,如果是四边形,我们继续使用“judgeQuadrilateral(Integer[] points)”方法判断四边形的类型:
我的思路:
①.判断是否为凸四边形:计算内角和是否为2PI
这个算法比较简单,只需要把四边形分成两个三角形,分别用余弦定理求出角度即可。注意,这里的角度都是以弧度制为单位的。
②.判断是否为平行四边形:
在①的基础上,我们只需要判断两组对边是否分别相等即可
③.判断是否为菱形:
在②的基础上,只用判断一组邻边是否相等即可,都很简单。
④.判断是否为矩形:
在②的基础上,只用判断一个角是否是直角即可。
⑤.判断是否为正方形:
在④的基础上,只需判断邻边是否相等即可。
在写这些代码前,我们要先把边和角都计算好。
核心代码:
public boolean[] judgeQuadrilateral(Integer[] points){
boolean[] judge = new boolean[5];
double AB = Math.sqrt((points[0] - points[2]) * (points[0] - points[2]) + (points[1] - points[3]) * (points[1] - points[3]));
double BC = Math.sqrt((points[2] - points[4]) * (points[2] - points[4]) + (points[3] - points[5]) * (points[3] - points[5]));
double CD = Math.sqrt((points[4] - points[6]) * (points[4] - points[6]) + (points[5] - points[7]) * (points[5] - points[7]));
double DA = Math.sqrt((points[6] - points[0]) * (points[6] - points[0]) + (points[7] - points[1]) * (points[7] - points[1]));
double BD = Math.sqrt((points[6] - points[2]) * (points[6] - points[2]) + (points[7] - points[3]) * (points[7] - points[3]));
double AC = Math.sqrt((points[0] - points[4]) * (points[0] - points[4]) + (points[1] - points[5]) * (points[1] - points[5]));
double ABD = AB * AB + DA * DA - BD * BD;
double ABC = AB * AB + BC * BC - AC * AC;
double CDB = CD * CD + BC * BC - BD * BD;
double CDA = CD * CD + DA * DA - AC * AC;
double A = Math.acos((ABD) / (2 * AB * DA));//"/"后面要加括号!
double B = Math.acos((ABC) / (2 * AB * BC));
double C = Math.acos((CDB) / (2 * BC * CD));
double D = Math.acos((CDA) / (2 * CD * DA));
judge[0] = Math.abs(A + B + C + D - 2 * Math.PI) < 0.0001;//判断是否为凸四边形(判断是否相等要加绝对值)
if (judge[0]) {
judge[1] = Math.abs(A - C) < 0.0001 && Math.abs(B - D) < 0.0001;//判断平行四边形
}
if (judge[1]) {
judge[2] = Math.abs(AB - BC) < 0.0001;//菱形
judge[3] = Math.abs(A - Math.PI / 2) < 0.0001;//矩形
}
if (judge[2]) {
judge[4] = Math.abs(BC - CD) < 0.0001;//正方形
}
return judge;
}
---------------------------------------------------------------------------------------------------------------------------------------------
踩坑心得:
计算除法时,我们一定要在“/”后加上括号防止出错。
-----------------------------------------------------------------------------------------------------------------------------------------------------
②:choice==2
这里我们仍是根据“ boolean[] judge = calculation.judgeQuadrilateral(point); ”语句判断四边形类型,方法还calculation.judgeQuadrilateral(point);
这段代码与“choice == 1”类似,我就不贴了。
----------------------------------------------------------------------------------------------------------------------------------------------------
③: choice == 3
这里计算的是面积和周长,我的思路:
计算周长的算法很简单,求边长再相加即可,用到了余弦定理
计算面积:将四边形分成两个三角形之后,使用向量积计算
原理:S = (1 / 2) * a * b * sinC, 正好是向量积绝对值的二分之一
注意:题目要求我们对超出三位小数点的保留三位小数。
核心代码:
public double areaTu(Integer[] points){//计算面积,利用向量积
double area1 = Math.abs((points[2] - points[0]) * (points[5] - points[1]) - (points[4] - points[0]) * (points[3] - points[1])) / 2.0;
double area2 = Math.abs((points[4] - points[0]) * (points[7] - points[1]) - (points[6] - points[0]) * (points[5] - points[1])) / 2.0;
return (int)(((area1 + area2) * 1E3) + 0.5) / 1E3;
}
if (judge[0]) {
double length = calculation.zhouchang(point);
System.out.println(judge[0] + " " + length + " " + calculation.areaTu(point));
} else{
System.out.println("false " + calculation.zhouchang(point) + " " + calculation.areaAo(point));
}
踩坑心得:
这里的心得很怪,因为我发现如果用1E3就可以得到三位小数,但是1e3就不行,我在网上也找了很多资料,目前尚未解决...
-----------------------------------------------------------------------------------------------------------------------------------------------------------
④.choice == 4
这里是判断直线与四边形的交点,并且如果有两个交点还要输出面积:
我的思路:
对于凸四边形,我认为还是很好解决的:
计算出小的三角形面积,在用四边形面积减去小三角型面积即可,比较大小之后就可输出结果了。
但是关于凹四边形的相关代码的实现,我没有想到什么方法,所以这部分代码没有写全。
Integer[] point4 = new Integer[8];
for(int i = 0; i < 8; i++){
point4[i] = point[i + 4];
}
calculation.isIntersect(point4, point[0], point[1], point[2], point[3]);
if((isA == 0 && isB == 0) || (isB == 0 && isC == 0) || (isC == 0 && isD == 0) || (isD == 0 && isA == 0)){
System.out.println("The line is coincide with one of the lines");
}else if(isA * isB * isC * isD < 0){//有两个交点
double area = areaTu(points);
}else if((isA == 0 && isC == 0) || (isB == 0 && isD == 0)){//对角线分割
double area1, area2;
if(isA == 0){//对角线AC分割
area1 = Math.abs((points[2] - points[0]) * (points[5] - points[1]) - (points[4] - points[0]) * (points[3] - points[1])) / 2.0;
area2 = Math.abs((points[4] - points[0]) * (points[7] - points[1]) - (points[6] - points[0]) * (points[5] - points[1])) / 2.0;
}else{
area1 = Math.abs((points[2] - points[0]) * (points[7] - points[1]) - (points[6] - points[0]) * (points[3] - points[1])) / 2.0;
area2 = Math.abs((points[2] - points[4]) * (points[7] - points[5]) - (points[6] - points[4]) * (points[3] - points[5])) / 2.0;
}
if(area1 > area2){
System.out.println("2 " + area1 + " " + area2);
}else{
System.out.println("2 " + area2 + " " + area1);
}
}
【改进意见】
1.要有自定义的Point类,Line类,triangle类,四边形类
2.自定义方法之间要独立,即不受约束的调用,如果调用A方法时要先调用B方法,这种代码就比较差了。
----------------------------------------------------------------------------------------------------------------------------------------------------------
第三题:
这一题很像java编程的经典例题,实现起来不难,但是有考到了很多知识点:
首先,我们自定义一个BankBusiness类,在类中定义属性和方法:
public static String bankName = "中国银行";
private String name;//账户名
private String password;//密码
private double balance;//余额
如果类中有带参构造器时,我们最好也提供一个空参构造器,这样可以避免一些潜在的麻烦:
举个例子:如果定义一个类继承于BankBusiness类,如果你没有显示地使用super(...),那么子类会自动调用父类的空参构造器,所以这个时候如果父类没有空参构造器的话就会报错。
----------------------------------------------------------------------------------------------------
一个小小的踩坑心得(忘记是什么时候踩的,但既然说到了我就和大家分享吧):
子类不会自动调用父类的带参构造器。
比如:Student(String name){
}
这里继承于People,并且people类中有形参为String name的构造器,但是这样写十错误的,我们必须以super(name)来调用;
------------------------------------------------------------------------------------------------
核心代码:
public BankBusiness(){
}
public BankBusiness(String name, String password){//开户
this.name = name;
this.password = password;
this.balance = 0;
}
接下来都是一些很简单的自定义方法,在此我也不做过多赘述,因为代码都简单,我就不贴了。
-------------------------------------------------------------------------------------------------
【慕课作业】
这次的作业考察了子父类的关系,即继承性以及方法的重写
代码的实现很简单,即添加一个MP3子类继承于Item类,再加上特有的属性:private String singer,提供带参构造器以及重写print()方法即可
核心代码:
public class MP3 extends Item{
private String singer;
public MP3(String title, String singer, int playingTime, String comment){
super(title, playingTime, false, comment);
this.singer = singer;
}
public void print(){
System.out.println("MP3: " + title + " ,singer: " + singer + " ,playingTime: " + playingTime + " ,comment: " + comment);
}
}
---------------------------------------------------------------------------------------------------------------------------
【期中考试】
期中考试的代码虽然看起来长,但实际上难度不大,自定义的方法里的代码都比较简单。
由于第三题是结合了前两题的代码,在此我分析第三题即可:
首先我们会用到ArrayList<E>,这个和Arrays最大第区别在于他可以动态构建一个数组,也就是说,我们在使用是不用指定元素个数:
例如:ArrayList<Element> elementList = null;
注意:这里的E指的是引用对象,代表这个list里面都是E型的对象,我们是不可以放基本数据类型的。
---------------------------------------------------------------------------------------------------
其次,我们还使用了抽象类:
abstract class Element{
public abstract void display();
}
首先我们要知道的是,抽象类中除了抽象方法,也可以有非抽象类的方法;但是抽象方法一定要在抽象类中。
举个不太恰当的比例,小王一定是老王生的,但是老王除了小王也可以有别的孩子,这里的小王相当于抽象方法,老王相当于抽象类。
并且抽象方法只有声明,继承于该抽象类的子类的只有重写了抽象方法才可以实例化,否则也是一个抽象类。
------------------------------------------------------------------------------------------------------------------
自定义类中大多均为get和set方法,以及一些show()方法打印属性,难度不高。
踩坑心得:
在增删改查的删中,我们要注意元素全部删除完的情况,即在删之前先判断这个列表是否为空,如果是,就退出程序;否则继续执行删除动作:
核心代码:
public void remove(int index){
if(elements.isEmpty()){
System.exit(0);
}
if(index >= 0 && index <= elements.size() - 1) {
elements.remove(index);
}
}
注:这里的exit(0)指的是正常退出程序
--------------------------------------------------------------------------------------------------------------
【实验题】
这几次的实验题都是对农夫过河游戏代码进行优化并增强游戏功能,是一个循序渐进的过程。
首先考察了我们继承与多态,例如在设计角色类(狼、羊、菜等等)时,我们会发现这些类之间有很多相同的属性和方法,比如isAlive,hasCross,
showStaus()方法,crossRiver()方法等等,那么这时我们可以考虑设计角色类的父类,在父类中定义共有的属性和方法:
public class Characters{
private boolean isAlive = true;
private boolean hasCross;
public void crossRiver(){
this.hasCross = true;
}
public boolean getIsAlive(){
return isAlive;
}
public void setIsAlive(boolean alive){
isAlive = alive;
}
public boolean getHasCross(){
return hasCross;
}
public void setHasCross(boolean hasCross){
this.hasCross = hasCross;
}
}
可以看出父类其实是比较简单的。
在子类继承父类中,我们只需在子类中定义特有的属性和方法完善子类即可,例如showStatus():
public void showStatus(){
if(this.name == null){
System.out.println("Sheep is alive: " + this.getIsAlive() + "\tSheep has Cross: " + this.getHasCross());
}else{
System.out.println("Sheep " + this.name + " is alive: " + this.getIsAlive() + "\tSheep " + this.name + " has Cross: " + this.getHasCross());
}
}
再比如eatSheep()方法:
该方法的定义不难,我们只要将Sheep类型的引用对象作为形参,并通过判断语句判断是否可以吃到即可,若可以吃到,就将该Sheep型的对象的isAlive设置成false;关于判断语句,我的思路是只要狼和羊均未过河,狼就可以吃羊,否则吃不到。
public void eatSheep(Sheep sheep){
if(!sheep.getHasCross() && !this.getHasCross()) {
sheep.setIsAlive(false);
}
}
根据这些例子举一反三,我们也可以设计好其他的角色类,那么根据实验要求,我们还要设计船类帮助农夫过河。
根据角色类中的hasCross属性可知,船类方法一定是可以改变hasCross的值,那么我们可以定义crossRiver()方法,并将角色类的实例作为形参,若调用了船类的过河方法,该角色类的hasCross属性的值变为true。
但是,随着角色类的增多以及每次过河的人数增多,我们要写的过河方法也越来越多,即使用很多次的方法重载,这使代码看起来很长。
由此,java的多态性的优点就体现出来了-----有了Characters作为父类,我们只需要将过河方法的形参声明为Characters类的对象即可:
public void crossRiver(Characters character){
character.crossRiver();
}
正确使用多态性,可以让我们的代码看起来更加简洁,并且减少我们的工作量。
--------------------------------------------------------------------------------------------------------------------------------------
三、改进意见:
自定义方法之间要独立调用,即没有限制和约束调用,如果调用A方法时要先调用B方法,这种代码就比较差了。比如我在PTA第四题里就有很多复合调用,这种写法显然不好,代码的复用性也不高。
----------------------------------------------------------------------------------------------------------------------------------------
四、总结:
对于本阶段(7-10周)的学习,我学到了:
1.继承和多态的使用
2.抽象类和抽象方法的使用
3.泛型ArrayList<E>的使用
我认为在我提高代码复用性方面要进一步学习与研究,避免方法内部嵌套调用别的方法,希望自己的代码更加灵活。(革命尚未成功,同志仍需努力)
改进建议及意见:
希望老师们可以在每一次的PTA大作业结束之后分享题目的源码。通过自己的代码和老师代码的对比,我们可以更加直观地看出自己代码的不足,也可以学习老师的编程风格。
------------------------------------------------------------------------------------------------------
本次的博客到这里就全部结束了,感谢各位的阅读~~~🧡💛💚💙💜