第一次JAVA实验收获

986198@第一次JAVA实验收获

JAVA的初体验

本次实验使用的是java语言编写,对我而言java是一门全新的语言,包,类,方法,对象……与c大相径庭,一切都是如此的陌生。通过第一次完成第一次实验的几个问题,对java也渐渐变得熟悉起来,体验到了java的便利(内置各种类和方法等)以及Ecipse IDE的优秀之处(错误提示、快捷修改、Junit等)。通过本次实验,让我们快速地熟悉了java语言,甚至学习了使用以及编写Test文件,对以后的课程实验以及工作都有很大帮助。

遇到的难题及解决方法

异常的产生以及处理

在c语言中,判断打开一个文件指针是否成功可以通过指针是否为空判断

FILE pointer=fopen("D:\\test.txt“,"r"));
if (pointer == NULL) 
    printf("failed to open the txt\n");

在java中无法这么做,失败会抛出异常。此外,Lab1 P1中有将String类型转化为int类型的操作,此操作在转化失败时也会抛出异常。为了使代码正常运行,我们必须对抛出的异常进行检测并作出正确的处理。于是我们引入try - catch 语句。

try {
    逻辑代码块1;
} catch(ExceptionType e) {
    处理代码块1;
}

具体示例如下

	    //判断行列数是否相等
	    int columns=0;//列数
	    String[] column_array=line_array[0].split("\t");
	    columns = column_array.length;
	    System.out.println("测试columns"+columns);//测试用,查看是否正确
	    if (columns != rows) {//行列数不相等则不是方阵,返回false。
	    	flag = false;
	    	return flag;
	    }
	    
	    //将数据拆分成二维数组并转化位int
	    for (int i=0; i < rows; i++)
	    {
	    	magic_str_array[i] = line_array[i].split("\t");//按制表符把数据单个拆开
	    	for(int j=0; j < rows; j++)
	    	{ 
	    		try {
	    		MagicSquare[i][j]=Integer.valueOf(magic_str_array[i][j]);//将string转化成int
	    		}catch(NumberFormatException e) {//失败则抛出异常NumberFormatException
	    			e.printStackTrace();
	    			flag = false;
	    			return flag;//抓取到NumberFormatException,则有非int元素,判断不是magic square,返回false
	    		}
	    	}
	    }
输出重定向问题

Lab1 P1中要求我们将生成的MagicSquare输入到文件中并保存。对于该要求我采取的实现方法是将输出流重定向到指定文件

PrintStream ps= new PrintStream("src/p1/txt/6.txt");
System.setOut(ps);
//实现你的具体操作
ps.close();

以上操作就可以实现文件输出的要求。但问题出现在文件输出完成以后。当我需要使用控制台输出时,我无论如何也无法打印到我的屏幕。
当我查找多方资料后发现这是因为我没有备份原本的输出流,所以当我关闭我原本的文件输出流后,我也无法回到原本的输出流。解决方法如下

        PrintStream ps= new PrintStream("src/p1/txt/6.txt");
		PrintStream out = System.out;//备份输出流
		System.setOut(ps);
		//方法的具体实现
		ps.close();//关闭文件输出
		System.setOut(out);//切换输出流
谁修改了我的数据?

在Lab1 P3中,我们需要使用广度优先搜索来判断两个人之间的距离。代码实现似乎没有问题

@Test
	public void getDistance( ) {
		FriendshipGraph graph = new FriendshipGraph();
		Person rachel = new Person("Rachel");
		Person ross = new Person("Ross");
		Person ben = new Person("Ben");
		Person kramer = new Person("Kramer");
		graph.addVertex(rachel);
		graph.addVertex(ross);
		graph.addVertex(ben);
		graph.addVertex(kramer);
		graph.addEdge(rachel, ross);
		graph.addEdge(ross, rachel);
		graph.addEdge(ross, ben);
		graph.addEdge(ben, ross);
		assertEquals(1,  graph.getDistance(rachel, ross));
		assertEquals(2, graph.getDistance(rachel, ben));
		assertEquals(0,  graph.getDistance(rachel, rachel));
		assertEquals(-1,  graph.getDistance(rachel, kramer));
	}

上述测试文件成功通过,当我们更进一步添加几行代码进行注释时

        graph.addEdge(rachel, kramer);
		graph.addEdge(kramer, rachel);
		assertEquals(3,  graph.getDistance(kramer, ben));
		Person hugh = new Person("hugh");
		graph.addVertex(hugh);
		assertEquals(-1,  graph.getDistance(hugh, ben));

发现后续的测试结果为1的和0的能正确通过,其他的都会返回-1.这是为什么呢?
经过长时间调试,发现问题在于源数据被改动了。一开始为了便于广度优先搜索的进行,我在Person这个类里添加了一个boolean 属性

boolean visit = false;//记录遍历情况

初始置为false,表示未被遍历,一旦被遍历则置为true,防止重复遍历
我们会发现问题出现在当函数运行一次后,被遍历过的点的visit属性都被置为true了。以至于再次进入函数就会出现错误结果。然而原代码中

Person pointer = bsf_queue.get(0);

每次循环前把队列顶端的点赋值给pointer防止源数据更改,那为什么我的数据仍然被改动了呢?

        for(int j = 0; j <pointer.friend.size(); j++) { 
				if(!pointer.friend.get(j).visit) {
				bsf_queue.add(pointer.friend.get(j));
				pointer.friend.get(j).visit = true;
				count++;
				}

仔细检查代码才发现,我们确实保证了pointer这个当前对象的源数据没有被更改,但是在后续代码中我们对pointer.friend这个ArrayList里所保存的Person进行了遍历并更改了他们的visit属性,原来是我们自己在不知不觉间修改了源数据引起错误!
既然找到问题所在那就要解决问题,已知代码运行错误的原因是源数据在运行时visit属性被更改,那只要在每次进入函数时重新置为false就能保证函数运行正确,那么这样改真的好吗?
答案显然是否定的。第一,这样会大大增加代码的时间复杂度,带来不必要的开销;第二,广度优先搜索函数的功能应当是搜索,不应当改变源数据的内容,贪图方便添加的visit本身就是不合理的,这个属性是没必要的,会增加空间复杂度。
所以认真思考以后,我决定优化代码,将Person的visit属性删去。改为在广度优先搜索中创建一个ArrayList visit来记录已遍历过的点,通过visit.contains函数判断是否遍历过该点,实现如下

		ArrayList<Person> bsf_queue = new ArrayList<Person>();
		bsf_queue.add(person_1);
		ArrayList<Person> visit = new ArrayList<Person>();
		visit .add(person_1);//已遍历的点录入,防止重复遍历
		ArrayList<Integer> counter = new ArrayList<Integer>(); 
		for(int i = 1; !bsf_queue.isEmpty(); i++) {
			Person pointer = bsf_queue.get(0);
			int count = 0;
			if (pointer.friend.contains(person_2)) 
				return path;
			for(int j = 0; j <pointer.friend.size(); j++) { 
				if(!visit.contains(pointer.friend.get(j))) {
				bsf_queue.add(pointer.friend.get(j));
				visit.add(pointer.friend.get(j));
				count++;
				}
			}
			counter.add(count);
			if(counter.contains(i)) {
				path++;
				i=0;
			}
			bsf_queue.remove(0);
		}
		return -1;

感想

本次的实验给了我很深刻的认识
1.java程序完成必须通过覆盖度足够的Test文件的测试,优秀的Test文件能让你代码中的问题暴露出来,否则即使是运行通过也可能存在后续问题(如上文中广度优先搜索隐藏的问题)。一个差的Test文件对我们是负帮助。
2.编写java时要有良好的代码风格,这样有助于我们编写良好的代码以及发现代码中的错误,尤其是以后工作中尤为重要!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值