IO流简述

1.IO流初识

IO就是input和output,所以IO流就可以理解为输入输出流。
而流是指一连串流动的字符,是以先进先出方式发送信息的通道。
IO流的作用就是当我们的Java程序需要和外界(文件,程序…)进行通信时为我们提供一种机制,而这种机制就是IO流。

1.1IO流分类

按流向分:输出流:OutputStream和Writer为基类
输入流:InputStream和Reader为基类
按处理数据单元划分:
字节流:
字节输入流:InputStream基类
字节输出流:OutputStream基类
字符流:
字符输入流:Reader基类
字节输出流:Writer基类
(字节流是 8 位通用字节流,字符流是16位Unicode字符流)
在这里插入图片描述

2.字节流

分为,字节输出流(Outputstream)和字节输入流(Inputstream)
在这里插入图片描述

2.1字节输出流(OutputStream)

java.io.OutputStream是一个抽象类,也是所有字节输出流的父类。作用是将指定的内容写入到目的地。
关于字节输出流的基本方法

  1. public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
  2. public void flush():刷新此输出流并强制任何缓冲的输出字节被写出。
  3. public void write(byte[] b):将 b.length个字节从指定的字节数组写入此输出流。
  4. public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。 也就是说从off个字节数开始读取一直到len个字节结束
  5. public abstract void write(int b) :将指定的字节输出流。
FileOutputStream
我们学习io流最主要的就是为了文件的传输与接受,所以我们以FileOutputStream为例。
首先是FileOutputStream的构造方法
  1. public FileOutputStream(File file):根据File对象为参数创建对象。
  2. public FileOutputStream(String name): 根据名称字符串为参数创建对象。
FileOutputStream outputStream = new FileOutputStream("abc.txt");//1

File fil = new File("abc.txt");
FileOutputStream outputStream = new FileOutputStream(fil);//2

注意:在创建一个输出流对象时就是在为你的内容创建一个目的地,而且即使你没有该文件,系统也会为你自动创建出该文件。同时如果该文件里已经有数据则该数据将会被清空。
FileOutputStream写出数据的方法write();

  1. public void write(int b)
  2. public void write(byte[] b)
  3. public void write(byte[] b,int off,int len) //从off索引开始,len个字节

前面两个我就不写代码了,就是正常的调用,我写一下第三种的。

    public static void main(String[] args) throws IOException {
        FileOutputStream f = new FileOutputStream("abc.txt");     
      	byte[] b = "abcde".getBytes();
        f.write(b,2,2);
        f.close();
    }

结果为:cd(也就是从下标为2的开始,取两个字节)
注意:io流使用完毕后,必须调用close方法关闭,否则会造成系统资源浪费。
FileOutputStream实现数据追加续写
因为每次创建一个输出流都会对目标文件的内容进行清空,所以有两种方法可以实现续写。

  1. public FileOutputStream(File file, boolean append)

  2. public FileOutputStream(String name, boolean append)

代码

  public static void main(String[] args) throws IOException {
        FileOutputStream f = new FileOutputStream("abc.txt"true);     
      	byte[] b = "abcde".getBytes();
        f.write(b);
        f.close();
    }

原文件:cd
现文件:cdabcde
也就是说true代表保留原文件内容,false(默认)清空原文件内容

2.2字节输入流(InputStream)

java.io.InputStream是一个抽象类,也是所有字节输入流的父类。作用是将读取目标文件的内容。
另外,对于所有的输入流而言,你所写的文件名必须存在,否则会报异常。
构造方法与字节输出流相同。

关于字节输入流的基本方法

  1. public void close() :关闭此输入流并释放与此流相关联的任何系统资源。
  2. public abstract int read(): 从输入流读取数据的下一个字节,读取不到返回-1
  3. public int read(byte[] b): 该方法返回的int值代表的是读取了多少个字节,读到几个返回几个,读取不到返回-1

主要讲一下后面两个方法
read():

 public static void main(String[] args) throws IOException{
       	FileInputStream fil = new FileInputStream("abc.txt");
         int b ;
        while ((b = fil.read())!=-1) {
            System.out.println((char)b);
        }
        fil.close();
    }
    结果:cda

read(byte[ ] b):

public static void main(String[] args) throws IOException{
       	FileInputStream fil = new FileInputStream("abc.txt"); 
        int len ; 
        byte[] b = new byte[3];
        while (( len= fil.read(b))!=-1) {
            System.out.println(new String(b));
        }
        fil.close();
    }
    结果:cda
    	bcd
    	ecd

注意:原文件abc.txt是cdabcde,多出来的cd是因为在打印是输出的是数组,当达到e后数组中的值都是已经存在的,是后面的数顶掉了前面的,由于e后面已经没有字节了,就会保留原来的字节。所以我们用就会用到另一种方法

 public static void main(String[] args) throws IOException{
       	FileInputStream fil = new FileInputStream("abc.txt"); 

        int len ;  
        byte[] b = new byte[3];
        
        while (( len= fil.read(b))!=-1) {
            System.out.println(new String(b,0,len));
        }
        fil.close();
    }
    结果为;cda
    	bcd
    	e

在Java中使用数组读取文件的效率更高

3.字符流(Reader和Writer)

字符流的由来:因为数据编码的不同,因而有了对字符进行高效操作的流对象,字符流本质其实就是基于字节流读取时,去查了指定的码表,而字节流直接读取数据会有乱码的问题(读中文会乱码),必须借用String类才能正常输出。
构造方法都是:

  1. 类名(File file)创建一个新的 FileReader ,给定要读取的File对象
  2. 类名 (String fileName): 创建一个新的 FileReader ,给定要读取的文件的字符串名称。

