IO的核心组成就是五个类(File
、 OutputStream
、InputStream
、Reader
、Writer
) 一个接口(Serializable
)
File文件操作类
在Java.io包之中,File类是唯一一个与文件本身操作(创建、删除、取得信息…)有关,与文件内容无关的的程序类。
File类即可以描述真实文件,也可以是个文件夹
File类使用
- File类的两种实例化方式:
public File(String pathname)
public File(String parent, String child)
- 创建新文件
public boolean createNewFile() throws IOException
- 判断文件是否存在
public boolean exits()
- 删除文件
public boolean delete()
范例:编写文件的基本操作(如果文件不存在则进行创建;存在则删除)
package www.bit.FileTest;
import java.io.File;
import java.io.IOException;
public class MyFile {
public static void main(String[] args) {
// 定义要操作的文件路径
// 路径分隔符:File.separator
File file = new File("C:"+File.separator+"Users"+File.separator
+"DELL"+File.separator+"Desktop"+File.separator+"TestIO.java");
if (file.exists()){
file.delete();
}else{
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
有关目录的操作
- 取得父路径与父File对象
public String getParent()
public File getParentFile()
- 创建目录(无论有多少级父目录,都会一次性创建)
public boolean mkdirs()
范例:Java文件目录操作
import java.io.File;
import java.io.IOException;
public class MyFile {
public static void main(String[] args) throws IOException{
File file = new File("C:"+File.separator+"Users"+File.separator
+"DELL"+File.separator+"Desktop"+File.separator+"Test"+
File.separator+"Java IO"+File.separator+"TestIO.java");
if (!file.getParentFile().exists()){ // 创建父目录
file.getParentFile().mkdirs(); // 有多少级父目录就创建多少级
}
if (file.exists()){
// 文件存在,进行删除
file.delete();
}else {
file.createNewFile();
}
}
}
文件信息
- 判断File对象是否是文件
public boolean isFile()
- 判断File对象是否是路径
public boolean isDirectory()
- 取得文件大小
public long length()
- 取得最后修改日期
public long lastModified()
范例:取得文件信息
import java.io.File;
import java.io.IOException;
import java.util.Date;
public class MyFile {
public static void main(String[] args) throws IOException{
File file = new File("C:"+File.separator+"Users"+File.separator
+"DELL"+File.separator+"Desktop"+File.separator+"sifanchao.jpg");
if (file.exists() && file.isFile()){
System.out.println("文件大小:" + file.length()/1024 + "Kb");
System.out.println("最后一次修改日期: " + new Date(file.lastModified()));
}
}
}
- 列出一个目录的全部组成:
public File[] listFiles()
范例:列出C盘目录中的全部组成
import java.io.File;
public class MyFile {
public static void main(String[] args){
new Thread(()-> {
File file = new File("C:");
// 列出桌面目录中的全部组成
// +File.separator+"Users"+File.separator+"DELL"+File.separator+"Desktop"
System.out.println("遍历文件开始...");
long start = System.currentTimeMillis();
listAllFiles(file);
long end = System.currentTimeMillis();
System.out.println("遍历文件结束。共耗时:" + (end-start) + "毫秒");
}).start();
}
public static void listAllFiles(File file){
if (file.exists() && file.isFile()){
System.out.println(file);
}else {
File[] files = file.listFiles();
if (files != null){
for (File file1 : files){
listAllFiles(file1);
}
}
}
}
}
IO相关处理属于阻塞式耗时操作,一般放在子线程中进行
字节流与字符流
File类不支持文件内容处理,如果要处理文件内容,必须要通过流的操作模式来完成。流分为输入流和输出流。 在java.io包中,流分为两种:字节流与字符流
- 字节流:InputStream、OutputStream
- 字符流:Reader、Writer
字节流与字符流操作的本质区别只有一个:字节流是原生的操作,而字符流是经过处理后的操作。
一般使用字节流(无论是网络传输还是磁盘数据保存均以字节为单位)。
而所有磁盘中的数据必须先读取到内存后才能进行操作,内存中会帮助我们把字节变为字符。所以,只有处理中文文本时才会用到字符流。
流操作流程
无论是字节流还是字符流,操作流程几乎一样,以文件操作为例:
- 取得File对象
- 取得File对象的输入、输出流
- 进行数据的读取或写入
- 关闭流(close)
对于IO操作属于资源处理,所有的资源处理操作(IO操作、数据库操作、网络)后必须要进行关闭。
字节输出流(OutputStream)
如果要想通过程序进行内容输出,则可以使用java.io.OutputStream
。
public abstract class OutputStream implements Closeable, Flushable
OutputStream类实现了Closeable,Flushable两个接口,这两个接口中的方法:
- Closeable:
public void close() throws IOException
; - Flushable:
public void flush() throws IOException
;
在OutputStream类中还定义有其他方法:
- 将指定的字节数组全部输出
public void write(byte[] b)throws IOException
- 将部分字节数组输出
public void write(byte[] b,int offset,int len)throws IOException
- 输出单个字节
public abstract void write(int b)throws IOException
由于OutputStream是一个抽象类,所以要想为父类实例化,就必须要使用子类。由于方法名称都由父类声明好 了,所以我们在此处只需要关心子类的构造方法。如果要进行文件的操作,可以使用FileOutputStream类来处理, 这个类的构造方法如下:
- 文件内容覆盖
public FileOutputStream(File file) throws FileNotFoundException
- 文件内容追加
public FileOutputStream(File file, boolean append) throws FileNotFoundException
范例:实现文件的内容输出
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class MyFile {
public static void main(String[] args) throws Exception{
File file = new File("C:" + File.separator + "Users" + File.separator
+ "DELL" + File.separator + "Desktop" + File.separator + "Test.txt");
if (!file.getParentFile().exists()){ // 必须保证父目录存在
file.getParentFile().mkdirs(); // 创建多级父目录
}
// OutputStream是一个抽象类,所以需要通过子类进行实例化,
// 此时只能操作File类
OutputStream outputStream = new FileOutputStream(file);
// 要求输出到文件的内容
String msg = "你好,中国!";
outputStream.write(msg.getBytes());
outputStream.close();
}
}
当使用FileOutputStream
进行文件内容输出时时候,只要文件的父路径存在,所有的文件会自动帮助用户创建,不在需要调用createFile()
方法手工创建。
这个时候程序如果重复执行,并不会出现内容追加的情况而是一直在覆盖。如果需要文件内容追加,则需要调用 FileOutputStream提供的另外一种构造方法。
范例:文件内容追加
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class MyFile {
public static void main(String[] args) throws Exception{
File file = new File("C:" + File.separator + "Users" + File.separator
+ "DELL" + File.separator + "Desktop" + File.separator + "Test.txt");
if (!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
// true表示允许内容的追加操作
OutputStream outputStream = new FileOutputStream(file,true);
String msg = "我爱你\r\n";
outputStream.write(msg.getBytes());
outputStream.close();
}
}
AutoCloseable自动关闭支持
从JDk1.7开始追加了一个AutoCloseable接口,这个接口的主要目的是自动进行关闭处理,但是这种处理一般不好用,因为使用自动关闭接口有一个前提,需要结合try…catch…代码块。
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class MyFile {
public static void main(String[] args) throws Exception{
File file = new File("C:" + File.separator + "Users" + File.separator
+ "DELL" + File.separator + "Desktop" + File.separator + "Test.txt");
if (!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
try (OutputStream outputStream = new
FileOutputStream(file,true)){
String msg = "改革开放40周年\r\n";
outputStream.write(msg.getBytes());
}catch (Exception e) {
e.printStackTrace();
}
}
}
字节输入流:InputStream
利用了OutputStream实现了程序输出内容到文件的处理,下面使用InputStream类在程序中读取文件内容。 InputStream类的定义如下:
public abstract class InputStream implements Closeable
- 读取数据到字节数组b中
public int read(byte b[]) throws IOException
返回值三种情况:
- 返回b长度:当读取的数据大小>字节数组大小,返回字节数组大小
- 返回大于0但是小于b长度:当读取的数据大小<字节数组大小,返回真正读取大小
- 返回-1:数据读取完毕
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class MyFile {
public static void main(String[] args) throws Exception{
File file = new File("C:" + File.separator + "Users" + File.separator
+ "DELL" + File.separator + "Desktop" + File.separator + "Test.txt");
if (file.exists()){
InputStream inputStream = new FileInputStream(file);
// 每次可以读取的大数量
byte[] data = new byte[1024];
// 此时的数据读取到了数组之中
int len = inputStream.read(data);
System.out.println(new String(data,0,len));
inputStream.close();
}
}
- 读取单个字节
public int read() throws IOException
字符输出流Writer
字符适合于处理中文数据,Writer是字符输出流的处理类,这个类的定义如下:
public abstract class Writer implements Appendable, Closeable, Flushable
与OutputStream
相比多了一个Appendable
接口。 在Writer类里面也提供write()方法,而且该方法接收的类提供了一个直接输出字符串的方法:
public void write(String str) throws IOException
范例:通过Writer实现输出
import java.io.*;
public class MyFile {
public static void main(String[] args) throws Exception {
// 取得File对象
File file = new File("C:" + File.separator + "Users" + File.separator
+ "DELL" + File.separator + "Desktop" + File.separator + "Test.txt");
if (!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
// 取得输出流
Writer out = new FileWriter(file);
// 写入数据
String msg = "你好,世界!" ;
out.write(msg);
// 关闭
out.close();
}
}
Writer
类的结构与方法的使用与OutputStream
非常相似,只是Writer类对于中文的支持很好并且提供了直接写入 String的方法而已。
字节的输入流Reader
Reader依然也是一个抽象类。如果要进行文件读取,同样的,使用FileReader。
在上面讲到的Writer类中提供有方法直接向目标源写入字符串,Reader类中没有方法可以直接读取字符串,只能通过字符数组来读取。
import java.io.*;
public class MyFile {
public static void main(String[] args) throws Exception {
// 取得File对象
File file = new File("C:" + File.separator + "Users" + File.separator
+ "DELL" + File.separator + "Desktop" + File.separator + "Test.txt");
if (!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
// 取得输入流
Reader reader = new FileReader(file);
// 读入数据
char[] data = new char[1024];
int len = reader.read(data);
System.out.println(len);
System.out.println(new String(data,0,len));
reader.close();
}
}
字符流适合处理中文,字节流适合处理一切数据类型(对中文支持不好)
字符流VS字节流
- 从实际开发来讲,字节流优先考虑,只有处理中文时才会考虑使用字符流
- 所有字符流操作,无论是写入还是输出,数据都先保存在缓存中
如果字符流不关闭,数据就有可能保存在缓存中并没有输出到目标源。这种情况下就必须强制刷新才能够得到完整数据。
import java.io.*;
public class MyFile {
public static void main(String[] args) throws Exception {
// 取得File对象
File file = new File("C:" + File.separator + "Users" + File.separator
+ "DELL" + File.separator + "Desktop" + File.separator + "Test.txt");
if (!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
// 取得输出流
Writer out = new FileWriter(file);
// 写入数据
String msg = "你好,世界!" ;
out.write(msg);
out.flush();
}
}
转换流
现在为止已经知道了两种数据流:字节流和字符流。实际上这两种流是可以进行互相转换处理的。
OutputStreamWriter:将字节输出流变为字符输出流(Writer对于文字的输出要比OutputStream方 便)
InputStreamReader:将字节输入流变为字符输入流(InputStream读取的是字节,不方便中文的处理)
要想知道这两个类的实际意义,我们首先来看这两个类的继承关系以及构造方法:
public class OutputStreamWriter extends Writer
/**
* Creates an OutputStreamWriter that uses the default character encoding.
*
* @param out An OutputStream
*/
public OutputStreamWriter(OutputStream out) {
super(out);
try {
se = StreamEncoder.forOutputStreamWriter(out, this, (String)null);
} catch (UnsupportedEncodingException e) {
throw new Error(e);
}
}
public class InputStreamReader extends Reader
/**
* Creates an InputStreamReader that uses the default charset.
*
* @param in An InputStream
*/
public InputStreamReader(InputStream in) {
super(in);
try {
sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
} catch (UnsupportedEncodingException e) {
// The default encoding should always be available
throw new Error(e);
}
}
范例:观察字节流与字符流的转换
package www.bit.FileTest;
import java.io.*;
public class TestFileStream {
public static void main(String[] args) throws Exception{
File file = new File("C:" + File.separator + "Users" + File.separator
+ "DELL" + File.separator + "Desktop" + File.separator + "Test.txt");
if (!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
OutputStream outputStream = new FileOutputStream(file);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
outputStreamWriter.write("你好,中国!");
outputStreamWriter.close();
}
}
这种操作在实际开发中并没有什么意义,我们主要用它来分析FileOutputStream、FileInputStream、FileWriter、 FileReader之间的继承关系。
字符编码
常用的字符编码:
GBK、GB2312:国际编码。GBK即包含简体中文也包含繁体中文,而GB2312只包含简体中文
UNICODE:java提供的16进制编码,可以描述世界上任意的文字。由于采用16进制编码,导致编码体积过大,造成网络传输负担。
ISO-8859-1:国际通用编码,浏览器默认编码,不支持中文。
UTF编码(UTF-8):相当于结合了ISO-8859-1和UNICODE编码,支持所有语言且体积较小。
乱码产生原因(95%):编解码不一致
/**
* 查看系统信息
*/
public class TestFileStream{
public static void main(String[] args) {
System.getProperties().list(System.out);
}
}