IO流详解(字符流与字节流)

一、字符流与字节流
1.标准的输入输出流概述
IO流详解(字符流与字节流)_第1张图片
IO流详解(字符流与字节流)_第2张图片
标准输入流in
public static final InputStream in   in为System类中的一个静态成员变量,为字节输入流,用来读取键盘录入的数据;
使用:

InputStream is = System.in;   //创建InputStream类型的对象
Scanner sc = new Scanner(System.in);

注意:这里的标准输入流in作为引用类型修饰的成员变量,区别于普通的:public static final int x;
标准输出流out
public static final PrintStream out   out也为System类中的一个静态变量,为字节输出流,将数据输出到命令行;
使用:

System.out.println();  //比较熟悉了

2.OutputStreamWriter的概述和使用
概述:由于标准输出流是一个字节输出流,所以只能输出字节或者字节数组,这里的高效输入流BufferedReader从硬盘中读取到的是字符串,存入内存中,如果想输出,还需要转换成字节数组。因此要想通过标准输出流输出字符串,可使用OutputStreamWriter类将标准输出流转换成字节输出流。
IO流详解(字符流与字节流)_第3张图片
在这里插入图片描述

	public static void main(String[] args) throws IOException {
		//创建输入流对象
		BufferedReader br = new BufferedReader(new FileReader("Student.java"));
		//创建输出流对象
		Writer w = new OutputStreamWriter(System.out);
		BufferedWriter bw = new BufferedWriter(w);
		
		//进行数据的读写
		String line;    //定义字符串存储读取的字符串
		while((line = br.readLine()) != null) {
			bw.write(line);   //将字符串输出到命令行
			bw.newLine();   //解决换行符问题
		}
		
		//释放资源
		br.close();
		bw.close();		
	}	

3.InputStreamReader的概述和使用
概述:标准输入流是一个字节输入流,所以在使用字符输出流将读取到的字节写入文件时,需要将字节数组转换成字符串,这里使用创建String对象的方式实现,然后相当于对字符串进行写入操作;

	public static void main(String[] args) throws IOException {
		//创建输入流对象
		InputStream is = System.in;   //这里创建字节输入流对象
		//创建输出流对象
		FileWriter fw = new FileWriter("a.txt");  //这里创建字符输出流对象
		
		//进行数据的读写
		byte[] bys = new byte[1024];   //以字节数组的方式读写
		int len;

		while((len = is.read(bys)) != -1) {
			fw.write(new String(bys, 0, len));//写数据,这里的new String(bys, 0, len)是将读取到的字节数组转换成字符串,方便write写入
			fw.flush();   //刷新缓冲区

		}
		
		//释放资源
		is.close();
		fw.close();		
	}	

使用InputStreamReader类将字节流对象转换成字符输入流对象,直接利用字符数组方式进行数据的读写:

	public static void main(String[] args) throws IOException {
		//创建输入流对象
		InputStream is = System.in;   //这里创建字节输入流对象
		Reader r = new InputStreamReader(is);   //将字节输入流对象转换成字符输入流对象
		//创建输出流对象
		FileWriter fw = new FileWriter("a.txt");  //这里创建字符输出流对象
	
		//进行数据的读写
		char[] chs = new char[1024];   
		int len;

		while((len = r.read(chs)) != -1) {
			fw.write(chs, 0 ,len);//写数据,这里直接写入字符数组
			fw.flush();   //刷新缓冲区
		}
		
		//释放资源
		is.close();
		fw.close();		
	}	

二、打印流
1.打印流的概述
IO流详解(字符流与字节流)_第4张图片
概述:向文本输出流打印对象的格式化表示形式,此类包含 PrintStream 中的所有 print 方法,它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入。 与 PrintStream 类不同,如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作,而不是每当正好输出换行符时才完成。这些方法使用平台自有的行分隔符概念,而不是换行符。

此类中的方法不会抛出 I/O 异常,尽管其某些构造方法可能抛出异常。客户端可能会查询调用 checkError() 是否出现错误。

	public static void main(String[] args) throws IOException {
		//创建打印流对象
		PrintWriter pw = new PrintWriter("a.txt");
		
		//写出数据
		pw.write("hello");
		pw.write("world");
		pw.write("java");
		
		//释放资源
		pw.close();
	}	

2.打印流特有的功能
自动换行:使用方法println()取代write()实现自动换行;
自动刷新:创建PrintWriter对象时,可以利用构造方法启动刷新开关,同时必须使用println等3方法才能实现自动刷新;

注意:创建FileWriter对象时构造方法中的boolean类型参数是是否追加;
   创建打印流对象时构造方法中的boolean类型参数是是否自动刷新;
代码如下:(实现的功能:将几个字符串hello、world、java打印到文件a.txt中)

	public static void main(String[] args) throws IOException {
		//创建打印流对象
		PrintWriter pw = new PrintWriter(new FileWriter("a.txt"),true);
		
		//写出数据
		pw.println("hello");
		pw.println("world");
		pw.println("java");
		
		//释放资源
		pw.close();
	}	

