20145235 《Java程序设计》第6周学习总结
教材学习内容总结
10.1 InputStream与OutputStream
串流设计的概念
- Java将输入/输出抽象化为串流,数据有来源及目的地,衔接两者的是串流对象。
- 从应用程序角度来看,如果要将数据从来源取出,可以使用输入串流,如果要将数据写入目的地,可以使用输出串流。在Java中,输入串流代表对象为java.io.Inputstream实例,输出串流代表对象为java.io.OutputStream实例。
- 在不使用InputStream与OutputStream时,必须使用close()方法关闭串流。由于InputStream与OutputStrem操作了java.io.Closeable接口,其父接口为java.lang.AutoCloseable接口。
串流继承架构
- System.in与System.out查看API文件的话,会发现它们分别是InputStream与PrintStream的实例,分别代表标准输入与标准输出。
- 可以使用System的setIn()方法指定InputStream实例,重新指定标准输入来源。标准输出可以重新导向至文档,只要执行程序时使用>将输出结果导向至指定文档。可以使用System的setOut()方法指定printStream实例,将结果输出至指定的目的地。
- FileInputStream是InputStream的子类,可以指定文件名创建实例,一旦创建文档就开启,接着就可用来读取数据。FileOutputStream是OutputStream的子类,可以指定文件名创建实例,一旦创建文档就开启,接着就可以用来写出数据。无论FileInputStream还是FileOutputStream,不使用时都要使用close()关闭文档。
- FileInputStream主要操作了InputStream的read()操作方法,使之可以从文档中读取数据,FileOutputStream主要操作了OutputStream的write()操作方法,使之可以写出数据至文档。
- ByteArryInputStream是InputStrteam的子类,可以指定byte数组创建实例,一旦创建就可将byte数组当做数据源进行读取。ByteArryOutputStream是OutputStream的子类,可以指定byte数组创建实例,一旦创建将byte数组当做目的地写出数据。
- ByteArryInputStream主要操作了InputStream的read()操作方法,使之可以从byte数组中读取数据,ByteArryOutputStream主要操作了OutputStream的write()操作方法,使之可以写出数据至byte数组。
串流处理装饰器
- InputStream、OutStream提供串流基本操作,如果想要为输入/输出的数据做加工处理,则可以使用打包器类。常用的打包器具备缓冲区作用的BufferedOutputStream、BufferedInputStream,具备数据转换处理的DataInputStream、DataOutputStream,具备对象串行化能力的ObjectInputStream、ObjectOutputStream等。
- BufferedInputStream与BufferedOutputStream主要在内部提供缓冲区功能,操作上与InputStream、OutputStream并没有太大区别。DataInputStream、DataOutputStream用来装饰InputStream、OutputStream,DataInputStream、DataOutputStream,提供读取、写入Java基本数据类型的方法,像是读写int、double、boolean等的方法。
- -来看例子。下面的Member类可以调用save()储存Member实例本身的数据,文件名为Member的会员号码,调用Member.load()指定会员号码,则可以读取文档中的会员数据。代码运行结果如下:
package cc.openhome; import java.io.*; publicclass Member { privateStringnumber; privateString name; private int age; public Member(Stringnumber, String name, int age) { this.number = number; this.name = name; this.age = age; } publicString getNumber() { returnnumber; } publicvoid setNumber(Stringnumber) { this.number = number; } publicString getName() { return name; } publicvoid setName(String name) { this.name = name; } public int getAge() { return age; } publicvoid setAge(int age) { this.age = age; } @Override publicString toString() { returnString.format("(%s, %s, %d)", number, name, age); } publicvoid save() throws IOException { try(DataOutputStream output = new DataOutputStream(new FileOutputStream(number))) { output.writeUTF(number); output.writeUTF(name); output.writeInt(age); } } publicstatic Member load(Stringnumber) throws IOException { Member member; try(DataInputStream input = new DataInputStream(new FileInputStream(number))) { member = new Member( input.readUTF(), input.readUTF(), input.readInt()); } return member; } }
- ObjectInputStream提供readObject()方法将数据读入为对象,而ObjectOutputStream提供writeObject()方法将对象写至目的地。可以被这两个方法处理的对象,必须操作java.io.Serializable接口,这个接口并没有定义任何方法,只是作为标示之用,表示这个对象是可以串行化的。如下面范例Member2Demo的运行结果与MemberDemo的运行结果是一样的。运行截图如下:
10.2 字符处理类
- 针对字符数据的读取,Java SE 提供了 java.io.Reader 类,其抽象化了字符数据读入的来源。
- 针对字符数据的写入,Java SE 提供了 java.io.Writer 类,其抽象化了数据写出的目的地。
- 使用 CharUtil.dump() 方法从来源读入字符数据、将字符数据写至目的地。代码如下:
import java.io.*; public class CharUtil { public static void dump(Reader src, Writer dest) throws IOException { try(Reader input = src; Writer output = dest) { char[] data = new char[1024]; int length; while((length = input.read(data)) != -1) { output.write(data, 0, length); } } } }
- read():每次从Reader 读入的数据,都会先置入 char 数组中,Reader 的 read() 方法,每次会尝试读入 char 数组长度的数据,并返回实际读入的字符数,只要不是-1,就表示读取到字符。
- write():使用 write() 方法,指定要写出的 byte 数组、初始索引与数据长度。
- 使用CharUtil.dump() 读入文档、转为字符串并显示在文本模式中。代码如下:
package cc.openhome; import java.io.*; public class CharUtilDemo { public static void main(String[] args) throws IOException { FileReader reader = new FileReader(args[0]); StringWriter writer = new StringWriter(); CharUtil.dump(reader, writer); System.out.println(writer.toString()); } }
- InputStreamReader 与 OutputStreamWriter:可对串流数据打包。
- BufferedReader 与 BufferedWriter:可对 Reader、Writer 提供缓冲区作用,在处理字符输入\输出时,对效率也会有所帮助。
- BufferedReader 与 BufferedWriter:可对 Reader、Writer 提供缓冲区作用,在处理字符输入\输出时,对效率也会有所帮助。
11.1线程
TortoiseHareRace2代码运行结果:
从抽象观点来看,JVM是台虚拟计算机,只安装一颗称为主线程的CPU,可执行main()定义的执行流程。如果想要为JVM加装CPU,就是创建Thread实例,要启动额外CPU就是调用Thread实例的start()方法,额外CPU执行流程的进入点,可以定义在Runnale接口的run()方法中。- Daemon线程:主线程会从main()方法开始执行,直到main()方法结束后停止JVM。如果主线程中启动了额外线程,默认会等待被启动的所有线程都执行完run()方法才中止JVM。
- 在所有的非Daemon线程都结束时,JVM自动就会中止。可以使用setDeamon()方法来设定一个线程是否为Daemon线程,可以使用isDaemon()方法来判断线程是否为Daemon线程。
- Daemon线程,在所有的非Daemon线程都结束时,JVM就会终止。
- 每个线程都属于某个线程群组,如果没有指定,则归入产生该子线程的线程群组,也可以自行指定线程群组,线程一旦归入某个群组,就无法再更换。ThreadGroup的某些方法,可以对群组中所有线程产生作用。如果想要一次取得群组中所有线程,可以使用enumerate()方法。activeCount()方法取得群组的线程数量。
-
11.2 并行API
- wait()、notify()、notifyAll()是object定义的方法,可以通过这3个方法控制线程释放对象的锁定,或者通知线程参与锁定竞争。
- 若调用锁定对象的wait()方法,线程会释放对象锁定,并进入对象等待集合从而处于阻断状态,其他线程可以竞争对象锁定,取得锁定的线程可以执行synchronized范围的程序代码。
- 放在等待集合的线程不会参与CPU排班,wait()可以指定等待时间,时间到之后线程会再次加入排班。
- 如果指定时间0或不指定,则线程会持续等待,直到被中断(调用interrupt())或是告知(notify())可以参与排班。
- LOCK:锁定lock对象,可以调用其lock方法,只有取得lock对象锁定的线程,才可以继续往后执行程序代码。解除锁定要调用unlock()。
- Executor:将Runnable的指定与实际如何执行分离。
其他(感悟、思考等,可选)
这周突然开始想这么一个问题:“若干年后有人问我,‘你会什么?’。”是啊,我会什么?我会什么!我会什么呢?或许我会淡定的对他说:“我会敲代码!”。所以,希望从现在开始,我还来得及!看书说实话,我不怎么看的懂,从明天开始认真看视频,认真做好笔记。不负青春年华!
代码托管情况