IO流

IO流

概念

IO流用来处理设备之间的数据传输

Java对数据的操作是通过流的方式

Java用于操作流的类都在IO包中

分类

graph TD A[流向] A --> 输入流 A --> 输出流 B[操作] B --> 字节流 B --> 字符流

字节流 : 字节流可以操作任何数据, 计算机任何数据都是按照字节存储的

字符流 : 字符流只能操作字符数据, 比较方便

常用父类

  • 字节流的抽象父类
    • InputStream
    • OutputStream
  • 字符流的抽象父类
    • Reader
    • Writer

操作流程

graph LR 导入IO包中的类 --> A[进行IO异常处理] A --> 释放资源

字节流

FileInputStrem

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class DemoFileInputStream {

	public static void main(String[] args) throws IOException {
			// demo1();	
		FileInputStream fis = new FileInputStream("Demo.txt");
		int b ;
		while((b = fis.read()) != -1) {
			System.out.println(b);
		}
		fis.close();
	}

	private static void demo1() throws FileNotFoundException, IOException {
		// 创建流对象
		FileInputStream fis = new FileInputStream("Demo.txt");
		
		// 从硬盘上读取一个字节
		int x = fis.read();		
		int y = fis.read();		
		System.out.println(x);
		System.out.println(y); 
		// 结束符是-1
		// 关流释放资源
		fis.close();
	}

}

read()方法返回值为什么int

因为字节输入流可以操作任何类型的文件, 比如图片视频等,这些文件底层都是以二进制形式存储的,如果每次读取都返回byte,有可能读到中间时候遇到11111111,而11111111是byte类型-1,程序遇到-1就会终止,所以使用int, 如果在11111111在前面补上24个0凑足4个字节,那么byte类型的-1就变成int类型的255这样就可以保证整个数据读完,而结束符-1就是int类型

10000001 byte类型-1的原码

11111110 byte类型-1的反码

11111111 byte类型-1的补码

OutputStream

import java.io.FileOutputStream;
import java.io.IOException;

public class DemoFileOutputStream {

	public static void main(String[] args) throws IOException {
		// 创建字节输出流,如果没有就自动创建,如果有就会将文件清空 true是否追加,
		FileOutputStream fos = new FileOutputStream("Test.txt",true);
        // 虽然写出的是int类型的数,但是到文件上的是一个字节,会自动取出前三个八位转换成byte类型
		fos.write(97);
		fos.write(98);
		fos.write(99);
		
		fos.close();
	}
}

字节数组拷贝

available()方法

  • int read(byte[] b): 一次读取一个字节数组
  • write(byte [] b): 一次写入一个字节数组
  • available() 获取文件中所有的字节个数
# 此方法在copy大文件时可能会导致内存溢出
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DemoCopy {
	public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream("Demo.txt");
		FileOutputStream fos = new FileOutputStream("Copy.txt");
		
		// 创建与文件相同大小的字节数组
		byte [] arr = new byte[fis.available()];
		fos.write(fis.read(arr));
		
		fis.close();
		fos.close();
		System.out.println("Okay");
	}
}

小数组copy

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DemoCopy {
public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream("Demo.txt");
		FileOutputStream fos = new FileOutputStream("Copy.txt");
		
		// 创建与文件相同大小的字节数组
		byte [] arr = new byte[1024 * 8];
		int len;
		
		// 如果没有加arr,返回的就不是字节个数,而是字节码标值
		while((len = fis.read(arr)) != -1) {
			fos.write(arr, 0, len);
		}
		
		fis.close();
		fos.close();
		System.out.println("Okay");		
	}
}

缓冲区Copy

缓冲区:

  • 字节流一次读写一个数组的速度明显快于一个字节的读写速度
  • 8192字节

BufferedInputStream

  • BufferedInputStream内置了一个缓冲区(数组)
  • 从BufferedInputStream中读取一个字节时, BufferedInputStream会一次性性文件中读取8192个字节,存在缓冲区中,返回给程序
  • 程序在读时,就不用找文件了,直接从缓冲区中获取
  • 直到缓冲区中的字节都获取完,才从新从文件中读取8192个字节

