开学已近一个月了,关于软件构造这门课程,我在做完lab1试验后终于对其有了初步的了解。本文结合lab1,陈列了我这三个星期以来试验时发生的一些错误。
入门软件构造首要的基础就是java语言了,包括我在内的很多学生在大一时都没接触过java,身为转专业生的我甚至连一般的C程序都写得磕磕绊绊(。在很多叙述中,java都被认为相交C语言更简单一些。要说刚开学那时的我肯定是不信这句话的,乍一看java,那么多class,那么多修饰符,那么多包,怎么可能比C简单嘛!但当自己了解类的相关操作后,自己才真正了解到java的魅力,可以说,很多软件代码编写,是C语言难以完成或需要付出巨大代价的,但通过java来编写,就会变得简易,安全,结构清晰。即横向C++,Python等众多面向对象语言,java也是毋庸置疑的首选软件编程语言。
就目前我个人的使用感受而言,java的优点如下:1.易上手,继承了大部分C的基础语法 2.结构清晰,更倾向于模块化 3.数据结构众多,随用随取 4.丰富的外部库 5.面向对象编程,极为完善的类 6.静态检查以及IDE的提示改错建议。
最后的第六点对我来说由为重要,在lab1的实验中,鬼知道eclipse帮我改了多少错误(,但即使是这样的情况,在lab1中我仍然遇到了不少困难,毕竟从C到java之间的转变并不能那么一蹴而就,而有些错误也是和C一脉相承的。下面我将叙述几点我在lab1中初次使用java犯的一些错误,很多错误看起来很弱智。。。但对于初学者来说常常不能理解,甚至很多这样的错误会花费掉自己数个小时的时间去发现改正。
下面展示代码,首先是main函数
public static void main(String[] args) throws IOException
{
try{
System.out.println("isLegalMagicSquare函数返回"+isLegalMagicSquare("src/P1/txt/1.txt"));
System.out.println("isLegalMagicSquare函数返回"+isLegalMagicSquare("src/P1/txt/2.txt"));
System.out.println("isLegalMagicSquare函数返回"+isLegalMagicSquare("src/P1/txt/3.txt"));
System.out.println("isLegalMagicSquare函数返回"+isLegalMagicSquare("src/P1/txt/4.txt"));
System.out.println("isLegalMagicSquare函数返回"+isLegalMagicSquare("src/P1/txt/5.txt"));
generateMagicSquare(9);
System.out.println("isLegalMagicSquare函数返回"+isLegalMagicSquare("src/P1/txt/6.txt"));
}
catch(IOException e){
}
}
这段代码是不是乍看上去没什么问题?事实上也的确没什么问题,但注意到打印的字符中出现了中文,eclipse默认的中文编码是GBK,如果代码中(包括注释!!)出现中文的话,那么虽然在eclipse中能正常运行,但提交到GitHub后用Travis-CI进行在线build时就会出错,Travis-CI只支持中文的UTF-8编码,因此如果事先不清楚这点上传之后再改正就会变得极为麻烦。所以在建立项目后第一个要做的是就是右击项目打开Properties对默认文本编码进行修改。
再看下一个错误
String[] str;
int n=0;
int i,j;
int sum1=0;
int sum2=0;
int sum3[]=new int[1000];
int sum4[]=new int[1000];
int B[][]=new int[1000][1000];
while((textLine=bf.readLine())!=null)
{
str[n]=textLine;
n++;
}
这个错误就很弱智了,或许在C中还会随便一点,在java中除了基本类型之外的变量使用前是一定要初始化的,而且基本上都是x=new …之类,另外值得一提的是java中空值是空值null是null,不能想当然的认为不初始化就是空值。
下一个错误
if(ax>=0 && ay>0)
{
angle=Math.atan((double)(ax)/ay);
}
if(ax>0 && ay<0)
{
angle=90-Math.atan((double)(ax)/ay);
}
if(ax<=0 && ay<0)
{
angle=180+Math.atan((double)(ax)/ay);
}
if(ax<0 && ay>0)
{
angle=270-Math.atan((double)(ax)/ay);
}
if(ay==0 && ax>0)
{
angle=90;
}
if(ay==0 && ax<0)
{
angle=270;
}
这是我计算方位角的一段代码,用反三角函数计算,乍看上去没什么问题,依靠这个方位角的之后的程序却怎么也通不过,整整花了我近两个小时我才意识到Math反三角函数算的是弧度制,要想转变为角度需要像这样angle=270-Math.atan((double)(ax)/ay)*180/Math.PI;所以吸取的教训就是以后用函数时一定要看函数的相关说明。
下一个错误和代码没什么关系,也是很弱智的一个问题,众所周知,eclipse在静态检查时会在代码前面打上红叉,代码中会出现红波浪线,当鼠标移到到红波浪线时eclipse会提出改错建议。但有时候会发现代码前有红叉代码上却没波浪线,我思考了半天后才发现红叉后面有一个加号,把加号点开,发现隐藏的代码中出现了红波浪线。。。还有一种难缠的问题就是不管根据提示怎么改红波浪线都不会消失,出现这种情况,大概率不是这行代码本身的问题,而是与之相关的其它代码发生的问题。
下个错误是关于Junit测试的问题
private Graph<String> A=emptyInstance();
public void testAdd() {
assertEquals(true, A.add("Apple"));
assertEquals(true, A.add("Orange"));
assertEquals(true, A.add("Peach"));
assertEquals(true, A.add("Banana"));
assertEquals(false,A.add("Banana"));
}
@Test
public void testSet() {
assertEquals(0, A.set("Apple","Orange",1));
assertEquals(1, A.set("Apple","Orange",2));
assertEquals(0, A.set("Orange","Apple",2));
assertEquals(0, A.set("Apple","Pear",1));
assertEquals(0, A.set("Peach","Banana",1));
assertEquals(1, A.set("Peach","Banana",0));
}
这其实是lab2中的代码,但对于任何Junit测试都是同理,一定要记住即使变量定义在所以test之前,如果不是静态变量,这个变量也无法被所有test共享,对于每个test,该变量都只是初始化的变量,不注意这点的话就会发生很多莫名其妙的错误。比如说在main函数中通过的assert在测试中却无法通过。
下一个错误,与lab1关系不确定(具体看个人实现的方式),但与lab2却密切相关。
for(Edge edge:edges)
{
if(edge.judge(source, target))
{
int lastweight=edge.getWeight();
edges.remove(edge);
return lastweight;
}
}
这也是一段看上去没什么问题的代码,但java会直接抛出异常。一定要牢记这种迭代方式无法一边遍历一边删除元素,一开始写代码时没注意到这点,很多段代码都用了这种结构,等到测试发现问题修改量巨大。
正确的代码要么用迭代器,要么就用java保留的C风格遍历方式
代码如下
for(int i=0;i<edges.size();i++)
{
if(edges.get(i).judge(source, target))
{
int lastweight=edges.get(i).getWeight();
edges.remove(i--);
return lastweight;
}
}
以上的几点错误就是我进行lab1和lab2前一点部分遇到的一些困难,当然还有很多小错,比如数组下标写错导致数组越界,死循环,变量指向null等,未一一列出。本文只是作为实验的一些主要错误记录,希望以后能吸取教训。