201521123091 《Java程序设计》第10周学习总结

Java 第十周总结

第十周的作业。

目录
1.本章学习总结
2.Java Q&A
3.码云上代码提交记录及PTA实验总结


1.本章学习总结

1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容。
  异常上次整理的差不多了,这边就记录一些多线程基本的知识。
  多线程就是可以运行多个独立的任务,任务可能会在共享资源上彼此干涉,所以需要有synchronized来防止这种冲突。

  1. 线程可以驱动任务,我们通常使用操作Runnable接口来定义任务。
  2. yield()方法是该线程的一种建议,表示现在自己没有在做很紧急的事情,所以可以让其他线程来执行。
  3. 也可以用线程Thread来驱动任务,Thread的构造器需要一个Runnable的对象。
  4. 执行器(Executor)可以用来管理Thread对象。
  5. 如果希望任务完成产生返回值,则可以使用Callable而不是Runnable。
  6. 对于线程可以使用休眠、让步、加入等操作。
  7. 线程具有优先级,也可以将线程设置为后台线程。后台线程在程序终止的时候,会被全部杀死。
  8. 原子操作不能被线程调度机制打断,一旦操作开始,就一定会执行完毕。
  9. 线程的协作可以通过wait()和notifyAll()(或者notify())来完成
  10. 线程具有四种状态:
    - 新建(new):分配到了必须的系统资源,执行了初始化
    - 就绪(Runnable):可以运行,也可以不运行,就看调度器给不给机会了
    - 阻塞(Blocked):线程能够运行,但有东西组织它的运行
    - 死亡(Dead):彻底GG了,不能再调度了

2.Java Q&A

1. 常用异常

1.1 截图你的提交结果(出现学号)


1.2 finally中捕获异常需要注意什么?

System.out.println("resource open success");
}catch (Exception e) {
	System.out.println(e);
}finally{
	try {
		resource.close();
		System.out.println("resource release success");
	} catch (Exception e) {
		System.out.println(e);
	}
}
  • 只有执行过try语句块,finally语句块才会执行。
  • return和finally:因为finally(在try执行了)总是会被执行,所以无论在try块的哪个地方返回,finally块都会被执行到,而且在返回之间执行。
  • 不恰当的使用finally会导致异常丢失,比如在finally块当中抛出新的异常,将会覆盖之前的异常,或者在finally块中return,则不会察觉到异常曾经被抛出。
public class Main {
	public static void main(String[] args) throws InterruptedException {
    	try {
			try {
				throw new NumberFormatException();
			} finally {
				// TODO: handle finally clause
				throw new IOException();
			}
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println(e);
		}
    }
}

  只会输出IOException,而NumberFormatException即使有被抛出也不知道。


2.用异常改进ArrayIntegerStack

2.1 截图你的提交结果(出现学号)


2.2 实验总结

  当栈满的时候压栈、栈空的时候弹栈或者取栈顶会抛出相应异常。其他没了。
  所以用上ArrayList类就是一个很好的选择,因为不会有栈满的情况,而且栈空的时候本身remove或者get就会抛出数组越界的异常。


3.自定义异常

3.1 截图你的提交结果(出现学号)


3.2 自定义异常有哪几个关键点?

  自己自定义异常类,必须从已有的异常类继承,而且最好是选择意思相近的异常类进行继承。建立新的异常类型的最简单的方法就是让编译器产生默认的构造器,编译器可以自动调用父类的默认构造器,而且这就足够了,对于异常来说,比较重要的是类名,应该让人一看到类名,就知道出现了什么样的异常。我们可以选择将捕获异常的信息发送到标准输出流,更好的选择是发送到标准错误流,这样更容易引起用户的注意。因为标准输出流可能有的时候会被重定向到其他地方去,通过这样的函数:static void setOut(PrintStream out)。当然标准输出流也可以被重定向:static void setErr(PrintStream err)。而且把出错信息和输出信息分开也是比较好的选择。


4.读取文件并组装对象

try {
	in = new Scanner(new File("身份证号.txt"));

	while (in.hasNextLine()) {
		String line = in.nextLine();// 读出myfile.txt的下一行

		@SuppressWarnings("resource")
		Scanner lineScanner = new Scanner(line);// 为每一行建立一个扫描器
		// System.out.println("lineScanner="+lineScanner);
		lineScanner.useDelimiter(" ");// 使用空格作为分隔符
		// System.out.println(line);
		try {
			String a1 = lineScanner.next();// 姓名
			String a2 = lineScanner.next();// 身份证号
			String a3 = lineScanner.next();// 性别
			String a4 = lineScanner.next();// 年龄
			String a5 = lineScanner.next();// 地址
			while (lineScanner.hasNext()) {// 谨防地址只有一段
				String tmp = lineScanner.next();
				if (!a5.contains(tmp)) {
					a5 += tmp;
				}
			}
			System.out.println("a1:" + a1 + " a2:" + a2 + " a3:" + a3 + " a4:" + a4 + " a5:" + a5);
			try {
				if (a1.length() == 0 || a1.length() > 10) {
					throw new Exception("a1.length = " + a1.length());
				}
				if (a2.length() == 0) {
					throw new Exception("a2.length = " + a2.length());
				}
				if (!a3.equals("男") && !a3.equals("女")) {
					throw new Exception("性别格式错误");
				}
				arrayList.add(new User(a1, a2, (a3 == "男" ? Gender.男 : Gender.女), Integer.parseInt(a4), a5));
			} catch (Exception e) {
				System.err.println(lineCnt + " " + e);
				continue;
			}
		} catch (Exception e) {
			// TODO: handle exception
			continue;
		}
		lineCnt++;
	}
} catch (FileNotFoundException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
} finally {
	if (in != null) {
		in.close();
	}
}

