概念
java.io.File用于表示文件(目录),可以通过File类在程序中操作硬盘上的文件和目录。File类只能用于描述文件(目录)的属性信息(名称、大小等),不能对文件内容进行访问(即不具有从文件读取信息和向文件写入信息的功能)。
文件路径
文件路径尽量使用相对路径,"."表示当前路径(当前类所在项目的根目录)。文件路径分隔符尽量使用File.separator常量代替,它会根据不同操作系统返回不同的文件路径分隔符(windows返回\,Linux返回/)。
源码
public class File implements Serializable, Comparable
{ //...... } 构造方法
File(String pathname) 通过将给定路径名字符串转换为抽象路径名来创建一个新的File实例 File(URI uri) 通过将给定的URI转换为一个抽象路径名来创建一个新的File实例 File(String parent, String child) 根据parent路径名字字符串和child路径名字符串创建一个新的File实例 File(File parent, String child) 根据parent抽象路径名和child路径名字符串创建一个新的File实例。 使用
/** * 根据给定抽象路径创建对象,仅仅只是创建文件对象而不表示创建文件 * 文件路径尽量使用相对路径 */ File file = new File("." + File.separator + "1.txt");
常用方法
boolean exists() 测试此抽象路径所表示文件或者文件夹是否存在。
true表示存在;false表示不存在。
long length() 返回此抽象路径所表示的文件的长度 long lastModified() 返回此抽象路径所表示的文件最后一次被修改的时间毫秒 String getName() 返回此抽象路径所表示的文件或文件夹的名称 String getPath() 返回此抽象路径所表示的文件或文件夹的抽象路径 boolean isFile()
boolean isDirectory()
测试试此抽象路径所表示文件是否是一个标准的文件
测试试此抽象路径所表示文件是否是一个标准的文件夹
boolean createNewFile() 创建此抽象路径所表示的文件。
返回true表示创建成功;返回false表示创建失败。
boolean delete() 删除此抽象路径所表示文件或文件夹
返回true表示删除成功;返回false表示删除失败。
注意事项:若此抽象路径表示文件夹其该文件夹下存在子文件或者文件夹,则需要将子文件或子文件夹删除之后才能删除当前文件夹。
boolean mkdir()
创建此抽象路径名指定的文件夹(只能创建一层目录结构,对于多层目录结构需要使用mkdirs()方法)。
返回true表示创建成功;返回false表示创建失败。
boolean mkdirs() 创建此抽象路径下指定的文件夹,包括所有必需但不存在的父目录。建议使用此方法。 String[] list() 返回此抽象路径下所有子文件和子文件夹的名称数组 File[] listFiles() 返回此抽象路径下所有子文件和子文件夹数组 File[] listFiles(FileFilter fileter) 返回此抽象路径下经过文件过滤规则筛选过的子文件和子文件夹。
FileFilter是一个接口,需要自定义一个类实现它。
class MyFileFilter implements FileFilter{
//文件筛选的过滤逻辑。返回true则保留当前文件或文件夹;返回false则不保留。
public boolean accept(File pathname){
String fileName = pathname.getName(); //获取当前文件名//例如判断文件名以.txt结尾则返回true,否则返回false
return fileName.endWith(".txt");
}
}或者使用匿名内部类方式:
FIle[] subFiles = file.listFiles(new FileFilter(){
public boolean accept(File pathname){
String fileName = pathname.getName();return fileName.endWith(".txt");
}});
IO流概念
IO流指的是输入流(Input)和输出流(Output)。站在内存的角度作为参考,外部文件到内存叫做输入,内存到外部文件叫输出。
分类
- 字节流与字符流
按照数据传输方式或是运输方式角度来看,可以将IO流分为:字节流和字符流。两种区别如下:
1.字节流读取单个字节;字符流读取单个字符(一个字符根据编码的不同对应的字节也不同。如UTF-8编码是3个字节,中文编码是2个字节)。 2.字节流用来处理二进制文件(如图片、音频、视频文件等非文本文件);字符流只能用来处理纯文本文件(可以看做是特殊的二进制文件,使用了某种编码,人可以阅读)。字节流也可以处理文本文件,故字节流使用较多。 3.形象比喻,字节是给计算机看的,字符是给用户看的。
- 节点流与处理流
按照流的功能可以将IO流分为:分为处理流和节点流。
处理流用于包装节点流,对其功能进行加强,处理流单独存在没有意义。 处理流(高级流)
1.BufferedInputStream, BufferedOutputStream
使用如:BufferedInputStream bfInput = new BufferedInputStream(new FileInputStream(参数));
2.ObjectInputStream, ObjectOutputStream
3.OutputStreamWriter, InputStreamReader
4.BufferedReader, BufferedWriter, PrintWriter
节点流(低级流)
1.FileInputStream, FileOutputStream
- 规律
以InputStream结尾的流是字节出入流;以OutputStream结尾的流是字节输出流;以Reader结尾的流是字符输入流;以Writer结尾的流是字符输出流 体系结构
常用的类:File开头的文件最基本的读写类;Buffered开头的文件读写带缓存区的类;Object开头的对象序列化和反序列化相关的类。
从上图可以看见,IO的最基本的4个抽象类为:InputStream、OutputStream、Reader、Writer。
注意事项
带缓冲的流需要调用close()方法关闭流。文件写入丢失数据现象的原因:对于带缓冲的输出流需要等待缓冲数组写满时才会一起被写入文件,因此流使用完一定要调用close()方法。close()方法内部会调用flush()方法,将缓存数组中剩余的数据强制刷新到文件中。
InputStream
InputStream是所有字节输入流的父类,是抽象类(不能直接new)。其源码如下所示:
public abstract class InputStream implements Closeable { //MAX_SKIP_BUFFER_SIZE is used to determine the maximum buffer size to use when skipping. private static final int MAX_SKIP_BUFFER_SIZE = 2048; public abstract int read() throws IOException; public int read(byte b[]) throws IOException { return read(b, 0, b.length); } public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int c = read(); if (c == -1) { return -1; } b[off] = (byte)c; int i = 1; try { for (; i < len ; i++) { c = read(); if (c == -1) { break; } b[off + i] = (byte)c; } } catch (IOException ee) { } return i; } public long skip(long n) throws IOException { long remaining = n; int nr; if (n <= 0) { return 0; } int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining); byte[] skipBuffer = new byte[size]; while (remaining > 0) { nr = read(skipBuffer, 0, (int)Math.min(size, remaining)); if (nr < 0) { break; } remaining -= nr; } return n - remaining; } public int available() throws IOException { return 0; } public void close() throws IOException {} public synchronized void mark(int readlimit) {} public synchronized void reset() throws IOException { throw new IOException("mark/reset not supported"); } public boolean markSupported() { return false; } }
- 常用方法
int read() 从此输入流中一次读取一个字节,返回int值,低8位有效,高24位全部补0。
返回-1表示已经读取到文件的末尾。
int read(byte[] b) 从此输入流中将最多b.length个字节读入字节数组b中。
返回值代表实际读取的字节数个数,如果因为已经到达文件末尾而没有更多的数据,则返回
-1
。解码:将字节数组安按照给的字符集编码还原成为字符串,若不指定编码集,则按照系统默认的编码集。
String s = new String(b, "编码格式(UTF-8、GBK等)");
注意:编码和解码使用的编码集要一致。
int read(byte[] b, int offset, int len) 从此输入流中将最多 len
个字节的数据读入一个 byte 数组中。void close() 关闭此输入流并释放与该流关联的所有系统资源。 long skip(long n) 跳过和丢弃此输入流中数据的 n
个字节。OutputStream
OutputStream是所有字节输出流的父类,是抽象类(不能直接new)。其源码如下所示:
public abstract class OutputStream implements Closeable, Flushable { public abstract void write(int b) throws IOException; public void write(byte b[]) throws IOException { write(b, 0, b.length); } public void write(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return; } for (int i = 0 ; i < len ; i++) { write(b[off + i]); } } public void flush() throws IOException {} public void close() throws IOException {} }
- 常用方法
void write(int b) 一次写出一个字节,将int的低8位写出。 void wtite(byte[] b)
一次写出一个字节数组
注String的方法:byte[] getBytes(String charsetName)
将字符串按照给的的字符集编码转换成对应字节数组。
编码:将Unicode形式的字节数组转换成指定编码集的字节数组。若不指定编码集,则按照系统默认的编码集编码。Windows默认编码集为GBK,Linux默认编码集为UTF-8。
byte[] b = s.getBytes("编码格式(UTF-8、GBK等)");
void write(byte[]b, int offset, int len) 从字节数组的offset位置开始连续写出len个字节到输出流中。 void flush() 刷新此输出流并强制写出所有的缓冲的输出字节。 void close() 关闭此输出流并释放与此流有关的所有系统资源。 关闭流
关闭此文件输入流并释放与此流有关的所有系统资源都调用其close()方法。
注意事项
1.关闭处理流内部会自动关闭其包装的节点流,故只需要关闭处理流即可。 2.文件写入丢失数据现象的原因:对于带缓冲的输出流需要等待缓冲数组写满时才会一起被写入文件,因此流使用完一定要调用close()方法。close()方法内部会调用flush()方法,将缓存数组中剩余的数据强制刷新到文件中。 比如ObjectOutputStream的close()方法源码如下所示,调用了flush()方法:
public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants{ //...... public void close() throws IOException { flush(); clear(); bout.close(); } }
FileInputStream
FileInputStream是文件输入流,用于从文件系统中的某个文件中获得输入字节。以字节为单位,一次读取一个字节。用于读取诸如图像数据之类的原始字节流,要读取字符流,使用
FileReader
。
- 源码
public class FileOutputStream extends OutputStream{ //...... }
- 构造方法
FileInputStream(File file) 通过打开一个到实际文件的连接来创建一个 FileInputStream
,该文件通过文件系统中的File
对象file
指定。FileInputStream(String name) 通过打开一个到实际文件的连接来创建一个 FileInputStream
,该文件通过文件系统中的路径名name
指定。FileInputStream(FileDescriptor fdObj) 通过使用文件描述符 fdObj
创建一个FileInputStream
,该文件描述符表示到文件系统中某个实际文件的现有连接。FileOutputStream
FileOutputStream是文件输出流,以字节为单位,一次向文件写出一个字节。用于写入诸如图像数据之类的原始字节的流。要写入字符流,使用
FileWriter
。
- 源码
public class FileInputStream extends InputStream{ //...... }
- 构造方法
FileOutputStream(File file) 创建一个向指定 File
对象表示的文件中写入数据的文件输出流。默认以覆盖的方式FileOutputStream(String name) 创建一个向具有指定名称的文件中写入数据的输出文件流。默认以覆盖的方式 FileOutputStream(FileDescriptor fdObj) 创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示一个到文件系统中的某个实际文件的现有连接。 FileOutputStream(File file, boolean append) 创建一个向指定 File
对象表示的文件中写入数据的文件输出流。以追加的方式。FileOutputStream(String name, boolean append) 创建一个向具有指定名称的文件中写入数据的输出文件流。以追加的方式。 BufferedInputStream
在创建
BufferedInputStream
时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark
操作记录输入流中的某个点,reset
操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次mark
操作后读取的所有字节。将数据读取到内存中,从内存中获取数据比从文件中获取效率高。
- 源码
public class BufferedInputStream extends FilterInputStream { private static int DEFAULT_BUFFER_SIZE = 8192; private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; protected volatile byte buf[]; private static final AtomicReferenceFieldUpdater
bufUpdater = AtomicReferenceFieldUpdater.newUpdater (BufferedInputStream.class, byte[].class, "buf"); protected int count; protected int pos; protected int markpos = -1; protected int marklimit; private InputStream getInIfOpen() throws IOException { InputStream input = in; if (input == null) throw new IOException("Stream closed"); return input; } private byte[] getBufIfOpen() throws IOException { byte[] buffer = buf; if (buffer == null) throw new IOException("Stream closed"); return buffer; } public BufferedInputStream(InputStream in) { this(in, DEFAULT_BUFFER_SIZE); } public BufferedInputStream(InputStream in, int size) { super(in); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; } private void fill() throws IOException { byte[] buffer = getBufIfOpen(); if (markpos < 0) pos = 0; /* no mark: throw away the buffer */ else if (pos >= buffer.length) /* no room left in buffer */ if (markpos > 0) { /* can throw away early part of the buffer */ int sz = pos - markpos; System.arraycopy(buffer, markpos, buffer, 0, sz); pos = sz; markpos = 0; } else if (buffer.length >= marklimit) { markpos = -1; /* buffer got too big, invalidate mark */ pos = 0; /* drop buffer contents */ } else if (buffer.length >= MAX_BUFFER_SIZE) { throw new OutOfMemoryError("Required array size too large"); } else { /* grow buffer */ int nsz = (pos <= MAX_BUFFER_SIZE - pos) ? pos * 2 : MAX_BUFFER_SIZE; if (nsz > marklimit) nsz = marklimit; byte nbuf[] = new byte[nsz]; System.arraycopy(buffer, 0, nbuf, 0, pos); if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { // Can't replace buf if there was an async close. // Note: This would need to be changed if fill() // is ever made accessible to multiple threads. // But for now, the only way CAS can fail is via close. // assert buf == null; throw new IOException("Stream closed"); } buffer = nbuf; } count = pos; int n = getInIfOpen().read(buffer, pos, buffer.length - pos); if (n > 0) count = n + pos; } public synchronized int read() throws IOException { if (pos >= count) { fill(); if (pos >= count) return -1; } return getBufIfOpen()[pos++] & 0xff; } private int read1(byte[] b, int off, int len) throws IOException { int avail = count - pos; if (avail <= 0) { /* If the requested length is at least as large as the buffer, and if there is no mark/reset activity, do not bother to copy the bytes into the local buffer. In this way buffered streams will cascade harmlessly. */ if (len >= getBufIfOpen().length && markpos < 0) { return getInIfOpen().read(b, off, len); } fill(); avail = count - pos; if (avail <= 0) return -1; } int cnt = (avail < len) ? avail : len; System.arraycopy(getBufIfOpen(), pos, b, off, cnt); pos += cnt; return cnt; } public synchronized int read(byte b[], int off, int len) throws IOException { getBufIfOpen(); // Check for closed stream if ((off | len | (off + len) | (b.length - (off + len))) < 0) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int n = 0; for (;;) { int nread = read1(b, off + n, len - n); if (nread <= 0) return (n == 0) ? nread : n; n += nread; if (n >= len) return n; // if not closed but no bytes available, return InputStream input = in; if (input != null && input.available() <= 0) return n; } } public synchronized long skip(long n) throws IOException { getBufIfOpen(); // Check for closed stream if (n <= 0) { return 0; } long avail = count - pos; if (avail <= 0) { // If no mark position set then don't keep in buffer if (markpos <0) return getInIfOpen().skip(n); // Fill in buffer to save bytes for reset fill(); avail = count - pos; if (avail <= 0) return 0; } long skipped = (avail < n) ? avail : n; pos += skipped; return skipped; } public synchronized int available() throws IOException { int n = count - pos; int avail = getInIfOpen().available(); return n > (Integer.MAX_VALUE - avail) ? Integer.MAX_VALUE : n + avail; } public synchronized void mark(int readlimit) { marklimit = readlimit; markpos = pos; } public synchronized void reset() throws IOException { getBufIfOpen(); // Cause exception if closed if (markpos < 0) throw new IOException("Resetting to invalid mark"); pos = markpos; } public boolean markSupported() { return true; } public void close() throws IOException { byte[] buffer; while ( (buffer = buf) != null) { if (bufUpdater.compareAndSet(this, buffer, null)) { InputStream input = in; in = null; if (input != null) input.close(); return; } // Else retry in case a new buf was CASed in fill() } } } 其中FilterInputStream类继承自InputStream
- 构造方法
BufferedInputStream(InputStream in) 创建一个 BufferedInputStream
BufferedInputStream(InputStream in, int size) 创建具有指定缓冲区大小的 BufferedInputStream
- 常用方法
见InputStream类
BufferedOutputStream
该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
- 源码
public class BufferedOutputStream extends FilterOutputStream { protected byte buf[]; protected int count; public BufferedOutputStream(OutputStream out) { this(out, 8192); } public BufferedOutputStream(OutputStream out, int size) { super(out); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; } /** Flush the internal buffer */ private void flushBuffer() throws IOException { if (count > 0) { out.write(buf, 0, count); count = 0; } } public synchronized void write(int b) throws IOException { if (count >= buf.length) { flushBuffer(); } buf[count++] = (byte)b; } public synchronized void write(byte b[], int off, int len) throws IOException{ if (len >= buf.length) { /* If the request length exceeds the size of the output buffer, flush the output buffer and then write the data directly. In this way buffered streams will cascade harmlessly. */ flushBuffer(); out.write(b, off, len); return; } if (len > buf.length - count) { flushBuffer(); } System.arraycopy(b, off, buf, count, len); count += len; } public synchronized void flush() throws IOException { flushBuffer(); out.flush(); } }
其中FilterOutputStream类继承自OutputStream
- 构造方法
BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。 BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
- 常用方法
见OutputStream类
ObjectInputStream
- 概念
ObjectInputStream表示对象输入流。对使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
- 源码
public class ObjectInputStreamextends InputStream implements ObjectInput, ObjectStreamConstants{ //...... }
- 构造方法
ObjectInputStream(InputStream in) 创建写入指定 InputStream 的 ObjectInputStream。
- 常用方法
Object readObjectj) 从输出流读取对象。 ObjectOutputStream
- 概念
ObjectOutputStream表示对象输出流,将 Java 对象的基本数据类型写入 OutputStream流,通过在流中使用文件可以实现对象的持久存储。
- 源码
public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants{ //...... }
- 构造方法
ObjectOutputStream(OutputStream out) 创建写入指定 OutputStream 的 ObjectOutputStream。
- 常用方法
void writeObject(Object obj) 将对象后出到输出流中。
Reader
Reader是所有字符输入流的父类。
- 源码
- 常用方法
int read() 读取一个字符,返回的int值低16位有效 Writer
Writer是所有字符输出流的父类。
- 源码
- 常用方法
void write(int c) 写出一个int值低16位表示的字符 void write(char[] chs) 将给的字符数组中所有的字符写出 void write(String str) 将给的字符串写出 void write(char[] chs, int offset, int len)
InputStreamReader
字符输入流,使用该字符流可以设置字符集,
BufferedReader
- 源码
public class BufferedReader extends Reader { private Reader in; private char cb[]; //...... }
可以看到,定义了一个字符缓冲数组cb。
- 常用方法
String readLine() 一次读取一行数据。通过下列字符之一即可认为某行已终止:换行 ('\n')、回车 ('\r') 或回车后直接跟着换行。
如果已到达流末尾,则返回 null
OutputStreamWriter
BufferedWriter
PrintWriter
PrintWriter是具有自动行刷新的缓存字符输出流。
- 源码
- 构造方法
PrintWriter(File file) 使用指定文件创建不具有自动行刷新的新 PrintWriter。 PrintWriter(File file, String csn) 创建具有指定文件和字符集且不带自动刷行新的新 PrintWriter。 PrintWriter(OutputStream out) 根据现有的 OutputStream 创建不带自动行刷新的新 PrintWriter。 PrintWriter(OutputStream out, boolean autoFlush) 通过现有的 OutputStream 创建是否带自动行刷新的 PrintWriter。
自动行刷新:指每调用一次println()方法,会强制刷新缓冲。会增加写出次数,从而降低IO性能。因此,对于及时性要求不高的系统不推荐使用
PrintWriter(String fileName) 创建具有指定文件名称且不带自动行刷新的新 PrintWriter。 PrintWriter(String fileName, String csn) 创建具有指定文件名称和字符集且不带自动行刷新的新 PrintWriter。 PrintWriter(Writer out) 创建不带自动行刷新的新 PrintWriter。 PrintWriter(Writer out, boolean autoFlush) 创建新 PrintWriter。
- 常用方法
概念
Properties类是Map集合的实现类,表示了一个持久的属性集,具备键值对的特征,位于java.util包下。Properties类常常用于读取和加载属性配置文件(以.properties结尾的文件)。如下所示:db.properties
url=jdbc:mysql://localhost:3306/test driver=com.mysql.cj.jdbc.driver username=root password=root
注意事项:
1.一行只能书写一个配置,每个配置包含key-value键值对,以等号或者空格进行分隔。 2.不支持中文,如果输入了中文,则会将中文自动转换为对应的Unicode码。 3.注释使用# 源码
public class Properties extends Hashtable
构造方法
Properties() 创建一个无默认值的空属性列表。 Properties(Properties defaults) 创建一个带有指定默认值的空属性列表。 常用方法
void load(InputStream in)
void load(Reader reader)
从输入流中读取属性列表(键和元素对)。
按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
String getProperty(String key) 用指定的键在此属性列表中搜索属性。如果不存在对应的属性值,则返回null String getProperty(String key, String defaultValue) 用指定的键在此属性列表中搜索属性。如果不存在对应的属性值,则返回defaultValue 示例
读取上面的db.properties文件代码如下所示:
public class PropertiesDemo { public static void main(String[] args) throws IOException { Properties properties = new Properties(); //相当于类路径(src下)下去查询文件并返回该文件的资源输入流 InputStream in = PropertiesDemo.class.getClassLoader().getResourceAsStream("db.properties"); properties.load(in); System.out.println(properties.getProperty("url")); System.out.println(properties.getProperty("driver")); System.out.println(properties.getProperty("username")); System.out.println(properties); } } 输出结果: jdbc:mysql://localhost:3306/test com.mysql.cj.jdbc.driver root root