20155303 2016-2017-2 《Java程序设计》第七周学习总结

教材学习中的问题和解决过程

  • 『问题一』:SimpleDateFormat中每个字符的含义都是什么?

『问题一解决』:API文档中有详细的说明:

javafx 下载 进度条 ProgressBar_API

比如以下程序,我定义的格式是 yyyy-ww-DD ,输出结果表示当前日期是该年第14周,第97天。

javafx 下载 进度条 ProgressBar_字符流_02

javafx 下载 进度条 ProgressBar_java_03

  • 『问题二』:课本P435提到 clone() 复制对象的功能,这属于创建对象的方式吗?

『问题二解决』: clone() 复制对象与 new 都是创建对象的方式。之前课本上曾介绍过浅层复制(Shallow Copy)与深层复制(Deep Copy),这次借助 clone() 进一步学习。

【复制参考与复制对象】:回顾之前学过的“复制参考”,编写以下程序:

javafx 下载 进度条 ProgressBar_Java_04

当执行完Person p1 = p;之后真的创建了一个新对象吗?我们来看一下打印结果:

javafx 下载 进度条 ProgressBar_字符流_05

可以看到打印的地址值是相同的。也就是说,这一步只实现了引用的复制,也就是p1与p指向了一个相同的对象“Person("Justin", 20150001)”。

而clone()能够实现复制对象,而不仅仅是复制参考。以下面这个程序为例:

javafx 下载 进度条 ProgressBar_java_06

打印结果为:

javafx 下载 进度条 ProgressBar_字符流_07

可以看出,clone()重新分配内存,而不是把原对象的地址赋给了一个新的引用变量。

【浅拷贝与深拷贝】:继续分析上例,ID是基本数据类型,直接将数值拷贝过来就行。但是name是String类型的,它只是一个引用,指向一个真正的String对象,那么对它的拷贝有两种方式:

①直接将源对象中的name的引用值拷贝给新对象的name字段;

②根据原Person对象中的name指向的字符串对象创建一个新的相同的字符串对象,将这个新字符串对象的引用赋给新拷贝的Person对象的name字段。

这两种拷贝方式分别叫做浅拷贝和深拷贝。

修改上面的代码,如果name的地址值相同,说明两个对象的name都指向同一个String对象,是浅拷贝;如果两个对象的name的地址值不同,那么就说明指向不同的String对象,也就是在拷贝对象的时候,同时拷贝了name引用的String对象,也就是深拷贝。

javafx 下载 进度条 ProgressBar_API_08

运行结果如下:

javafx 下载 进度条 ProgressBar_Java_09

name的地址值是相同的,所以这种拷贝方式是浅拷贝。

那么如何实现深拷贝呢?我们必须在类中实现Cloneable接口,并且重写clone方法,创建一个拷贝类的对象,如下程序:

javafx 下载 进度条 ProgressBar_java_10

javafx 下载 进度条 ProgressBar_Java_11

打印结果为:

javafx 下载 进度条 ProgressBar_API_12

可以看到,两个对象的p指向的不是同一个对象,这就是深拷贝。

  • 『问题三』:课本P435提到“想比较两个Calendar”的时间日期先后,可以使用after()或before()方法。那么,这两个方法具体怎么使用呢?

『问题三解决』:查询API文档可知,after()返回值为布尔类型,其等价于compareTo(when)>0,为真返回true,否则返回false。before()同理,等价于compareTo(when)<0。

javafx 下载 进度条 ProgressBar_java_13

代码调试中的问题和解决过程

  • 『问题一』:运行课本P432HowOld程序时,忽略了L类型转换,得到了下面错误的结果(433岁!?):

javafx 下载 进度条 ProgressBar_Java_14

在365*24*60*60*1000后面添加L或l得到正确输出:

javafx 下载 进度条 ProgressBar_Java_15

『问题一解决』:365*24*60*60*1000的计算结果是31536000000,超出了int范围,在二进制中只保留后32位,为1471228928,因此会出现以上错误结果。使用计算器或许能更加清晰地展示出来:

javafx 下载 进度条 ProgressBar_java_16

javafx 下载 进度条 ProgressBar_java_17

  • 『问题二』:关于System.currentTimeMills()方法的使用问题。

『问题二解决』:查询API文档可知,currentTimeMills()返回值代表1970年1月1日0时0分0秒0毫秒至今经过的毫秒数。

javafx 下载 进度条 ProgressBar_Java_18

