一、File

<font style="background-color:rgb(248, 249, 250);">java.io</font>提供了<font style="background-color:rgb(248, 249, 250);">File</font>对象来操作文件和目录。构造<font style="background-color:rgb(248, 249, 250);">File</font>对象时,可以传入绝对路径,也可以传入相对路径。

windows用<font style="background-color:rgb(248, 249, 250);">\</font>作为路径分隔符,java需要用<font style="background-color:rgb(248, 249, 250);">\\</font>表示一个<font style="background-color:rgb(248, 249, 250);">\</font>。linux使用<font style="background-color:rgb(248, 249, 250);">/</font>作为路径分隔符。用<font style="background-color:rgb(248, 249, 250);">.</font>表示当前目录,<font style="background-color:rgb(248, 249, 250);">..</font>表示上级目录。

<font style="background-color:rgb(248, 249, 250);">File</font>对象有一个静态变量用来表示当前平台的系统分隔符:<font style="background-color:rgb(248, 249, 250);">separator</font>

<font style="background-color:rgb(248, 249, 250);">File</font>对象有3种形式表示路径:

  • <font style="background-color:rgb(248, 249, 250);">getPath()</font>:返回构造方法传入的路径
  • <font style="background-color:rgb(248, 249, 250);">getAbsolutePath()</font>:返回绝对路径
  • <font style="background-color:rgb(248, 249, 250);">getCanonicalPath()</font>:返回规范路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package org.example;
import java.io.*;

public class Main {
public static void main(String[] args) {
try {
File file=new File(".\\one.txt");
System.out.println(file.getAbsolutePath());
System.out.println(file.getPath());
System.out.println(file.getCanonicalPath());
}
catch (IOException e) {
throw new RuntimeException("getCanonicalPath()有问题",e);
}
}
}

<font style="background-color:rgb(248, 249, 250);">File</font>对象可以表示文件,也可以表示目录。

  • <font style="background-color:rgb(248, 249, 250);">isFile()</font>:是否是一个存在的文件
  • <font style="background-color:rgb(248, 249, 250);">isDirectory()</font>:是否是一个存在的目录
  • <font style="background-color:rgb(248, 249, 250);">canRead()</font>:是否可读
  • <font style="background-color:rgb(248, 249, 250);">canWrite()</font>:是否可写
  • <font style="background-color:rgb(248, 249, 250);">canExecute()</font>:是否可执行,对于目录而言,表示能否列出包含的文件和子目录
  • <font style="background-color:rgb(248, 249, 250);">length()</font>:文件字节大小
1
2
3
4
5
6
7
8
9
10
11
12
13
package org.example;
import java.io.*;

public class Main {
public static void main(String[] args) {
File file=new File(".\\one.txt");
if(file.isFile()){
System.out.println(File.separator);
}else{
throw new RuntimeException("文件不存在");
}
}
}
  • <font style="background-color:rgb(248, 249, 250);">craeteNewFile()</font>:创建文件
  • <font style="background-color:rgb(248, 249, 250);">delete()</font>:删除文件,删除目录时只有目录为空才能删除成功
  • <font style="background-color:rgb(248, 249, 250);">createTempFile()</font>:创建临时文件
  • <font style="background-color:rgb(248, 249, 250);">deleteOnExit()</font>:在JVM退出时自动删除该文件
  • <font style="background-color:rgb(248, 249, 250);">mkdir()</font>:创建目录
  • <font style="background-color:rgb(248, 249, 250);">mkdirs()</font>:创建目录并且创建不存在的父目录
  • <font style="background-color:rgb(248, 249, 250);">list()</font>:列出文件名
  • <font style="background-color:rgb(248, 249, 250);">listFiles()</font>:列出文件对象,好分辨哪个是目录,哪个是文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;


public class Main {
public static void main(String[] args) throws IOException {
File file=new File(".\\one.txt");
File dir=new File(".\\src\\main\\java\\demo");
File newFile=new File(dir,file.getName());
if(!dir.exists()){
dir.mkdirs();
}
if (!newFile.exists()) {
newFile.createNewFile();
}
File[] files=dir.listFiles();
printFile(files);
}
static void printFile(File[] file){
if(file == null || file.length == 0){
System.out.println("目录为空或不存在");
return;
}
for(File f:file){
try {
System.out.println(f.getCanonicalFile());
} catch (IOException e) {
throw new RuntimeException("路径有问题");
}
}
}
}

Path类

二、OutputStream

<font style="background-color:rgb(248, 249, 250);">write</font>方法会写入一个字节到输出流,虽然传入的是<font style="background-color:rgb(248, 249, 250);">int</font>参数,但只会写入一个字节。

