Java IO流简单梳理

tech2024-06-20  74

1.流的概念

在计算机中,流是个抽象的概念,是对输入输出设备的抽象,在java程序中,对

于数据的输入/输出操作,都是以流的方式进行。

数据以二进制的形式在程序和设备之间流动传输,就像水在管道里流动一样,所以

就把这种数据传输的方式称之为输入流、输出流。

流具有方向性,可以分为输入流和输出流。

数据从程序流向文件,就是输出流。

数据从文件流向程序,就是输入流。

2.流的分类

java中的IO流可以根据很多不同的角度进行划分,最常见的是以数据的流向和数

剧的类型划分。

根据数据的流向分为:输入流和输出流;

输入流:把数据从其他设备上读取到程序中的流输出流:把数据从程序中写出到其他设备上的流

根据数据的类型分为:字节流(byte)和字符(char)流;

字节流:传输以字节为单位,byte。字符流:传输以字符为单位,char。

3.流的结构

几乎所有的流,都是派生自四个抽象的父类型:

InputStream,字节输入流OutputStream,字节输出流Reader,字符输入Writer,字符输出

常用输入输出流

InputStream: ByteArrayInputStream、PipedInputStream(管道输入流,负责从管道中读取数据)、FileInputStream

OutputStream:ByteArrayOutputStream、PipedOutputStream(管道输出流)、FileOutputStream

一般情况下,一个流。会具备最起码的三个特点:

是输入还是输出是字符还是字节目的地在哪里

4. 字节流

4.1 字节数组流

ByteArrayInputStream,字节数组流,将读取到的数据存储到一个数组中,或者是一个字节一个字节的读取数据。

输入流:

//从字节数组里面将数据读取到程序中 byte[] buf = "abcdefg".getBytes(); //构建流 去读取数据 InputStream input = new ByteArrayInputStream(buf); int result=-1; /* 无参的read方法返回读取到的字节的Unicode编码 是一个十进制的数 每执行一次读取一个字节 */ while((result=input.read())!=-1){ System.out.println((char) result); } byte[] buf2 ="hello".getBytes(); byte[] buf3 = new byte[10]; input=new ByteArrayInputStream(buf2); //将读取到的数据存到byte数组中,即将buf2的内容存到buf3中 //存储数组的长度是多少,流一次就读多少 //如果数组长度超过原数组的长度,就可以一次读完,多余位置为默认值 //如果数组长度小于原数组的长度,则需要读取多次,每次都会从上一次结束的位置继续读。 input.read(buf3); System.out.println(Arrays.toString(buf3)); //有参的read方法返回读取到的字符个数 int length; while((length=input.read(buf3))!=0){ System.out.println(length); System.out.println(Arrays.toString(buf3)); } //使用完流后要关闭流,节省资源 input.close();

输出流:

OutputStream os = new ByteArrayOutputStream(); //创建一个字节数组输入流 读取一个 //就用一个字节数组输出一个 InputStream is = new ByteArrayInputStream("helloworld".getBytes()); int result =-1; byte[] count = new byte[1024]; while((result=is.read(count))!=-1){ //读取一个字符,写入对象自己的数组中 ,默认大小32 //将读取的内容写入到自己的数组里面 os.write(result); }

4.2 字节管道流

管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有字节数据。通常,数据由某个线程从PipedInputStream对象读取,并由其他线程将其写入到相应的PipedOutputStream。

通常用于线程之间的数据通讯

4.3 字节文件流

字节文件流分为输入流与输出流:FileInputStream和FileOutputStream

在使用字节文件流之前,需要先学习File对象

4.3.1 文件

java中一个File对象就代表一个文件,通过File对象可以完成对文件的创建删除等操作

用File进行文件的创建:

//创建一个文件对象 //File对象用来表示文件和文件夹,File对象有多个构造函数 //其中之一为File(String Path) 直接填写文件的路径 可以为相对路径或者为绝对路径 //File file = new File("D:\\钉钉\\DingDing\\a.txt"); //相对路径默认创建在当前项目目录下 //File file = new File("a.txt"); //File(String parent, String child) //parent代表父目录,child代表子目录或者文件 File file = new File("D:\\钉钉\\DingDing","a.txt"); //判断文件是否存在 System.out.println(file.exists()); //可以自己创建一个新的文件 //如果文件存在就不会创建 //不存在时就会自动创建一个新的文件 file.createNewFile(); //获取文件的绝对路径 String path=file.getAbsolutePath(); System.out.println(path); //获取文件的相对路径 相对于项目所在的路径 System.out.println(file.getPath()); //获取文件的名字 System.out.println(file.getName()); //获取文件内容的字节数 System.out.println(file.length());

