IO总结-1

从招聘说起

​ 以前没事的时候总会去看各大公司招聘网站上招JAVA的要求,最后总结一件事情就是,几乎所有的公司的招聘要求都有一条:精通IO,虽然之前大家肯定也或多或少接触过IO。比如照着网上的代码写一个创建“hello.txt”的文件到硬盘中,然后往里写内容再读取出来,又或者是写个小爬虫然后各种百度用了(str = buffer.readLine())!=null,把一个html打印出来, 估计一般人都这么干过。当时想的是这几行代码乱七八糟,又是File、又是Out、又是In,反正是不明觉厉,用的又不多,写的时候百度一下就好,所以各种面试的时候就提心吊胆,能搪塞就搪塞。至少我以前是有这种感悟。其实仔细梳理一番,才发现也没什么,基础好的花个半天时间就能不用百度了,半天换来对一个重要知识点的领悟,我想没有什么比这还要划算了吧?当然还是要干一行爱一行,云里雾里何时才是头呢?

从File开始

​ 个人习惯,我喜欢先从字面上理解,这里插一句写JDK的大神命名是相当规范的,用“中式英语”翻译一遍基本都能理解几乎所有的类名,方法名大致是干什么的了。顾明思议,file就是文件或者文件夹的,有各种格式:txt、mp3等等。这些文件都是存在硬盘上的文件,File就是JDK提供给我们来代表文件或者文件夹的类,它定义了一些与平台无关的方法来操作文件。通过调用File类提供的各种方法,能够完成创建、删除文件、重命名文件、判断文件的读写权限权限是否存在、设置和查询文件的最近修改时间等操作,所以File是流的基础。file的代码比较简单,这里就不列了。

讨厌的byte和byte[]

​ byte是啥?byte[]又是啥?你能答出来吗?

​ 大家都知道,在计算机的世界是以二进制位存储的,1个0或者1个1就代表1个位,8个0或1即8个“位”就构成了最小的信息单位:byte,中文叫做字节。常见的1KB=1024byte就是这个意思。在JAVA语言中byte是一个基础数据类型,取值范围是-128-127,即: -2^8 到2^8-1,它也占了8个位,只不过第一个位表示符号而已。那么String和byte有什么关系呢?在java中String是以unicode编码的,占了两个字节,即16位。简单介绍下加unicode,16位即范围在 2^16(65536),即这65536个数组中每一个元素都代表一个字符,可以是汉字、日文等世界上任何一个国家上语言的文字。这样我们的java就可以根据至少两个byte,再根据编码格式去知道怎么把byte[]转换成String了,反过来也可以把String转换成byte数组了。

字节流和字符流

​ java中流有四个基础的抽象类:InputStream、OutputStream、Reader、Writer。InputStream和OutputStream为字节流设计,Reader和Writer为字符流设计,字节流和字符流形成分离的层次结构。字符流最通俗的理解是就是直接可以拿到String,其实查看源码我们也可以发现,字符流也是在字节流的基础上操作的,只不过是java为我们友好的封装了一下而已,在字符流是先写到缓冲区的,最后在cloe()或者flush()方法时才会写到物理内存中去,所以在操作字符流时还是最好用BufferReader和BufferWriter这样可以避免字符和字节流的频繁转换(这下可以理解文章开始时提到的代码了吧?),所以一定记得close!可以注释下面代码中的close方法来体验下。IO的子类确实比较多,用到了大量的装饰模式,可是构造函数的参数给我们定义的很明确,多看看API,少百度,我觉得还是比较好理解的。

​ 上面是我自己的总结。下面看看前辈的总结,比较专业一点。

总结一下流类的使用

  • File是一些文件/文件夹操作的源头,File代表的就是文件/文件夹本身,因此无论如何,使用IO的第一步是建议开发者根据路径实例化出一个File
  • 考虑使用字符流还是字节流。操作文本一般使用字符流,即Reader和Writer;操作字节文件使用字节流,即InputStream和OutputStream
  • 选择使用输入流还是输出流。把内容从文件读入Java内存使用输入流,即Reader和InputStream;把内容从Java内存读到文件使用输出流,即Writer和OutputStream
  • 使用字符流使用BufferedReader和BufferedWriter,它们的构造函数中的参数分别是Reader和Writer,因此既可以实例化出FileReader和FileWriter,也可以实例化出InputStreamReader和OutputStreamWriter,作为构造函数的参数传入BufferedReader和BufferedWriter
  • FileInputStream和FileOutputStream可以直接操作文件的读写,它们没有做缓存
  • ObjectOutputStream和ObjectInputStream,它们分别以OutputStream和InputStream作为构造函数的参数,因此可以实例化出FileOutputStream和FileInputStream并传入

​ 最后,附上我的学习代码,有一个是关于序列化的知识,有兴趣根据注释去运行下。

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package IO;
import java.io.*;
/**
* Created by yuhang on 2016/12/7.
*/
public class MyIO_1 {
public static void main(String[] args) {
String path = MyIO_1.class.getResource("").getPath();
File file = new File(path + "file" + File.separator + MyIO_1.class.getSimpleName() + ".txt");
try {
// OutputStream out = new FileOutputStream(file);
// byte b0[] = "虞大眼".getBytes(); //操作字节流,要转换成字节
// out.write(b0);
// out.close();
// InputStream in = new FileInputStream(file);
// byte[] b1 = new byte[(int) file.length()];
// in.read(b1);
// String string = new String(b1, 0, 3);
// System.out.println(string);
Writer out = new FileWriter(file);
BufferedWriter bufferedWriter = new BufferedWriter(out);
// 声明一个String类型对象
String str = "Hello World!!!";
bufferedWriter.write(str);
bufferedWriter.close();
out.close();
// 读文件操作
Reader in = new FileReader(file);
// 开辟一个空间用于接收文件读进来的数据
char c0[] = new char[1024];
int i = 0;
// 将c0的引用传递到read()方法之中,同时此方法返回读入数据的个数
i = in.read(c0);
in.close();
if (-1 == i) {
System.out.println("文件中无数据");
} else {
System.out.println(new String(c0, 0, i) + " line 42");
}
in = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(in);
String string;
while ((string = bufferedReader.readLine()) != null) {
System.out.println(string + " line 47");
}
bufferedReader.close();
in.close();
} catch (Exception 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
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package IO;
import java.io.*;
/**
* Created by yuhang on 2016/12/9.
*/
public class MyIO_3 {
public static class Person implements Serializable {
private static final long serialVersionUID = 2247135007662524835L;
//treansient修饰的不能序列化
private transient String name;
private int age;
private final static String sex = "man";
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "姓名:" + this.name + ", 年龄:" + this.age + ", 性别:" + sex;
}
}
public static void main(String[] args) throws Exception {
String path = MyIO_3.class.getResource("").getPath();
File file = new File(path + "file" + File.separator + MyIO_3.class.getSimpleName() + ".txt");
serializable(file);
deserializable(file);
//执行完一次后,注释serializable,只执行deserializable。修改serialVersionUID会抛InvalidClassException异常
}
// 序列化对象方法
public static void serializable(File file) throws Exception {
OutputStream outputFile = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(outputFile);
oos.writeObject(new Person("张三", 25));
oos.close();
}
// 反序列化对象方法
public static void deserializable(File file) throws Exception {
InputStream inputFile = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(inputFile);
Person p = (Person) ois.readObject();
System.out.println(p);
}
}
坚持原创技术分享,您的支持将鼓励我继续创作!

热评文章