IO流
1、File类
File这个类,在命名上多少有些误导。实际上, File不只可以 表示一个文件,还可以表示一个路径(可以理解为文件夹)。
通过建立File对象,我们就可以操作具体的某个(些)文件 (文件夹)。
2、File类构造器
public File(String path)
如果path是实际存在的路径,则该File对象表示的是目录 如果path是文件名,则该File对象表示的是文件
File dir = new File("C:/io/File.java");
public File(String path, String name)
path是路径名, name是文件名或路径名
File myfile = new File("C:/io", "File.java");
public File(File dir, String name)
dir是路径名, name是文件名
File myDir = new File("C:/io");
File myFile = new File(myDir, "File.java");
3、File 常用方法
/* * File类相关方法 * 创建一个File对象,是不会在硬盘上生成对应的文件或 * 目录(文件夹)的,我们需要调用相关的方法来创建文件 * 或目录。 * * mkdir与mkdirs方法 * 两个方法都用来创建路径(文件夹),不同的是,当路径所在 * 的父路径不存在时,mkdir不会创建成功,返回false,而 * mkdirs会连同父路径一同创建。 */ package day16; import java.io.File; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; public class FileMethod { public static void main(String[] args) throws IOException { File file = new File("c:/1234.txt"); // File file = new File("1234.txt"); // 生成File对象所表示的文件。文件所在的路径必须 // 存在,才能创建成功。否则会产生IOException异常。 // file.createNewFile(); // 返回文件的名字 System.out.println(file.getName()); // 返回文件的路径 System.out.println(file.getPath()); // 返回文件的绝对路径。 System.out.println(file.getAbsolutePath()); // 返回上级路径 System.out.println(file.getParent()); // 判断文件是否存在,存在返回true,否则返回false。 System.out.println(file.exists()); // 判断文件是否可写,是返回true,否则返回false。 System.out.println(file.canWrite()); // 判断文件是否可读,是返回true,否则返回false。 System.out.println(file.canRead()); // 判断当前file对象表示的是否为文件,是则返回true, // 否则返回false。 System.out.println(file.isFile()); // 判断当前file对象表示的是否为路径(目录),是 // 则返回true,否则返回false。 System.out.println(file.isDirectory()); // 返回文件最后一次的修改时间。 long time = file.lastModified(); Date d = new Date(time); DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println(df.format(d)); // 返回文件的长度,以字节为单位。 System.out.println(file.length()); // 删除file对象所表示的文件,成功返回true,否则返回false。 System.out.println(file.delete()); File f2 = new File("c:"); // f2.createNewFile(); // 创建File对象所代表的路径。成功返回true,否则 // 返回false。 // 调用mkdir创建路径时,该路径所在的父路径必须要 // 存在,否则无法创建成功,返回false。 // System.out.println(f2.mkdir()); // 创建File对象所代表的路径,成功返回true,否则 // 返回false。 // 调用mkdirs创建路径时,如果该路径所在的父路径 // 不存在,则连同父路径一同创建。 // System.out.println(f2.mkdirs()); // 返回当前File对象所表示的路径下所有的文件与路径(名字)。 String[] names = f2.list(); for (String name : names) { System.out.println(name); } // 返回当前File对象所表示的路径下所有的文件与路径(File对象) File[] files = f2.listFiles(); for (File f : files) { System.out.println(f.getName() + ":" + f.lastModified()); } } }
4、流
流是一组有顺序的、有起点和终点的数据。 Java程序的输入输出功能是通过流(stream)来实现的。 在Java中有关流的操作类存放在java.io包中
输入流用来读取文件,输出流用来写入文件。
当程序需要读取数据的时候,就会开启一个通向数据源的流, 这个数据源可以是文件,内存,或是网络连接。类似的,当 程序需要写入数据的时候,就会开启一个通向目的地的流 。
5、流的分类
流按照处理数据的单位可以分为两种:
字节流( 8位)
字符流( 16位)
二进制文件的基本存储单位是字节
文本文件的基本存储单位是字符
6、字节流和字符流
字节流用于处理字节的输入和输出。例如使用字节流读取或 写入二进制数据。
字符流为字符输入和输出处理提供了方便,在某些场合,字 符流比字节流更高效。
字节流的父类是InputStream与OutputStream。字符流的父 类是Reader与Writer
7、InputStream类
int read() 读取一个字节,返回值为所读的字节。
int read(byte[ ] b) 读入b.length个字节到数组b并返回实际 读入的字节数。
int read(byte[ ] b, int off, int len) 读入流中的数据到数组b, 从off开始的长度为len的数组元素中。
int avaliable() 返回当前输入流中可读的字节数。
close() 流操作完毕后必须关闭。
说明:如果到了文件的末尾,以上三个方法均返回-1。
/* * 字节输入流读取文件 */ package day16; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; public class InputStreamTest { public static void main(String[] args) { try { String path = "D:/eclipse/workspace/java/src/day16/FileMethod.java"; // 使用输入流读取文件,该文件必须要存在,否则会产生 // FileNotFoundException异常。 InputStream in = new FileInputStream(path); // 读取一个字节,返回读取到的字节(无符号)。 // 如果没有字节可读,返回-1。 // int data = in.read(); // while (data != -1) { // System.out.print((char) data); // // 读取下一个字节。 // data = in.read(); // } // 改进 // int data; // while ((data = in.read()) != 1) { // System.out.print((char) data); // } // 通过数组方式实现 byte[] data = new byte[10]; int length; // 读取的数据放到字节数组当中。尽可能填满整个数组。 // 当填满数组或没有数据可读时,返回实际读取的字节数。 // 当没有数据可读时,返回-1。 // while ((length = in.read(data)) > 0) { // for (int i = 0; i < data.length; i++) { // System.out.print((char)data[i]); // } // 使用字节数组的所有元素(数据)去构造String对象。 // String s = new String(data); // 使用字节数组(区间)去构造String对象。 // 第2个参数指定偏移量,第三个参数指定元素的长度。 // String s = new String(data, 0, length); // System.out.print(s); // } // 使用字节数组的一部分(区间)实现 // 使用字节数组的部分(区间)来存储读取的数据。第二个参数指定 // 偏移量,第三个参数指定长度。当区别填满或无数据可读时,返回 // 实际读取的字节数,如果无数据可读,返回-1。 while ((length = in.read(data, 0, 5)) > 0) { String s = new String(data, 0, length); System.out.print(s); } // 可以释放资源,但如果之前产生异常,该语句 // 将不会得到执行,导致资源无法释放。造成 // 资源泄露。 // in.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
8、对资源释放
/* * 对资源的释放 * 虽然finally总是会得到执行,但是,在finally中 * 对资源进行释放过于繁琐。我们可以考虑使用try-with-resources * 来自动释放资源。 * * try (声明需要释放的资源) { * 可能产生异常的语句 * } catch (异常) { * * } * try-with-resources的优势在于:声明在小括号中的资源,可以 * 在try语句块执行完毕后得到自动的释放(自动调用该资源的close方法)。 * 只有AutoCloseable类型(子类型)才能作为try-with-resources * 的资源。 */ package day16; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; public class CloseTest { public static void main(String[] args) { String path = "D:/eclipse/workspace/java/src/day16/FileMethod.java"; InputStream in = null; try { in = new FileInputStream(path); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } // 使用try-with-resources自动释放资源,简化操作。 try (InputStream in2 = new FileInputStream(path)) { // 读取文件的操作 // in2.read(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
9、实现复制
/* * 使用字节输入流与字节输出流实现文件的复制。 */ package day16; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class Copy { public static void main(String[] args) { try (InputStream in = new FileInputStream("c:/第15天.pdf"); OutputStream out = new FileOutputStream("c:/第15天copy.pdf")) { // 返回流中可读取的字节数。 System.out.println(in.available()); int data; while ((data = in.read()) != -1) { out.write(data); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
10、OutputStream类
void write(int b ) 往流中写一个字节b。
void write(byte b[ ]) 往流中写一个字节数组b。
void write(byte b[ ], int off, int len) 把字节数组b中从下标 off开始,长度为len的字节写入流中。
flush( ) 刷新输出流,并输出所有被缓存的字节,由于某些 流支持缓存功能,该方法将把缓存中所有内容强制输出到流 中。
close( ) 流操作完毕后必须关闭。
/* * 字节输出流 * 输入流与输出流(文件不存在时对比) * 对于输入流,如果文件不存在,则会产生 * FileNotFoundException异常。 * 对于输出流,如果文件不存在,但是文件所在的路径存在, * 则该文件会被自动创建。如果文件不存在,文件所在的路径 * 也不存在,则会产生FileNotFoundException异常。 */ package day16; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; public class OutputStreamTest { public static void main(String[] args) { try (OutputStream out = new FileOutputStream("c:/1234.txt");) { // 写入一个字节 // out.write(65); // 写入一个字节数组(写入数组中所有的元素) byte[] data = { 65, 66, 67, 68, 69, 70 }; // out.write(data); // 写入数组的一个区间(第2个参数指定开始位置, // 第3个参数指定长度) out.write(data, 1, 2); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
11、Reader类
public int read() throws IOException 读取一个字符,返回 值为读取的字符。
public int read(char cbuf[ ]) throws IOException 读取一系 列字符到数组cbuf[]中,返回值为实际读取的字符的数量。
public abstract int read(char cbuf[],int off,int len) throws IOException 读取len个字符,从数组cbuf[]的下标off处开始 存放,返回值为实际读取的字符数量。
public abstract void close() throws IOException 关闭流。
说明:三个read方法如果已到达流的末尾,则返回 -1。
12、Writer类
public void write(int c) throws IOException 将整型值c的低 16位写入输出流。
public void write(String str) throws IOException 将字符串 str中的字符写入输出流。
public void write(char cbuf[]) throws IOException 将字符 数组cbuf[]写入输出流。
public abstract void write(char cbuf[],int off,int len)
throws IOException 将字符数组cbuf[]中的从索引为off的位 置处开始的len个字符写入输出流。
public void write(String str,int off,int len) throws
IOException 将字符串str 中从索引off开始处的len个字符写 入输出流。
flush() 刷新输出流,并输出所有被缓存的字节。
close() 关闭流。
/* * 字符流 */ package day16; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.Reader; import java.io.Writer; public class ReadWriterTest { public static void main(String[] args) throws IOException { Writer w = new FileWriter("c:/1234.txt"); w.write("写String"); // 刷新缓存中的数据,将缓存中的数据传输到目的地。 // w.flush(); // 当我们调用close方法关闭流资源时,会自动刷新缓存。 // 即会调用流的flush方法。 w.close(); } // 使用字符流也可以实现文件的复制。 public void copy(String src, String dst) { try (Reader reader = new FileReader(src); Writer writer = new FileWriter(dst)) { char[] data = new char[10]; int length; while ((length = reader.read(data)) > 0) { writer.write(data, 0, length); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
13、标准输入输出
public static final InputStream in :
标准输入,从标准输入获取输入(通常是键盘)
public static final PrintStream out :
标准输出,把输出送到缺省的显示(通常是显示器)
public static final PrintStream err :
标准输出,把错误信息送到缺省的显示
/* * System类中的in,out,err * out与err都是输出流(PrintStream), * 区别在于: * System.out是执行正常输出的。 * System.err是执行异常输出的。 * * 使用System类的setIn,setOut,setErr方法可以实现 * IO流的重定向,这有利于我们对信息进行分类处理。 */ package day16; import java.io.IOException; import java.io.PrintStream; public class SystemTest { public static void main(String[] args) throws IOException { // System.out.println("正常输出"); // System.err.println("异常输出"); PrintStream p = new PrintStream("c:/error.txt"); // 错误流重定向 System.setErr(p); System.out.println("输出标准正常的信息。"); System.err.println("输出错误的信息!"); } }
14、字符流
主要用来读写字符串数据,即文本数据。
二进制文件与文本文件的区别 文件最终都是以二进制的形式存储,如果一个文件中的每个字 节或每相邻的几个字节的数据都可以表示成某种字符,这样的 文件就是文本文件,可见文本文件只是二进制文件的一种,如 mp3、图像的文件就不能转换成字符,而txt文件里面存储的都 是字符。
15、读写文件
InputSteam类、 OutputSteam类、 Reader类和Writer类都是 抽象的,无法创建该类的对象,我们需要使用子类来进行相 关操作。
FileInputSteam与FileOutputSteam继承InputSteam与 OutputSteam。而FileReader与FileWriter继承Reader与 Writer。
/* * 使用输出流时,可以指定是否以追加的形式写入数据。 * 如果追加,会在文件末尾继续写入, * 如果不追加,则会覆盖文件,重新写入。 */ package day16; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; public class AppendTest { public static void main(String[] args) { // 指定以追加的方式写入数据,默认为false(覆盖)。 try (OutputStream out = new FileOutputStream("c:/1234.txt", true);) { out.write(65); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
16、FileInputStream类构造器
FileInputStream对象封装了将要读取的文件,读文件必须存在 且包含数据,该类类型的构造器只能为存在的文件创建对象。
FileInputStream(String filename) 从String对象创建 FileInputStream对象,该String对象用来指定文件名,该构 造方法会抛出FileNotFoundException异常。
FileInputStream in = new FileInputStream("fileIn.txt");
FileInputStream(File file) 使用File对象进行创建,也会抛出 FileNotFoundException异常。
File file = new File("c:\\tmp\\test.txt");
FileInputStream in = new FileInputStream(file);
17、FileOutputStream类构造器
FileOutputStream(String filename) 为文件filename创建一 个输出流,文件中存在的内容将被覆盖。
FileOutputStream(String filename,boolean append) 创建一 个文件输出流对象,并将内容写到指定的filename文件中, 如果append为true,则接着文件最后写,否则文件中的内容
将被覆盖。
FileOutputStream(File file) 为对象file表示的文件创建一个 输出流,文件中的内容将被覆盖。
FileOutputStream(File file, boolean append)
创建一个文件输出流对象,并将内容写到指定的file对象中,如 果append为true,则接着文件最后写,否则文件中的内容将被 覆盖。
说明:如果该文件不存在,则创建一个同名的新文件,但只有 在父目录存在的条件下创建,操作才会执行,所以使用前最后 检查下父目录是否存在。
18、缓存流
BufferedReader 缓存输入流
BufferedWriter 缓存输出流
使用缓存流可以使字符流读取(写入)更加高效。
/* * 缓存流 * BufferedReader * BufferedWriter * * 使用缓存可以减少程序与底层IO设备进行交互的次数, * 进而提高性能。(与底层IO设备进行交互是一个耗时的操作)。 * * 在进行输出是优先使用PrintWriter,因为其输出方法 * print不会产生异常(异常内部处理)。 */ package day16; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class BufferedTest { public static void main(String[] args) { try (BufferedReader br = new BufferedReader(new FileReader("c:/1234.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("c:/5678.txt"))) { String s; // 一次读取一行。当没有数据可读时,返回null。 while ((s = br.readLine()) != null) { bw.write(s); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } try (BufferedReader br = new BufferedReader(new FileReader("c:/1234.txt")); PrintWriter pw = new PrintWriter("c:/5678.txt")) { String s; // 一次读取一行。当没有数据可读时,返回null。 while ((s = br.readLine()) != null) { // pw.write(s); pw.print(s); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
19、转换流
Java提供两个转换流,用于将字节流转换成字符流。
InputSteamReader 字节输入流转换为字符输入流。
OutputSteamWriter 字节输出流转换为字符输出流。
说明:当对字符进行读取,写入时,将字节流转换成字符流可
以提高效率。
package day16; import java.awt.Insets; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; public class TransfromTest { public static void main(String[] args) { } public void f(InputStream in, OutputStream out) { // 将字节流转换成字符流,但是不太方便。 InputStreamReader is = new InputStreamReader(in); OutputStreamWriter os = new OutputStreamWriter(out); // 更加高效的操作,使用缓存流再进行一次包裹。 // BufferedReader br = new BufferedReader(is); // PrintWriter pw = new PrintWriter(os); BufferedReader br = new BufferedReader(new InputStreamReader(in)); // PrintWriter pw = new PrintWriter(new OutputStreamWriter(out)); PrintWriter pw = new PrintWriter(out); } }
20、数据流
数据流是对基本数据类型与String类型的进行输入输出操作。 数据流需要实现DataInput与DataOutput接口。最常使用的 数据流是DataInputStream与DataOutputStream。
使用数据流可以操作基本数据类型与String类型。
/* * 数据流 * 数据流支持的类型是基本数据类型与String类型。 * 写入数据调用的方法:writeT,T为相关的类型。 * String对应的方法是writeUTF。 */ package day16; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class DataStreamTest { public static void main(String[] args) { DataStreamTest d = new DataStreamTest(); // d.write(); d.read(); } public void write() { int[] x = { 3, 10, 20, 354 }; String[] s = { "kk", "xc", "323", "vadf" }; try (DataOutputStream out = new DataOutputStream(new FileOutputStream("c:/data.txt"));) { for (int i = 0; i < x.length; i++) { out.writeInt(x[i]); out.writeUTF(s[i]); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public void read() { // 数据流没有办法判断是否还有数据可以读取,只能靠 // 捕获EOFException来结束。(当无数据可以读取时, // 会抛出EOFException异常。) try (DataInputStream input = new DataInputStream(new FileInputStream("c:/data.txt"));) { while (true) { // 使用数据流读取时需要注意,读取的顺序与写入 // 时的顺序一致。 System.out.print(input.readInt()); System.out.println("-" + input.readUTF()); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (EOFException e) { System.out.println("文件到达末尾,读取完毕,捕获异常。"); } catch (IOException e) { e.printStackTrace(); } } }
21、对象流
对象流是对对象进行的输入输出操作。我们使用 ObjectInputStream与ObjectOutputStream类来实现对象的读 取与写入。以上两个类分别实现ObjectInput与ObjectOutput接 口( DataInput 与DataOutput的子接口)。
把Java对象转换为字节序列称为序列化,而把字节序列恢复 为Java对象称为反序列化。对象序列化需要实现Serializable 接口。
Serializable接口没有显式声明任何方法,是一个标记接口。
/* * 对象流 */ package day16; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class ObjectStreamTest { public static void main(String[] args) { ObjectStreamTest o = new ObjectStreamTest(); // o.write(); o.read(); } public void read() { try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("c:/object.txt"))) { // 从流中恢复之前保存的对象(对象反序列化) Person p = (Person) in.readObject(); // System.out.println(p.getName()); // System.out.println(p.getAge()); Person p2 = (Person) in.readObject(); // 如果写入时,同一个对象写入多次,则读取时, // 也只有一个对象。 System.out.println(p == p2); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public void write() { try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("c:/object.txt"))) { Person p = new Person("张三", 30); // 序列化对象 out.writeObject(p); out.writeObject(p); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } // 将对象序列化,则该对象必须要实现Serializable接口。 // 该接口没有任何方法,是一个标记接口。 class Person implements Serializable { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person(String name, int age) { super(); this.name = name; this.age = age; } }