1. 输入与输出流
- 输入流
可以从其中读入一个字节序列的对象 - 输出流
可以从其中写入一个字节序列的对象
1.1 读写字节
InputStream
类有一个抽象方法,用于读入一个直接,并返回读入的直接,当遇到结尾时返回-1
abstract int read()
- 类似的,
OutputStream
类定义了一个方法,用于向某个输出位置写出一个字节
abstract void write()
read
和write
方法在执行时都将被阻塞,直至直接确实被读入或写出。这就意味着如果留不能被立即访问,那么当前线程将被阻塞,这个时候,其他线程就有机会去执行更有用的工作available
方法用于检查当前可读入的字节数量close
方法用于当你完成输入/输出流的读写时,你可以通过此方法去关闭它。flush
方法可以人为的对输出流进行冲刷
1.2 完整的流家族
Java的流家族非常强大,包含各种输入/输出流类型,数量超过60个!
InputStream和OutputStream
如果把输入/输出流成员按使用方法来分,这样就形成了处理字节和字符两个单独的层次结构。
- InputStream
和OutputStream
用于读写单个字节或字节数组
- DataInputStream
和DataOutputStream
以二进制格式读写所有的基本Java类型
- ZipInputStream
和ZipOutputStream
可以以ZIP压缩格式读写文件
如果想要读取其他东西例如字符串数字,就需要功能更强大的子类。
Reader和Writer
对于Unicode文本,也是使用抽象类Reader
和Writer
的子类
Reader
和Writer
的基本方法与InputStream
和OutputStream
类似
abstract int read()
abstract void write(int c)
read
方法将返回一个Unicode码元(一个在0~65535之间的整数),或者在碰到文件结尾时返回-1write
方法在调用时,需要传入一个Unicode码元
4个附加接口
4个附加接口Closeable
、Flushable
、Readable
、Appendable
- Closeable
这个接口实现了void close() throws IOException
方法
InputStream
、OutputStream
、Reader
、Writer
都实现了此接口 - Flushable
这个接口实现了void flush()
方法
OutputStream
、Writer
都实现了此接口 - Readable
这个接口实现了int read(CharBuffer cb)
方法
CharBuffer
类拥有按顺序和随机地进行读写访问的方法,他表示一个内存中的缓冲区或者一个内存映像的文件 - Appendable
此接口有两个方法Appendable append(char c)
和Appendable append(CharSequence s)
CharSequence接口描述了一个char值序列的基本属性,在流类家族中,只有Writer
实现了他
1.3 组合输入/输出过滤器
FileInputStream
和FileOutputStream
可以提供附着在一个磁盘文件上的输入流和输出流,你只需要提供文件名或者文件的完整路径
FileInputStream fin new FileInputStream("1.txt");
但是他们和InputStream
、OutputStream
一样,只支持读写字节,所以万一你想从一个文件中读取或者写入一个数字,光靠这几个是不行的
相信你一定知道,DataInputStream
可以用来读入数值,这个时候你就可以对其进行组合
首先你得先创建一个FileInputStream
对象,然后将其传输给DataInputStream
FileInputStream fin = new FileInputStream("1.txt");
DataInputStream din = new DataInputStream(fin);
你也可以使用嵌套的方法创建这个DataInputStream
对象
DataInputStream din = new DataInputStream(new BufferedInputStream(new FileInputStream("1.txt")));
2. 文本输入与输出
在保存数据时,可以选择二进制格式或文本格式
* InputStreamReader
把输入==字节==流转为==字符==流
* OutputStreamWriter
把输出==字符==流转为==字节==流
2.1 如何写出文本输出
对于文本输出可以使用PrintWriter
。
这个类有很多以文本格式打印字符串和数字的方法
void print(boolean b) //打印布尔值
void print(char c) //打印一个字符
void print(char[] s) //打印字符数组
void print(double d) //打印双精度浮点数
void print(float f) //打印浮点数
void print(int i) //打印一个整数
void print(long l) //打印一个长整数
void print(Object obj) //打印一个对象
void print(String s) //打印字符串
PrintWriter printf(Locale l, String format, Object... args)
//使用指定的格式字符串和参数将格式化的字符串写入该writer的方便方法
PrintWriter printf(String format, Object... args) //使用指定的格式字符串和参数将格式化的字符串写入该writer的方便方法
void println() //通过写入行分隔符字符串来终止当前行
void println(boolean x) //打印一个布尔值,然后终止该行
void println(char x) //打印一个字符,然后终止该行
void println(char[] x) //打印字符数组,然后终止行
void println(double x) //打印双精度浮点数,然后终止行
void println(float x) //打印一个浮点数,然后终止该行
void println(int x) //打印一个整数,然后终止该行
void println(long x) //打印一个长整型,然后终止行
void println(Object x) //打印一个对象,然后终止该行
void println(String x) //打印一个字符串,然后终止行
他将把字符输出到写出器out,然后这些字符将会转为字节并写入文件中
2.2 字符编码方式
输入和输出流都是用于字节序列
- StandardCharsets类具有类型为Charset的静态变量,用于表示每种Java虚拟机都必须支持的字符编码方式
StandardCharsets.UTF_8
StandardCharsets.UTF_16
StandardCharsets.UTF_16BE
StandardCharsets.UTF_16LE
StandardCharsets.ISO_8859_1
StandardCharsets.US_ASCII
- 为了获得另一种编码方式的Charset,可以使用静态的forName方法
Charset shiftJIS = Charset.forName("Shift-JIS");
- 在读入或写出文本时,应该使用Charset对象
3. 读写二进制数据
文本格式虽说易于人们阅读,但是很多地方,他并不像二进制格式那么高效
3.1 DataInput和DataOutput
- DataOutput接口定义了下面用于以二进制格式写数组、字符、boolean值和字符串的方法
void write(byte[] b) //将输出流写入数组 b中的所有字节
void write(byte[] b, int off, int len) //从阵列 b写入 len字节,以输出流
void write(int b) //向输出流写入参数 b的八个低位
void writeBoolean(boolean v) //将 boolean值写入此输出流
void writeByte(int v) //向输出流写入参数 v的八个低位位
void writeBytes(String s) //将一个字符串写入输出流
void writeChar(int v) //将两个字节组成的 char值写入输出流
void writeChars(String s) //写入每一个字符在字符串中 s ,到输出流中,为了,每个字符使用两个字节
void writeDouble(double v) //将 double值(由8个字节组成)写入输出流
void writeFloat(float v) //将 float值写入输出流,该值由四个字节组成
void writeInt(int v) //将 int值(由四个字节组成)写入输出流
void writeLong(long v) //将 long值(由八个字节组成)写入输出流
void writeShort(int v) //将两个字节写入输出流以表示参数的值
void writeUTF(String s) //将两个字节的长度信息写入输出流,其后是 字符串 s中每个字符的s
- DataOutput接口定义了下面用于以二进制格式读数组、字符、boolean值和字符串的方法
boolean readBoolean() //读取一个输入字节,并返回 true如果该字节不为零, false如果该字节是零
byte readByte() //读取并返回一个输入字节
char readChar() //读取两个输入字节并返回一个 char值
double readDouble() //读取八个输入字节并返回一个 double值
float readFloat() //读取四个输入字节并返回一个 float值
void readFully(byte[] b) //从输入流读取一些字节,并将它们存储到缓冲区数组 b
void readFully(byte[] b, int off, int len) //从输入流读取 len个字节
int readInt() //读取四个输入字节并返回一个 int值
String readLine() //从输入流读取下一行文本
long readLong() //读取八个输入字节并返回一个 long值
short readShort() //读取两个输入字节并返回一个 short值
int readUnsignedByte() //读取一个输入字节,将其扩展到类型 int ,并返回结果,因此在 0到 255
int readUnsignedShort() //读取两个输入字节,并返回 0到 65535的 int值
String readUTF() //读取已使用 modified UTF-8格式编码的字符串
int skipBytes(int n) //尝试从输入流中跳过 n字节的数据,丢弃跳过的字节
3.2 随机访问文件
RandomAccessFile
类可以访问文件中的任意位置。很多资料把它翻译成随机访问文件,但是个人觉得应该翻译为任意访问文件,他是能访问文件中的任意位置。
- 你可以打开一个文件,只用于读入或者同时写入,只需要在构造器加上”r”(用于读入访问),”rw”(用于读入/写出访问)
RandomAccessFile in = new RandomAccessFile("employee.dat", "r");
RandomAccessFile inOut = new RandomAccessFile("employee.dat", "rw");
- 方法
void close() //关闭此随机访问文件流并释放与流相关联的任何系统资源。
FileChannel getChannel() //返回与此文件关联的唯一的FileChannel对象。
FileDescriptor getFD() //返回与此流关联的不透明文件描述符对象。
long getFilePointer() //返回此文件中的当前偏移量。
long length() //返回此文件的长度。
int read() //从该文件读取一个字节的数据。
int read(byte[] b) //从该文件读取最多 b.length字节的数据到字节数组。
int read(byte[] b, int off, int len) //从该文件读取最多 len个字节的数据到字节数组。
boolean readBoolean() //从此文件读取一个 boolean 。
byte readByte() //从此文件中读取一个带符号的八位值。
char readChar() //从此文件中读取一个字符。
double readDouble() //从此文件读取 double 。
float readFloat() //从此文件读取一个 float 。
void readFully(byte[] b) //从此文件读取 b.length字节到字节数组,从当前文件指针开始。
void readFully(byte[] b, int off, int len) //从此文件中读取 len个字节到字节数组,从当前文件指针开始。
int readInt() //从该文件读取一个带符号的32位整数。
String readLine() //从此文件中读取下一行文本。
long readLong() //从该文件中读取一个带符号的64位整数。
short readShort() //从此文件中读取一个已签名的16位数字。
int readUnsignedByte() //从此文件中读取一个无符号的八位数字。
int readUnsignedShort() //从该文件中读取一个无符号的16位数字。
String readUTF() //从该文件读取字符串。
void seek(long pos) //设置文件指针偏移,从该文件的开头测量,发生下一次读取或写入。
void setLength(long newLength) //设置此文件的长度。
int skipBytes(int n) //尝试跳过 n字节的输入丢弃跳过的字节。
void write(byte[] b) //从指定的字节数组写入 b.length个字节到该文件,从当前文件指针开始。
void write(byte[] b, int off, int len) //从指定的字节数组写入 len个字节,从偏移量 off开始写入此文件。
void write(int b) //将指定的字节写入此文件。
void writeBoolean(boolean v) //将 boolean写入文件作为一个字节值。
void writeByte(int v) //将 byte写入文件作为单字节值。
void writeBytes(String s) //将字符串作为字节序列写入文件。
void writeChar(int v) //将 char写入文件作为两字节值,高字节为先。
void writeChars(String s) //将一个字符串作为字符序列写入文件。
void writeDouble(double v) //双参数传递给转换 long使用 doubleToLongBits方法在类 Double ,然后写入该 long值到该文件作为一个八字节的数量,高字节。
void writeFloat(float v) //浮子参数的转换 int使用 floatToIntBits方法在类 Float ,然后写入该 int值到该文件作为一个四字节数量,高字节。
void writeInt(int v) //将 int写入文件为四个字节,高字节 int 。
void writeLong(long v) //将 long写入文件为八个字节,高字节为先。
void writeShort(int v) //将 short写入文件作为两个字节,高字节优先。
void writeUTF(String str) //以机器无关的方式使用 modified UTF-8编码将字符串写入文件。
3.3 ZIP压缩文件
ZIP通常是压缩文件包的格式,他能对一个或者一群文件进行压缩整合到一个.zip压缩包中。
在java中,使用输入/输出流可以快速的读入或者输出zip文件
* ZipInputStream
ZipInputStream
可以用来读入一个zip文件,使用ZipNextEntry
方法就可以返回一个描述这些项的ZipEntry对象。getInputStream
可以用来返回一个InputStream
输入流
ZipInputStream zin = new ZipInputStream(new FileInputStream(Zipname));
ZipEntry entry;
while ((entry = zin.getNextEntry()) != null{
InputStream in = zin.getInputStream(entry);
// read the contents of in
zin.closeEntry();
}
zin.close();
- ZipOutputStream
ZipOutputStream
可以用来输出一个zip文件,对于你想放到压缩包中的每一项,你都得建一个ZipEntry
对象,并将文件名传给ZipEntry的构造器。
FileOutputStream fout = new FileOutputStream("test.zip");
ZipOutputStream zout = new ZipOutputStream(fout);
// for all files
{
ZipEntry ze = new ZipEntry(filename);
zout.putNextEntry(ze);
// send data to zout
zout.cliseEntry();
}
zout.close();
4. 对象输入/输出流与序列化
Java语言支持一种称为对象序列化的非常通用的机制,它可以将任何对象写出到输出流,并在之后将其读回
4.1 保存和加载序列化对象
1. 输出
- 首先打开一个ObjectOutputStream对象
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.dat"));
- 然后使用writeObject方法
Employee harry = new Employee("Harry Hacker", 50000, 1989, 10, 1);
Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
out.writeObject(harry);
out.writeObject(boss);
2. 读入
- 首先获得一个ObjectInoutStream对象
ObjectInputStream in = new ObjectInputStream(new FileInputStream("employee.dat"));
- 用readObject方法
Employee e1 = (Employee) in.readObject();
Employee e2 = (Employee) in.readObject();
3. 前提
如果想要使用对象的输入输出,就得让类实现Serializable接口。
Serializable接口没有任何方法,因此不需要做啥更改,只需要extends Serializable就行了
5. 操作文件
Path
和Files
类封装了在用户机器上处理文件系统所需的所有功能。但是比前面对输入输出流操作方便得多。
他们两个都是Java SE 7新添加进来了。
5.1 Path
Path
表示的是一个目录名序列,其后还可以跟着一个文件名。路径中第一个参数可以是根目录。
- Paths.get
public static Path get(String first, String... more)
这个方法接收一个或多个字符串。如果只接受一个字符串,那么first参数的值是要转换的路径字符串。如果指定了多个字符串,则每个非空字符串被认为是名称元素的序列,并且被连接以形成路径字符串。
- resolve
调用p.resolve(q)
将按照下列规则返回一个路径
- 如果q是绝对路径,这结果就是q
- 否则,根据文件系统的规则,将p后面跟着q作为结果
Path workRelative = Paths.get("work");
Path workPath = basePath.resolve(workRelative);
- getName
Path getName(int index)
返回此路径的名称元素作为Path对象。index参数是要返回的名称元素的索引。目录层次结构中最靠近根的元素索引为0 。离根最远的元素索引为count -1
- isAbsolute
boolean isAbsolute()
判断这条路径是否是绝对路径
5.2 读写文件
Files类可以使得普通文件操作便得简单效率而且便捷。
- readAllBytes
public static byte[] readAllBytes(Path path) throws IOException
读取文件中的所有字节
- readAllLines
public static List<String> readAllLines(Path path) throws IOException
从文件中读取所有行
- write
public static Path write(Path path,
Iterable<? extends CharSequence> lines,
Charset cs,
OpenOption... options)
throws IOException
向指定文件追加内容
如果要处理的文件长度比较长,或者是二进制文件,那么还是应该使用所熟知的输入/输出流或者读入器/写出器
5.3 创建文件和目录
- createDirectory
public static Path createDirectory(Path dir,
FileAttribute<?>... attrs)
throws IOException
创建一个新的目录,除了最后一个要被创建的目录外,其他中间目录必须存在。
- createDirectories
public static Path createDirectories(Path dir,
FileAttribute<?>... attrs)
throws IOException
创建最终目录的同时创建中间目录
- createFile
public static Path createFile(Path path,
FileAttribute<?>... attrs)
throws IOException
创建一个新的和空的文件,如果该文件已存在,则抛出异常
- createTemp
createTempFile(Path dir, String prefix, String suffix, FileAttribute<?>... attrs)//在指定的目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称
createTempFile(String prefix, String suffix, FileAttribute<?>... attrs)//在默认临时文件目录中创建一个空文件,使用给定的前缀和后缀生成其名称
createTempDirectory(Path dir, String prefix, FileAttribute<?>... attrs)//在指定的目录中创建一个新目录,使用给定的前缀生成其名称
createTempDirectory(String prefix, FileAttribute<?>... attrs)//在默认临时文件目录中创建一个新目录,使用给定的前缀生成其名称
5.4 复制、移动和删除
- copy
Files.copy(fromPath, toPath);
将文件从一个位置复制到另一个位置
- move
Files.move(fromPath, toPath);
移动文件
- delete
Files.delete(path);
删除文件,如果文件不存在,则会抛出异常
- deleteIfExists
Files.deleteIfExists(path)
删除文件(如果存在)。如果文件是目录,那么该目录必须为空。
5.5 访问目录中的项
- Files.list
返回一个可以读取目录中各个项的Stream对象,但是这个方法不会进入子目录
try (Stream<Path> entries = Files.list(pathToDirectory))
{
...
}
- Files.walk
返回一个可以读取目录中各个项的Stream对象,这个方法会进入子目录
try (Stream<Path> entries = Files.walk(pathToDirectory))
{
// Contains all descendants, visited in depth-first order
}
6. 内存映射文件
6.1 内存映射文件的性能
- 构造器
从文件中获得一个通道
FileChannel channle = FileChannel.open(path, options);
- map
map方法可以从这个通道中获得一个ByteBuffer,你得选择映射的模式
- FileChannel.MapMode.READ_ONLY 只读
- FileChannel.MapMode.READ_WRITE 可写,任何修改都会在某个时刻写回到文件中
- FileChannel.MapMode.PRIVATE 缓冲区是可写的,但是任何修改对这个缓冲区来说都是私有的,不会传播到文件中
MappedByteBuffer inBuf = fcIn.map(FileChannel.MapMode.READ_ONLY, 0, size);
6.2 缓冲区数据结构
缓冲区是由具有相同类型的数值构成的数组,Buffer是一个抽象类,他有众多的子类,包括ByteBuffer,CharBuffer,DoubleBuffer、IntBuffer、LongBuffer和ShortBuffer。
StringBuffer与这些没关系