<font style="background-color:rgb(248, 249, 250);">flush()</font>方法将缓冲区的内容输出到目的地。因为向磁盘、网络写入数据的时候,出于效率的考虑,操作系统并不是输出一个字节就立刻写入到文件或者发送到网络,而是把输出的字节先放到内存的一个缓冲区里(本质上就是一个<font style="background-color:rgb(248, 249, 250);">byte[]</font>数组),等到缓冲区写满了,再一次性写入文件或者网络。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;


public class Main {
public static void main(String[] args) throws IOException {
File file=new File(".\\one.txt");
File dir=new File(".\\src\\main\\java\\demo\\");
File newFile=new File(dir,file.getName());
OutputStream out=new FileOutputStream(newFile);
out.write('a');
out.close();
}
}

可以通过重载<font style="background-color:rgb(248, 249, 250);">void wirte(byte[])</font>来一次性写入多个字节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;


public class Main {
public static void main(String[] args) throws IOException {
File file=new File(".\\one.txt");
File dir=new File(".\\src\\main\\java\\demo\\");
File newFile=new File(dir,file.getName());
OutputStream out=new FileOutputStream(newFile);
out.write("abc".getBytes("UTF-8"));
out.close();
}
}

上述代码如果发生异常就无法正确关闭资源,所以需要用<font style="background-color:rgb(248, 249, 250);">try(resource)</font>来保证无论是否发生IO错误都能正确关闭。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;


public class Main {
public static void main(String[] args){
File file=new File(".\\one.txt");
File dir=new File(".\\src\\main\\java\\demo\\");
File newFile=new File(dir,file.getName());
try(OutputStream out=new FileOutputStream(newFile)) {
out.write("abc".getBytes("UTF-8"));
}catch (IOException e){
e.printStackTrace();
}
}
}

ByteArrayOutputStream

<font style="background-color:rgb(248, 249, 250);">ByteArrayOutputStream</font>实际上是把一个<font style="background-color:rgb(248, 249, 250);">byte[]</font>数组在内存中变成一个 <font style="background-color:rgb(248, 249, 250);">OutputStream</font>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;