3.使用打印流复制文本文件
注:对于文本文件的复制,首先要明确数据源和目的地
数据源:a.txt
目的地:d:\a.txt

	public static void main(String[] args) throws IOException {
		//创建输入流
		BufferedReader br = new BufferedReader(new FileReader("a.txt"));   //从数据源输入到缓冲区
		//创建打印流对象
		PrintWriter pw = new PrintWriter(new FileWriter("d:\\a.txt"),true);   //从缓冲区输出到目的地
		
		//读写数据
		String line;
		while((line = br.readLine())!= null) {
			pw.println(line);
		}
		
		//释放资源
		pw.close();
	}	

三、对象操作流
1.对象操作流概述
对象操作流:可以用于读写任意类型的对象;
ObjectOutputStream:
  writeObject:用于将内存中的对象写入流中(这里的流指对应的硬盘中文件,比如.txt)
  ObjectOutputStream(OutputStream out):构造方法

ObjectInputStream:
  readObject:用于从流中读取对象到内存中(这里的流指对应的硬盘中文件,比如.txt)
  ObjectInputStream(InputStream in):构造方法
  
注意:只能使用对象输出流写出对象,使用对象输入流来读取对象;
   只能将支持实现 java.io.Serializable 接口的对象写入流中。
   
2.使用对象操作流读写对象
使用对象输出流写对象(写到.txt文件中)
注:这里的a.txt文件在执行代码后,会直接在项目根目录创建,不用自己另外创建

	public static void main(String[] args) throws IOException {
		//创建对象输出流的对象
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
		
		//创建学生对象
		Student s1 = new Student("hxf",25);
		Student s2 = new Student("ddd",24);
		
		//写学生对象到.txt文件
	    oos.writeObject(s1);
	    oos.writeObject(s2);
		
		//释放资源
		oos.close();
	}	

使用对象输入流读对象(将.txt文件中的student对象写到内存中,并输出到命令行)

	public static void main(String[] args) throws IOException, ClassNotFoundException {
		//创建对象输入流的对象
		ObjectInputStream iis = new ObjectInputStream(new FileInputStream("a.txt"));

		//读取对象
		try {   
			while(true) {
				Object obj = iis.readObject(); //这里抛出找不到类异常,即有可能Student类被删掉了
				System.out.println(obj);
			}
		}
		catch(EOFException e) {
			System.out.println("读到结尾了");
		}
		
		//释放资源
		iis.close();
	}	

分析:首先创建对象输入流对象,然后使用try…catch…语句,因为可能会出现对象读取到结尾继续往后读的异常,我们需要捕获该异常,try中使用while死循环,当读取到结尾后出现异常,即跳出该死循环,最后释放资源。

3.使用对象操作流读写集合对象
使用对象输出流写集合对象(写到.txt文件中)

		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));//创建对象输出流的对象
		ArrayList<Student> list = new ArrayList<Student>();   //创建集合
		list.add(new Student("hxf",25));
		list.add(new Student("ddd",24));
		oos.writeObject(list);   //将集合写入内存(.txt文件)
		oos.close();

使用对象输入流读集合对象(将集合对象读到内存中)

	public static void main(String[] args) throws IOException, ClassNotFoundException {
		//创建对象输入流的对象
		ObjectInputStream iis = new ObjectInputStream(new FileInputStream("a.txt"));

		//读取对象
		Object obj = iis.readObject();
		System.out.println(obj);
		
		//获取集合对象,可以进行集合操作
		ArrayList<Student> list = (ArrayList<Student>)obj;

		//释放资源
		iis.close();	
	}

4.序列化接口出现的ID不匹配的问题
需要进行对象读写的类必须实现接口java.io.Serializable,该接口作为一种表示(其中不含任何抽象方法),实现该接口的类会根据该类自身的成员变量、方法生成一个ID(serialVersionUID),若成员变量发生变化,则类中该ID也会发生变化;这时,如果我们将Student类对象写入.txt文件中,对应有一个serialVersionUID,若此时在Student类中增加一个成员变量,则Student类中的serialVersionUID发生变化,这时使用(读取也算使用).txt文件中的Student对象则会出现serialVersionUID与定义的Student不匹配问题(报错),所以采用以下方式解决,一开始就将serialVersionUID定义为一个常量:

private static final long serialVersionUID = 4414407504887034035L;

四、Properties
1.Properties概述
IO流详解(字符流与字节流)_第5张图片
概述:Properties 类表示了一个持久的属性集(比如把内存中的键和值存到硬盘中的文件中),Properties 可保存在流中或从流中加载,属性列表中每个键及其对应值都是一个字符串。
构造方法
在这里插入图片描述
继承自:类 Hashtable
IO流详解(字符流与字节流)_第6张图片
与HashMap的区别
Hashtable:非 null 对象可以用作键或值;是同步的,安全性高,效率低;
HashMap:可以存null的的对象作为键或值;是非同步的,安全性低,效率高;