BufferedOutputStream

  • BufferedOutputStream内置了一个缓冲区(数组)
  • 程序向流中写出字节时,不会直接写到文件, 先写到缓冲区中
  • 直到缓冲区写满,BUfferedOutputStream才会把缓冲区中的数据一次性写到文件里
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DemoBufferCopy {
	public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream("Demo.txt");
		FileOutputStream fos = new FileOutputStream("Copy.txt");
		
		BufferedInputStream bis = new BufferedInputStream(fis);
		
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		
		int b ;
		while((b = bis.read()) != -1) {
			bos.write(b);
		}
		bis.close();
		bos.close();	
	}
}

比较读写速度

  • 小数组的读写速度快于Buffer
  • 小数组操作一个数组
  • Buffer操作两个数组

flush()方法

用来刷新缓冲区,刷新后再次写出

close()方法

用来关闭流释放资源的, 如果是带缓冲区的流对象的close()方法, 不但会关闭流,还会在关闭流之前刷新缓冲区,将缓冲区中的字节全部刷新到文件上去,关闭后不能再写入

中文问题

字节流读取中文

  • 字节流在读中文的时候可能会读半个中文,造成乱码

字节流写出中文

  • 字节流直接操作字节,所以写出中文必须将字符串转化成字节数组
  • 写出回车换行write(" ".getBytes());
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DmeoChinese {
	public static void main(String[] args) throws IOException {
		try (
				BufferedInputStream bis = new BufferedInputStream(new FileInputStream("Demo.txt"));
				BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("Copy.txt"));
				){
			byte[] arr = new byte[4];
			int b;
			while ((b = bis.read(arr)) != -1) {
				System.out.println(new String(arr, 0, b));
			}
			bos.write("你好
".getBytes());
		}	
	}
}

图片加密

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DemoTest1 {
	public static void main(String[] args) throws IOException  {
		try(
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("copy.png"));
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("demo.png"));
		){
			int b;
			while((b = bis.read()) != -1) {
				// 一个数异或两次就等于本身
				bos.write(b ^ 123);
			}
		} 
	}
}

字符流

字符流可以直接读写字符的IO流

字符流读取字符,先读取到字节数据,然后转换成字符,如果要写字符,需要把字符转换成字节在写入

FileReader

FileReader类的read()方法可以按照字符大小读取

import java.io.FileReader;
import java.io.IOException;

public class DemoFileReader {

	public static void main(String[] args) throws IOException {
		FileReader fr = new FileReader("Demo.txt");
		int b;
		while((b = fr.read()) != -1) {
			System.out.println((char)b);
		}
        fr.close();
	}
}

FileWrite

FileWrite类的write()方法可以自动把字符转为字节写入

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class DemoFileReader {

	public static void main(String[] args) throws IOException {
		FileReader fr = new FileReader("Demo.txt");
		FileWriter fw = new FileWriter("Copy.txt");
		int b;
		while((b = fr.read()) != -1) {
			fw.write(b);
		}
		fr.close();
		fw.close();
	}
}

什么情况下使用字符流

字符流也可以拷贝文本文件,但是不推荐使用,因为读取时,会把字节转化为字符,写出时还要把字符转回字节

程序需要读取一段文本,或者需要写出一段文本时候可以使用字符流

读取的时候按照字符的大小进行读取, 不会出现半个中文的情况

写出的时候可以直接将字符串写出,不用转换成字节数组

字符流不可以拷贝非纯文本文件

自定义字符数组拷贝

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class DemoCopy {
public static void main(String[] args) throws IOException {
	FileReader fr = new FileReader("Demo.txt");
	FileWriter fw = new FileWriter("Copy.txt");
	char [] arr = new char[1024];
	int len;
	while((len = fr.read(arr)) != -1) {
		fw.write(arr,0,len);
	}
	fr.close();
	fw.close();
	}
}

带缓冲的字符流

BufferedReader

read()方法

读取字符时,回一次读取若干个字符到缓冲区, 然后逐个返回给程序, 降低读取文件的次数, 提高效率

