【IO流】

IO流

1.1 概念

File:只能操作文件或目录(文件中的内容不能读取到)
IO:Input/Output 即输入&输出,也称为流。指的是数据从一个地方到另一个地方
	例如:计算机中文件拷贝过程,文件的编写及保存,显示文件的内容等都需要使用IO,我们将传输的过程看做是一个输入和输出的过程。
  • 输入和输出的介质:
    文件
    网络
    键盘(输入)
    显示器(输出)

1.2 IO的分类

JAVA针对IO操作提供了相应的API,Java中几乎所有的IO操作都需要使用java.io包
对于流的分类包含各种方式:
	按流的方向(输入输出的过程通常是站在程序(计算机)的角度考虑)
		输入流(Input) 读read
		输出流(Output) 写write
	按流的处理类型
		字节流(byte) 读写都是一个字节
		字符流(char) 读取都是一个字符
	按流的功能(看构造器,构造器参数是一个流,则就是处理流)
		节点流 (直接跟输入输出源进行交互)
		处理流(对其他流进行包装的流,也称包装流)

1.3 顶级父类

流的种类虽然很多,但是规律性极强,几乎所有的流都是从4个基类中继承而来的
			输入流						输出流
字节流	java.io.InputStream		java.io.OutputStream	
字符流 	java.io.Reader			java.io.Writer
以上的4个流是所有java流的顶级父类,都是抽象类
一般以Stream结尾的都是字节流,一般以Reader/Writer结尾的都是字符流

1.4 IO使用场景

文件拷贝
文件上传和下载
网络程序中数据传输
Excel导入和导出
...

2. 字节流

2.1 字节概述

在计算机系统中,一切都是字节,系统中存储的各种文件(文本文档、图片、视频、音频等)的存储在计算机底层中,都是以字节的形式存储的,因此对于任何的文件操作都是可以使用一个一个字节来进行操作(读、写)
Java中的字节流顶层父类:InputStream/OutputStream

2.2 字节输入流

  • FileInputStream 从文件中读
  • ByteArrayInputStream 从byte[]数组中读
  • BufferedInputStream 高效读取(带缓冲区)
  • ObjectInputStream 需要写入对象时,才能使用到这个流读对象

InputStream常用方法

int read()  从流中读取一个字节,返回当前读取的字节数据
int read(byte[] b) 从读取字节数据存储到自己缓冲区,并返回实际的读取字节总数
int available()  获取流中可读字节数
2.2.1 FileInputStream

FileInputStream 是一个针对字节输入流的实现流,主要用于对文件进行读取操作,内部的方法主要是针对父类的实现

常见构造器
	FileInputStream(File file): 根据提供的文件构建的对象
	FileInputStream(String filePath): 根据提供的文件路径构建对象
	使用FileInputStream进行文件的读取操作 
1、基本读取(每次读取一个字节)

//创建File对象
 File file = new File("C:\\Users\\xxx\\Desktop\\新建 文本文档.txt");
//创建FileInputStream对象
FileInputStream fis = new FileInputStream(file);
//读取一个字节
int read = fis.read();
System.out.println((char)read);
2、使用字节缓冲区读取(缓冲区大小为总可读字节数)
由于上面的读取方式每次一个字节的读取,因此读取效率低,所有在实际开发中会使用一个字节缓冲区,提高读取效率。

FileInputStream fis = new FileInputStream("C:\\Users\\xxx\\Desktop\\新建 文本文档.txt");
//文件中可读的字节个数
System.out.println("字节个数:"+fis.availavle());
//创建一个byte数组
//相对于一个缓冲区(大小可以直接设置)
byte[] b = new byte[fis.available()];
int len = 0;
//read(b):把流中读取到的字节内容存储到数组中
while ((len = fis.read(b)) != -1){
 	//将数组中的有效内容转化为String类型
	System.out.println(new String(b,0,len));
}
//关闭流
fis.close();