Collections.sort(arrayList, (User o1, User o2) -> {
	if (o1.getAge() < o2.getAge()) {
		return -1;
	} else if (o1.getAge() > o2.getAge()) {
		return 1;
	} else {
		return 0;
	}
});

  主要的问题就是有,信息的缺失,那么首先读入的字符串不能为空,其次读名字的时候长度不能过长,如果长度超过10,肯定是不存在这样的名字的,还有性别只能是男和女,不能出现其他字符,在看了一遍数据集之后就只找出了这几个错,由于错误的形式是可以非常多样的,所以这边不可能针对所有的错误都找出正确的解决方案。只能之后出现新的问题的时候在修改代码了。这边排序也是使用lamda表达式,代码显得较为简洁。


5.学会使用Eclipse进行调试

观看相关调试视频

5.1 简述使用Eclipse进行调试需要几步?

  1. 设置断点
  2. 开启debug模式
  3. 运行程序
  4. 查看状态值
  5. 执行完毕或遇到问题

5.2 调试时F5, F6, F7快键键各有什么不同?什么情况该使用哪个快捷键?

F5:单步进入所执行的方法中(问题可能出现在方法中)
F6:单步执行并跳过(除了上下两种情况)
F7:单步执行并返回(确定问题没有出现在方法中,然而程序在方法内部运行,跳出去)


5.3 除了Eclipse的调试器,你还使用什么方法调试程序?

  一般都只使用输出来调试Java程序。


5.4 使用Eclipse进行调试中的选做

public Message(String mes2, String username) {
		this.mes = mes2;
}

public Message(String mes2, User user) {
	this.mes = mes2;
	this.user = user;

}

  Message类中需要知道user的详细情况(至少需要名字),原来的代码中虽然username是一个参数,但是实际上没有传进去,修改之后将user对象传进去,就可以正确地调用getName()方法了。

try {
	if (mes.length() > Message.limitwords) {
		throw new Exception();
	}
	meslist.add(new Message(mes, user));
} catch (NullPointerException e) {
	System.out.println("尚未登录");
} catch (Exception e) {
	// TODO Auto-generated catch block
	System.out.println("消息长度不得超过" + Message.limitwords +  "字");
}

  针对本来可能会没有登录的时候就添加消息的问题,所以现在增加在没有登录的时候抛出 空指针异常进行提示。还有就是利用消息长度不得大于140字的限制,当消息过长的时候,也抛出异常并给出提示。而且Exception应该放在最后catch。其实做的更好一点这边可以自定义一个异常继承自Exception,这样可读性会好一点。

public void searchMessage() {
	if (!meslist.isEmpty()) {
		for (Message e : meslist) {
			System.out.println(e);
		}
	} else {
		System.out.println("无留言");
	}

}

可能会出现消息列表为空,所以这边需要判断一下。


6.题集多线程

6.1 程序填空3-1、3-2。(截图提交结果,出现你的学号)


6.2 函数4-1(Thread)、4-2(Runnable)(截图提交结果,出现你的学号)

  4-1

  4-2


6.3 函数4-3(Runnable与匿名内部类)(截图提交结果,出现你的学号),并使用Labmda表达式改写。

使用lamda表达式改写:

Thread t1 = new Thread(
        () -> {
        	System.out.println(mainThreadName);
			System.out.println(Thread.currentThread().getName());
			System.out.println(Arrays.toString(Thread.class.getInterfaces()));
        }
);

6.4 实验总结

  1. 3-1 通过setDaemon()方法将t1设置为后台线程,这种线程在程序终止的时候会被统统干掉,但是如果是非后台线程,只要运行,那么程序就不会终止。所以我们这边当main()方法完成的时候,就没什么能够阻止程序终止的了。
  2. 3-2 主线程在调用t1线程的join()方法,则被挂起,直到目标线程t1结束才恢复(即t1.isAlive()为假)
  3. 4-1 在构造该线程的时候,将要循环的次数n传入。然后重写run()方法,循环输出0 - n - 1,结束之后输出该线程的名称,并且调用isAlive()来表示线程还活着。然后就死了。
  4. 4-2 当MonitorTask运行的时候,一直在检查送来的单词,如果包含"alien",就输出相应信息。然后要重新将word置为null。否则因为while条件始终满足,程序将陷入死循环。然后通过Thread的静态方法yield()切换到其他线程(这边是主线程)的使用,来获得下一个输入。
  5. 4-3 就是匿名内部类的使用,没什么好多说的。