readline()方法

可以读取一行字符(不包含换行符)

BufferWriter

writer()方法

写出字符时,会先写到缓冲区,缓冲区写满时才会写到文件,降低文件写入的次数,提高效率

newline()方法

可以输出一个跨平台的换行符号" "

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class DemoBuffer {

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new FileReader("Demo.txt"));
		BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.txt"));
		
		
		int c;
		while((c = br.read()) != -1) {
			bw.write(c);
		}
		br.close();
		bw.close();
	}

}

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class DemoBuffer {

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new FileReader("Demo.txt"));
		BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.txt"));
		
		String line;
		while((line = br.readLine()) != null) {
			bw.write(line);
			bw.newLine();
		}
		br.close();
		bw.close();
	}

}

LineNumberReader

LineNumberReader是Buffered对象的子类,具有相同的功能, 并且可以统计行号

方法

  • 调用getLineNumber()方法可以获取当先行号
  • 调用setLineNumber()方法可以设置当前行号
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;

public class DemoLine {
	public static void main(String[] args) throws IOException {
		LineNumberReader lnr = new LineNumberReader(new FileReader("Demo.txt"));
		
		String line;
		
		while((line = lnr.readLine()) != null) {
			System.out.println(lnr.getLineNumber() + " : " +line);
		}
	}

}

装饰设计模式

  • 耦合性不强, 被装饰类的变化与装饰类的变化无关
public class DemoWarp {

	public static void main(String[] args) {
		Mephisto mephisto = new Mephisto(new Student());
		mephisto.code();

	}

}
interface Coder{
	public void code();
}

class Student implements Coder{

	@Override
	public void code() {
		System.out.println("javase");
		System.out.println("javaweb");
	}
	
}

class Mephisto implements Coder{
	// 获取被装饰类的引用
	private Student s;

	// 在构造方法中传入被装饰类的对象
	public Mephisto(Student s) {
		super();
		this.s = s;
	}
	
	// 对原有的功能进行升级
	@Override
	public void code() {
		s.code();
		System.out.println("ssh");
		System.out.println("ssm");
		System.out.println("DB");
		System.out.println("soon");
		
	}
	
}

序列流

可以把多个字节整合成一个, 从序列流中读取数据,将被整合的第一个流开始读, 读完一个之后继续读第二个,一次类推

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;

public class DemoSequenceInputStream {
	public static void main(String[] args) throws IOException {
		SequenceInputStream sis = new SequenceInputStream(new FileInputStream("Demo1.txt"), new FileInputStream("Demo2.txt"));
		
		int b;
		FileOutputStream fos = new FileOutputStream("Copy.txt");
		while((b = sis.read()) != -1) {
			fos.write(b);
		}
		sis.close();
		fos.close();
	}

}

内存输出流

该输出流可以想内存中写数据,把内存当做缓冲区,写出之后可以一次性获取所有数据

使用方法

  • 创建对象 new ByteArrayOutputStream()
  • 写出数据 write(int), write(byte[])
  • 获取数据 toByteArray()
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class DemoByteArrayOutputStream {

	public static void main(String[] args) throws IOException {

		FileInputStream fis = new FileInputStream("Demo1.txt");
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		byte [] arr = new byte[5];
		int len;
		while ((len = fis.read(arr)) != -1) {
			baos.write(arr,0,len);
			
		}
		// 将缓冲区的数据全部获取出来
		System.out.println(baos);
		fis.close();
	}
}

随机访问流

概述

RandomAccessFile类不属于流,是Object类的子类,但是它融合了InputStream和OutputStream的功能

支持对随机访问文件的读写

方法

  • read()
  • write()
  • seek()
import java.io.IOException;
import java.io.RandomAccessFile;

public class DemoRandomAccessFile {

	public static void main(String[] args) throws IOException {
		RandomAccessFile raf = new RandomAccessFile("Demo1.txt", "rw");
		// raf.write(97);
		
		// int x = raf.read();
		
		// System.out.println(x);
		
		raf.seek(0);
		
		raf.write(97);
		
		raf.close();
	}

}