用File实现文件夹的创建和删除:

File file = new File("src/kk"); if (!file.exists()){ //创建新文件夹 //只能创建单级文件夹 //如果是多层文件夹则不会创建 file.mkdir(); } //创建多级文件夹 //只会创建文件夹 不会创建文件 //最后一级会存在文件夹a.txt file = new File("E:kk/pp/ss//a.txt"); if (!file.exists()){ file.mkdirs(); } //删除最底层的文件或文件夹 返回是否成功 file.delete(); //文件如果存在则 删除最低层的文件或文件夹 file.deleteOnExit(); //得到父目录的路径 System.out.println(file.getParent()); //得到上级目录的文件对象 File file1 = file.getParentFile(); System.out.println(file1.getName());

4.3.2 文件流

使用文件流实现文件的传输拷贝:

//把d:/a.txt的内容输出到src/a.txt InputStream input = new FileInputStream("d:/a.txt"); OutputStream os = new FileOutputStream("src/a.txt"); byte[] bytes = new byte[1024]; int length = -1; while((length=input.read(bytes))!=-1){ os.write(bytes,0,length); } //把d:/test.jpg输出到src/test.jpg input = new FileInputStream("d:/test.jpg"); os = new FileOutputStream("src/test.jpg"); bytes = new byte[1024]; length = -1; while((length=input.read(bytes))!=-1){ os.write(bytes,0,length); } //将管道里面的数据强制刷新到目的文件中 //一旦写了数据就需要flush防止管道关闭时有数据 //残留在管道内 os.flush(); os.close(); input.close();

向文件追加内容:

FileOutputStream fos = new FileOutputStream("src/a.txt",true);//构造器后面 append 选项为追加 选项 ,设为true之后将向文件追加内容 fos.write("成功了".getBytes()); fos.flush(); fos.close();

5.字符流

字符流:就是在字节流的基础上,加上编码,形成的数据流

字符流出现的意义:因为字节流在操作字符时,可能会有中文导致的乱码,所以由字节流引申出了字符流。

常用字符输入输出流:FileReader、Filewriter

字符输入流FileReader:

Reader reader = new FileReader("src/a.txt"); int read = -1; while ((read=reader.read())!=-1) System.out.print((char)read); reader.close();

字符输出流FileWriter:

Writer writer = new FileWriter("src/c.txt"); writer.write("我要当太空人!"); writer.flush(); writer.close();

6.节点流

以上介绍的字符流和字节流都属于节点流。

它们的特点是可以直接从或向一个地方读写数据。

而除了节点流之外 还存在处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader.处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。

常用的处理流:

数据流 DataInputStream DataOutputStream 等-提供将基础数据类型写入到文件中,或者读取出来.缓冲流:BufferedInputStrean BufferedOutputStream BufferedReader BufferedWriter PrintWriter—增加缓冲功能,避免频繁读写硬盘。转换流:InputStreamReader OutputStreamReader实现字节流和字符流之间的转换。

7.数据流

7.1 概述

字节流操作数据的时候都是一个字节一个字节的进行读写,很多时候并不方便。

我们希望读出来的若干个字节自动转换为指定类的数据,例如:int 、float、char等

类似的,我们也希望每次能直接把一个数据自动转换城字节再写进去。

我们需要一个流,可以将指定类型的数据,自动转换成字节进行操作后,再写到其他文件中

7.2 输入

FileOutputStream fileout = new FileOutputStream("src/c.txt"); DataOutputStream output = new DataOutputStream(fileout); //数据流写入什么类型数据 读取就要选择什么数据。 //向文件写入了一个Long类型的整数1 output.writeLong(1l); // output.writeBoolean(true); output.flush(); output.close();

7.3 输出

DataInputStream input = new DataInputStream(new FileInputStream("src/c.txt")); System.out.println(input.readLong());//从文件读取一个整数

8.缓冲流

8.1 概述

缓冲流的工作原理是创建流对象时候,会创建一个内置的默认大小的缓冲区数组,通过缓冲区书写.

缓冲流,也叫高效流,是对4个基本的节点流的增强,所以也是4个流,按照数据类型分类:

字节缓冲流:BufferedInputStream,BufferedOutputStream字符缓冲流:BufferedReader,BufferedWriter

8.2字节缓冲流

字节缓冲流在创建时需要在里面传入一个字节流,真正进行流传输的是传入的字节流。缓冲流一般多用在多媒体音频文件

输入流:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src/a.txt")); //从缓冲流里面读取到的数据 //跳过读取的字节数 bis.skip(1); int i =bis.read(); System.out.println( i);

输出流:

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src/a.txt",true)); bos.write("哇哇哇哇哇".getBytes()); bos.flush(); bos.close();

8.3字符缓冲流

字符缓冲流通常拿来处理文本文件

输入流:

//非常常用的一个流 //一般用来读取文本文件 BufferedReader reader = new BufferedReader(new FileReader("src/c.txt")); // int result = 0; // while((result=reader.read())!=-1){ // System.out.print((char) result); // } //读到\r\n代表一行结束 //如果文件读完返回null //回车一个字符,换行一个字符 //reader.skip(reader.readLine().length()+2); 这样写可以跳过一行 String str = null; while ((str=reader.readLine())!=null){ System.out.println(str); }

输出流:

BufferedWriter

//不常用 BufferedWriter bw = new BufferedWriter(new FileWriter("src/c.txt")); bw.write("进一步海阔天空,退一步越想越气"); bw.flush(); bw.close();

PrintWriter

//PrintWriter 封装了 BufferedWriter PrintWriter pw = new PrintWriter(new FileWriter("src/c.txt",true),true);//PrintWriter可以实现自动flush pw.println("哈哈哈哈"); // pw.flush(); pw.close();

在输出流中,因为BufferedWriter功能较少,所以我们通常使用PrintWriter。这个类封装了BufferedWriter,具有许多功能,

不仅可以在构造方法中直接写文件路径,也可以通过设置autoFlush实现管道自动推送,在输出时还可以选择输出一个对象。

9.转换流

9.1 概述

转换流,可以在把一个字节流转换为字符流的同时,指定转换的字符编码。

9.2 输入流

FileInputStream fis = new FileInputStream("src/a.txt"); //java中只有将字节转换为字符的流,不存在字符转字节的流 //将字节流转换为字符流 InputStreamReader isr = //new InputStreamReader(fis); new InputStreamReader(fis,"UTF-8");//可以设置按什么编码去读取,不指定编码时默认为UTF-8 //将该字符流套一个字符缓冲流就可以实现行读取 BufferedReader bfr = new BufferedReader(isr); System.out.println(bfr.readLine()); bfr.close();

9.3 输出流

FileOutputStream fos = new FileOutputStream("src/a.txt",true); //将字节输出流转换为字符输出流 OutputStreamWriter osw = new OutputStreamWriter(fos); //同样可以套一个流 PrintWriter pw = new PrintWriter(osw,true); pw.println("冲冲冲"); pw.flush(); pw.close();

10. 对象流

10.1 概述

又叫序列化流,可以实现对象的序列化和反序列化。要序列化的对象必须实现Serializable接口

10.2 对象输出流

将对象序列化为文件,实现对象的持久化保存

Student stu = new Student("呵呵",1); //要序列化数组或集合前需要保证集合或者数组中的对象全部实现序列化接口 List<Student> stus = new ArrayList<>(); //序列化 将对象转换成一个可传输的字节序列 //被序列化的对象必须实现Serializable接口 ObjectOutputStream ops = new ObjectOutputStream(new FileOutputStream("src/c.txt")); ops.writeObject(stu); ops.flush(); ops.close();

序列化对象之前需要保证,对象实现了序列化接口(Serializable),否则将会抛出异常

10.3 对象输入流

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src/c.txt")); //反序列化创建对象是没有调用该对象的构造方法的 //只能从一个文件中读一个对象,要想从文件中读取多个对象 //要么把多个对象放在集合中 //要么把多个对象写到不同文件中 //即readObject方法只能调用一次 //反序列化时 流中对象的版本号和本地的版本号需要对应 Student stu=(Student)ois.readObject(); System.out.println(stu.getAge()+stu.getName());

10.4 序列化版本号

实现了序列化接口的类都会有一个序列化版本号。一个类如果没有手动指定版本号,那么编译器解决会根据类的属性信息生成一个版本号反序列化时 如果流中对象和本地类的版本号不一致就会报错。如果想要不报错可以手动写死版本号一旦实现序列化接口,最好能提供一个版本号

11.随机访问流

RandomAccessFile raf = new RandomAccessFile("src/b.txt","rw");//rw标示读写权限 raf.write("天天学习,好好向上".getBytes()); raf.seek(0);//光标移动位置 类似c++文件操作 System.out.println(raf.readLine());

12.IO流总结

* 如果是对多媒体文件操作 * 最好是字节操作使用 * FileOutPutStream * FileInputStream * BufferedInputStream * BufferedOutputStream * 如果是文本文件 * 最好是字符操作 * BufferedReader * 用来读 一次读一行 * PrintWriter * 最强大的一个文本字符输出流
最新回复(0)