考虑到这一点,可以调用该方法计算程序运行时间。以下面这个程序为例,在主线程开始和结束时分别调用currentTimeMills()方法,计算差值即为程序运行时间(单位:毫秒):

javafx 下载 进度条 ProgressBar_API_19

需要注意的是,同一程序程序运行时间也有可能不同,这与此时CPU状态有关,所以此方法只能大致估计程序的运行时间。不过可作为程序优化的一点参考。

代码托管

javafx 下载 进度条 ProgressBar_API_20

javafx 下载 进度条 ProgressBar_API_21

上周考试错题总结

  • 『题目一』:

下面哪条命令可以把 f1.txt 复制为 f2.txt ?(AC)

A .cp f1.txt f2.txt

B .copy f1.txt f2.txt

C .cat f1.txt > f2.tx

D .cp f1.txt | f2.tx

E .copy f1.txt | f2.tx

『考点』:copy是Windows下的命令。cat f1.txt > f2.tx 通过输出重定向实现了复制。

  • 『题目二』:

Given an instance of a Stream, s, and a Collection, c, which are valid ways of creating a parallel stream? (Choose all that apply.)

给定一个Stream的实例s, 一个Collection的实例c, 下面哪些选项可以创建一个并行流?(DF)

A .new ParallelStream(s)

B .c.parallel()

C .s.parallelStream()

D .c.parallelStream()

E .new ParallelStream(c)

F .s.parallel()

『考点』:Parallelstream()方法不存在,“P”不能大写,所以A和E是不正确的。API中对parallel()的定义为:在Stream类中从现有流创建一个并行流,因此F是正确的,C是错误的。API中对parallelstream()的定义为:在Collection类中从一个集合创建一个并行流,因此D是正确的,B是错误的。

  • 『题目三』:

Which of the following statements about the Callable call() and Runnable run() methods are correct? (Choose all that apply.) (ACDF)(My answer:CD)

A .Both can throw unchecked exceptions.

B .Callable takes a generic method argument.

C .Callable can throw a checked exception.

D .Both can be implemented with lambda expressions.

E .Runnable returns a generic type.

F .Callable returns a generic type.

G .Both methods return void

『考点』:查询API文档关于Callable接口的说明可以看到,Callable支持泛型,且Callable和Runable都能抛出非受检异常,而能抛出受检异常的只有Callable。

javafx 下载 进度条 ProgressBar_API_22

  • 『题目四』:

What are some reasons to use a character stream, such as Reader/Writer, over a byte stream, such as InputStream/OutputStream? (Choose all that apply.)

在下列哪些情况下使用字符流(比如Reader/Writer)而不使用字节流(比如InputStream/OutputStream)?(AC)

A .More convenient code syntax when working with String data

B .Improved performance

C .Automatic character encoding

D .Built-in serialization and deserialization

E .Character streams are high-level streams

F .Multi-threading support

『考点』:

A:字符流包含能够非常便利处理字符串数据的方法,所以A是正确的;C:字符流有其特有的编码方式,能够自动处理字符编码问题,C也是正确的。

其他选项,如B:性能改进,D:内置的序列化和反序列化,E:字符流是更高级的流,F:字符流支持多线程等等,这些说法都是不相关,或是不正确的。

  • 『题目五』:

Assuming / is the root directory, which of the following are true statements? (Choose all that apply.)(A)

A ./home/parrot is an absolute path.

B ./home/parrot is a directory.

C ./home/parrot is a relative path.

D .The path pointed to from a File object must exist.

E .The parent of the path pointed to by a File object must exist.

『考点』:

根目录开始的路径是绝对路径,所以A是正确的,C是错误的。B是不正确的,因为路径可能是文件系统中的文件(file)或目录(directory)。文件对象可以指向文件系统中不存在的路径,所以D和E是错误的。

  • 『题目六』:

What is the result of executing the following code? (Choose all that apply.)(BDE)

String line;
Console c = System.console();
Writer w = c.writer();
if ((line = c.readLine()) != null)
    w.append(line);
w.flush();

A .The code runs without error but prints nothing.

B .The code prints what was entered by the user.

C .An ArrayIndexOutOfBoundsException might be thrown.

D .A NullPointerException might be thrown.

E .An IOException might be thrown.

F .The code does not compile.

『考点』:这是从控制台读取一行并将其写入控制台的正确代码,B正确;查询API文档可知,调用console()方法可能会抛出NullPointerException异常,D正确;调用append()方法可能会抛出IOException异常。

javafx 下载 进度条 ProgressBar_API_23

javafx 下载 进度条 ProgressBar_API_24