7.源代码阅读:多线程程序BounceThread

7.1 哪个类是支持多线程的类,它实现了什么接口。这个类做了些什么?

  BallRunnable,实现了Runnable接口。这个类就是调用小球移动的函数,移动之后,对界面进行重画,并睡眠一段时间。


7.2 Ball.java这个程序只做了两件事,这两件事分别是什么?

  实现小球的移动,获取小球的坐标和大小。


7.3 BallComponent也只做了两件事,这两件事分别是什么?

  加小球,和画出小球。


7.4 BounceThread中,什么时候启动了新线程?

  点击添加按钮的时候调用addBall()方法,先加入一个小球,然后启动新线程。


7.5 这个程序是如何实现?一个大致的执行流程是什么?

  先把图形界面的框架搭出来,加入显示小球运动的组件,点击添加按钮就会有小球出现在界面中并开始移动,每点击一次,就会多出来一个移动的小球。最后当小球移动一定的步数之后,就停止。


8.购物车系统中的多线程

8.1 购物车系统中可能存在哪些多线程问题?

每个顾客都可以是一个线程
类比生产者和消费者的例子,商品的数量不可能是无限的,所以会出现有些顾客抢先买到东西,后面的顾客就没法购买的情况。


9.单元测试JUint4

  测试自己编写的冒泡排序和系统类库的Arrays.sort()的性能

public static void bubbleSort(int a[]) {
	int temp = 0;
    for (int i = a.length - 1; i > 0; --i)
    {
        for (int j = 0; j < i; ++j)
        {
            if (a[j + 1] < a[j])
            {
                temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
            }
        }
    }
}

public static void quickSort(int a[]) {
	Arrays.sort(a);
}

JUnit测试

public class SortTest {
	
	int a[][];
	
	@Test
	public void testQuickSort() {
		Sort.quickSort(a[1]);
	}

	@Test(timeout = 6000)
	public void testBubbleSort() {
		Sort.bubbleSort(a[0]);
	}



	@Before
	public void testMakeRandomArray() {
		a = Sort.makeRandomArray(100000, 100000000);
	}

}

  开1000W的数组,冒泡跑到宇宙爆炸都跑不出来(当然我只是等了半个小时),而且timeout的测试必须是得等到跑完之后才会显示失败,所以基本上是等不出结果,所以这边将数组的长度改为10w。
  


10.JavaFX入门

如果未完成作业1、2的请先完成。如果已完成的请完成第五部分:将数据用 XML 格式存储或第六部分:统计图(漂亮的图表)。注:内有代码,可在其上进行适当的改造。建议按照里面的教程,从头到尾自己搭建。

  打开软件,对数据进行随机增减,如图所示:

  打开现有的数据文件:

  成功读入数据:

  对生日进行统计并画出图表:


3.码云上代码提交记录及PTA实验总结

题目集:异常、多线程(3-1, 3-2, 4-1, 4-2, 4-3)

3.1 码云代码提交记录

  在码云的项目中,依次选择“统计-Commits历史-设置时间段”, 然后搜索并截图
  
  


4. 课外阅读

  1. 当写一个可能被其他线程读取的变量或者读取一个已经被其他线程写过的变量时,应该使用同步。否则,就无法保证一个线程的修改可以被另一个线程得知。
  2. volatile具有可视性,一个域被声明为volatile,只要对这个域进行写操作,那么其他的读操作都能够看到这个修改。
  3. 可以使用interrupt来中断线程,会使wait或者sleep方法马上抛出被中断的异常,被中断的话,线程还是活着的,isAlive不为false
  4. 用synchronize相对于volatile是更好的选择,对于自增这种操作,volatile并不能保证可以正确执行。volatile只能确保同步它所修饰的变量值,synchronized可以确保该代码块的所有变量do
  5. notifyAll()比notify()更安全,使用notify()的时候只能唤醒一个线程,所以必须保证唤醒的是正确的线程。notifyAll()将唤醒所有正在等待的任务。
  6. 应该用while去包围wait()方法,可能条件在其他的线程当中已经被改变,所以这个任务在此时应该不执行。或者检查是不是正确地被唤醒,如果没有的话,应该继续wait()。
  7. 自增对于Java来说并不是原子操作,需要读,加,写三个操作。
  8. volatile适合于return操作和写操作,这些都是原子操作。
  9. 可以用TimeUnit的sleep()方法来替代Thread的sleep()方法,因为前者的可读性更好,可以指定时间单位。

  材料非常丰富啊,但是时间有限,我觉得能把编程思想上的代码都敲过一遍,就能收获很多了。这边立个flag吧。volatile的。


看的不过瘾的请点下面
回到顶部


原文地址:https://www.cnblogs.com/ljl36/p/6772624.html