IO流
缓冲流
缓冲流也称为高效流、或者高级流。之前学习的字节流可以称为原始流。作用就是缓冲流自带缓冲区、可以提高原始字节流、字符流读写数据的性能
字节缓冲流:
字节缓冲输入流: BufferedInputStream
字节缓冲输出流:BufferedOutputStream
字节缓冲输入流:public BufferedInputStream(InputStream is) 可以把低级的字节输入流包装成一个高级的缓冲字节输入流管道,从而提高字节输入流读数据的性能,读写功能上并无变化。
字节缓冲输出流:public BufferedOutputStream(OutputStream os) 可以把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高写数据的性能,读写功能上并无变化。
public class ByteBufferDemo {
public static void main(String[] args) {
try (
// 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
// 1、创建一个字节输入流管道与原视频接通
InputStream is = new FileInputStream("D:\\resources\\newmeinv.jpeg");
// a.把原始的字节输入流包装成高级的缓冲字节输入流
InputStream bis = new BufferedInputStream(is);
// 2、创建一个字节输出流管道与目标文件接通
OutputStream os = new FileOutputStream("D:\\resources\\newmeinv222.jpeg");
// b.把字节输出流管道包装成高级的缓冲字节输出流管道
OutputStream bos = new BufferedOutputStream(os);
) {
// 3、定义一个字节数组转移数据
byte[] buffer = new byte[1024];
int len; // 记录每次读取的字节数。
while ((len = bis.read(buffer)) != -1){
bos.write(buffer, 0 , len);
}
System.out.println("复制完成了!");
} catch (Exception e){
e.printStackTrace();
}
}
}
总结:字节缓冲输入流和字节缓冲输出流结合字节数组的方式读写数据非常快,可以说是秒传。
字符缓冲流:
字符缓冲输入流:BufferedReader
字符缓冲输出流:BufferedWriter
字符缓冲输入流:public BufferedReader(Reader r) 可以把低级的字符输入流包装成一个高级的缓冲字符输入流管道,从而提高字符输入流读数据的性能
字符缓冲输出流:public BufferedWriter(Writer w) 可以把低级的字符输出流包装成一个高级的缓冲字符输出流管道,从而提高字符输出流写数据的性能
public class BufferedReaderDemo1 {
public static void main(String[] args) {
try (
// 1、创建一个文件字符输入流与源文件接通。
Reader fr = new FileReader("io-app2/src/data01.txt");
// a、把低级的字符输入流包装成高级的缓冲字符输入流。
BufferedReader br = new BufferedReader(fr);
){
/*2、用循环,每次读取一个字符数组的数据。 1024 + 1024 + 8
char[] buffer = new char[1024]; // 1K字符
int len;
while ((len = br.read(buffer)) != -1) {
String rs = new String(buffer, 0, len);
System.out.print(rs);
}*/
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class BufferedWriterDemo2 {
public static void main(String[] args) throws Exception {
// 1、创建一个字符输出流管道与目标文件接通
Writer fw = new FileWriter("io-app2/src/out02.txt"); // 覆盖管道,每次启动都会清空文件之前的数据
//Writer fw = new FileWriter("io-app2/src/out02.txt", true); // 追加数据
BufferedWriter bw = new BufferedWriter(fw);
//a.public void write(int c):写一个字符出去
bw.write(98);
bw.write('a');
bw.write('徐'); // 不会出问题了
bw.newLine(); // bw.write("\r\n"); // 换行
//b.public void write(String c)写一个字符串出去
bw.write("abc我是中国人");
bw.newLine(); // bw.write("\r\n"); // 换行
//c.public void write(char[] buffer):写一个字符数组出去
char[] chars = "abc我是中国人".toCharArray();
bw.write(chars);
bw.newLine(); // bw.write("\r\n"); // 换行
//d.public void write(String c ,int pos ,int len):写字符串的一部分出去
bw.write("abc我是中国人", 0, 5);
bw.newLine(); // bw.write("\r\n"); // 换行
//e.public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
bw.write(chars, 3, 5);
bw.newLine(); // bw.write("\r\n"); // 换行
// fw.flush();// 刷新后流可以继续使用
bw.close(); // 关闭包含刷线,关闭后流不能使用
}
}
总结:字符缓冲流自带8K缓冲区,可以提高读写数据的性能,将低级的字符流进行包装。字符缓冲输入流多了readLine()按行读取的功能,字符缓冲输出流多了newLine()换行的功能。
测试:出师表文章顺序进行恢复到新文件中
思路理解:
定义一个缓存字符输入流管道与源文件接通
定义一个List集合存储读取的每行数据
定义一个循环按照行读取数据,存入到List集合中去
对List集合中的每行数据按照首字符编号升序排序
定义一个缓存字符输出管道与目标文件接通
遍历List集合中的每个元素,用缓冲输出管道写出并换行
public class BufferedCharTest3 {
public static void main(String[] args) {
try(
// 1、创建缓冲字符输入流管道与源文件接通
BufferedReader br = new BufferedReader(new FileReader("io-app2/src/csb.txt"));
// 5、定义缓冲字符输出管道与目标文件接通
BufferedWriter bw = new BufferedWriter(new FileWriter("io-app2/src/new.txt"));
) {
// 2、定义一个List集合存储每行内容
List<String> data = new ArrayList<>();
// 3、定义循环,按照行读取文章
String line;
while ((line = br.readLine()) != null){
data.add(line);
}
System.out.println(data);
// 4、排序
// 自定义排序规则
List<String> sizes = new ArrayList<>();
Collections.addAll(sizes, "一","二","三","四","五","陆","柒","八","九","十","十一");
Collections.sort(data, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// o1 八.,....
// o2 柒.,....
return sizes.indexOf(o1.substring(0, o1.indexOf(".")))
- sizes.indexOf(o2.substring(0, o2.indexOf(".")));
}
});
System.out.println(data);
// 6、遍历集合中的每行文章写出去,且要换行
for (String datum : data) {
bw.write(datum);
bw.newLine(); // 换行
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
做之前要想好需要一些什么,掌握IO流的使用。把分析列出来。
转换流
字符输入转换流InputStreamReader:
public class InputStreamReaderDemo01 {
public static void main(String[] args) throws Exception {
// 代码UTF-8 文件 GBK "D:\\resources\\data.txt"
// 1、提取GBK文件的原始字节流。 abc 我
InputStream is = new FileInputStream("D:\\resources\\data.txt");
// 2、把原始字节流转换成字符输入流
// Reader isr = new InputStreamReader(is); // 默认以UTF-8的方式转换成字符流。 还是会乱码的 跟直接使用FileReader是一样的
Reader isr = new InputStreamReader(is , "GBK"); // 以指定的GBK编码转换成字符输入流 完美的解决了乱码问题
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
}
}
这个字符输入转换流可以解决字符流读取不同编码乱码的问题。
public InputStreamReader(InputStream is,String charset) 可以指定编码把字节流转换成字符流,这个一定要记住很重要。
字符输出转换流:OutputStreamWriter
public class OutputStreamWriterDemo02 {
public static void main(String[] args) throws Exception {
// 1、定义一个字节输出流
OutputStream os = new FileOutputStream("io-app2/src/out03.txt");
// 2、把原始的字节输出流转换成字符输出流
// Writer osw = new OutputStreamWriter(os); // 以默认的UTF-8写字符出去 跟直接写FileWriter一样
Writer osw = new OutputStreamWriter(os , "GBK"); // 指定GBK的方式写字符出去
// 3、把低级的字符输出流包装成高级的缓冲字符输出流。
BufferedWriter bw = new BufferedWriter(osw);
bw.write("啦啦啦啦1..");
bw.write("啦啦啦啦2..");
bw.write("啦啦啦啦3..");
bw.close();
}
}
字符输出转换流可以指定编码把字节输出流转换成字符输出流,从而实现指定写出去的字符编码
public OutputStreamWriter(OutputStream os) : 用当前默认编码UTF-8把字节输出流转换成字符输出流
public OutputStreamWriter(OutputStream os , String charset):指定编码把字节输出流转换成字符输出流
若果字符流直接读取文本,必须文件和代码编码一致才不会乱码,如果文件和代码编码不一致,读取将出现乱码。这时候使用字符输入转换流可以提取文件的原始字节流,因为原始字节流不会有什么问题他就是最基本的编码,然后把字节流以指定编码转换成字符输入流,这样字符输入流中的字符就不乱码了。然后字符转换输出流可以指定编码把字节输出流转换成字符输出流,从而可以指定写出去的字符编码。
对象序列化
对象字节输出流:ObjectOutputStream 以内存为基准,把内存中的对象存储到磁盘文件中去,称为对象序列化。
public class ObjectOutputStreamDemo1 {
public static void main(String[] args) throws Exception {
// 1、创建学生对象
Student s = new Student("陈磊", "chenlei","1314520", 21);
// 2、对象序列化:使用对象字节输出流包装字节输出流管道
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("io-app2/src/obj.txt"));
// 3、直接调用序列化方法
oos.writeObject(s);
// 4、释放资源
oos.close();
System.out.println("序列化完成了~~");
}
}
对象必须实现序列化接口,transient修饰的成员变量不参与序列化,对象如果要序列化,必须实现Serializable序列化接口。申明序列化的版本号码,序列化的版本号与反序列化的版本号必须一致才不会出错
对象字节输入流:ObjectInputStream 以内存为基准,把存储到磁盘文件中去的对象数据恢复成内存中的对象,称为对象反序列化。
public class ObjectInputStreamDemo2 {
public static void main(String[] args) throws Exception {
// 1、创建对象字节输入流管道包装低级的字节输入流管道
ObjectInputStream is = new ObjectInputStream(new FileInputStream("io-app2/src/obj.txt"));
// 2、调用对象字节输入流的反序列化方法
Student s = (Student) is.readObject();
System.out.println(s);
}
}
直接调用对象字节输入流ObjectInputStream就可以把磁盘中的对象数据恢复到内存的Java对象中。
打印流
打印流可以实现方便、高效的打印数据到文件中去。打印流一般是指:PrintStream,PrintWriter两个类。可以实现打印什么数据就是什么数据,例如打印整数97写出去就是97,打印boolean的true,写出去就是true。
public class PrintDemo1 {
public static void main(String[] args) throws Exception {
// 1、创建一个打印流对象
//PrintStream ps = new PrintStream(new FileOutputStream("io-app2/src/ps.txt"));
//PrintStream ps = new PrintStream(new FileOutputStream("io-app2/src/ps.txt" , true)); // 追加数据,在低级管道后面加True
//PrintStream ps = new PrintStream("io-app2/src/ps.txt" );
PrintWriter ps = new PrintWriter("io-app2/src/ps.txt"); // 打印功能上与PrintStream的使用没有区别
ps.println(97);
ps.println('a');
ps.println(23.3);
ps.println(true);
ps.println("我是打印流输出的,我是啥就打印啥");
ps.close();
}
}
共同点和不同点:
打印数据功能上是一模一样的,都是使用方便,性能高效(核心优势)
PrintStream继承自字节输出流OutputStream,支持写字节数据的方法。
PrintWriter继承自字符输出流Writer,支持写字符数据出去。
输出语句重定向:
属于打印流的一种应用,可以把输出语句的打印位置改到文件。
PrintStream ps = new PrintStream("文件地址")System.setOut(ps);
只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。输入流只能进行读操作,输出流只能进行写操作,程序中需要根据待传输数据的类型特性决定使用那种流。Input读取是为了读给程序用,Output写出就是把文件写到硬盘上。