20155313 2016-2017-2 《Java程序设计》第六周学习总结
20155313 2016-2017-2 《Java程序设计》第六周学习总结
教材内容学习
第十章 输入/输出
10.1 InputStream与OutputStream
1、串流设计的概念
Java将输入/输出抽象化为串流,数据有来源及目的地,衔接两者的是串流对象。
从应用程序角度来看,如果要将数据从来源取出,可以使用输入串流,如果要将数据写入目的地,可以使用输出串流。在Java中,输入串流代表对象为java.io.Inputstream实例,输出串流代表对象为java.io.OutputStream实例。
在不使用InputStream与OutputStream时,必须使用close()方法关闭串流。由于InputStream与OutputStrem操作了java.io.Closeable接口,其父接口为java.lang.AutoCloseable接口。
2、串流继承架构
(1)标准输入/输出
System.in与System.out查看API文件的话,会发现它们分别是InputStream与PrintStream的实例,分别代表标准输入与标准输出。
可以使用System的setIn()方法指定InputStream实例,重新指定标准输入来源。标准输出可以重新导向至文档,只要执行程序时使用>将输出结果导向至指定文档。可以使用System的setOut()方法指定printStream实例,将结果输出至指定的目的地。
(2)FileInputStream与FileOutputStream
FileInputStream是InputStream的子类,可以指定文件名创建实例,一旦创建文档就开启,接着就可用来读取数据。FileOutputStream是OutputStream的子类,可以指定文件名创建实例,一旦创建文档就开启,接着就可以用来写出数据。无论FileInputStream还是FileOutputStream,不使用时都要使用close()关闭文档。
FileInputStream主要操作了InputStream的read()操作方法,使之可以从文档中读取数据,FileOutputStream主要操作了OutputStream的write()操作方法,使之可以写出数据至文档。
(3)ByteArryInputStream与ByteArryOutputStream
ByteArryInputStream是InputStrteam的子类,可以指定byte数组创建实例,一旦创建就可将byte数组当做数据源进行读取。ByteArryOutputStream是OutputStream的子类,可以指定byte数组创建实例,一旦创建将byte数组当做目的地写出数据。
ByteArryInputStream主要操作了InputStream的read()操作方法,使之可以从byte数组中读取数据,ByteArryOutputStream主要操作了OutputStream的write()操作方法,使之可以写出数据至byte数组。
3、串流处理装饰器
InputStream、OutStream提供串流基本操作,如果想要为输入/输出的数据做加工处理,则可以使用打包器类。常用的打包器具备缓冲区作用的BufferedOutputStream、BufferedInputStream,具备数据转换处理的DataInputStream、DataOutputStream,具备对象串行化能力的ObjectInputStream、ObjectOutputStream等。
(1)BufferedInputStream与BufferedOutputStream
BufferedInputStream与BufferedOutputStream主要在内部提供缓冲区功能,操作上与InputStream、OutputStream并没有太大区别。
(2)DataInputStream与DataOutputStream
DataInputStream、DataOutputStream用来装饰InputStream、OutputStream,DataInputStream、DataOutputStream,提供读取、写入Java基本数据类型的方法,像是读写int、double、boolean等的方法。
-来看例子。下面的Member类可以调用save()储存Member实例本身的数据,文件名为Member的会员号码,调用Member.load()指定会员号码,则可以读取文档中的会员数据。代码如下:
package cc.openhome;
import java.io.IOException;
import static java.lang.System.out;
public class MemberDemo {
public static void main(String[] args) throws IOException {
Member[] members = {
new Member("B1234", "Justin", 90),
new Member("B5678", "Monica", 95),
new Member("B9876", "Irene", 88)
};
for(Member member : members) {
member.save();
}
out.println(Member.load("B1234"));
out.println(Member.load("B5678"));
out.println(Member.load("B9876"));
}
}
(3)ObjectInputStream与ObjectOutputStream
ObjectInputStream提供readObject()方法将数据读入为对象,而ObjectOutputStream提供writeObject()方法将对象写至目的地。可以被这两个方法处理的对象,必须操作java.io.Serializable接口,这个接口并没有定义任何方法,只是作为标示之用,表示这个对象是可以串行化的。如下面范例Member2Demo的运行结果与MemberDemo的运行结果是一样的。
10.2 字符处理类
1、Reader与Writer继承架构
针对字符数据的读取,Java SE提供了java.io.Reader类,其抽象化了字符数据读入的来源。针对字符数据的写入,则提供了java.io.Writer类。其抽象化了数据写出的目的地。FileReader、FileWriter则可以对文档做读取与写入,读取或写入时默认会使用操作系统默认编码来做字符转换。在启动JVM时,可以指定-Dfile.encoding来指定FileReader、FileWriter所使用的编码。
2、字符处理装饰器
正如InputStream、OutputStream有一些装饰器类,Reader、Writer也有一些装饰器类可供使用。如果串流处理的字节数据,实际上代表某些字符的编码数据,而你想要将这些字节数据转换为对应的编码字符,可以使用InputStreamReader、OutputStreamWriter对串流数据打包。BufferedReader、BufferedWriter可对Reader、Writer提供缓冲区作用,在处理字符输入/输出时,对效率也会有所帮助。PrintReader、PrintStream使用上极为类似,不过除了可以对OutputStream打包之外,PrintWriter还可以对Writer进行打包,提供print()、println()、format()等方法。
第十一章 线程与并行API
11.1 线程
1.线程简介
有时候需要设计程序可以拥有多个流程,也就是所谓的多线程程序。
2.Thread与Runnable
从抽象观点来看,JVM是台虚拟计算机,只安装一颗称为主线程的CPU,可执行main()定义的执行流程。如果想要为JVM加装CPU,就是创建Thread实例,要启动额外CPU就是调用Thread实例的start()方法,额外CPU执行流程的进入点,可以定义在Runnale接口的run()方法中。
3.线程生命周期
Daemon线程:主线程会从main()方法开始执行,直到main()方法结束后停止JVM。如果主线程中启动了额外线程,默认会等待被启动的所有线程都执行完run()方法才中止JVM。
在所有的非Daemon线程都结束时,JVM自动就会中止。可以使用setDeamon()方法来设定一个线程是否为Daemon线程,可以使用isDaemon()方法来判断线程是否为Daemon线程。
Thread基本状态图:在调用Thread实例start()方法后,基本状态为可执行(Runnable)、被阻断(Blocked)、执行中(Running)。一个进入Blocked状态的线程,可以由另一个线程调用该线程的interrupt()方法,让它离开Blocked状态。 而使用Thread.sleep()会让线程进入Bocked状态。
安插线程:当线程使用join()加入至另一个线程时,另一个线程会等待被加入的线程工作完毕,然后在继续它的动作,join()的意思表示将线程加入称为另一个线程的流程中。
停止线程:线程完成run()方法后,就会进入Dead,进入Dead的线程不可以再次调用start()方法,否则会抛出IllegalThreadStateException异常。
4.关于ThreadGroup
可以使用以下程序片段取得目前线程所属线程群组名:Thread.currentThread().getThreadGroup().getName()。ThreadGroup的某些方法,可以对群组中所有线程产生作用,例如interrupt()方法可以中断群组中所有线程,setMaxPriority()方法可以设定群组中所有线程最大优先权,activeCount()方法获取群组的线程数量 ,enumerate()方法要传入Thread数组,这会将线程对象设定至每个数组索引。
5.synchronized与volatile
使用synchronized:每个对象都会有个内部锁定,或称为监控锁定。被标示为synchronized的区块将会被监控,任何线程要执行synchronize区块都必须先取得指定的对象锁定。java的synchronize提供的是可重入同步,也就是线程取得某对象锁定后,若执行过程总又要执行synchronize,尝试取得锁定的对象来源又是同一个,则可以直接执行。
使用volatile:synchronized要求达到的所标示区域的互斥性和可见性。互斥性是指synchronized区块同时间只能有一个线程;可见性是指线程离开synchronized区块后,另一线程接触到的就是上一线程改变后的对象状态。可以在变量上声明volatile,标示变量是不稳定、易变的,也就是可能在多线程下存取,这保证变量的可见性,也就是若有线程变动了变量值,另一线程一定可看到变更。被标示为volatile的变量,不允许线程快取,变量值的存取一定是在共享内存中进行。
11.2 并行API
1.Lock、ReadWriteLock与Condition
使用Lock:lock接口主要操作类之一为ReentrantLock,可以达到synchronized的作用,也提供额外的功能。
使用ReadWriteLock:ReadWriteLock接口定义了读取锁定与写入锁定行为,可以使用readLock()、writeLock()方法返回Lock操作对象。ReentrantReadWriteLock是ReadWriteLock接口的主要操作类,readLock()方法会返回ReentrantReadWriteLock.ReadLock实例,writeLock()犯法会返回ReentrantReadWriteLock.WriteLock实例。
使用StampedLock:JDK8新增了StampedLock类,可支持乐观读取操作。也就是若读取线程很多,写入线程很少的情况下,你可以乐观地认为,写入与读取同时发生的机会很少,因此不悲观的使用哇暖的读取锁定,程序可以查看数据读取之后,是否遭到写入线程的变更,再采取后续的措施。
使用Condition:Condition接口用来搭配Lock,最基本用法就是达到Object的wait()、notify()、notifyAll()方法的作用。
2.并行Collection简介
CopyOnWriteArrayList操作了List接口,CopyOnWriteArraySet操作了Set接口,BlockedQueue是Queue的子接口,ConcurrentMap是Map的子接口.ConcurrentHashMap是ConcurrentMap的操作类,ConcurrentNavigableMap是ConcurrentMap的子接口,其操作类为ConcurrentSkipListMap,可视为支持并行操作的TreeMap版本。
教材学习中的问题和解决过程
问题1:Read()方法作用具体是什么?
解决1:
Read()方法的作用就是从终端命令行中读取字符到java程序中,读取的数据的范围在0~255之间。Read()方法一次只能从终端读取一个字符,这个字符取值范围在byte范围内,但是返回值类型是int型。
不带参数的read()方法在读取字符时,读到用户按下回车键或者数组被填满为止。两种带参数的read()方法中是把读取到的字符存入byte数组中,最后的带有3个参数的read()方方法的解释:后面两个参数指明了读到的字符放入byte数组中的起始位置和最多读取的字符个数。
问题2:字节流和字符流的基类各是什么?
解决2:
流一般分为输入流(Input Stream)和输出流(Output Stream)两类,但这种划分并不是绝对的。比如一个文件,当向其中写数据时,它就是一个输出流;当从其中读取数据时,它就是一个输入流。当然,键盘只是一个数人流,而屏幕则只是一个输出流。
Java的标准数据流标准输入输出指在字符方式下(如DOS),程序与系统进行交互的方式,分为三种:
- 标准输入studin,对象是键盘。
- 标准输出stdout,对象是屏幕。
- 标准错误输出stderr,对象也是屏幕。
问题3:什么是对象的串行化?对象串行化的作用是什么?
解决3:
对象的寿命通常随着生成该对象的程序的终止而终止。有时候,可能需要将对象的状态保存下来,在需要时再将对象恢复。我们把对象的这种能记录自己的状态以便将来再生的能力。叫作对象的持续性(persistence)。对象通过写出描述自己状态的数值来记录自己 ,这个过程叫对象的串行化(Serialization-连续) 。串行化的主要任务是写出对象实例变量的数值。如果变量是另一对象的引用,则引用的对象也要串行化。这个过程是递归的,串行化可能要涉及一个复杂树结构的单行化,包括原有对象、对象的对象、对象的对象的对象等等。对象所有权的层次结构称为图表(graph)。
Java对象的单行化的目标是为Java的运行环境提供一组特性,如下所示:
- 尽量保持对象串行化的简单扼要 ,但要提供一种途径使其可根据开发者的要求进行扩展或定制。
- 串行化机制应严格遵守Java的对象模型 。对象的串行化状态中应该存有所有的关于种类的安全特性的信息。
- 对象的串行化机制应支持Java的对象持续性。
- 对象的串行化机制应有足够的 可扩展能力以支持对象的远程方法调用(RMI)。
- 对象串行化应允许对象定义自身 的格式即其自身的数据流表示形式,可外部化接口来完成这项功能。
代码调试中的问题和解决过程
问题1:为什么更新了IEDA以后,运行程序的时候会出现下图的错误?
解决1:
我在网上查找了相关的“idea source1.3不支持”为主题的博文,根据博主的意思是因为-source 1.3不支持某些代码功能,因此需要更改ProjectStructure。下面是链接:
解决办法:IntelliJ Java:-source 1.3中不支持,使用-source 5 或更高的版本一启用
问题2:*号处为什么输入“&&”的时候没有跳出循环?
import java.io.*;
public class text18 {
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
File f1=new File("c:\\1.txt");
InputStreamReader isr=new InputStreamReader(System.in);
BufferedReader bfrd=new BufferedReader(isr);
FileWriter fopt=new FileWriter(f1);
BufferedWriter bfwt=new BufferedWriter(fopt);
System.out.println("输入姓名+学号(中间用空格):");
String s;
s=bfrd.readLine();
while(true){
*if(s=="&&")break;
bfwt.write(s+"\r\n");
bfwt.flush();
s=bfrd.readLine();
}
}
}
解决2:
if(s=="&&")break修改为if(s.equals("&&"))break;
"=="的是引用地址
代码托管
- 代码提交过程截图:
- 运行 git log --pretty=format:"%h - %an, %cd : %s" 并截图
- 代码量截图:
- 运行 find src -name "*.java" | xargs cat | grep -v ^$ | wc -l 并截图
结对及互评
点评过的同学博客和代码
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 50/100 | 1/2 | 10/20 | |
第二周 | 150/200 | 1/2 | 20/30 | |
第三周 | 168/200 | 1/2 | 15/20 | |
第四周 | 300/500 | 1/2 | 20/20 | |
第五周 | 885/1000 | 1/2 | 30/30 | |
第六周 | 1211/1000 | 1/3 | 30/30 |
参考资料
《Java学习笔记(第8版)》学习指导
Java多线程之Runable与Thread
2016-2017-2 《Java程序设计》教学进程