20155303 2016-2017-2 《Java程序设计》第六周学习总结
课堂笔记
- 高效学习法推荐
看视频学习(2h)→ 以代码为中心看课本,思考运行结果并验证(3h)→ 课后作业验证(5h)→ 教材指导
【充分利用教材指导,积极思考遇到的问题,在实践中学习,不要拘泥于记忆教材讲解的知识点和概念。】
教材学习中的问题和解决过程
- 『问题一』:以课本P306为例,
args[0]
、args[1]
代表什么?
『问题一解决』:java的主方法为:public static void main(String [] args)
,args[0]
表示命令行输入时传的第一个参数,args[1]
同理。例如执行以下程序:
运行java WhatIsArgs No1 No2
命令可得到结果:No1
和No2
。
- 『问题二』:
hasNextLine()
与nextLine()
的用法?
『问题二解决』:查询API文档可知,hasNextLine()
与nextLine()
均继承自java.util.Scanner
。它们的用法是,hasNextLine()
用来判断下一行是否存在,常用在while
语句中,当且仅当下一行有输入时返回true;而nextLine()
返回值是当前行的剩余内容。
两者具体使用方法可参考以下代码:
- 『问题三』:课本P316提到“如果在做对象串行化时,对象中某些数据成员不希望被写出,则可以标上
transient
关键字”一句该如何理解?
『问题三解决』:一个对象只要实现了Serilizable
接口,这个对象就可以被序列化,不过有些时候,一个类的有些属性需要序列化,而其他属性不需要被序列化。java的transient
关键字为我们提供了便利,你只需要实现Serilizable
接口,将不需要序列化的属性前添加关键字transient
,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
比如以下程序:
在上面的例子中,将属性b前添加关键字transient,虽然我们序列化的对象b的属性值为“Transient or not”,但是当我们反序列化之后发现这个属性为空,说明这个属性没有进行序列化。
【关于什么是序列化,参考博客深入理解Java对象序列化】
- 『问题四』:守护线程与非守护线程
『问题四解决』:简单来说,java可以创建两种线程,即守护线程与非守护线程。非守护线程(User Thread用户线程)就是平时创建的一般线程,而守护线程(Daemon Thread守护线程)是用来服务用户线程的。
区分守护线程与非守护线程:当线程只剩下守护线程的时候,JVM就会退出.但是如果还有其他的任意一个用户线程还在,JVM就不会退出。
setDaemon()与isDaemon():setDaemon
方法用来设定一个线程。如果setDaemon(true)
表示设定一个线程为Daemon线程。isDaemon
则用来测试一个线程是否为守护线程。如果是,返回true。
- 『问题五』:Thread常用方法以及状态图
『问题五解决』:
常用方法:
start();
//启动线程
getId();
//获得线程ID
getName();
//获得线程名字
getPriority();
//获得优先权
isAlive();
//判断线程是否活动
isDaemon();
//判断是否守护线程
getState();
//获得线程状态
sleep(long mill);
//休眠线程
join();
//等待线程结束
yield();
//放弃cpu使用权利
interrupt();
//中断线程
currentThread();
//获得正在执行的线程对象
Thread状态图:
- 『问题六』:对于课本P334
join()
的介绍产生疑问:Thread B 什么时候加入主线程呢?是不是从join()
方法出现的位置开始呢?
『问题六解决』:在不同位置调用join()
方法,程序如下:
“尝试一”运行结果:
“尝试二”运行结果:
可以看出,程序启动后主线程就开始了,调用join()
之后把Thread B加入主线程流程中,执行完毕后再继续执行原本的线程。
注:可以在join()
指定时间,如join(1000)表示让加入的线程执行1000毫秒,也就是1秒。1秒结束后线程可以继续执行原本流程。
代码调试中的问题和解决过程
本周跟视频学习的过程中思考一个问题:字节流和字符流的优势各在哪里呢?使用哪一个比较好呢?
答案是字节流。首先因为硬盘上的所有文件都是以字节的形式进行传输和保存的,包括图片,mp3等等。但是字符只是在内存中才会形成,所以在开发过程中,字节流使用更加广泛。
下面通过几个例子总结了字节流的应用情况。
- 『问题一』:向文件中写入字符串
运行以下程序:
查看hello.txt会看到“你好”:
也可以向文件中一个字节一个字节地写入字符串,运行结果同上:
- 『问题二』:向文件中追加新内容
运行以下程序:
查看hello.txt会看到“你好DiWeijia”:
- 『问题三』:读取文件内容
运行以下程序,可以读出hello.txt里的内容:
知识拓展
谢涛老师在之前的一篇博客中提出问题:如何把一个Java源文件里的注释去掉(处理结果可以输出到新的文件里),保证修改后源文件仍然能正常编译,正确运行。得到娄老师的指点之后,明白这个问题的解决需要用到正则表达式。
正则表通常被用来检索、替换那些符合某个模式(规则)的文本。我们经常使用Windows/Dos下用于文件查找的通配符(wildcard),也就是* 和?。如果想查找某个目录下所有的word文档,我们会搜索 * .doc。在这里,* 会被解释成任意的字符串。和通配符类似,正则表达式也是用来进行文本匹配的工具,只不过比起通配符,它能更精确地描述你的需求。
比如要求填写5-12位的QQ号,就可以使用正则表达式:^\d{5,12}$。^表示匹配字符串的开始,$表示匹配字符串的结束,\d表示匹配数字,{5,12}表示数字为5-12位。这样一来,如果用户输入能匹配这个表达式的话,就符合要求了。
像以上的“^”、“$”等都是正则表达式的元字符。元字符以及常用的正则表达式如下:(参考娄老师博客正则表达式入门)
“去注释”问题可以利用正则表达式的相关知识,结合之前学到的“转义符”解决。基本思路是:对待分析的带注释段的字符串进行遍历,声明一个缓冲字符串变量来记录非注释的部分,最后返回这个缓冲字符串变量作为结果。这样就能把去除注释之后的文件保存下来了。可以从以下四个方面考虑:
1.考虑双引号:双引号中的注释部分是不能去掉的,比如print("//Hello"World"/ * comment * /")。以下几条都应在没有双引号的前提下。如果发现了开始双引号,在匹配结束双引号的时候要注意可能会遇到转义双引号,需要跳过以\开始的双引号,从而匹配到正确的结束双引号;
2.考虑 / * comment * / 形式的注释 :当遇到 / * 部分便停止记录,继续往后遍历到 * / 部分,实现跳过 / * * / 段;
3.考虑/ * comment / * inside * / out * /形式的嵌套注释:声明一个数字变量来记录 / * 的开始的次数,遇到一个 / * 就+1,遇到一个 * / 就-1,实现嵌套匹配;
【注意】:注释不能嵌套:/ * / * inside * / * /,所以这种情况不予考虑。感谢谢涛老师的指正:)
4.考虑双斜杠注释 发现 // 形式的字符串的时候表明遇到了双斜杠注释,这时候使用while循环继续向后遍历,直到发现一个换行符,从而跳过整个这一行;
正则表达式的应用领域非常广,以上提到的这些只是一点皮毛。要想熟练掌握正则表达式的用法,还需要多动手多实践。
代码托管
上周考试错题总结
- 『问题一』:
现有:
- list是一个合法的集合引用
- getCollection()返回一个合法集合的引用
哪个是合法的?
A.or(Object o ; list)
√B.for(Object o : getCollection())
C.for(Object o : list.iterator())
D.for(lterator i ; list.iterator() ; i.hasNext () )
√E.for(lterator i=list.iterator(); i.hasNext (); )
- 『考点』:
B选项是增强式for循环。增强式for循环能对数组和集合进行遍历,使用上更加简洁。D选项是普通循环,i操作了iterator()
接口,如果没有抛出异常,则i.hasNext()
返回值为true。
- 『问题二』:
Which of the following methods will not compile? :
A.
private void method1(String name) {
if (name.equals("star"))
throw new IllegalArgumentException(name);
}
√B.
private void method2(int age) {
if (age > 30)
throw Exception();
}
C.
public double method5() throws Exception {
return 0.7;
}
√D.
protected double method4() throws Exception {
throw new Throwable();
}
- 『考点』:
B选项无法编译,因为Exception
是受检异常,必须使用throws
声明此方法会抛出的异常类型或父类型。D选项无法编译,因为子类不能抛出比父类更一般的异常。
- 『问题三』:
What is the output of the following code?
class EJava {
void method() {
try {
guru();
return;
} finally {
System.out.println("finally 1");
}
}
void guru() {
System.out.println("guru");
throw new StackOverflowError();
}
public static void main(String args[]) {
EJava var = new EJava();
var.method();
}
}
A.guru
finally 1
√B.guru
finally 1
Exception in thread "main" java.lang.StackOverflowError
C.guru
Exception in thread "main" java.lang.StackOverflowError
D.guru
E.The code fails to compile.
- 『考点』:
首先程序可以通过编译。其次,StackOverflowError()是非受检异常,方法guru()在try-catch
块中,异常会被捕捉。由于guru()本身没有处理堆栈溢出错误,但method()定义了finally
区块,所以程序在执行完毕finally
区块之后将错误传播至JVM,中断程序。
结对及互评
本周结对学习情况
结对同学学号20145202马超
结对学习内容:查看对方代码,并对学习中遇到的疑问进行交流。解答对方博客中未解决的问题。
第六周博客互评情况
其他(感悟、思考等,可选)
1、最近几周的学习内容系统性和连贯性很强,所以要经常查询API文档,了解常用的类和方法以及其继承架构。
2、学习过程中应该把思考作为重中之重,这一点之前就领悟到了,不过在实践之中经常被忽略。所以我们不应该单纯地把代码行数看做衡量自己学习情况的标准,理解得透彻了才能达到举一反三的效果。按照这种方法学习不仅可以深刻理解所学知识,也提高了学习效率,减轻了学习负担。
3、不学则已,一学即专。心不能静下来的时候宁可不看书。万万不可一边打着学习的名义捧书研读,一边还在为其他事困扰,这样只能欺骗自己“我真的学习了”,却仅仅是耗费时间,而达不到理想的效果。
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 16/16 | 1/1 | 18/18 | 初步认识了Java |
第二周 | 219/235 | 1/2 | 28/46 | 学习了Java的基本语法知识 |
第三周 | 766/1001 | 1/3 | 23/69 | 了解对象与参考的关系,以及封装的概念与实现 |
第四周 | 984/1985 | 1/4 | 18/87 | 学习了继承与多态的关系,以及接口的多态操作 |
第五周 | 866/2851 | 1/5 | 12/99 | 学习了异常处理,学会使用Collection收集对象 |
第六周 | 664/3515 | 1/6 | 15/114 | 认识字节流和字符流的继承架构,学习线程与并行API |
尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
耗时估计的公式
:Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。
计划学习时间:20小时
实际学习时间:15小时
改进情况:这周的效率跟之前比有了很大的提高,我想应该归功于娄老师上节课提到的学习方法。以后的学习应该抓住重点,多思考,不要把时间浪费在照搬书上的代码之类的无用功上。
(有空多看看现代软件工程 课件
软件工程师能力自我评价表)