带行号的缓冲区:LineNumberReader
操作基本数据类型的流:DataInputStream/DataOutputStream
用于处理临时存储信息的,程序结束,数据就从内存中消失。
通过查看源码我们知道close()什么都没做,所以根本不需要close()。
操作字节数组:ByteArrayInputStream/ByteArrayOutputStream
操作字符数组:CharArrayReader/CharArrayWrite
操作字符串:StringReader/StringWriter
字节流打印流:PrintStream
字符流打印流:PrintWriter
public static void copy(String srcString, String destString) throws IOException {
// 封装数据源
BufferedReader br = new BufferedReader(new FileReader(srcString));
// 封装目的地
PrintWriter pw = new PrintWriter(new FileWriter(destString), true);
String line = null;
while ((line = br.readLine()) != null) {
pw.println(line);
}
pw.close();
br.close();
}
特点
只有写数据的,没有读取数据。只能操作目的地,不能操作数据源。
可以操作任意类型的数据。
如果启动了自动刷新,能够自动刷新。
PrintWriter pw = new PrintWriter(new FileWriter(destString), true);
//还是应该调用println()的方法才可以,print()不可以,这个时候不仅仅自动刷新了,还实现了数据的换行。
pw.println(line);
该流是可以直接操作文本文件的。
哪些流对象是可以直接操作文本文件的呢?
FileInputStream,FileOutputStream
FileReader,FileWriter
PrintStream,PrintWriter
看API,查流对象的构造方法,如果同时有File类型和String类型的参数,一般来说就是可以直接操作文件的。
基本流:就是能够直接读写文件的。
高级流:在基本流基础上提供了一些其他的功能。
System类中的字段
in:标准输入流
out:标准输出流
它们各代表了系统标准的输入和输出设备。默认输入设备是键盘,输出设备是显示器。
三种键盘录入方式
main方法的args接收参数
System.in通过BufferedReader进行包装:BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
Scanner sc = new Scanner(System.in);
输出语句的原理和如何使用字符流输出数据
原理:
System.out.println("helloworld");
PrintStream ps = System.out;
ps.println("helloworld");
把System.out用字符缓冲流包装一下使用:BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
RandomAccessFile类不属于流,是Object类的子类。但它融合了InputStream和OutputStream的功能。支持对随机访问文件的读取和写入。
SequenceInputStream类可以将多个输入流串流在一起,合并为一个输入流,因此,该流也被称为合并流。
构造方法,把多个文件的内容写入到一个文本文件:
SequenceInputStream(InputStream s1, InputStream s2)
SequenceInputStream(Enumeration extends InputStream> e)
序列化流:把对象按照流一样的方式存入文本文件或者在网络中传输。对象 --> 流数据(ObjectOutputStream)
反序列化流:把文本文件中的流对象数据或者网络中的流对象数据还原成对象。流数据 --> 对象(ObjectInputStream)
NotSerializableException:未序列化异常
类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
该接口没有任何方法,类似于这种没有方法的接口被称为标记接口。
我们在实际开发中,可能还需要使用以前写过的数据,不能重新写入怎么办呢?
使用transient关键字声明不需要序列化的成员变量。
属性集合类,是一个可以和IO流相结合使用的集合类。
Properties可保存在流中或从流中加载,属性列表中每个键及其对应值都是一个字符串。
是Hashtable的子类,说明是一个Map集合。
方法
Object setProperty(String key,String value)//添加元素
String getProperty(String key)//获取元素
String stringPropertyNames()//获取所有的键的集合
Properties和IO流的结合使用,把键值对形式的文本文件内容加载到集合中
void load(Reader reader)//把文件中的数据读取到集合中,这个文件的数据必须是键值对形式
void load(InputStream inStream)
void store(Writer writer,String comments)//把集合中的数据存储到文件
public void store(OutputStream out,String comments)
JDK7的之后的NIO方法:
public static Path get(URI uri)//Paths类:通过静态方法返回一个路径
public static long copy(Path source,OutputStream out)//Files类:提供了静态方法供我们使用,复制文本文件
例如:
Files.copy(Paths.get("ByteArrayStreamDemo.java"), new FileOutputStream("Copy.java"));
public static Path write(Path path,Iterable extends CharSequence> lines,Charset cs,OpenOption... options)//把集合中的数据写到文本文件
例如:
ArrayList array = new ArrayList();
array.add("hello");
array.add("world");
array.add("java");
Files.write(Paths.get("array.txt"), array, Charset.forName("GBK"));
IO是面向流的,NIO是面向缓冲的;
IO是阻塞的,NIO是非阻塞的;
IO是单线程的,NIO 是通过选择器来模拟多线程的;
面向流与面向缓冲
Java IO和NIO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
阻塞与非阻塞IO
Java IO的各种流是阻塞的。这意味着,当一个线程调用read()或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
选择器(Selectors)
Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。