((len = fis.read(b))!=-1)
①执行fis=read(b),返回读取到的字节个数,若返回-1表示读取到文件末尾
②执行len=返回值结果
③判断len!=-1是否成立
3 使用合适的大小缓冲区中读取
以上读取是一次性将文件的数据读取到缓冲区中,因此,缓冲区的容量可以会很大,如果针对一个大文件的读取,使用一个过大的缓冲区,可能会造成空间的浪费以及不必要的麻烦,从而影响其他程序的执行,所有需要合适大小的缓冲区进行反复读取

 FileInputStream fis = new    FileInputStream("C:\\Users\\xxx\\Desktop\\新建 文本文档.txt");
 byte[] b = new byte[fis.available()];
 int len = 0;
 while ((len = fis.read(b)) != -1){
  	System.out.println(new String(b,0,len));
 }

2.3 字节输出流

根据数据的流向除了可以进行读取(输入)操作之外,数据的写(输出)操作也是十分常见的,java中的字节输出流都是从java.io.OutputStream继承过来。
由于OutputStream是一个抽象类,无法实例化对象,因此JDK也提供了针对该流的子类:

  • FileOutputStream
  • ByteOutputStream
  • BufferedOutputStream
  • ObjectOutputStream
  • PrintStream
    OutputStream类中常见的方法:
  • write(int b): 将一个字节通过输出流写出到目标输出源
  • weite(byte b): 将一个字节数组通过输出流写出到目标输出源
  • write(byte b, int offset, int len): 将一个数组的offset开始写出len个字节到目标输出源
2.3.1 FileOutputStream

FileOutputStream是一个针对字节输出流的实现流,主要用于对文件进行写入内容,内部的方法主要是针对父类的实现

  • 常见构造器
    FileOutputStream(String filePath): 基于一个标准文件的路径创建对其操作的输出流
    FileOutputStream(String filePath,boolean append): 基于一个标准文件的路径创建对其操作的输出流,使用追加模式
    FileOutputStream(File file): 基于一个标准文件的路径 创建对其操作的输出流
    FileOutputStream(File file,boolean append): 基于一个标准文件的路径创建对其操作的输出流,使用追加模式
  • 使用FileOutputStream进行文件写出操作
FileOutputStream fos = new FileOutputStream("d:/abv.txt",true);
/*fos.write(97);
fos.write('a');*/
String msg = "湖北大学";
byte[] bytes = msg.getBytes("gbk");
	fos.write(bytes,0,4);
} catch (FileNotFoundException e) {
	e.printStackTrace();
} catch (IOException e) {
	e.printStackTrace();
}
读一个文件,若文件不存在,则会抛出FileNotFoundException
思考:向一个文件写入内容,文件可以不存在?
	  可以,若不存在则会创建一个文件
若文件不存在,则会帮我们创建
但是若文件的上一级目录不存在,则会抛出FileNotFoundException

FileOutputStream fos = new FileOutputStream("d:/abv.txt",true);
true:若文件里面有内容,再去写内容到这个文件中
默认的效果则是替换原来的内容,只会显示现在添加的内容
若想要保留原来的内容,追加新内容,则需要在FileOutputStream
构造方法中添加一个参数true,用来表示是追加的内容,而不是替换内容

2.4 文件拷贝(图片、视频、音频)

通过对于字节输入和输出流的使用,我们可以将这两个流结合起来实现对文件的拷贝操作:

try {
	long start = System.currentTimeMillis();
    FileInputStream fis = new FileInputStream("D:/HBuilder_Code/H5/day02_html/sp/九尾和他的小尾巴们-鱼大仙儿《我曾》(超清).mp4");
    FileOutputStream fos = new FileOutputStream("d:/a/1.mp4");
    byte[] b = new byte[1024];
    int len = 0;
    while ((len=fis.read(b))!= -1){
    	fos.write(b);
    }
    //刷新
    fos.flush();
    long end = System.currentTimeMillis();
    System.out.println("所需时间"+ (end - start));
    } catch (FileNotFoundException e) {
    	e.printStackTrace();
    } catch (IOException e) {
    	e.printStackTrace();
    }

3 字符流

3.1 字符概述

