Java基础学习——I/O
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java基础学习——I/O相关的知识,希望对你有一定的参考价值。
1.流的概念
在java中,流(stream)是从源到目的地的字节的有序序列。流中的字节依据先进先出,具有严格顺序,因此流式I/O是一种顺序存取方式。
2.两种基本的流
在java中有两种基本——输入流(InputStream)与输出流(OutputStream),对于这两种流都采用相同的顺序读写方式,其读写操作过程如下:
流的读操作过程,打开流—>当流中还有数据时执行读操作—>关闭流
流的写操作过程,打开流—>当有数据需要输出时执行写操作—>关闭流。
Java中实现上述流式I/O的类都在java.io包中。
这些流根据流相对于程序的另一个端点的不同,分为节点流(Node Stream)和过滤流(Filter Stream)
节点流:以特定流如磁盘文件、内存某区域或线程之间的管道为端点构造的输入/输出流,它是一种最基本的流。
过滤流:以其他已经存在的流为端点构造的输入/输出流,称为过滤流或者处理流,它要对其相连的另一种流进行某种转换。
流根据数据单位不同又分为字节流和字符流:字节流(8位)和字符流(16位)。
3.字节流(其中带阴影的内是节点流,其他为过滤流)
1.输入字节流
InputStream是输入字节流的抽象顶层父类。
2.输出字节流
4.字符流
1.输入字符流
2.输出字符流
5.经常使用的流
节点流类型常见的有:
对文件操作的字节流有FileInputStream/FileOutputStream,
字符流有FileReader/FileWriter。
处理流类型常见的有:
缓冲流:缓冲流要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写效率,同事增加了一些新的方法。
字节缓冲流有BufferedInputStream/BufferedOutputStream,
字符缓冲流有BufferedReader/BufferedWriter,字符缓冲流分别提供了读取和写入一行的方法ReadLine和NewLine方法。
对于输出地缓冲流,写出的数据,会先写入到内存中,再使用flush方法将内存中的数据刷到硬盘。所以,在使用字符缓冲流的时候,一定要先flush,然后再close,避免数据丢失。
转换流:用于字节数据到字符数据之间的转换。
仅有字符流InputStreamReader/OutputStreamWriter。其中,InputStreamReader需要与InputStream“套接”,OutputStreamWriter需要与OutputStream“套接”。
数据流:提供了读写Java中的基本数据类型的功能。
DataInputStream和DataOutputStream分别继承自InputStream和OutputStream,需要“套接”在InputStream和OutputStream类型的节点流之上。
对象流:用于直接将对象写入写出。
流类有ObjectInputStream和ObjectOutputStream,本身这两个方法没什么,但是其要写出的对象有要求,该对象必须实现Serializable接口,来声明其是可以序列化的。否则,不能用对象流读写。
6.使用实例
-
输入字节流:FileInputStream
--------| InputStream 所有输入字节流的基类 抽象类
------------| FileInputStream 读取文件数据的输入字节流
使用FileInputStream读取文件数据的步骤:
1. 找到目标文件
2. 建立数据的输入通道。
3. 读取文件中的数据。
4. 关闭 资源.
在使用时经常会使用自定义的缓冲数组以加快读取速度。
1 public class MyFileInputStream { 2 3 public static void main(String[] args) throws IOException { 4 readTest4(); 5 } 6 7 //使用缓冲数组配合循环一起读取。 推荐使用 8 public static void readTest4() throws IOException{ 9 long startTime = System.currentTimeMillis(); 10 //找到目标文件 11 File file = new File("F:\\\\81179-106.jpg"); 12 //建立数据的输入通道 13 FileInputStream fileInputStream = new FileInputStream(file); 14 //建立缓冲数组配合循环读取文件的数据。 15 int length = 0; //保存每次读取到的字节个数。 16 //存储读取到的数据 缓冲数组 的长度一般是1024的倍数,因为与计算机的处理单位。 理论上缓冲数组越大,效率越高 17 byte[] buf = new byte[1024]; 18 19 while((length = fileInputStream.read(buf))!=-1){ // read方法如果读取到了文件的末尾,那么会返回-1表示。 20 System.out.print(new String(buf,0,length)); 21 } 22 23 //关闭资源 24 fileInputStream.close(); 25 long endTime = System.currentTimeMillis(); 26 System.out.println("读取的时间是:"+ (endTime-startTime)); //103 27 } 28 }
-
输出字节流:FileOutStream
--------| OutputStream 是所有输出字节流 的父类。 抽象类
-----------| FileOutStream 向文件输出数据的输出字节流。
FileOutputStream要注意的细节:
1. 使用FileOutputStream 的时候,如果目标文件不存在,那么会自动创建目标文件对象。
2. 使用FileOutputStream写数据的时候,如果目标文件已经存在,那么会先清空目标文件中的数据,然后再写入数据。
3.使用FileOutputStream写数据的时候, 如果目标文件已经存在,需要在原来数据基础上追加数据的时候应该使用new FileOutputStream(file,true)构造函数,第二参数为true。
4.使用FileOutputStream的write方法写数据的时候,虽然接收的是一个int类型的数据,但是真正写出的只是一个字节的数据,只是
把低八位的二进制数据写出,其他二十四位数据全部丢弃。
1 public class MyFileOutStream { 2 3 public static void main(String[] args) throws IOException { 4 writeTest3(); 5 } 6 7 8 //使用字节数组把数据写出。 9 public static void writeTest3() throws IOException{ 10 //找到目标文件 11 File file = new File("F:\\\\b.txt"); 12 //建立数据输出通道 13 FileOutputStream fileOutputStream = new FileOutputStream(file); 14 //把数据写出。 15 String data = "abc"; 16 byte[] buf = data.getBytes(); 17 fileOutputStream.write(buf, 0, 3); // 0 从字节数组的指定索引值开始写, 2:写出两个字节。 18 19 //关闭资源 20 fileOutputStream.close(); 21 } 22 }
- 输入字符流:FileReader
----------| Reader 输入字符流的基类 抽象类
-------------| FileReader 读取文件的输入字符流。
1 public class MyFileReader { 2 3 public static void main(String[] args) throws IOException { 4 readTest2(); 5 } 6 public static void readTest2() throws IOException{ 7 //找到目标文件 8 File file = new File("F:\\\\Demo1.java"); 9 // 建立数据的输入通道 10 @SuppressWarnings("resource") 11 FileReader fileReader = new FileReader(file); 12 //建立缓冲字符数组读取文件数据 13 char[] buf = new char[1024]; 14 int length = 0 ; 15 while((length = fileReader.read(buf))!=-1){ 16 System.out.print(new String(buf,0,length)); 17 } 18 fileReader.close(); 19 } 20 21 }
- 输出字符流:FileWriter
------| Writer 输出字符流的基类。 抽象类
-----------| FileWriter 向文件数据数据的输出字符流
FileWriter要注意的事项:
1. 使用FileWriter写数据的时候,FileWriter内部是维护了一个1024个字符数组的,写数据的时候会先写入到它内部维护的字符数组中,如果需要
把数据真正写到硬盘上,需要调用flush或者是close方法或者是填满了内部的字符数组。
2. 使用FileWriter的时候,如果目标文件不存在,那么会自动创建目标文件。
3.使用FileWriter的时候, 如果目标文件已经存在了,那么默认情况会先情况文件中的数据,然后再写入数据 , 如果需要在原来的基础上追加数据,
需要使用“new FileWriter(File , boolean)”的构造方法,第二参数为true。
- 缓冲输入字节流:BufferedInputStream
----| InputStream 输入字节流的基类。 抽象
----------| FileInputStream 读取文件数据的输入字节流
----------| BufferedInputStream 缓冲输入字节流 缓冲输入字节流的出现主要是为了提高读取文件数据的效率。
其实该类内部只不过是维护了一个8kb的字节数组而已。注意: 凡是缓冲流都不具备读写文件的能力。
- 缓冲输出字节流:Bufferedoutputstream
-------| OutputStream 所有输出字节流的基类 抽象类
------------| FileOutputStream 向文件 输出数据 的输出字节流
------------| Bufferedoutputstream 缓冲输出字节流 BufferedOutputStream出现的目的是为了提高写数据的效率。
内部也是维护了一个8kb的字节数组而已。
BufferedOutputStream 要注意的细节
1. 使用BufferedOutStream写数据的时候,它的write方法是是先把数据写到它内部维护的字节数组中。
2. 使用BufferedOutStream写数据的时候,它的write方法是是先把数据写到它内部维护的字节数组中,如果需要把数据真正的写到硬盘上面,需要
调用flush方法或者是close方法、 或者是内部维护的字节数组已经填满数据的时候。
1 public class MyBufferedOutputStream { 2 3 public static void main(String[] args) throws IOException { 4 //找到目标文件 5 File file = new File("F:\\\\a.txt"); 6 //建立数据的输出通道 7 FileOutputStream fileOutputStream = new FileOutputStream(file); 8 //建立缓冲输出字节流对象 9 BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); 10 //把数据写出 11 bufferedOutputStream.write("hello world".getBytes()); 12 //把缓冲数组中内部的数据写到硬盘上面。 13 //bufferedOutputStream.flush(); 14 bufferedOutputStream.close(); 15 } 16 17 }
- 缓冲输入字符流:BufferedReader
输入字符流:
-------| Reader 所有输入字符流的基类。 抽象类
----------| FileReader 读取文件字符串的输入字符流。
----------| BufferedReader 缓冲输入字符流 。 缓冲 输入字符流出现的目的是为了提高读取文件 的效率和拓展了FileReader的功能。 其实该类内部也是维护了一个字符数组 - 缓冲输出字符流:BufferedWriter
----------| Writer 所有输出字符流的基类, 抽象类。
--------------- | FileWriter 向文件输出字符数据的输出字符流。
----------------| BufferedWriter 缓冲输出字符流 缓冲输出字符流作用: 提高FileWriter的写数据效率与拓展FileWriter的功能。
BufferedWriter内部只不过是提供了一个8k长度的字符数组作为缓冲区而已,拓展了FileWriter的功能。 - 对象输出流:ObjectOutputStream
对象输入输出流要注意的细节:
1. 如果对象需要被写出到文件上,那么对象所属的类必须要实现Serializable接口。 Serializable接口没有任何的方法,是一个标识接口而已。
2. 对象的反序列化创建对象的时候并不会调用到构造方法的、
3. serialVersionUID 是用于记录class文件的版本信息的,serialVersionUID这个数字是通过一个类的类名、成员、包名、工程名算出的一个数字。
4. 使用ObjectInputStream反序列化的时候,ObjeectInputStream会先读取文件中的serialVersionUID,然后与本地的class文件的serialVersionUID
进行对比,如果这两个id不一致,那么反序列化就失败了。
5. 如果序列化与反序列化的时候可能会修改类的成员,那么最好一开始就给这个类指定一个serialVersionUID,如果一类已经指定的serialVersionUID,然后
在序列化与反序列化的时候,jvm都不会再自己算这个 class的serialVersionUID了。
6. 如果一个对象某个数据不想被序列化到硬盘上,可以使用关键字transient修饰。
7. 如果一个类维护了另外一个类的引用,那么另外一个类也需要实现Serializable接口。
1 class Address implements Serializable{ 2 3 private static final long serialVersionUID = 1L; 4 5 String country; 6 7 String city; 8 9 public Address(String country,String city){ 10 this.country = country; 11 this.city = city; 12 } 13 14 } 15 16 class User implements Serializable{ 17 18 private static final long serialVersionUID = 1L; 19 20 String userName ; 21 22 String password; 23 24 transient int age; // transient 透明 25 26 Address address ; 27 28 29 public User(String userName , String passwrod) { 30 this.userName = userName; 31 this.password = passwrod; 32 } 33 34 35 public User(String userName , String passwrod,int age,Address address) { 36 this.userName = userName; 37 this.password = passwrod; 38 this.age = age; 39 this.address = address; 40 } 41 42 @Override 43 public String toString() { 44 return "用户名:"+this.userName+ " 密码:"+ this.password+" 年龄:"+this.age+" 地址:"+this.address.city; 45 } 46 } 47 public class ObjectOutputStream1 { 48 49 public static void main(String[] args) throws IOException, Exception { 50 writeObj(); 51 // readObj(); 52 } 53 54 //把文件中的对象信息读取出来-------->对象的反序列化 55 public static void readObj() throws IOException, ClassNotFoundException{ 56 //找到目标文件 57 File file = new File("F:\\\\obj.txt"); 58 //建立数据的输入通道 59 FileInputStream fileInputStream = new FileInputStream(file); 60 //建立对象的输入流对象 61 ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); 62 //读取对象信息 63 User user = (User) objectInputStream.readObject(); //创建对象肯定要依赖对象所属 的class文件。 64 System.out.println("对象的信息:"+ user); 65 } 66 //定义方法把对象的信息写到硬盘上------>对象的序列化。 67 public static void writeObj() throws IOException{ 68 //把user对象的信息持久化存储。 69 Address address = new Address("中国","广州"); 70 User user = new User("admin","123",15,address); 71 //找到目标文件 72 File file = new File("F:\\\\obj.txt"); 73 //建立数据输出流对象 74 FileOutputStream fileOutputStream = new FileOutputStream(file); 75 //建立对象的输出流对象 76 ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); 77 //把对象写出 78 objectOutputStream.writeObject(user); 79 //关闭资源 80 objectOutputStream.close(); 81 82 83 } 84 }
- 序列流:SequenceInputStream
1 /* 2 3 需求: 把一首mp3先切割成n份,然后再把这些文件合并起来。 4 5 */ 6 public class SequenceInputStream2 { 7 8 public static void main(String[] args) throws IOException { 9 // cutFile(); 10 mergeFlile(); 11 } 12 13 //合并 14 public static void mergeFlile() throws IOException{ 15 //找到目标文件 16 File dir = new File("F:\\\\music"); 17 //通过目标文件夹找到所有的MP3文件,然后把所有的MP3文件添加到vector中。 18 Vector<FileInputStream> vector = new Vector<FileInputStream>(); 19 File[] files = dir.listFiles(); 20 for(File file : files){ 21 if(file.getName().endsWith(".mp3")){ 22 vector.add(new FileInputStream(file)); 23 } 24 } 25 //通过Vector获取迭代器 26 Enumeration<FileInputStream> e = vector.elements(); 27 //创建序列流 28 SequenceInputStream inputStream = new SequenceInputStream(e); 29 //建立文件的输出通道 30 FileOutputStream fileOutputStream = new FileOutputStream("F:\\\\合并.mp3"); 31 //建立缓冲数组读取文件 32 byte[] buf = new byte[1024]; 33 int length = 0 ; 34 while((length = inputStream.read(buf))!=-1){ 35 fileOutputStream.write(buf,0,length); 36 } 37 //关闭资源 38 fileOutputStream.close(); 39 inputStream.close(); 40 41 } 42 43 //切割MP3 44 public static void cutFile() throws IOException{ 45 File file = new File("F:\\\\美女\\\\1.mp3"); 46 //目标文件夹 47 File dir = new File("F:\\\\music"); 48 //建立数据的输入通道 49 FileInputStream fileInputStream = new FileInputStream(file); 50 //建立缓冲数组读取 51 byte[] buf = new byte[1024*1024]; 52 int length = 0; 53 for(int i = 0 ; (length = fileInputStream.read(buf))!=-1 ; i++){ 54 FileOutputStream fileOutputStream = new FileOutputStream(new File(dir,"part"+i+".mp3")); 55 fileOutputStream.write(buf,0,length); 56 fileOutputStream.close(); 57 } 58 //关闭资源 59 fileInputStream.close(); 60 } 61 62 }
以上是关于Java基础学习——I/O的主要内容,如果未能解决你的问题,请参考以下文章