Properties 类使用举例

	public static void main(String[] args) throws IOException, ClassNotFoundException {
		//创建属性列表
		Properties prop = new Properties();
		//添加映射关系
		prop.put("001", "hxf");
		prop.put("002", "ddd");
		
		//遍历属性列表,两种方法
		//第一种,获取所有key,通过key获取value
		Set<Object> keys = prop.keySet();
		for(Object key:keys) {
			Object value = prop.get(key);
			System.out.println("key:"+key+" "+"value:"+value);
		}
		
		//第二种,利用Map.Entry创建的对象
		Set<Map.Entry<Object, Object>> entrys = prop.entrySet();
		for(Map.Entry<Object, Object> entry:entrys) {
			Object key = entry.getKey();
			Object value = entry.getValue();
			System.out.println("key:"+key+" "+"value:"+value);
		}
	}	

2.Properties和IO流结合的功能
1.void list(PrintWriter out) :将属性列表输出到指定的输出流。

		//创建属性列表
		Properties prop = new Properties();
		
		//添加映射关系
		prop.setProperty("001", "hxf");
		prop.setProperty("002", "ddd");
		
		//创建打印流对象
		PrintWriter out = new PrintWriter("c.txt");
		prop.list(out);
		
		//释放资源
		out.close();

运行结果:
在这里插入图片描述
2. void load(Reader reader) :按简单的面向行的格式从输入字符流中读取属性列表(键值对)。

		//创建属性列表
		Properties prop = new Properties();
		
		//创建输入流对象
		FileReader fr = new FileReader("c.txt");
		prop.load(fr);
		System.out.println(prop);
		
		//释放资源
		fr.close();

运行结果:
在这里插入图片描述
3. void store(Writer writer, String comments) :将此 Properties 表中的属性列表(键和元素对)写入到字符输出流。

		//创建属性列表
		Properties prop = new Properties();
		
		//添加映射关系
		prop.setProperty("001", "hxf");
		prop.setProperty("002", "ddd");
		
		//创建输出流对象
		FileWriter fw = new FileWriter("d.txt");
		
		//使用void store(Writer writer, String comments)  
		prop.store(fw,"use store");
		
		//释放资源
		fw.close();

运行结果:
IO流详解(字符流与字节流)_第7张图片
五、编码
1.编码表的概述
编码表作用:把计算机底层的二进制数据转换成我们能看到的字符;
常见编码表
ASCII:最早的编码表
GB2312 - - GBK:中国使用的码表
Unicode:所有字符都占2个字节
UTF - 8:长度可变的码表

Java中默认使用:Unicode

注:ANSI为本地编码表,跟操作系统的环境、语言等有关。

2.Java中字符串的编码
Java中字符串默认编码:ANSI(对于简体中文系统为:GBK)
解决乱码的方式:读写(或者说编解码)都采用同一种编码方式,则不会出现乱码。

	public static void main(String[] args) throws IOException, ClassNotFoundException {
		String s = "高薪就业";    //默认将字节以GBK的方式编码成字符串
		byte[] bys = s.getBytes("UTF-8");  //通过指定"UTF-8"方式将字符串类型转换(解码)成字节数组,这里
		
		//创建输出流对象
		FileOutputStream fos = new FileOutputStream("a.txt");
		fos.write(bys);
		
		//创建输入流对象
		FileInputStream fis = new FileInputStream("a.txt");
		byte[] bys1 = new byte[1024];
		int len;
		while((len = fis.read(bys1))!=-1) {
			System.out.println(new String(bys1,0,len,"UTF-8"));  //将读取到的字节数组使用"UTF-8"的方式编码成字符串
		}
		
		//释放资源
		fos.close();
		fis.close();		
	}	

分析:这里解码使用“UTF-8”的方式将字符串解码成字节数组,编码也用“UTF-8”的方式将字节数组编码成字符串,所以不会出现乱码。

3.Java字符流中的编码
问题:下面的代码是使用默认的GBK的编码方式,正常输出会在b.txt中写入“北京”,若此时将b.txt的编码方式改为“UTF-8”,则会出现乱码

	    FileWriter fw = new FileWriter("b.txt");
	    String s = "北京";
	    fw.write(s);
	    fw.close();

解决:
OutputStreamWriter 是字符流通向字节流的桥梁,其包含一个构造方法,可以指定编码方式:
在这里插入图片描述

        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"),"UTF-8");
        String s = "北京";
        osw.write(s);
        osw.close();

分析:由于将要写入的.txt文件的编码方式为“UTF-8”,所以输出流也需要以“UTF-8”的方式写入数据到该文件,标准输出流OutputStreamWriter 具有指定编码方式的功能。

你可能感兴趣的:(java基础)