3.1Reader(字符输入流)

java.io.Reader抽象类是字符输入流的所有类的超类(父类),作用是读取字符信息到内存中。
常见的方法

  1. public void close() :关闭此流并释放与此流相关联的任何系统资源。
  2. public int read():从输入流读取一个字符。
  3. public int read(char[] cbuf): 从输入流中读取一些字符,并将它们存储到字符数组cbuf中

上面两个同InputStream用法相同,只是读取的从字节变成了字符
read(char[] cbuf):
在这里插入图片描述

3.2Writer(字符输出流)

java.io.Writer抽象类是字符输出流的所有类的父类,作用是将指定的字符信息写出到目的地。
字符输出流的基本方法:
1、void write(int c) 写入单个字符。
2、void write(char[] cbuf)写入字符数组。
3、 abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
4、 void write(String str)写入字符串。
5、void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
6、void flush()刷新该流的缓冲。
7、void close() 关闭此流,但要先刷新它。
以FileWriter为例
字符输出流与字节输出流不同之处在于。字节输出流如果没有close资源,内容可以保存到文件,但是字符输出流不会保存到文件。而是在缓冲区。
关于close和flush
因为存在内置的缓冲区,所以我们不关闭数据是无法保存到文件中,此时我们就需要用到flush方法——刷新,意思就是,将缓冲区的内容保存到文件中,使得缓冲区清空。此时就可以实现同时使用流和保存数据。
以图为例
在这里插入图片描述
在这里插入图片描述
这里报出异常,显示流已经关闭
在文件中内容和之前是一样的。

写在最后:字符流,只能操作文本文件,不能操作图片,视频等非文本文件。当我们单纯读或者写文本文件时 使用字符流 其他情况使用字节流

4.转换流

由来:因为字符编码的种类很多,所以经常会出现编码表不匹配导致数据乱码的情况,所以出现了转换流
在这里插入图片描述

4.1InputStreamReader类——字节流到字符流

构造方法

  1. InputStreamReader(InputStream in): 创建一个使用默认字符集的字符流。
  2. InputStreamReader(InputStream in, String charsetName): 创建一个指定字符集的字符流。

以代码为例

在这里插入图片描述
我的文件编码为UTF-8,默认编码也是UTF-8,所以可以正常读取,而当使用GBK是就是乱码。同理当你电脑的文件编码为其他时,使用默认也会出现乱码。

4.2OutputStreamWriter类——字符流到字节流

构造方法
OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字符流。
OutputStreamWriter(OutputStream in, String charsetName): 创建一个指定字符集的字符流。

作用就是对目标输出指定编码的数据
以代码为例
在这里插入图片描述
uzi文件
在这里插入图片描述

uzi2文件
在这里插入图片描述

5.对象流和对象序列化、持久化

5.1对象流

有的时候,我们可能需要将内存中的对象持久化到硬盘上,或者将硬盘中的对象信息读到内存中,这个时候我们需要使用对象输入输出流。

5.2对象序列化与反序列化、持久化

用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。
持久化就是将对象永久保存,借助于序列化。

5.2.1ObjectOutputStream类

java.io.ObjectOutputStream 类,作用时将Java对象的原始数据类型写出到文件,实现对象的持久存储。
构造方法:
public ObjectOutputStream(OutputStream out): 创建一个指定OutputStream的ObjectOutputStream。
实现序列化:
以代码为例

public class People implements java.io.Serializable {
 /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
    public String name;
    public String birthday;
    public transient int age;
    public void getname() {
      	System.out.println(name);
    }
}
public class Test{
   	public static void main(String [] args)   {
    	People p = new People();
    	p.name = "zhangsan";
    	p.birthday= "5th July";
    	p.age = 20; 
    	try {
            FileOutputStream fileOut = new FileOutputStream("people.txt");
    		ObjectOutputStream out = new ObjectOutputStream(fileOut);
        	out.writeObject(p);
        	out.close();
        	fileOut.close();
        	System.out.println("已被序列化");
        } catch(IOException i)   {
            i.printStackTrace();
        }
   	}
}
  1. transient瞬态修饰成员,不会被序列化
  2. 一个对象要想序列化,必须满足两个条件:
    a.该类必须实现java.io.Serializable 接口,Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException 。
    b.该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。

5.2.2ObjectInputStream类

ObjectInputStream反序列化流,作用是将之前使用ObjectOutputStream序列化的原始数据恢复为对象
构造方法
public ObjectInputStream(InputStream in): 创建一个指定InputStream的ObjectInputStream
实现反序列化
以代码为例

public static void main(String [] args)   {
        People p = null;
        try {		
             FileInputStream fileIn = new FileInputStream("people.txt");
             ObjectInputStream in = new ObjectInputStream(fileIn);
             p = (People) in.readObject();
             in.close();
             fileIn.close();
        }catch(IOException i) {
             i.printStackTrace();
             return;
        }catch(ClassNotFoundException c)  {
             System.out.println("没有找到People ");
             c.printStackTrace();
             return;
        }
        System.out.println("Name: " + p.name);	
        System.out.println("birthday: " + p.birthday); 
        System.out.println("age: " + p.age); 
    }
  1. public final Object readObject () : 读取一个对象。
  2. 在进行反序列化时,它必须是能够找到class文件的类。如果找不到该类的class文件,则会抛出一个 ClassNotFoundException 异常。
  3. 当class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。
  4. Serializable 接口给需要序列化的类,提供了一个序列版本号。serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值