20145330第六周《Java学习笔记》###
.
这周算是很忙碌的一周。因为第六周陆续很多实验都开始进行,开始要准备和预习的科目日渐增多,对Java分配的时间不知不觉就减少了,然而第十和十一章内容相对有很多,所以效率是一件很重要的事情。
第十章 输入与输出
- Java将输入输出抽象化为串流,数据有来源及目的地,衔接两者的是串流对象。
- 比喻来说,数据就好比水,串流就好比水管,通过水管的衔接,水由一端流向另一端
- 如果想要将数据从来源取出,可以使用输入串流。
- 如果想要将数据写入目的地,可以使用输出串流。
- 标准输入输出
- System.in: 标准输入,默认关联到键盘(终端输入)
- System.out: 标准输出,默认关联到显示器(终端输出)
- System.err: 标准错误,默认关联到显示器(终端输出)
- 输入输出重定向:setIn,setOut,setErr
- 来源与目的都不知道的情况下,可以设计一个通用的dump()方法,如果要将某个文档读入并另存为另一个文档,则可以这么使用:
import java.io.*;
public class StreamIO {
public static void dump(InputStream src, OutputStream dest)
throws IOException {
try (InputStream input = src; OutputStream output = dest) {
byte[] data = new byte[1024];
int length;
while ((length = input.read(data)) != -1) {
output.write(data, 0, length);
}
}
}
}
import java.io.*;
public class Copy {
public static void main(String[] args) throws IOException {
StreamIO.dump(
new FileInputStream(args[0]),
new FileOutputStream(args[1])
);
}
}
- 这个程序可以由命令行自变量指定读取的文档来源与写出的目的地
- 如果要从HTTP服务器读取某个网页,另存为文档,也可以使用这里设计的dump()方法,例如:
import java.io.*;
import java.net.URL;
public class Download {
public static void main(String[] args) throws IOException {
URL url = new URL(args[0]);
InputStream src = url.openStream();
OutputStream dest = new FileOutputStream(args[1]);
StreamIO.dump(src, dest);
}
}
- 串流继承架构
- 标准输入输出
- 可以使用
system
的setIn()
方法指定InputStream
实例,重新指定便准输入来源。
- 可以使用
- 标准输入输出
import java.io.*;
import java.util.*;
public class StandardIn {
public static void main(String[] args) throws IOException {
System.setIn(new FileInputStream(args[0]));
try (Scanner file = new Scanner(System.in)) {
while (file.hasNextLine()) {
System.out.println(file.nextLine());
}
}
}
}
- 除了 system.in 与 system.out 之外,还有个system.err 为PrintStream 实例,成为标准错误输出串流,它是用来立即显示错误信息。
-
FileInputStream与FileOutputStream
- FileInputStream是InputStream的子类,可以指定文件名创建实例,一旦创建文档就开启,接着就可以用来读取数据。
- FileOutputStream是OutputStream的子类,可以指定文件名创建实例,一旦创建文档就开启,接着就可以用来读出数据。
- FileInputStream与FileOutputStream在读取、写入文档时,是以字节为单位,通常会使用一些高阶类加以打包,进行一些高阶操作。
-
ByteArrayInputStream与ByteArrayOutputStream
- ByteArrayInputStream是InputStream的子类,可以指定byte数组创建实例,一旦创建就可将byte数组当作数据源进行读取。
- ByteArrayOutputStream同理
-
串流处理装饰器
- BufferedInputStream与BufferedOutputStream
- BufferedInputStream与BufferedOutputStream主要在内部提供缓冲区功能,操作上与InputStream与OutputStream并没有太大差别。例如,改写前面的IO.dump()为BufferedIO.dump()方法:
- BufferedInputStream与BufferedOutputStream
import java.io.*;
public class BufferedIO {
public static void dump(InputStream src, OutputStream dest)
throws IOException {
try(InputStream input = new BufferedInputStream(src);
OutputStream output = new BufferedOutputStream(dest)) {
byte[] data = new byte[1024];
int length;
while ((length = input.read(data)) != -1) {
output.write(data, 0, length);
}
}
}
}
- DataInputStream与DataOutputStream
- 实际应用例子,下面的Member类可以调用save()储存Member实例本身的数据:
import java.io.*;
public class Member {
private String number;
private String name;
private int age;
public Member(String number, String name, int age) {
this.number = number;
this.name = name;
this.age = age;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return String.format("(%s, %s, %d)", number, name, age);
}
public void save() throws IOException {
try(DataOutputStream output =
new DataOutputStream(new FileOutputStream(number))) {
output.writeUTF(number);
output.writeUTF(name);
output.writeInt(age);
}
}
public static Member load(String number) throws IOException {
Member member;
try(DataInputStream input =
new DataInputStream(new FileInputStream(number))) {
member = new Member(
input.readUTF(), input.readUTF(), input.readInt());
}
return member;
}
}
-
ObjectInputStream与ObjectOutputStream
- ObjectInputStream提供readObject()方法将数据读入为对象,ObjectOutputStream提供writeObject()方法将对象写至目的地
-
字符处理类
- Reader与Writer继承结构
- 针对字符数据的读取,JavaSE提供了
java.io.Reader
类,其抽象化了字符数据读入的来源。 - 针对字符数据的写入,JavaSE提供了
java.io.Writer
类,其抽象化了字符数据读入的目的地。 - FileReader与FileWriter则可以对文档做读取与写入,读取或写入时默认会实用操作系统默认编码来做字符转换。
- 针对字符数据的读取,JavaSE提供了
- Reader与Writer继承结构
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);
}
}
}
}
- 字符处理装饰器
- InputStreamReader与OutputStreamWriter
- 串流处理的字节数据,实际上代表某些字符的编码数据
- BufferedReader与BufferedWriter
- 可对Reader、Writer提供缓冲区作用,在处理字符输入输出时,对效率也会有所帮助
- PrintWriter
- 除了可以对OutputStream打包之外,还可以对Writer进行打包,提供print()、printLn()、format()等方法。
- InputStreamReader与OutputStreamWriter
import java.io.*;
public class CharUtil2 {
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);
}
}
}
public static void dump(InputStream src, OutputStream dest,
String charset) throws IOException {
dump(
new InputStreamReader(src, charset),
new OutputStreamWriter(dest, charset)
);
}
//
public static void dump(InputStream src, OutputStream dest)
throws IOException {
dump(src, dest, System.getProperty("file.encoding"));
}
}
线程与并行API##
要让目前流程暂停指定时间,可以使用
java.lang.Thread
的静态sleep()方法,指定的单位是毫秒,调用这个方法必须处理java.lang.InterruptedException
Thread与Runnable
撰写多线程程序的方式:操作Runnable接口,在run()中定义额外流程;继承Thread类,在run()中定义额外流程;
差别:操作Runnable接口的好处就是较有弹性,类还有机会继承其他类;若继承了Thread类,那该类就是一种Thread,通常是为了直接利用Thread中定义的一些方法,才会继承Thread来操作。
如果主程序中启动了额外线程,默认会等待被启动的所有线程都执行完run()方法才中止JVM。如果一个Thread被标示为Daemon线程,在所有的非Daemon线程都结束时,JVM自动就会终止。
public class DaemonDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
while (true) {
System.out.println("Orz");
}
});
// thread.setDaemon(true);
thread.start();
}
}
- 在调用Thread实例start()方法后,基本状态为可执行(Runnable)、被阻断(Blocked)、执行中(Running)。
- 线程有其优先权,可使用Thread的setPriority()方法设定优先权,可设定值为1到10,默认是5,超出1到10外的设定值会抛出IllegalArgumentException。数字越大优先权越高,排班器越优先排入CPU,如果优先权相同,则输流执行。
- 线程完成run()方法后,就会进入Dead,进入Dead(或已经调用过start()方法)的线程不可以再次调用start()方法,否则会抛出IllegalArgumentException。
- 如果要停止线程,最好自行操作,让线程跑完应有的流程,而非调用Thread的stop()方法。不仅停止线程必须自行根据条件操作,线程的暂停、重启,也必须视需求操作,而不是直接调用suspend()、resume()等方法。
- 每个线程产生时,都会归入某个线程群组,这视线程是在哪个群组中产生,如在main()主流程中产生一个线程,该线程会属于main线程群组。如果没有指定,则归入产生该子线程的线程群组,也可以自行指定线程群组,线程一旦归入某个群组,就无法更换群组。
- 每个对象都会有个内部锁定,或称为监控锁定。被标示为synchronized的区块将会被监控,任何线程要执行synchronized区块都必须先取得指定的对象锁定。
- 如果在方法上标示synchronized,则执行方法必须取得该实例的锁定。synchronized不只可声明在方法上,也可以描述句方式使用。不正确的使用可能会造成死结。
class Resource {
private String name;
private int resource;
Resource(String name, int resource) {
this.name = name;
this.resource = resource;
}
String getName() {
return name;
}
synchronized int doSome() {
return ++resource;
}
synchronized void cooperate(Resource resource) {
resource.doSome();
System.out.printf("%s 整合 %s 的资源%n",
this.name, resource.getName());
}
}
public class DeadLockDemo {
public static void main(String[] args) {
Resource resource1 = new Resource("resource1", 10);
Resource resource2 = new Resource("resource2", 20);
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
resource1.cooperate(resource2);
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
resource2.cooperate(resource1);
}
});
thread1.start();
thread2.start();
}
}
- Java的synchronized提供的是可重入同步,也就是线程取得某对象锁定后,若执行过程中又要执行synchronized,尝试取得锁定的对象又是同一个,则可以直接执行。
- 执行synchronized范围的程序代码期间,若调用锁定对象的wait()方法,线程会释放对象锁定,并进入对象等待集合而处于阻断状态,其他线程可以竞争对象锁定,取得锁定的线程可以执行synchronized范围的程序代码。
- 放在等待集合的线程不会参与CPU排班,wait()可以指定等待时间,时间到之后线程会再次加入排班,如果指定时间0或不指定,则线程会持续等待,直到被中断(调用interrupt()或是告知notify())可以参与排班。
- 在使用高级并行API时,Lock接口的操作对象可实现synchronized的功能。
import java.util.Arrays;
public class ArrayList {
private Object[] list;
private int next;
public ArrayList(int capacity) {
list = new Object[capacity];
}
public ArrayList() {
this(16);
}
public void add(Object o) {
if(next == list.length) {
list = Arrays.copyOf(list, list.length * 2);
}
list[next++] = o;
}
public Object get(int index) {
return list[index];
}
public int size() {
return next;
}
}
- 在使用高级并行API时,Condition接口的操作对象可实现Object的wait()、notify()、notifyAll()功能。在使用高级并行API时,Future接口的操作对象可以让你在未来取得执行结果。
教材学习中的问题
- 撰写单线程序、多线程程序的方式差别:操作Runnable接口的好处就是较有弹性,类还有机会继承其他类;若继承了Thread类,那该类就是一种Thread,通常是为了直接利用Thread中定义的一些方法,才会继承Thread来操作。
代码托管截图
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 200/200 | 2/2 | 20/20 | |
第二周 | 300/500 | 2/4 | 18/38 | |
第三周 | 500/1000 | 3/7 | 22/60 | |
第四周 | 300/1300 | 2/9 | 30/90 | |
第五周 | 300/1600 | 1/10 | 20/110 | |
第六周 | 250/1850 | 2/12 | 20/130 |
参考资料###
- 《Java学习笔记》