Java.io
是一个基于流
的输入输出包,其中有两大基类字节流
和字符流
字节流
可以让我们方便的操作字节
数据,而字符流
可以让我们方便的操作字符
数据
字节流
操作的最小单元是1个字节
,而字符流
操作的最小单元是1个Unicode字符
字符流
还提供了对字节的编码
,解码
的能力
- FileInputStream
- FileOutputStream
- BufferedInputStream
- BufferedOutputStream
- FileReader
- FileWriter
- BufferedReader
- BufferedWriter
- InputStreamReader
- OutputStreamWriter
- PrintStream
- PrintWriter
- SequenceInputStream
- ObjectInputStream
- ObjectOutputStream
- PipedInputStream
- PipedOutputStream
- DataInputStream
- DataOutputStream
- ByteArrayInputStream
- ByteArrayOutputStream
可以看到java.io
包中关于流的类一大堆,下面只演示几个基本的使用方法,实际生产中不建议使用这些东西,而是应该使用别人已经写好的经过检验的第三方库
,记住不要重复造轮子
,这些只作为了解即可
最基础的文件读取
1
2
3
4
5
6
7
8
9
10
11
12
|
public class Main {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("./Main.java");
do {
int b = fileInputStream.read();
if (b == -1) {
break;
}
System.out.print((char) b);
} while (true);
}
}
|
最基础的文件写入
1
2
3
4
5
6
|
public class Main {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("./Test.java");
fileOutputStream.write("Hello World".getBytes(StandardCharsets.UTF_8));
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
public class Main {
public static void main(String[] args) throws IOException {
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("demo.txt"));
do {
int read = bufferedInputStream.read();
if (read == -1) {
break;
}
System.out.print((char) read);
} while (true);
}
}
|
1
2
3
4
5
6
7
|
public class Main {
public static void main(String[] args) throws IOException {
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("demo.txt"));
bufferedOutputStream.write("Hello World!".getBytes(StandardCharsets.UTF_8));
bufferedOutputStream.flush();
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
public class Main {
public static void main(String[] args) throws IOException {
FileReader fileReader = new FileReader("pom.xml");
do {
int ch = fileReader.read();
if (ch == -1) {
break;
}
System.out.print((char) ch);
} while (true);
}
}
|
字符写入流
默认有8K的缓冲区
1
2
3
4
5
6
7
|
public class Main {
public static void main(String[] args) throws IOException {
FileWriter fileWriter = new FileWriter("demo.txt");
fileWriter.write("Hello World!");
fileWriter.flush();
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new FileReader("demo.txt"));
do {
String line = bufferedReader.readLine();
if (line == null) {
break;
}
System.out.println(line);
} while (true);
}
}
|
1
2
3
4
5
6
7
|
public class Main {
public static void main(String[] args) throws IOException {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("demo.txt"));
bufferedWriter.write("Hello Java!");
bufferedWriter.flush();
}
}
|
老师说Java中,File类代表一个文件路径
,因为它可以表示一个文件
或目录
,甚至是一个不存在的
,但我还是觉得把它想象成一个文件对象
比较好理解,因为文件路径
实在是想不到那去,正常思维都觉得,它应该代表一个文件对象
,所以这个就这么理解算了,但你要知道它其实是代表文件路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public class Main {
public static void main(String[] args) throws IOException {
File file = new File("src/main");
System.out.println(file.isDirectory()); // 判断是否是文件夹
System.out.println(file.isFile()); // 判断是否是文件
System.out.println(file.exists()); // 判断是否存在
System.out.println(file.getAbsolutePath()); // 获取绝对路径
// listFiles 列出目录下的文件
for (File f : Objects.requireNonNull(file.listFiles())) {
System.out.println(f.getName()); // 获取文件名
}
System.out.println(file.isAbsolute()); // 判断是否是绝对路径
System.out.println(file.length()); // 获取文件长度(大小)
System.out.println(file.getParentFile()); // 获取父级File对象
System.out.println(file.getParent()); // 获取父级名字
// 一个重要构造器,根据一个父目录,获取一个子文件或目录对象
File file2 = new File("src/main/java/com/github/wjinlei", "Main.java");
}
}
|
关于NIO
有两种说法
- New IO(新IO)
- Non-blocking IO(非阻塞IO)
NIO
是JAVA 7 +
引入的东西,NIO
中有一个Path
类,你可以把它理解为之前的File
类
Path
和File
可以相互转换,通过toPath
方法和toFile
方法
NIO
中引入了一个Files
工具类,可以了解下
- Files.write
- Files.walkFileTree
- Files.readAllBytes
- Files.readAllLines
- Files.copy
- Files.createDirectory
- Files.createFile
- Files.createLink
- Files.createSymbolicLink
- Files.createTempDirectory
- Files.createTempFile
- Files.delete
- Files.deleteIfExists
- Files.exists
- Files.find
- Files.getAttribute
- Files.getOwner
- Files.isDirectory
- Files.isHidden
- ...
NIO
的引入就解决了File
的不好理解问题,所以如果你想把File
理解成路径,那么用Path
就比较好理解了,NIO
的引入主要是解决了基于流的Stream
模型慢
的问题,因为我们知道流
就像一个水管
写入时,必须等前面一个字符写完了,才能接着写后面的字符,它没办法并发写入
,因此NIO
的引入将基于流的Stream
模型,改成了基于块Block
的模型,它提供了并发
的写入数据的能力
读一个文件,这他妈读文件多方便,还有很多方法可以自行研究下
1
2
3
4
5
6
7
8
9
|
public class Main {
public static void main(String[] args) throws IOException {
String result = FileUtils.readFileToString(new File("demo.txt"), StandardCharsets.UTF_8);
System.out.println(result);
List<String> results = FileUtils.readLines(new File("demo.txt"), StandardCharsets.UTF_8);
results.forEach(System.out::println);
}
}
|
将一个字节流的数据读取到字符串,还有很多方法可以自行研究下串
1
2
3
4
5
6
7
|
public class Main {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("demo.txt");
String result = IOUtils.toString(fileInputStream, StandardCharsets.UTF_8);
System.out.println(result);
}
}
|
在Java中,异常
是程序不正常的表现,一般情况下,当程序发生一个异常
时,我们要么处理它
要么抛出它
,一个方法如果声明了throws XXXException
那么这个方法的调用者也只有两种选择,即要么处理它
要么继续抛出它
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public class Main {
public static void main(String[] args) {
// 使用 try...catch 语句抓住异常并处理
try {
a();
} catch (IOException e) {
e.printStackTrace();
}
}
// 继续抛出异常
public static void a() throws IOException {
b();
}
// 抛出IOException
public static void b() throws IOException {
FileInputStream fileInputStream = new FileInputStream("demo.txt");
fileInputStream.close();
}
}
|
异常
还可以主动抛出,有些时候我们希望程序在某种情况下,停止运行并给出一个异常
信息,我们就可以通过throw
,语句来主动抛出一个异常,并终止当前方法的执行,throw
语句会击穿所有的栈帧,如果它的上级没有处理这个异常,那么将会一直沿着栈向下抛出,直到遇到处理它的方法为止
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
|
public class Main {
public static void main(String[] args) {
a();
}
public static void a() {
try {
b();
} catch (RuntimeException e) {
e.printStackTrace();
}
}
public static void b() {
c();
}
public static void c() {
d();
}
public static void d() {
throw new RuntimeException("C");
}
}
|
我们可以通过try...catch
语句来捕捉被调用者
抛出来的异常
,finally
代码块是无论如何都会执行的,因此它通常用于做一些最后的清理工作
,try...catch
语句不一定非要写catch
,也可以try...finally
,finally
也不是必须的你可以只写try...catch
,也可以只写try...finally
更可以三个都写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class Main {
public static void main(String[] args) {
try {
a();
} catch (RuntimeException e) {
System.out.println("RuntimeException");
} finally {
System.out.println("finally");
}
}
public static void a() {
throw new RuntimeException("a is run...");
}
}
|
Java 7+
引入的语法糖,它可以简化我们的代码,并且自动帮我们完成资源的释放
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class Main {
public static void main(String[] args) {
/*
bufferedWriter 会被自动close
声明在try括号里的最后会被自动释放,但有个前提
前提是得实现了AutoCloseable接口
*/
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("demo.txt"))) {
bufferedWriter.write("Hello Java!");
bufferedWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
|
- throw
- 抛出一个异常
- throws
- 只是一个声明
- throws 没有必要声明
无毒
的异常,换句话说throws
声明的都应该是有毒
的异常
- Throwable (有毒)
- 一旦方法抛出了该异常体系下的类,调用者
要么处理
要么继续抛出
,具备传染性
- Throwable 代表了一个类是否具备
可抛性
,换句话说就是能不能被throw
语句使用
- Exception (有毒)
- 一旦方法抛出了该异常体系下的类,调用者
要么处理
要么继续抛出
,具备传染性
- Exception 代表了一种
预期错误
,就是我知道可能会抛出某异常,因此你需要显示的处理它
- RuntimeException (无毒)
- 就算方法抛出了该异常体系下的类,调用者不需要
继续抛出
,而且可以根据需要选择性处理
,不具备传染性
- RuntimeException 代表了
运行时异常
,换句话说就是只要出现了这个异常就是个bug
,它不是预料之中的
因此你没办法显示的处理它,所以它不具备传染性
- Error (无毒)
- 就算方法抛出了该异常体系下的类,调用者不需要
继续抛出
,而且可以根据需要选择性处理
,不具备传染性
- Error 代表了
错误
,这是非常严重
的问题,是不可恢复的
,它也不是预料之中
的,所以也不具备传染性
- 能用
if...else
处理的一定不要用try...catch
- 尽早的抛出异常
- 异常要准确,并带有详细信息
- 能抛出异常一定要抛出,不能若无其事的继续运行,哪怕你抛出异常也比执行错误的逻辑好的多
- 不归你管的异常,自己最好不要处理,而是继续向上抛出
- 如果有资源待释放的情况,最好处理异常后再向上抛出,而不要直接抛出
- 如非万分必要,不要忽略异常
一个反例,下面这段程序捕捉了异常,但只是打印了一下堆栈信息就若无其事的继续返回了result,这是非常错误的做法,因为你不能得到正确的结果,继续程序只会带来无法预料的后果,因此一旦遇到异常,应该想想这个方法能不能处理这个,异常,如果不能处理,就应该把这个异常继续向上抛出,无论如何不能假装若无其事的继续你的程序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public class Main {
public static void main(String[] args) {
List<String> list = readFile("demo.txt");
System.out.println(list);
}
public static List<String> readFile(String filepath) {
List<String> result = new ArrayList<>();
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader(filepath));
while (true) {
String line = bufferedReader.readLine();
if (line == null) break;
result.add(line);
}
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
}
|
这是一个正例
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
|
public class Main {
public static void main(String[] args) {
List<String> list = readFile2("demo.txt");
System.out.println(list);
}
/* 直接将异常向上抛出 */
public static List<String> readFile1(String filepath) throws IOException {
List<String> result = new ArrayList<>();
BufferedReader bufferedReader = new BufferedReader(new FileReader(filepath));
while (true) {
String line = bufferedReader.readLine();
if (line == null) break;
result.add(line);
}
return result;
}
/* 包装一下再抛出 */
public static List<String> readFile2(String filepath) {
List<String> result = new ArrayList<>();
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader(filepath));
while (true) {
String line = bufferedReader.readLine();
if (line == null) break;
result.add(line);
}
} catch (IOException e) {
throw new ReadFileException("读取文件失败", e);
}
return result;
}
static class ReadFileException extends RuntimeException {
public ReadFileException(String message, Throwable cause) {
super(message, cause);
}
}
}
|