Java I/O 流学习总结

一、I/O流基本类的继承关系及简单应用

I/O流按不同的属性可以分为不同的类型:

  • 输入流(InputStream、Reader);输出流(OutputStream、Writer)
  • 用于处理字节的输入输出流(InputStream、OutputStream);用于处理字符的输入输出流(Reader、Writer)

InputStream、OutputStream、Reader和Writer都属于抽象基类,不能直接new对象操纵I/O流。因此,我们必须使用其实现类进行操纵:

1. InputStream常用的实现类:FileInputStream 和 ByteArrayInputStream

public static void main(String[] args) {
	//创建源文件
	File src = new File("D:/Java/abc.txt");
	//选择流
	InputStream is = null;
	try {
		is = new FileInputStream(src);
		//操作
		byte[] flush = new byte[1024];//缓冲容器
		int len = -1;//返回值为-1表示读完了
		while ( (len = is.read(flush)) != -1 ) {
			//字节数组-->字符串(解码)
			String str = new String(flush,0,len);
			System.out.println(str);
		}
	} catch (IOException e) {
		e.printStackTrace();
	}finally {
		//释放
		try {
			if (null != is) {
				is.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

//-------------------------------------------------
public static void main(String[] args) {
	//创建源:字节数组不要太大
	byte[] src = "talk is cheap show me the code".getBytes();		
	//选择流
	InputStream is = null;	
	try {
		//操作
		is = new ByteArrayInputStream(src);
		int len = -1;
		byte[] flush = new byte[5];
		while((len = is.read(flush)) != -1) {
			String str = new String(flush,0,len);
			System.out.println(str);
		}		
	} catch (IOException e) {
		e.printStackTrace();
	}finally {
		//释放
		try {
			if (null != is) {
				is.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

2. OutputStream常用的实现类: FileOutputStream 和 ByteArrayOutputStream

public static void main(String[] args) {
	//1、创建源
	File src = new File("dest.txt");
	//2、选择流
	OutputStream os = null;
	try {
		os = new FileOutputStream(src,true);//追加
		//3、操作
		String str = "hhhhh";
		byte[] datas = str.getBytes();
		os.write(datas, 0, datas.length);
		os.flush();//刷新
	} catch (IOException e) {
		e.printStackTrace();
	}finally {
		try {
			if(null != os) {
				os.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

//--------------------------------------------
public static void main(String[] args) {
	byte[] dest = null;
	//2、选择流
	ByteArrayOutputStream bos = null;
	try {
		bos = new ByteArrayOutputStream();
		//3、操作
		String str = "show me the code";
		byte[] datas = str.getBytes();//编码
		bos.write(datas, 0, datas.length);
		bos.flush();
		//获取数据
		dest = bos.toByteArray();
		System.out.println(new String(dest,0,dest.length));
	} catch (IOException e) {
		e.printStackTrace();
	}finally {
		try {
			if(null != bos) {
				bos.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

3. Reader常用实现类: FileReader

public static void main(String[] args) {
	File src = new File("abc.txt");
	Reader reader = null;
	try {
		reader = new FileReader(src);
		int len = -1;
		char[] flush = new char[1024];
		while((len = reader.read(flush)) != -1) {
			String str = new String(flush,0,len);
			System.out.println(str);
		}
	} catch (IOException e) {
		e.printStackTrace();
	}
}

4. Writer常用实现类: FileWriter

public static void main(String[] args) {
	File src = new File("abc.txt");
	Writer writer = null;
	try {
		writer = new FileWriter(src,true);
		String msg = "jjjjj";
        //第一种方式,转为字符数组进行写
		char[] datas = msg.toCharArray();
		writer.write(datas, 0, datas.length);
        //第二种方式,直接写
		writer.write(msg);
		writer.flush();
	} catch (IOException e) {
		e.printStackTrace();
	}
}

二、加入缓冲流(装饰器)

I/O操作中为加快速度,加入了缓冲流的概念(其本质为I/O流的装饰器,需要以普通的I/O流为修饰对象,实际上操纵数据的还是第一部分讲到的四种基本I/O操作),I/O中的缓冲流有:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter

1.不带缓冲的流的工作原理:

  • 它读取到一个字节/字符,就向用户指定的路径写出去,读一个写一个,所以就慢了。

2.带缓冲的流的工作原理:

  • 读取到一个字节/字符,先不输出,等凑足了缓冲的最大容量后一次性写出去,从而提高了工作效率
  • 优点:减少对硬盘的读取次数,降低对硬盘的损耗,提升输入输出速度。
//以BufferedInputStream为例,其余类似
public static void main(String[] args) {
	//创建源
	File src = new File("abc.txt");
	//选择流
	InputStream is = null;
	try {
		is = new BufferedInputStream(new FileInputStream(src)); 
		//操作,一个字节的读取
		byte[] flush = new byte[1024];
		int len = -1;
		while ( (len = is.read(flush)) != -1 ) {
			String str = new String(flush,0,len);
			System.out.println(str);
		}
	} catch (IOException e) {
		e.printStackTrace();
	}finally {
		//释放
		try {
			if (null != is) {
				is.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

三、转换流:连接字节流与字符流的桥梁

InputStreamReader和OutputStreamWriter是字符和字节的桥梁,也可称之为字符转换流。原理:字节流+编码。

扫描二维码关注公众号,回复: 16515010 查看本文章

FileReader和FileWriter作为子类,仅作为操作字符文件的便捷类存在。当操作的字符文件,使用的是默认编码表时可以不用父类,而直接使用子类完成操作,简化代码。一旦要指定其他编码时,不能使用子类,必须使用字符转换流。否则容易出现乱码的问题!!!

Reader r = new InputStreamReader(System.in,"UTF-8");//指定CharSet防止乱码
  • 这里以操作网络流为例,实现下载文件源码 >网络爬虫的基本操作(爬取百度首页源码),既使用了缓冲流,也实现了转换流
public static void main(String[] args){
	//try  with不用释放资源
	try(BufferedReader read = 
			new BufferedReader(
					new InputStreamReader(
							new URL("http://www.baidu.com").openStream(),"UTF-8"));
			BufferedWriter writer = 
					new BufferedWriter(
							new OutputStreamWriter(
									new FileOutputStream("baidu.html")));
	){
		String line = null;
		while((line = read.readLine()) != null) {
			writer.write(line);
			writer.newLine();
		}
		writer.flush();
	}catch (Exception e) {
		System.out.println("操作异常");
	}
}

四、数据流:DataInputStream 和 DataOutputStream

  • DataInputStreams是InputStream的子类。
  • DataOutputStreams是OutputStream的子类。

以DataOutputStreams为例进行讲解(DataInputStreams类似):此类继承自FillterOutputStream类,继承了close和write方法;同时实现DataOutput接口,在DataOutput接口定义了一系列写入各种数据的方法。DataOutputStreams是对普通流的功能的一个扩展,可以更加方便地读取int,long,字符等类型数据 。

  • 功能: 实现八种基本类型数据的输入/输出。 同时,也可实现字符串的输入/输出。
  • 特点: 八种基本类型的数据在输入/输出时,会保持类型不变。(适合在网络上实现基本类型数据和字符串的传递)
  • 数据流属于处理流,它必须套接在节点流上使用。(与BufferedOutputStream地位类似,使用方法类似)
  • 注意: 数据流在读取与存储时的顺序要一致。否则,读取数据会失真。
public static void main(String[] args) throws IOException {
	//写出
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos));
	dos.writeUTF("啊哈哈");
	dos.writeInt(66);
	dos.writeBoolean(true);
	dos.writeChar('a');
	dos.flush();
	byte[] datas = baos.toByteArray();
	//----------------------------------
	//按写出顺序读入
	ByteArrayInputStream bais = new ByteArrayInputStream(datas);
	DataInputStream dis = new DataInputStream(new BufferedInputStream(bais));
	String s =  dis.readUTF();
	int age = dis.readInt();
	boolean flag = dis.readBoolean();
	char ch = dis.readChar();
	System.out.println(s + age + flag + ch);
}

五、对象流:ObjectInputStream 和 OjbectOutputSteam

用于存储和读取对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。

原理:对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。

  • 序列化(Serialize):用ObjectOutputStream类将一个Java对象写入IO流中(将一个特定的数据结构转换为一组字节的过程)
  • 反序列化(Deserialize):用ObjectInputStream类从IO流中恢复该Java对象(将一组字节转换为特定的数据结构的过程 )
  • 持久化:将数据写入硬盘长久保存的过程称之为持久化
  • 注意:只能将支持 java.io.Serializable 接口的对象写入流中,因此对象必须实现Serializable接口

1.对象类Employee 

public class Employee implements Serializable{
	private int id;
	private transient String ename;//transient:该数据不许要序列化
	private double salary;
	
	public Employee(int id, String ename, double salary) {
		super();
		this.id = id;
		this.ename = ename;
		this.salary = salary;
	}
	
	@Override
	public String toString() {
		return super.toString();
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getEname() {
		return ename;
	}

	public void setEname(String ename) {
		this.ename = ename;
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}
}

2.写出(序列化)与读取(反序列化)

public static void main(String[] args) throws IOException, ClassNotFoundException {
	//写出--》序列化
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(baos));
	oos.writeObject(new Date());
	oos.writeObject(new Employee(11, "dada", 44.2));
	oos.flush();
	//-------------------------------
	//反序列化
	byte[] datas = baos.toByteArray();
	ByteArrayInputStream bais = new ByteArrayInputStream(datas);
	ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(bais));
	//数据还原
	Object date = ois.readObject();
	Object emp = ois.readObject();
	if(date instanceof Date) {
		Date dateObj = (Date)date;
		System.out.println(dateObj);
	}
	if(emp instanceof Employee) {
		Employee empObj = (Employee)emp;
        //name不能输出,因为未序列化
		System.out.println(empObj.getId() + "-" + empObj.getEname());
	}
}

六、使用I/O流实现简单的文件copy操作,创建工具类FileUtils 

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class FileUtils {
	/**
	 * try .. with .. resource,释放资源 jdk9之后
	  *  直接在try(is;os)释放资源
	  *  实现文件copy
	 * @param is:输入
	 * @param os:输出
	 */
	public static void copy(InputStream is, OutputStream os) {
		try {
			//操作
			int len = -1;
			byte[] flush = new byte[1024];
			while((len = is.read(flush)) != -1) {
				os.write(flush,0,len);
			}
			//编码
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			//先打开的后关闭,分别关闭
			close(is,os);
		}
	}
	

	/**
	  * 传入可变参数,来几个closeable接几个
	  * 关闭IO流
	 * @param ios io流组
	 */
	public static void close(Closeable... ios) {
		for(Closeable io : ios) {
			try {
				if(null != io) {
					io.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/Jeff_fei/article/details/103313938