到底什么是I/O流?
I/O就是 input 和 output(数据流向)
in:进(源头)
out:出(目的地)
流:不间断的数据形态
所以总得来说:I/O流就是用来完成java的输入输出工作,是包括磁盘文件、设备、其他程序以及内存数据等等的输入输出
输入流(input)
数据源 → 本程序
程序从一个数据源读取数据到本程序中,这些数据的方向是从程序的外部到程序内部,这样就构成了一个输入流
输出流(output)
本程序 → 数据源
把当前程序中保存的数据输出到程序外部的目的源中,方向从程序内部到外部,这样就构成了一个输出流
按照传输形式可以分为字节流和字符流
还可以按照操作数据的种类分为文件流、数据流、对象流 等
注意!有些类别的数据操作支持字节和字符流,但有些类别的数据操作只支持字节流操作
节点流: 可以从或向一个特定的地方(节点)读写数据,
处理流: 是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写
下面介绍的各种种类的数据传输流我都会说是节点流还是处理流,到时候在具体例子里就能看出来区别
字节流为输入和输出提供了最原始的环境,字节流可以直接操作二进制数据
① FileInPutStream(文件字节输入流操作,所有文件都可读)
用法:
new FileInputStream(“需要输入的文件地址”); // 这是实例化,将需要输入的文件地址传入
.read方法 —— 可以读取文件内容给n,当n读取时没有值可以读了就会变成-1
② FileOutPutStream(文件字节输出流操作,所有文件都可生成)
用法:
new FileOutputStream(“需要输出的文件地址”); // 这是实例化,将需要输出的文件地址传入
.write方法 —— 输出n的内容
.flush方法 —— 相当于呈现文件方法,不写这个方法的话就不会显示内容
例子(通过文件字节传输流操作将 A.txt 文件传输成 newA.txt 文件):
public class Work25_IOwenjianzijieLiu { public static void main(String[] args) { FileInputStream in = null; // 实例化FileInputStream文件字节输入流 FileOutputStream out = null; // 实例化FileOutputStream文件字节输出流 try { in = new FileInputStream("./A.txt"); // in实例化将需要输入的文件地址传入 out = new FileOutputStream("./newA.txt"); // out实例化将需要输出的文件地址传入 int n = -2; // 当n读取时没有值可以读了就会变成-1,所以声明n时随便给个不是-1的就可以 while((n = in.read()) != -1) { // read方法可以读取文件内容给n,用while循环当n为-1也就是读不到数据的时候结束循环 out.write(n); // out的write方法输出n的内容 out.flush(); // out的flush方法相当于呈现文件方法,不写这个方法的话就不会显示内容 } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { // 关闭I/O流的操作,下文会讲 if(in != null) { in.close(); in = null; } if(out != null) { out.close(); out = null; } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
① ObjectInPutStream(对象字节输入流)
用法:
因为是处理流所以实例化时要传入其他类型数据输入流的实例化
.readObject()方法 —— 读取文件存的数据,因为读取的是字节所以要进行强转给一个新对象存着,读取的时候再用 新对象.toString()方法输出为正常格式
② ObjectOutPutStream(对象字节输出流)
用法:
.writeObject(对象名)方法 —— 输出对象数据
例子(通过对象字节传输流操作将Student类的 s 对象传输成 temp对象):
package com.javawork25; // Object对象流的操作 import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class Work25_IOduixiangliucaozuo { public static void main(String[] args) { ObjectInputStream in = null; // 实例化ObjectInputStream对象流读取 ObjectOutputStream out = null; // 实例化ObjectOutputStream对象流输出 Student s = new Student("wyh", 21); // 实例化进行操作的对象 try { out = new ObjectOutputStream(new FileOutputStream("./objectTest.txt")); out.writeObject(s); // 对象输出流的 .writeObject(对象名)方法就是输出对象数据 out.flush(); in = new ObjectInputStream(new FileInputStream("./objectTest.txt")); Student temp = (Student)in.readObject(); // 对象读取流的 .readObject()方法就是读取文件存的数据,因为读取的是字节所以要进行(Student)强转给一个新对象temp System.out.println(temp.toString()); // 读取的对象再用toString方法输出为正常格式 } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { // 关闭in和out操作 if(in != null) { in.close(); in = null; } if(out != null) { out.close(); out = null; } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
① DataInPutStream(数据字节输入流)
用法:
因为是处理流所以实例化时要传入其他类型数据输入流的实例化
.readUTF()方法 —— 读取指定文件里的字符串形式的数据
.readInt()方法 —— 读取指定文件里的数字形式的数据
② DataOutPutStream(数据字节输出流)
用法:
.writeUTF()方法 —— 写下字符串形式的数据
.writeInt()方法 —— 写下数字形式的数据
例子(通过数据字节传输流操作将names数组和ages数组传输给dataTest.txt然后再输出):
package com.javawork25; // Data数据流的操作 import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class Work25_IOshujuliuyongfa { public static void main(String[] args) { DataInputStream in = null; // 实例化DataInputStream数据字节流读取 DataOutputStream out = null; // 实例化DataOutputStream数据字节流输出 try { out = new DataOutputStream(new FileOutputStream("./dataTest.txt")); // 因为是处理流所以用法与Buffered一样,实例化时要传入其他类型流数据的实例化 in = new DataInputStream(new FileInputStream("./dataTest.txt")); String[] names = {"tom", "jack", "rose"}; int[] ages = {11, 12, 13}; for(int i = 0; i < 3; i++) { out.writeUTF(names[i]); // 数据输出流的 .writeUTF()方法就是写下字符串形式的数据 out.flush(); out.writeInt(ages[i]); // 数据输出流的 .writeInt()方法就是写下数字形式的数据 out.flush(); } for(int i = 0; i < 3; i++) { System.out.print(in.readUTF()); // 数据输入流的 .readUTF()方法就是读取指定文件里的字符串形式的数据 System.out.println(in.readInt()); // 数据输入流的 .readInt()方法就是读取指定文件里的数字形式的数据 } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { // 关闭in和out操作 if(in != null) { in.close(); in = null; } if(out != null) { out.close(); out = null; } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
标准流的源头和目的地都是控制台
① 标准流输出System.out
用法: System.out.println(“输出的字”);
② 标准流错误System.err
用法: System.err.println(“输出的字”); // 其实就是输出的文字字体变成红色了
③ 标准流输入System.in
用法: System.in 获得的数据是字节流,然后要用数据转换流InputStreamReader转换为字符,再用Buffered缓冲流进行读取变成获取到控制台输入的数据
例子(实现标准流的三个方法):
package com.javawork25; // 用了标准流Stream操作,并用了字节/字符转换流Input/OutputStreamReader操作 import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; public class Work25_IObiaozhunliuAndzhuanhuanliuyongfa { public static void main(String[] args) { // // 标准流输出System.out的原理和操作 // PrintStream out = System.out; // out.println("aaa"); // out.println("bbb"); // System.out.println("ccc"); // // 标准流错误System.err的原理和操作(其实就是输出的字体颜色变成红色了) // PrintStream err = System.err; // err.println("aaa"); // err.println("bbb"); // System.err.println("ccc"); // 先用Buffered缓冲流进行读取,然后参数传入读取数据转换流InputStreamReader,最后传入标准输入流System.in // 意思就是用户通过System.in输入一些东西然后经过数据转换流InputStreamReader转换为字符,Buffered缓冲流进行读取变成reader BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); try { while(true) { // 再用reader.readLine()的方法读取内容赋值给字符串型的变量info String info = reader.readLine(); System.out.println(info); // 标准流输出通过在控制台输出这个info if("再见".equals(info)) { // 写一个跳出循环的方法不然会一直死循环,当输入的内容也就是info里存的内容为“再见”时退出循环 break; } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
尽管字节流提供了处理任何类型输入/输出操作的足够功能,但它们不能直接操作字符,字符流面向字符,读写的单位是2字节,字符流以Reader和Writer为顶层,所以当文件中有文本时使用字符流
① FileReader(文件字符输入流)
用法:
new FileReader(“文件地址”); // 实例化
.read()方法 —— 字符流读取的方法
② FileWriter(文件字符输出流)
用法:
new FileWriter(“文件地址”); // 实例化
.write()方法 —— 字符流输出的方法
.flush()方法 —— 字符流总结显示的方法
例子(通过字符流的输入输出将带有汉字的 A.txt 文件输出为 chineseA.txt 文件):
package com.javawork25; // 当传输数据中有中文时用字符流进行传输 import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class Work25_IOwenjianzifuliu { public static void main(String[] args) { FileReader reader = null; // 字符流读取FileReader实例化 FileWriter writer = null; // 字符流读取FileWriter实例化 try { reader = new FileReader("./A.txt"); // 读取reader实例化时文件地址作为参数传入 writer = new FileWriter("./chineseA.txt"); // 输出writer实例化时新文件地址作为参数传入 int n = -2; while((n = reader.read()) != -1) { // 字符流读取的 .read()方法 writer.write(n); // 字符流输出的 .write()方法 writer.flush(); // 字符流输出的 .flush()总结显示方法 } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { // 关闭in和out操作 if(reader != null) { reader.close(); reader = null; } if(writer != null) { writer.close(); writer = null; } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
这个很常用,比如说有一些文件只能通过字节流传输,但是你希望输出为字符流时就可以使用
① InputStreamReader 输入字节流转换成输入字符流
将输入字节流转换成输入字符流:
InputStreamReader inputStreamReader = new InputStreamReader(字节流实例);
② OutputStreamWriter 输出字节流转换成输出字符流
将输出字节流转换成输出字符流:
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(字节流实例);
在前面的应用中我们使用的都是无缓冲 I/O,这意味着每次读写操作都依赖于低层的操作系统,这样效率很低而且更占用资源,所以Java平台实现了缓冲 I/O 流也就是 Buffered 数据传输,而且这是一个处理流
Buffered 数据传输的原理类似这么实现(缓冲区读取数据是先将数据按字节形式存到一个固定大小的字节byte数组中再进行读取,这个byte数组就相当于缓冲区):
public class Work25_IOhuanchongqu { public static void main(String[] args) { FileInputStream in = null; // 实例化FileInputStream文件字节输入流 FileOutputStream out = null; // 实例化FileOutputStream文件字节输出流 int n = -2; byte[] pool = new byte[1024]; // 声明一个byte[]的pool,实例化的参数必须为8的倍数 try { in = new FileInputStream("./ppt.rar"); out = new FileOutputStream("./ppt1.rar"); while((in.read(pool, 0, 1024)) != -1) { // in.read(byte[]的实例化, 从哪, 读到哪)) != -1 out.write(pool, 0, pool.length); // out.write(byte[]的实例化, 从哪, byte[]的实例化.length); out.flush(); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try {// 关闭in和out操作 if(in != null) { in.close(); in = null; } if(out != null) { out.close(); out = null; } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
例子(通过Buffered缓冲字符流配合File文件字符传输流将 A.txt 文件传输为 bufferedA.txt 文件):
package com.javawork25; // java自带的缓冲数据流Buffered的操作 import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class Work25_IOhuanchongliuyongfa { public static void main(String[] args) { BufferedReader reader = null; // 实例化BufferedReader缓冲读取 BufferedWriter writer = null; // 实例化BufferedWriter缓冲输出 try { reader = new BufferedReader(new FileReader("./A.txt")); // Buffered是处理流所以在实例化时要传入其他类型流数据的实例化 writer = new BufferedWriter(new FileWriter("./bufferedA.txt")); String info = null; while((info = reader.readLine()) != null) { // 读取的 .readLine()方法会返回一个字符串,我们一般声明一个info接着,当数据读取完了info就是null了 writer.write(info + "\n"); // 缓冲的读法是见到换行就为一段读取 writer.flush(); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { // 关闭in和out操作 if(reader != null) { reader.close(); reader = null; } if(writer != null) { writer.close(); writer = null; } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
当使用完 I/O 流就需要关闭释放资源
细心的同学可以发现在上述每个例子里都有finally块,那里面写的就是关闭I/O流的操作
就是下面这段:
finally { try { if(in != null) { in.close(); in = null; } if(out != null) { out.close(); out = null; } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
到目前为止我们关注的都是流,流能够在多种多样的输入源输出源上工作,这些源包括磁盘文件,然而,流不支持操作系统磁盘文件级别的操作,所以我们来看一下Java的文件操作。
用法:
实例化File对象时参数传入文件的地址
.isDirectory()方法 —— 判断该文件是不是一个文件夹
.list()方法 —— 返回某个目录下的所有文件和目录的文件名,返回的是String数组
.listFiles()方法 —— 返回某个目录下所有文件和目录的绝对路径,返回的是File数组
.exists()方法 —— 看文件是否存在
.createNewFile()方法 —— 新建一个这个文件
.isFile()方法 —— 检查该文件是否是一个正常的文件
.getParent()方法 —— 获得父路径
.getAbsolutePath()方法 —— 获得绝对路径
.getPath()方法 —— 返回的是定义时的路径
.getName()方法 —— 获得该文件的名字
例子(file类的常用方法):
package com.javawork25; // File类操作 import java.io.File; import java.io.IOException; public class Work25_IOwenjianliuyongfa { public static void main(String[] args) { File f1 = new File("c:/01MY Tools"); // 实例化一个File对象参数传入文件的地址 System.out.println(f1.isDirectory()); // File对象 .isDirectory()方法是判断该文件是不是一个文件夹 f1.listFiles(); // File对象 .list()方法是返回某个目录下的所有文件和目录的文件名,返回的是String数组 // File对象 .listFiles()方法是返回某个目录下所有文件和目录的绝对路径,返回的是File数组 File f2 = new File("c:/01MY Tools/a.txt"); try { if(f2.exists()) { // File对象 .exists()方法看文件是否存在 System.out.println("文件已存在"); } else { f2.createNewFile(); // File对象 .createNewFile()方法新建一个这个文件 } System.out.println(f2.isFile()); // File对象 .isFile()方法检查该文件是否是一个正常的文件 System.out.println(f2.getParent()); // File对象 .getParent()方法获得父路径 System.out.println(f2.getAbsolutePath()); // File对象 .getAbsolutePath()方法获得绝对路径 System.out.println(f2.getPath()); // File对象 .getPath()方法返回的是定义时的路径 System.out.println(f2.getName()); // File对象 .getName()方法获得该文件的名字 } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
用法:
实例化RandomAccessFile时参数要传入(new File(“操作文件的地址”), “rw(读写)或者r(读)或者w(写)”)
每次使用RandomAccessFile实例时要用 .seek(0)方法将seek设为最初
.write(“要写的文字”.getBytes()) —— 写入文件方法
.getFilePointer()方法 —— 获取当前seek指针的位置,也就是当指针的位置小于RandomAccessFile实例的长度时退出循环
.readLine()方法 —— 获取文件的内容
.getBytes(“ISO-8859-1”), “GBK”)方法 —— 是将该内容从GBK形式转换为ISO-8859-1形式
例子(RandomAccessFile类的常用方法):
package com.javawork25; // RandomAccessFile随机访问读写文件类操作 import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; public class Work25_IOwenjianduxieyongfa { public static void main(String[] args) { RandomAccessFile accessFile = null; // 实例化一个RandomAccessFile try { accessFile = new RandomAccessFile(new File("./g.txt"), "rw"); // 实例化RandomAccessFile时参数要传入(new File("操作文件的地址"), "rw(读写)或者r(读)或者w(写)") accessFile.seek(0); // 每次使用RandomAccessFile实例时要用 .seek(0)方法将seek设为最初 accessFile.write("你好随机访问".getBytes()); // RandomAccessFile实例 .write("要写的文字".getBytes())写入文件方法 // 进行读取文件的操作 accessFile.seek(0); while(accessFile.getFilePointer() < accessFile.length()) { // .getFilePointer()方法是获取当前seek指针的位置,也就是当指针的位置小于RandomAccessFile实例的长度时退出循环 String info = accessFile.readLine(); // RandomAccessFile实例 .readLine()方法获取文件的内容 String infook = new String(info.getBytes("ISO-8859-1"), "GBK"); // .getBytes("ISO-8859-1"), "GBK")方法意思是将该内容从GBK形式转换为ISO-8859-1形式 System.out.println("内容:" + infook); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
指路陈哈哈大佬的Java数据库相关面试题原帖