1. 处理流PrintStream的简单使用:
1) 处理流最大的特点就是对节点流的包装,因此只要构造器的参数是节点流的一定就是处理流(PrintStream、BufferedStream等),而构造器参数是节点本身的就是节点流(FileInputStream等);
2) 处理流的两大好处:操作简单(对象方法使用很简单,基本源自各编程语言的模型,通用性强)、执行效率更高;
3) PrintStream简介:
i. System.out就是PrintStream,只不过底层包装的节点流是标准输出流(显示屏);
ii. 虽然它是字节流,但是它可以方便输出字符,里面提供了print、println等方法;
!!print系列最强大的地方就是可以输出Java对象(前提是Java对象要实现串行化,否则就默认只能输出类的名字,以及对象哈希码);
iii. 其输出字符的效率要比用PrintWriter输出字符的效率还要高(因为它处理直接就是二进制字节),而字符在处理的时候还要经过一层编解码环节;
iv. 因此输出字符都应该优先考虑使用字节处理流!!
4) 示例:使用PrintStream写文件
public class Test {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
try (
FileOutputStream fis = new FileOutputStream("test");
PrintStream ps = new PrintStream(fis)
) {
ps.println("lalala"); // 输出文本
ps.println(new Test()); // 输出对象
}
catch (IOException e) {
e.printStackTrace();
}
}
}
5) 处理流关闭后底层包装的节点流会自动关闭(其实API中处理流的close就是直接调用包装的节点流的close方法的):void PrintStream.close();
2. Java的I/O体系:
1) Java的I/O体系非常庞大,主要分为两大部分:
i. java.io包下的标准I/O流:InputStream、Reader、OutputStream、Writer、ByteArrayInputStream、PrintStream、BufferedReader等等常见的
ii. JDK其它包下的特殊I/O流:AudioInputStream、ZipInputStream等处理多媒体、加解密、压缩/解压的I/O流;
2) 内容、类型繁多,光是标准I/O流就有40多种,这是因为Java为了实现更好的设计,按照I/O流的功能进行细分了;
3) 虽然东西多,但是非常规律,就按照字节流、字符流分,而每种字节流和字符流又可以分为输入流和输出流,因此你真正需要记忆的大约就是(总数量÷4)那么多就行了,也就是10种作用,用的多了就记住了;
3. 以字符串作为流节点进行I/O:
1) 前面介绍了,既然可以用数组(字节、字符)作为流节点,那么自然会想到用字符串作为流节点专门用来处理字符流;
2) 其Java类是StringReader和StringBuffer,既然是专门处理字符的,因此就没有StringInputStream、StringOutputStream的版本了;
3) 构造器:
i. StringReader的构造器很正常,直接以一个String作为节点:StringReader(String s);
ii. StringWriter的构造器不用传字符串,它内部自己维护这一个StringBuffer作为字符串节点:
public class StringWriter extends Writer {
private StringBuffer buf;
/**
* Create a new string writer using the default initial string-buffer
* size.
*/
public StringWriter() {
buf = new StringBuffer();
lock = buf;
}
...
...
!!由于String是不可变的,而输出流又是需要改变缓冲区内容的,因此输出流需要使用StringBuffer充当流节点(可变);
a. 默认的无参构造器可以看到开辟的缓存空间是0,即从0开始增长;
b. 有参构造器:
public StringWriter(int initialSize) {
if (initialSize < 0) {
throw new IllegalArgumentException("Negative buffer size");
}
buf = new StringBuffer(initialSize);
lock = buf;
}
!!给定一个初始的缓存大小,后期超过了会自动增长;
!!那你可能会问,既然不能自己指定流节点StringBuffer,那有什么意义呢??我就是想查看输出后的结果,但是现在那个StringBuffer是StringWriter的内部成员,难道我还要隔着一层StringWriter来访问吗?
a. StringWriter的toString其实就是返回其StringBbuffer中的字符串内容,因此是可以轻松访问到的;
b. 那上面那个问题还是没有解决,那为什么不可以直接传入一个StringBuffer作为流节点呢?为什么一定要使用内部的StringBuffer呢?
c. 答案其实很简单,Java就是把StringWriter设计成最最高级的StringBuffer,其实就是StringBuffer的强有力升级,StringBuffer对内容进行修改只能通过像什么setCharAt之类的传统方法,而这些方法在几乎所有编程语言中都有提供(类似的),但是StringWriter可以经过处理流包装(PrintWriter)使用更加高级、强大的方法来修改其中的字符,那不就变成了一个可以使用print、println之类的方法来修改字符的高级StringBuffer了吗??
4) 示例:
public class Test {
public static void main(String[] args) throws IOException {
String src = "hello!\nOK\nGoodMorning!\n";
char[] cbuf = new char[128];
int hasRead = 0;
try (StringReader sr = new StringReader(src)) {
while ((hasRead = sr.read(cbuf)) > 0) {
System.out.println(new String(cbuf, 0, hasRead));
}
}
try (StringWriter sw = new StringWriter(20)) {
sw.write("lalala\n");
sw.write("goodgoodgood\n");
System.out.println(sw); // 默认调用了toString方法
}
}
}