public class Main {
public static void main(String[] args){
try(ByteArrayOutputStream out=new ByteArrayOutputStream()) {
out.write("Hello ".getBytes("UTF-8"));
out.write("World".getBytes("UTF-8"));
byte[] data=out.toByteArray();
System.out.println(new String(data,"UTF-8"));
}catch (IOException e){
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;


public class Main {
public static void main(String[] args){
File file=new File(".\\one.txt");
File dir=new File(".\\src\\main\\java\\demo\\");
File newFile=new File(dir,file.getName());
try(ByteArrayOutputStream out=new ByteArrayOutputStream()) {
out.write("Hello ".getBytes("UTF-8"));
out.write("World".getBytes("UTF-8"));
byte[] data=out.toByteArray();
if(newFile.exists()){
FileOutputStream fis=new FileOutputStream(newFile);
fis.write(data);
}
}catch (IOException e){
e.printStackTrace();
}
}
}

三、InputStream

<font style="background-color:rgb(248, 249, 250);">InpuStream</font>是一个抽象类,是所有输入流的超类。<font style="background-color:rgb(248, 249, 250);">read</font>是该类中最重要的方法。这个方法会读取输入流的下一个字节,并返回字节表示的int值,如果读到末尾会返回-1表示不能继续读取。

<font style="background-color:rgb(248, 249, 250);">FileInputStream</font><font style="background-color:rgb(248, 249, 250);">InpuStream</font>的一个子类。

<font style="background-color:rgb(248, 249, 250);">InpuStream</font>提供了两个重载方法来支持读取多个字节:

  • <font style="background-color:rgb(248, 249, 250);">int read(byte[] b)</font>:读取若干个字节并填充到<font style="background-color:rgb(248, 249, 250);">byte[]</font>数组,返回读取的字节数
  • <font style="background-color:rgb(248, 249, 250);">int read(byte[] b,int off,int len)</font>:指定<font style="background-color:rgb(248, 249, 250);">byte[]</font>数组的偏移量和最大填充数

InputStream也有缓冲区。读取一个字节时,操作系统往往会一次性读取若干字节到缓冲区,并维护一个指针指向未读的缓冲区。每次我们调用<font style="background-color:rgb(248, 249, 250);">read()</font>读取下一个字节时,可以直接返回缓冲区的下一个字节,避免每次读一个字节都导致IO操作。当缓冲区全部读完后继续调用<font style="background-color:rgb(248, 249, 250);">read()</font>,则会触发操作系统的下一次读取并再次填满缓冲区。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;


public class Main {
public static void main(String[] args) throws IOException {
File file=new File(".\\one.txt");
File dir=new File(".\\src\\main\\java\\demo\\");
File newFile=new File(dir,file.getName());
try(InputStream in=new FileInputStream(newFile)){
byte[] buffer=new byte[1024];
int len=in.read(buffer);
for(int i=0;i<len;i++){
System.out.print((char)buffer[i]);
}

}
}
}

ByteArrayInputStream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;


public class Main {
public static void main(String[] args) throws IOException {
File file=new File(".\\one.txt");
File dir=new File(".\\src\\main\\java\\demo\\");
File newFile=new File(dir,file.getName());
byte[] data = { 72, 101, 108, 108, 111, 33 };
try (InputStream input = new ByteArrayInputStream(data)) {
int n;
while ((n = input.read())!= -1) {
System.out.println((char)n);
}
}
}
}

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;


public class Main {
public static void main(String[] args) throws IOException {
File file=new File(".\\one.txt");
File dir=new File(".\\src\\main\\java\\demo\\");
File newFile=new File(dir,file.getName());
if(!dir.exists()){
dir.mkdirs();
}
if(!newFile.exists()){
newFile.createNewFile();
}
try(OutputStream out=new FileOutputStream(newFile)){
out.write("Hello World".getBytes());
out.flush();
try(InputStream in=new FileInputStream(newFile)){
byte[] buffer=new byte[1024];
int i=in.read();
while(i!=-1){
System.out.print((char)i);
i=in.read();
}
}
}
}
}

四、Filter模式

JDK将<font style="background-color:rgb(248, 249, 250);">InputStream</font><font style="background-color:rgb(248, 249, 250);">OutputStream</font>分为两大类,避免过多的子类继承导致子类爆炸:

  • 直接提供数据的基础类,如:<font style="background-color:rgb(248, 249, 250);">FileInputStream</font><font style="background-color:rgb(248, 249, 250);">ByteArrayInputStream</font><font style="background-color:rgb(248, 249, 250);">ServletInputStream</font>
  • 提供额外附加功能的类,如:<font style="background-color:rgb(248, 249, 250);">BufferedInputStream</font><font style="background-color:rgb(248, 249, 250);">CipherInputStream</font>
1
2
InputStream file = new FileInputStream("test.gz");
InputStream buffered = new BufferedInputStream(file);

不管包装多少次,得到的对象始终是<font style="background-color:rgb(248, 249, 250);">InputStream</font>就可以正常读取。这种通过一个“基础”组件再叠加各种“附加”功能组件的模式,称之为Filter模式或者装饰器模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;


public class Main {
public static void main(String[] args) throws IOException {
byte[] data="hello world".getBytes("UTF-8");
try(CountInputStream input=new CountInputStream(new ByteArrayInputStream(data))){
int n;
while((n=input.read())!=-1){
System.out.print((char)n);
}
System.out.println("\n"+"总共"+input.getBytesRead()+"个byte");
}
}
}
class CountInputStream extends FilterInputStream {
private int count=0;
CountInputStream(InputStream in) {
super(in);
}
public int getBytesRead(){
return this.count;
}
public int read() throws IOException {
int n=in.read();
if(n!=-1){
this.count++;
}
return n;
}
public int read(byte[] b) throws IOException {
int n=in.read(b);
if(n!=-1){
this.count+=n;
}
return n;
}
}

序列化

序列化必须实现<font style="background-color:rgb(248, 249, 250);">java.io.Serializable</font>接口。序列化本质就是将一个java对象转化为<font style="background-color:rgb(248, 249, 250);">byte[]</font>数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;
import java.util.Arrays;

public class Main {
public static void main(String[] args) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try(ObjectOutputStream oos = new ObjectOutputStream(baos)){
oos.writeInt(12345);
oos.writeObject(Double.valueOf(123.345));
byte[] bytes = baos.toByteArray();
System.out.println(Arrays.toString(bytes));
}
}
}

五、Reader

是java的IO库中提供的另一个输入流接口。与<font style="background-color:rgb(248, 249, 250);">InputStream</font>的区别在于:

  • <font style="background-color:rgb(248, 249, 250);">IntputStream</font>是一个字节流,以<font style="background-color:rgb(248, 249, 250);">byte</font>为单位读取;
  • <font style="background-color:rgb(248, 249, 250);">Reader</font>是一个字符流,以<font style="background-color:rgb(248, 249, 250);">char</font>为单位读取。

<font style="background-color:rgb(248, 249, 250);">FileReader</font><font style="background-color:rgb(248, 249, 250);">Reader</font>的一个子类,可以打开文件并获取<font style="background-color:rgb(248, 249, 250);">Reader</font>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class Main {
public static void main(String[] args) throws IOException {
try(Reader reader=new FileReader(".\\src\\main\\java\\demo\\one.txt", StandardCharsets.UTF_8)) {
char[] buffer=new char[1024];
int len=reader.read(buffer);
for(int i=0;i<len;i++) {
System.out.print(buffer[i]);
}
}
}
}

CharArrayReader

<font style="background-color:rgb(248, 249, 250);">ByteArrayInputStream</font>类似。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class Main {
public static void main(String[] args) throws IOException {
char[] body="hello world".toCharArray();
try(Reader reader=new CharArrayReader(body)){
char[] chars=new char[body.length];
int len=reader.read(chars);
for(int i=0;i<len;i++){
System.out.print(chars[i]);
}
}
}
}

<font style="background-color:rgb(248, 249, 250);">StringReader</font>可以直接把<font style="background-color:rgb(248, 249, 250);">String</font>作为数据源。

InputStreamReader

除了<font style="background-color:rgb(248, 249, 250);">CharArrayReader</font><font style="background-color:rgb(248, 249, 250);">StringReader</font>,普通的<font style="background-color:rgb(248, 249, 250);">Reader</font>都是基于<font style="background-color:rgb(248, 249, 250);">InputStream</font>构造的,都需要从<font style="background-color:rgb(248, 249, 250);">InputStream</font>中读入<font style="background-color:rgb(248, 249, 250);">byte</font>,再根据编码设置转换为<font style="background-color:rgb(248, 249, 250);">char</font>

<font style="background-color:rgb(248, 249, 250);">InputStreamReader</font>可以把任何<font style="background-color:rgb(248, 249, 250);">InputStream</font>转换为<font style="background-color:rgb(248, 249, 250);">Reader</font>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class Main {
public static void main(String[] args) throws IOException {
byte[] bytes = "Hello World".getBytes(StandardCharsets.UTF_8);
InputStream input=new ByteArrayInputStream(bytes);
Reader reader=new InputStreamReader(input, StandardCharsets.UTF_8);
char[] buf=new char[bytes.length];
int len=reader.read(buf);
for(int i=0;i<len;i++){
System.out.print(buf[i]);
}
reader.close();
input.close();
}
}

六、PrintStream和PrintWriter

<font style="background-color:rgb(248, 249, 250);">PrintStream</font>是一种<font style="background-color:rgb(248, 249, 250);">FilterOutputStream</font>,在<font style="background-color:rgb(248, 249, 250);">OutputStream</font>的接口上额外提供了一些写入各种数据类型的方法。

<font style="background-color:rgb(248, 249, 250);">System.out</font>是系统默认提供的<font style="background-color:rgb(248, 249, 250);">PrintStream</font><font style="background-color:rgb(248, 249, 250);">System.err</font>是系统默认提供的标准错误输出。

<font style="background-color:rgb(248, 249, 250);">PrintStream</font><font style="background-color:rgb(248, 249, 250);">OutputStream</font>相比,除了添加了一组<font style="background-color:rgb(248, 249, 250);">print() / println()</font>方法,可以打印各种数据类型,比较方便外,它还有一个额外的优点,就是不会抛出<font style="background-color:rgb(248, 249, 250);">IOException</font>,这样我们在编写代码的时候,就不必捕获<font style="background-color:rgb(248, 249, 250);">IOException</font>

1
2
3
4
5
6
7
8
9
10
11
12
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class Main {
public static void main(String[] args) throws IOException {
PrintStream out = new PrintStream(System.out, true, "UTF-8");
out.println(123);
}
}

<font style="background-color:rgb(248, 249, 250);">PrintStream</font>最终输出的总是<font style="background-color:rgb(248, 249, 250);">byte</font>数据,而<font style="background-color:rgb(248, 249, 250);">PrintWriter</font>则是扩展了<font style="background-color:rgb(248, 249, 250);">Writer</font>接口,它的<font style="background-color:rgb(248, 249, 250);">print() / println()</font>方法最终输出的是<font style="background-color:rgb(248, 249, 250);">char</font>数据。两者的使用方法几乎是一模一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package org.example;
import javax.annotation.processing.FilerException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class Main {
public static void main(String[] args) throws IOException {
StringWriter sw = new StringWriter();
try(PrintWriter pw = new PrintWriter(sw)){
pw.println("Hello World");
}
System.out.println(sw);
}
}

七、Files

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package org.example;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;

public class Main {
public static void main(String[] args) throws IOException {
File file=new File(".\\src\\main\\java\\demo\\one.txt");
byte[] data= Files.readAllBytes(file.toPath());
System.out.println(data);
String dataString=Files.readString(file.toPath(), StandardCharsets.UTF_8);
System.out.println(dataString);
}
}