JavaIO编程
摘要:
Java.io编程是所有语言之最,它为了方便进行IO(Input&Output)提供有大量的程序类的支持。在整个java.io包中核心的组成为:
- 一个类:File
- 一个接口:Serializable
- 四个抽象类:InputStream、OutputStream、Reader、Writer
一、File类
File类是在整个java.io包里面唯一一个与文件本身操作有关的操作类,与文件本身有关指的是这个类可以操作文件路径的指派,可以创建或者删除文件,以及还可以获取文件的相关的信息内容,在使用File类的时候可以采用如下构造方法进行实例化:
- public File(String pathname) :设置要操作文件的完整路径
- public File(File parent, String child) :设置要操作文件的父目录与子文件路径
注意: 当指定的文件目录不存在相关文件时,只要不执行读操作基本上是没有问题的,因为File的构造方法会判断该文件目录是否存在对应的文件,不存在的话就创建一个
File类常用方法:
-
public boolean createNewFile() throws IOException :创建一个新文件
-
public boolean delete() :删除文件
-
public boolean exists() :判断文件是否存在
写法示例:
File file = new File("d:\\demo.txt");
if (file.exists()) {
System.out.println("【文件存在,执行删除操作】" + file.delete());
} else {
System.out.println("【文件不存在,执行创建操作】" + file.createFile());
}
注意:
-
Java为了支持跨平台,在IO包中定义了一个分隔符常量:separator,以避免后期代码迁移过程中出现因分隔符不同而产生的各种异常。因此,正确的路径写法应该是:
File file = new File("d:" + File.separator + "demo.txt");
-
当前程序是直接在D盘的根目录下进行了文件的创建,但在很多情况下有可能需要在一些子目录之中进行文件的创建。但是,在java.io包里面,必须保证有父目录的情况下才可以创建子文件。如果操作路径里面追加了很多目录的定义,那么程序在执行过程中就会产生异常:java.io.IOException:系统找不到指定的路径。如果要想进行目录的创建,则需要使用到File类中的如下几个方法:
- public boolean mkdir() :只创建单级目录
- public boolean mkdirs() :创建多级目录(同时创建多个子目录)
- public String getParent() :获取父路径的信息(String)
- public File getParentFile() :获取父路径的File实例(File)
写法示例:
File file = new File("d:" + File.separator + "layer1" + File.separator + "layer2" + File.separator + "demo.txt"); if (!file.getParentFile.exists()) {//如果父目录不存在 file.getParentFile().mkdirs();//创建多级目录 } if (file.exists()) { System.out.println("【文件存在,执行删除操作】" + file.delete()); } else { System.out.println("【文件不存在,执行创建操作】" + file.createFile()); }
当然,如果要考虑多线程的话,判断可以放在static代码块中,所有的线程只用执行一次即可
在File类里面除了提供有基本的文件与目录的创建之外,也提供了一些其它的操作信息,这些操作的方法如下:
- public boolean canExecute() :文件是否能执行
- public boolean canRead() :文件是否能读
- public boolean canWrite() :文件是否能写
- public File getAbsoluteFile() :获取文件的绝对路径实例
- public String getName() :获取文件的名称
- public boolean isDirectory() :当前路径是否为目录
- public boolean isFile() :当前路径是否为文件
- public long lastModified() :获取最后一次的修改时间(long,毫秒数)
- public long length() :获取文件长度(long,字节个数)
使用File类可以实现文件或者目录的操作,但在很多时候有可能会有一些子目录存在,现在要求可以将某个父目录下的所有子目录和文件全部列出,那么这个时候就必须使用到File类中提供的一个重要方法:
- public String[] list() :描述的是子路径的信息(只列出当前路径下所有子路径信息,包括子文件和子目录,不存在父路径)
- public File[] listFiles() :列出所有的路径信息(返回完整的路径信息)
实例: 递归列出文件目录
public static void listDir(File file){
//如果是目录
if(file.isDirectory()){
File result [] = file.listFiles();
if(result != null){//确定已经列出了内容
for(int x = 0; x < result.length; x++){
listDir(result[x]);//递归遍历每个目录
}
}
}
//如果不是目录
System.out.println(file);
}
文件更名操作,用到的重要方法是:
- public void renameTo(File newFile) :将一个文件路径改为新的文件路径
实例:
File oldFile = new File("D:" + File.separator + "info.jpg");
File newFile = new File("D:" + File.separator + "view.jpg");
oldFile.renameTo(newFile);//将老路径改成新路径(新路径只更改了文件名)
二、流操作简介
File类本身是实现了文件自身的处理操作,但是并未规定其文件内容的操作。而要想实现文件内容的操作就必须通过流(Stream)
流主要指的是数据的处理方式,以及目标的内容处理机制。所有的流都分为输入流和输出流两种。java.io包中又对输入流和输出流分了两种类型:
- OutputStream(字节输出流)
- InputStream(字节输入流)
- Writer(字符输入流)
- Reader(字符输出流)
注意: 不管使用哪一种操作流,本质上的操作流程都是类似的
基本使用步骤:
- 通过File类设定要操作的文件路径(针对文件流而言)
- 通过字节流或字符流的子类为父类进行对象实例化处理
- 进行读、写的流操作
- 流属于宝贵资源,操作完毕后一定要进行关闭(close())
三、字节输出流OutputStream
java.io.OutputStream是实现字节输出流的操作类,该类的定义如下:
public abstract class OutputStream extends Object implements Closeable, Flushable {
……
}
继承了Object类,实现了两个接口Closeable和Flushable
Closeable接口的定义:
public interface Closeable extends AutoCloseable {
void close() throws IOException;//也就是关闭方法
}
Flushable接口的定义:
public interface Flushable {
public flush() throws IOException;//也就是清空缓存的方法
}
**注意:**缓冲区的存在是为了防止写入过多而导致的数据丢失和更快速的读取操作。flush方法就是为了清空缓冲区
OutputStream类提供的是一个流的输出操作,所以在OutputStream类中提供了三个输出方法:
- public void write(byte[] b) :输出全部字节数组的数据
- public void write(byte[] b, int off, int len) :输出部分字节数组的数据
- public abstract void write(int b) :输出单个字节数据(byte和int可以相互转换)
可以发现,在整个OutputStream类中的write()方法都是针对于字节数据进行处理的。但需要注意的是OutputStream是一个抽象类,所以想要通过此类进行操作就需要依靠子类对象实例化,如FileOutputStream,该子类是由操作系统实现的,所以只需要关注构造方法即可:
- public FileOutputStream(FIle file) throws FileNotFoundException :设置文件输出流,每次都对原始内容进行覆盖
- public FileOutputStream(File file, boolean append) throws FileNotFoundException :如果append设置为true,则表示内容追加
字节输出流实现的就是字节数据的输出,所有的内容一定要转为字节数组,或者使用单个字节进行输出处理
写法示例:
import java.io.File;
import java.io.OutputStream;
import java.io.FileOutputStream;
public class OutputStreamDemo01 {
public static void main(String args[]) throws Exception {
// 第1步,使用File类找到一个文件
File f = new File("d:" + File.separator + "test.txt");
// 第2步,通过子类实例化父类对象
OutputStream out = new FileOutputStream(f);
// 第3步,进行写操作
String str = "Hello World";
byte b[] = str.getBytes();//输出byte数组
out.write(b);// 将内容输出,保存文件
// 第4步,关闭输出流
out.close();
}
}
也可以使用循环方式去写,适用于数据量更大的应用场景
import java.io.File;
import java.io.OutputStream;
import java.io.FileOutputStream;
public class OutputStreamDemo02 {
public static void main(String args[]) throws Exception {
// 第1步,使用File类找到一个文件
File f = new File("d:" + File.separator + "test.txt");
// 第2步,通过子类实例化父类对象
OutputStream out = new FileOutputStream(f);
// 第3步,进行写操作
String str = "Hello World";
byte b[] = str.getBytes();
for(int i = 0;i < b.length; i++) {// 采用循环方式写入
out.write(b[i]);// 每次只写入一个内容
}
// 第4步,关闭输出流
out.close();
}
}
四、字节输入流InputStream
InputStream是一个提供了字节输入流的实现类,该类的定义如下:
public abstract class InputStream extends Object implements Closeable {
……
}
在进行读的时候是不会使用到系统的缓冲区的。为了防止过多的写入java.io才会提供写入缓冲区,写入的缓冲区才需要进行清空处理,所以Input Stream类不会实现Flushable父接口。
在InputStream中提供有如下读取操作方法:
- public abstract int read() throws IOException :读取单个字节数据,如果已经没有数据了则返回“-1”
- public int read(byte[] b) throws IOException :将内容读取到字节数组之中,返回读取数据的长度(个数),没有数据返回“-1”
- public int read(byte[] b, int off, int len) throws IOException :读取部分内容到字节数组中,返回读取数据的长度(个数),没有数据返回“-1”
InputStream是一个抽象类,可以利用FileInputStream子类为其进行实例化,以实现文件流的操作,其构造方法为:
- public FileInputStream(File file) throws FileNotFoundException
写法示例:
import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;
public class InputStreamDemo01 {
public static void main(String args[]) throws Exception {
// 第1步,使用File类找到一个文件
File f = new File("d:" + File.separator + "test.txt");
// 第2步,通过子类实例化父类对象
InputStream input = new FileInputStream(f);
// 第3步,进行读操作
byte b[] = new byte[1024];// 所有的内容都读到此数组之中
input.read(b);// 读取内容
// 第4步,关闭输出流
input.close();
System.out.println("文件内容为:" + new String(b));
}
}
以上写法存在的问题是byte数组大小固定,导致读取小文件时造成空间浪费,读取大文件时又会数组越界问题
优化方案有:
-
先获取文件大小,再分配空间
import java.io.File; import java.io.InputStream; import java.io.FileInputStream; public class InputStreamDemo02 { public static void main(String args[]) throws Exception { // 第1步,使用File类找到一个文件 File f = new File("d:" + File.separator + "test.txt"); // 第2步,通过子类实例化父类对象 InputStream input = new FileInputStream(f); // 第3步,进行读操作 byte b[] = new byte[(int)f.length()];//按文件大小创建数组 int len = input.read(b); // 第4步,关闭输出流 input.close(); System.out.println("读入数据的长度:" + len); System.out.println("文件内容为:" + new String(b)); } }
-
无法获取文件大小时,只能以循环的方式依次输入数据,并以读取的数据是否为-1为读完的标志
import java.io.File; import java.io.InputStream; import java.io.FileInputStream; public class InputStreamDemo03 { public static void main(String args[]) throws Exception { // 第1步,使用File类找到一个文件 File f= new File("d:" + File.separator + "test.txt"); // 第2步,通过子类实例化父类对象 InputStream input = new FileInputStream(f); // 第3步,进行读操作 byte b[] = new byte[1024]; int len = 0; int temp = 0; // 接收每一个读取操作的返回值 while((temp = input.read()) != -1) { // -1代表文件已读完 b[len] = (byte) temp; len++; } // 第4步,关闭输出流 input.close(); System.out.println("内容为:" + new String(b,0,len)); } }
五、字符输出流Writer
OutputStream是可以实现输出操作的,但是其在进行输出的时候使用的全都是字节数组的形式完成的,但在大部分情况下对于内容而言最方便的处理类型一定是字符串,所以为了简化输出操作,从JDK1.1开始又提供了字符输出流,Writer类定义:
public abstract class Writer extends Object implements Appendable, Closeable, Flushable {
……
}
Writer抽象类与之前的OutputStream相比多实现了一个Appendable接口,里面只有一个append()方法:
- public Appendable append(CharSequence csq) throws IOException
- public Appendable append(CharSequence csq, int Start, int end) throws IOException
- public Appendable append(char c) throws IOException
在使用Writer类进行输出的时候最大的操作特点在于其可以直接进行字符串的数据处理:
-
输出方法:public void write(String str) throws IOException
在后续版本里面才逐步有了CharSequence接口的支持,只要可以接收此接口的实例就意味着可以追加String、StringBuffer、StringBuilder类的实例写法示例:
import java.io.File; import java.io.Writer; import java.io.FileWriter; public class WriterDemo01 { public static void main(String args[]) throws Exception { // 第1步,使用File类找到一个文件 File f = new File("d:" + File.separator + "test.txt"); // 第2步,通过子类实例化父类对象 Writer out = new FileWriter(f); // 第3步,进行写操作 String str = "Hello World!!!"; out.write(str); // 第4步,关闭输出流 out.close(); } }
使用Writer可以实现与OutputStream类似的功能,但其最大的优势在于可以直接String对象
六、字符输入流Reader
Reader是实现字符数据的输入流类,这个类与之前三个类相同,也定义的是一个抽象类,具体定义如下:
public abstract class Reader extends Object implements Readable, Closeable {
……
}
写法示例:
import java.io.File;
import java.io.Reader;
import java.io.FileReader;
public class ReaderDemo01 {
public static void main(String args[]) throws Exception {
// 第1步,使用File类找到一个文件
File f = new File("d:" + File.separator + "test.txt");
// 第2步,通过子类实例化父类对象
Reader input = new FileReader(f);
// 第3步,进行读操作
char c[] = new char[1024];
int len = input.read(c);
// 第4步,关闭输出流
input.close();
System.out.println("内容为:" + new String(c,0,len));
}
}
也可以使用循环的方式,通过文件是否读到底的形式读取
import java.io.File;
import java.io.Reader;
import java.io.FileReader;
public class ReaderDemo02 {
public static void main(String args[]) throws Exception {
// 第1步,使用File类找到一个文件
File f= new File("d:" + File.separator + "test.txt");
// 第2步,通过子类实例化父类对象
Reader input = new FileReader(f);
// 第3步,进行读操作
char c[] = new char[1024];
int temp = 0;
int len = 0;
while((temp = input.read()) != -1){ // -1代表文件已读完
c[len] = (char)temp;
len++;
}
// 第4步,关闭输出流
input.close();
System.out.println("内容为:" + new String(c,0,len));
}
}
七、总结
在开发中,对于网络、文件等的操作等操作之中几乎都是以字节数据为主的,但是字节数据不方便进行中文处理。当遇到对中文的处理时,字符流才是最方便的