通常在文本文件中,文件内容的存在形式一般是以一个字符(一个汉字,一个英文字母、一个符号)的形式存在的,在GBK编码模式下通常一个字符=2个字节;字符流一般适应于对文本数据的处理,java中所有的字符流都是以Reader/Writer结尾,并且都是从这两个基本的抽象类中继承

  • java.io.Reader:字符输入流
  • java.io.Writer:字符输出流

3.2 字符输入流

字符输入流一般用于对文本数据进行读取操作,常见的字类:

  • InputStreamReader:转换流,将字节流------>字符流
  • FileReader:文件字符输入流,读文件中的内容(文本文件)
  • BufferedReader:自带缓冲区的字符串输入流
  • CharArrayReader:从字符数据中读取
3.2.1 FileReader

FileReader是一个以字符流的形式进行文件内容的读取操作的流,从InputStreamReader继承而来,内部没有多余的方法(与父类API一致)。

  • 常用的构造方法
    FileReader(File file): 根据提供的文件对象获取文件的字符输入流
    FileReader(String filePath): 根据提供的文件路径获取文件字符串输入流
具体使用

File file = new File("d:/abc.txt");

    try {
        //创建字符输入流
        FileReader fr = new FileReader(file);
        //获取当前的编码方式
        String encoding = fr.getEncoding();
        System.out.println("encoding = " + encoding);
        //创建char数组
        char[] c = new char[100];
        int len = 0;//数组中存储的有效字符个数
        while ((len = fr.read(c))!=-1){
            String s = new String(c,0,len);
            System.out.println(s);
        }
        fr.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
3.2.2 FileWriter

FileWriter是一个以字符流的形式进行文件内容写出的流,从OutputStreamWriter继承而来,内部没有多余的方法(与父类的API一致)。

  • 常用的构造方法
    FileWriter(File file): 根据提供的文件对象获取文件字符输出流
    FileWriter(String filePath): 根据提供的文件路径获取文件字符输出流
    FileWriter(File file, boolean append):根据提供的文件对象获取文件字符输出流,并且使用追加模式
    FileWriter(String filePath, boolean append):根据提供的文件路径获取文件字符输出流,并使用追加模式
具体使用

try {
       FileWriter fw = new FileWriter("d:/abc.txt",true);
       fw.write("湖北大学");

       //刷新
       fw.flush();
       //关闭
       fw.close();
} catch (IOException e) {
       e.printStackTrace();
}

//用于保存字符串和单个字符写入的临时缓冲区
private char[] writeBuffer;

对于字符输出流,内部使用一个字符缓冲区(字符数组),在进行数据写出时,
通常是将需要写出的数据缓存存入到了字符数组中,然后在关闭流的时候(或者缓冲区存满时),
一次性将缓冲区中的数据写出到目标输出源中。
如果需要在流关闭之前(或者缓存区未满时),
强制的将字符串缓冲区的数据写出,可以手动调用flush()方法,强制输出。

4 处理流

对于流的处理类型(功能)来分为节点流和处理流:

  • 节点流
    也成为低级流,直接跟输入输出源 交互的流
    如:FileInputStream、FileOutputStream、FileReader、FileWriter
  • 处理流
    也称为高级流或包装流,即可以用于对其他节点流进行包装,以实现流的类型转换或效率的提升。
    处理流主要由缓冲流和转换流组成

4.1 缓冲流

缓冲流是一种自带缓冲区的流,主要由四种构成:
BufferedInputStream:字节缓冲输入流
BufferedOutputStream:字节缓冲输出流
BufferedReader:字符缓冲输入流
BufferedWriter:字符缓冲输出流
try {
	//字节输入流
    FileInputStream fis = new FileInputStream("d:/abc.txt");
    //创建字节缓冲输入流(把FileInputStream升级为BufferedInputStream)
    BufferedInputStream bis = new BufferedInputStream(fis);
    byte[] b = new byte[1024];
    int len = 0;
    while ((len = bis.read(b))!=-1){
    	System.out.println(new String(b,0,len));
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

 try {
    BufferedReader br = new BufferedReader(new FileReader("d:/abd.txt"));
    char[] b = new char[1024];
    
//  int len = 0;
//  while ((len = br.read(b))!=-1){
//  System.out.println(new String(b,0,len));
//  }
//  readLine:读取一行的内容
    String s = null;
    while ((s = br.readLine()) != null){
    	System.out.println(s);
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

缓冲流内容实现原理:
	使用了一个默认大小为8KB的缓冲区,每次将缓冲区的空间存满之后,再将数据通过流对象完成读写操作

5 转换流

转换什么流		字节流和字符流
在一些需求中经常需要将一个字节流的数据转换为字符流,或又需要将一个字符流转换为字节流。
此时就需要通过转换流来完成此功能
一般转换流应用在:文件转码(文件的编码和解码)
	从网络中读取的数据为字节流时,但是流中包含内容是字符时,可以使用转换流实现转换
	若查看文件时,出现乱码的情况,则说明文件内容的编码方式和解码方式不一致
java.io主要有两个类完成转换的功能
	InputStreamReader:用于将字节输入流转换为字符输入流(字节--->字符)
	OutputStreamWriter:用于将字符输出流转换为字节输出流(字符--->字节)

解码:字节、字节数组------>字符数组、字符串		看不懂------>看得懂
编码:字符数组、字符串------>字节、字节数组		看得懂------>看不懂

字符集:

  • ASCII:美国标准信息交换码,用一个字节的7位可以表示
  • GB2312:中国的中文编码表,常用的汉字(不包括繁体字或复杂的字)
  • GBK:中国的中文编码表升级
  • ISO8859-1:拉丁码表。欧洲码表
  • Unicode:国际标准码,融合了目前人类使用的所有字符,是一个字符集,不是一个编码方式
    UTF-8:变长的编码方式,可用1-4字节来表示一个字符
    UTF-16:…
    UTF-32:…
public class InputStreamReaderDemo {
    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream("d:/abc.txt");

            //InputStreamReader(InputStream in)
            //InputStreamReader(InputStream in,String charsetName)
            /*
                参数2:指定字符集,具体使用哪一个字符集,取决于文件保存时使用的字符集
                若不指定字符集,默认使用平台的字符集
            */
            InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
            char[] c = new char[10];
            int len = 0;
            while ((len = isr.read(c))!=-1){
                String string = new String(c, 0, len);
                System.out.println(string);
            }
            //关闭Io资源
            isr.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void test1() throws IOException {
        //OutputStreamWriter(OutputStream out)
        //OutputStreamWriter(OutputStream out,string charsetName)
        FileOutputStream fos = new FileOutputStream("d:/abc.txt",true);
        OutputStreamWriter osw = new OutputStreamWriter(fos);
        osw.write("加油!!!");
        osw.flush();
        osw.close();
    }
}

6 打印流

6.1 标准的输入流、输出流

System.in:标准的输入流,默认从键盘输入
System.out:标准的输出流,默认从控制台输出

6.2

System类中的setln(InputStream is) / setOut(PrintStream ps)方式重新指定输入和输出流
练习:
	从键盘录入输入字符串,要求将读取到的整行字符串转成大写输出,然后继续进行输入操作,直到输入exit是,退出程序。
	方式一:使用Scanner类,调用next()返回一个字符串
	方式二:使用System.in  System.in返回的是InputStream---> 转换流 ---> BufferedReader类中readLine()

public class OtherStreamDemo {
    public static void main(String[] args) throws IOException {
        //字节流
        InputStream in = System.in;
        //使用转换流:字节流转换为字符流
        InputStreamReader isr = new InputStreamReader(in);
        BufferedReader br = new BufferedReader(isr);

        while (true){
            System.out.println("请输入一个字符串:");
            String data = br.readLine();
            //equalsIgnoreCase:忽略大小写
            if("exit".equalsIgnoreCase(data)){
                break;
            }
            String s = data.toUpperCase();
            System.out.println(s);
        }
        //关闭资源
        br.close();
    }
    /*
        练习:
            scanner类: nextInt()\next()\nextDouble()
            模拟scanner类完成类似的功能,输入什么类型
    */
    
使用setOut方法修改默认输出位置
    
    @Test
    public void test2() throws IOException {
        FileOutputStream fos = new FileOutputStream("d:/abc.txt");
        PrintStream ps = new PrintStream(fos,true);
        if (ps != null){
            System.setOut(ps);//把标准的输出流(默认输出在控制台)改为在文件去输出
        }
        //写内容
        for (int i = 0; i < 255; i++) {
            System.out.println((char)i);
        }
    }
}

7 数据流

7.1 DataOutputStream

DataOutputStream:数据专属流
这个流可以将数据连同数据的类型一并写入文件。
这个文件不是普通文本文档。(这个文件用记事本打不开)
/**
 * DataOutputStream:数据专属流
 * 这个流可以将数据连同数据的类型一并写入文件。
 * 这个文件不是普通文本文档。(这个文件用记事本打不开)
 */
public class DataOutputStreamTest {
	public static void main(String[] args) {
		//创建数据专属的字节输出流
		DataOutputStream dos = null;
		try {
			dos = new DataOutputStream(new FileOutputStream("E:\\IO测试\\data"));
			//写数据
			byte b = 100;
			short s = 200;
			int i = 300;
			long l = 400L;
			float f = 3.0F;
			double d = 3.14;
			boolean sex = false;
			char c = 'a';
			//写
			dos.writeByte(b);//把数据以及数据的类型一并写入到文件当中。
			dos.writeShort(s);
			dos.writeInt(i);
			dos.writeLong(l);
            dos.writeFloat(f);
			dos.writeDouble(d);
			dos.writeBoolean(sex);
			dos.writeChar(c);
			
			//刷新
			dos.flush();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			if(dos!=null){
				try {
					dos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

7.2 DataInputStream

DataInputStream:数据字节输入流
DataOutputStream写的文件,只能使用DataInputStream去读
并且读的时候你需要提前知道写入的顺序。
读的顺序需要和写的顺序一致。才可以正常取出数据。
/**
 * DataInputStream:数据字节输入流
 * DataOutputStream写的文件,只能使用DataInputStream去读
 * 并且读的时候你需要提前知道写入的顺序。
 * 读的顺序需要和写的顺序一致。才可以正常取出数据。
 */
public class DataInputStreamTest {
	public static void main(String[] args) {
		DataInputStream dis = null;
		try {
			dis = new DataInputStream(new FileInputStream("E:\\IO测试\\data"));
			//开始读
			byte b = dis.readByte();
			short s = dis.readShort();
			int i = dis.readInt();
			long l = dis.readLong();
			float f = dis.readFloat();
			double d = dis.readDouble();
			boolean sex = dis.readBoolean();
			char c = dis.readChar();
			System.out.println(b);
			System.out.println(s);
			System.out.println(i);
			System.out.println(l);
			System.out.println(f);
			System.out.println(d);
			System.out.println(sex);
			System.out.println(c);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			if(dis!=null){
				try {
					dis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

8 对象流

ObjectInputStream 和 ObjectOutputStream
作用:
	用于存储和读取基本数据类型或对象的处理流,强大之处在于可以把Java中的对象写入到磁盘中,也能把对象读取出来
	
序列号过程:把Java中的对象写入到磁盘中
反序列化:从磁盘中读取对象到内存中
 
对象序列化的使用场景:
	1、软件的配置信息
	2、缓存实现
	3、游戏存档
	......
public class ObjectInputStreamDemo {
    public static void main(String[] args) throws IOException {
        //创建对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/abc.txt"));
        //调用方法
        oos.writeObject(new String("hhhhh"));

        oos.writeObject(new Person("文健溪",20,1));

        oos.flush();
        oos.close();
    }

    @Test
    public void test1() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/abc.txt"));
        Object object = ois.readObject();
        System.out.println(object);

        Object object1 = ois.readObject();
        System.out.println(object1);
    }
}


Person类需要满足如下的要求:才能进行序列化
	1、需要实现接口:Serializable
	2、当前类提供一个全局常量:SerialVersionUID
	3、除了当前Person类需要实现Serializable接口之外,改必须保证其内部所有属性也能序列化(默认情况下,基本数据类型都是可以序列化的)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值