对象操作流

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

public class DemoObjectOutputStream {


	public static void main(String[] args) throws IOException, ClassNotFoundException {
		ArrayList<Person> list = new ArrayList<>();
		list.add(new Person("Mephisto",18));
		list.add(new Person("Mephist",18));
		list.add(new Person("Mephis",18));
		
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Demos.txt"));
		for (Person person : list) {
			oos.writeObject(person);
		}
		
		oos.close();
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Demo.txt"));
		
		for (Person person : list) {
			System.out.println(person);
		}
		ois.close();
	}
}

数据输入输出流

DataInputStream , DataOutputStream可以按照数据类型的大小读写数据

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DemoDataStream {
	public static void main(String[] args) throws IOException {
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("Demo.txt"));
		dos.writeInt(998);
		dos.writeChars("你");
		dos.close();
		
		
		DataInputStream dis = new DataInputStream(new FileInputStream("Demo.txt"));
		int x = dis.readInt();
		char y = dis.readChar();
		
		System.out.println(x +" "+ y);
		dis.close();
	}
}

打印流

该流可以很方便的将对象toString()结果输出,并自动加上换行,而且可以使用自动刷出的模式

System.out就是一个PrintStream,其默认向控制台输出信息

PrintStream和PrintWrite分辨是打印字节流和字符流

只操作数据目的

标准输出输入流

System.in是InputStream,标准输入流, 默认可以从键盘输入读取字节数据

System.out是PrintStream,标准输出流, 默认可以想Console中输出字符和字节数据

修改标准输入输出流

  • 修改标准输入流 System.setIn(InputerStream)
  • 修改标准输出流 System.setOut(OutputStream)
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;

public class DemoSystemInOut {

	public static void main(String[] args) throws IOException {
		// InputStream输入流只有一个
		//InputStream is = System.in;
//		int x = is.read();
//		System.out.println(x);
		
		System.setIn(new FileInputStream("Demo.txt"));
		System.setOut(new PrintStream("Copy.txt"));
		InputStream is = System.in;
		PrintStream ps = System.out;
		int b;
		while((b = is.read()) != -1) {
			ps.write(b);
		}
		is.close();
		ps.close();
	}
}

实现键盘录入两种方式

  • BufferedReader的readLine方法

    • BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  • Scanner

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;


public class DemoSystemIn {

	public static void main(String[] args) throws IOException {
//		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//		String line1 = br.readLine();
//		System.out.println(line1);
//		br.close();
		
		Scanner scanner = new Scanner(System.in);
		String line2 = scanner.nextLine();
		System.out.println(line2);
		scanner.close();
	}
}

Properties

Properties类表示了一个持久的属性集

可以保存在流中或从流中加载

属性列表中每个键及其对应的值都是一个字符串

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Properties;

public class DemoProperties {
	public static void main(String[] args) throws FileNotFoundException, IOException {
		// demo1();
		// demo2();
		Properties properties = new Properties();
		properties.load(new FileReader("demo.properties"));

		Enumeration<String> enumeration = (Enumeration<String>) properties.propertyNames();
		while(enumeration.hasMoreElements()) {
			String key = enumeration.nextElement();
			String value = properties.getProperty(key);
			System.out.println(key + " : " + value);
		}
//		Properties properties1 = new Properties();
//		properties1.store(new FileOutputStream("Demo.txt"),null);
//		properties1.setProperty("name", "Mephisto");
	}

	private static void demo2() {
		Properties properties = new Properties();
		properties.setProperty("name", "Mephisto");
		properties.setProperty("age", "18");
		
		// System.out.println(properties);
		Enumeration<String> enumeration = (Enumeration<String>) properties.propertyNames();
		while(enumeration.hasMoreElements()) {
			System.out.println(properties.getProperty(enumeration.nextElement()));
		}
	}

	private static void demo1() {
		Properties properties = new Properties();
		properties.put("name", "Mephito");
		System.out.println(properties);
	}
}
原文地址:https://www.cnblogs.com/mephisto03/p/9474576.html