0%

【Java基础】IO流

关于Java IO流的学习笔记

一、File类

1.1 FIle类的理解

  • (1)java.io.File类:文件和文件目录路径的抽象表达形式,与平台无关
  • (2)File功能:新建、删除、重命名文件和目录。但不能打开文件,访问内容
  • (3)在Java程序中表示文件/目录,必须要有一个File对象。但是File对象,不一定存在真实的本地文件/目录
  • (4)File对象可以作为参数传递给流的构造器

1.2 FIle类的实例化(构造器)

1
2
3
4
5
6
7
8
9
1)File file = new File(String pathName);---文件路径作为参数
// 路径:相对路径 / 绝对路径
// 路径分割符:"\",由于"\"也是一个转义字符,需要使用"\\"来表示路径分隔符

2)File file = new File(String parentPath, String childPath);
// 在parentPath路径下,创建childPath文件/文件夹,即parentPath\childPath

3)File file = new File(File parentFile, String childPath);
// 和第二种方法一样,只不过参数改为了File对象
1
2
3
4
5
6
举例:
File file1 = new File("hello.txt");

File file2 = new File("..\\", "FileTest");

File file3 = new File(file2, "hi.txt");

1.3 File类的常用方法

1
2
3
4
5
6
public String getAbsolutePath():获取绝对路径
public String gtePath():获取路径(根据File类实例传入的参数)
public String getName():获取名称(根据File类实例传入的参数)
public String getParent():获取上层文件目录路径。若无,返回null(根据File类实例传入的参数)
public long length():获取文件大小(即:字节数)。不能获取文件目录大小
public long lastModified():获取最后一次的修改时间,毫秒值
1
2
3
只适用于文件目录:
public String[] list():获取指定目录下的所有文件或文件目录的名称数组
public File[] listFile():指定目录下的所有文件或者文件目录的File数组
1
2
public boolean rename(File dest):把文件重命名为指定的文件路径(即:把文件进行剪切并且重命名)
//注意:被重命名的对象,必须在硬盘中存在,否则失败
1
2
3
4
5
6
7
判断条件:
public boolean isDirectory():判断是否是文件目录
public boolean isFile():判断是否是文件
public boolean exists():判断你是否存在
public boolean canRead():判断是否可读
public boolean canWrite():判断是否可写
public boolean isHidden():判断是否隐藏
1
2
3
4
硬盘中创建文件 / 文件目录
public boolean createNewFile():创建文件。若文件存在,则不创建,返回false(会抛异常)
public boolean mkdir():创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建了
public boolean mkidrs():创建文件目录。如果上层文件目录不存在,一并创建
1
2
3
硬盘中删除文件:
public boolean delete():删除文件 / 文件夹(Java删除的文件不会出现在回收站)
//注意:如果删除的是文件目录,则文件目录下不能存放任何东西,否则失败

1.4 总结

  • File类中涉及到关于文件后文件目录的创建、删除、重命名、修改时间、文件大小等方法,并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容,必须使用IO流来完成
  • 后续File类的对象常会作为参数传递到六的构造器中,指定读取或写入的“终点”

二、IO流

2.1 流的分类

  • 按操作数据单位不同:字节流(8 bit)、字符流(16 bit)
  • 按数据流的流向不同:输入流、输出流
  • 按流的角色不同:节点流、处理流

2.2 四个流的抽象基类

抽象基类 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer
  • 后面的流都是这些了流的派生

三、文件流(常用流1)

3.1 文件流种类

文件流 字节流 字符流
输入流 FileInputStream FileReader
输出流 FileOutputStream FileWriter
  • 就在抽象基类中多加了个”File”

3.2 FileReader使用

  • 操作步骤:
    • 1.指明文件对象
    • 2.提供具体的流
    • 3.数据的读入
    • 4.流的关闭操作
    • 5.try-catch-finally处理异常,finally填写资源的关闭操作
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
FileReader fr = null;//5.需要异常处理
try {
//1.指明文件对象
File file = new File("hello.txt");

//2.提供具体的流
fr = new FileReader(file);

//3.数据的读入
int data;
while((data = fr.read()) != -1){
System.out.print((char)data);
}

} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流的关闭操作
try {
if (fr != null){
fr.close();//close()也会出现异常,还需要在try-catch一次
}
} catch (IOException e) {
e.printStackTrace();
}
}
  • 写入数据的两种方法:
    • (1)调用空参方法read(),返回读取的整数数据,需要强转转为字符
1
2
3
4
int data;
while((data = fr.read()) != -1){
System.out.print((char)data);
}
    • (2)创建一个字符数组,调用read(char[] arr)方法,将数据读取数组里,返回一个读取长度
1
2
3
4
5
6
7
8
9
10
11
12
char[] cbuf = new char[5];//数组长度随意
int len;
while ((len = fr.read(cbuf)) != -1){
//方式一:
for (int i = 0; i < len; i++) {
System.out.print(cbuf[i]);
}

//方式二:
String str = new String(cbuf, 0, len);
System.out.print(str);
}

3.3 FileWriter使用

  • 操作步骤:
    • 1.指明文件对象
    • 2.提供具体的流,参数2是boolean型,true是继续文件内容往下写,false是覆盖文件内容重新写
    • 3.数据的写入
    • 4.流的关闭操作
    • 5.try-catch-finally处理异常,finally填写资源的关闭操作
1
2
3
4
5
6
7
8
9
10
11
12
13
此处省略了第五步:异常处理
//1.提供指定文件对象
File file = new File("hello1.txt");

//2提供FileWriter的对象,用于数据的写出
FileWriter fw = new FileWriter(file, true);

//3.写出操作
fw.write("I have a dream!\n");
fw.write("You need to have a dream!\n");

//4.流资源关闭
fw.close();
  • 读取数据的两种方法:
    • (1)调用write(String str),进行数据写入
1
fw.write("I have a dream!\n");
    • (2)调用write(char[] arr, int start, int end),传入一个字符数组,写入这数组从arr[start],到arr[end]的数据,一般配合FileReader的read(char[])来使用
1
2
3
4
5
char[] cbuf = new byte[1024];
int len;
while((len = fis.read(cbuf)) != -1){
fos.write(cbuf, 0,len);
}

3.4 FileInputStream 和 FileOutputStream使用

  • 用法与字符流的FileReader 和 FileWriter区别不大
  • 读取的数据变成字节,所以创建数组存储时,应该使用byte[]
  • 字节流适合用于非文本文件,例如:(.jpg, .mp3, .mp4, .ppt, .doc …)

四、缓冲流(常用流2)

4.1 缓冲流分类

缓冲流 字节流 字符流
输入流 BufferedInputStream BufferedReader
输出流 BufferedOutputStream BufferedWriter

4.2 缓冲流说明

  • 缓冲流作用:提高流的读取,写入速度
    • 提高读写速度的原因:内部提供了一个缓冲区
  • 缓冲流属于处理流的一种,处理流就是“套接”在已有的流的基础上

4.3 使用方法

  • 在原来的流,套一个缓冲流。即创建一个缓冲流,用原来的流作为参数
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
以:文件的复制为例
//1.创造文件对象
File file = new File("original.txt");
File copyFile = new File("original-copy.txt");

//2.创造流
//2.1创造节点流
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(copyFile);
//2.2创造缓冲流
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);

//3.读写数据
byte[] bbuf = new byte[1024];
int len;
while((len = bis.read(bbuf)) != -1){
bos.write(bbuf, 0, len);
}

//4.关闭流
//要求:先关闭外层流,再关闭内层流
//但是,关闭外层流的同时,内层流也会自动关闭
bos.close();
bis.close();

4.4 额外说明

  • 缓冲流的用法和文件流的用法几乎没有差异
  • 缓冲流的BufferedReader,提供了一个新的方法String readline(),可以读取一整行的数据,但该数据不包括换行符,需要自己添加换行符

五、转换流(常用流3)

5.1 转换流分类

转换流 作用
输入流 InputStreamReader InputStream –> Reader(解码)
输出流 OutPutStreamWriter Writer –> OutputStream(编码)

5.2 转换流说明

  • 作用:提供了字节流和字符流之间的转换
  • 转换流也是处理流的一种,套在原有流的基础上

5.3 使用方法

  • 方法和缓冲流一样,要套在原有的流,使字节流变成字符流,字符流变成字节流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
例:对文件进行重新编码
FileInputStream fis = new FileInputStream("hello.txt");
FileOutputStream fos = new FileOutputStream("hello-gbk.txt");

InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");

char[] cbuf = new char[5];
int len;
while((len = isr.read(cbuf)) != -1){
osw.write(cbuf, 0, len);
}

isr.close();
osw.close();

5.4 额外补充

  • 创建转换流会有个参数2,参数2填写的是字符集
  • 字符集种类:
    • ASCII:美国标准信息交换码
    • ISO8859-1:拉丁码表。欧洲码表
    • GB2312:中国的中文编码表,最多两个字节编码所有字符
    • GBK:中国的中文编码表升级,融合了更多的中文文字符号,最多两个字节编码
    • Unicode:国际标准码,融合了目前人类使用的所有字符
    • UTF-8:目前最常用的字符集,编程的编码方式,可用1-4个字节表示一个字符

六、其他流(不常用)(了解)

6.1 标准输入 / 输出流

  • 种类:
    • System.in:标准的输出流(字节流),默认从键盘输入
    • System.out:标准输出流(字节流),默认从控制台输出
  • 说明:
    • 标准流本身就是一个流,不需要创建对象,可以直接使用
    • System类的setIn(InputStream is) / setOut(PrintStream os),重新指定输入和输出的流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
使用例子:
从键盘输入字符串,要求将读到的整行字符串转变成大写输出,然后继续进行输入操作
,直到输入'e' 或者 "exit"时,退出程序(非Scanner方式)
思路:System.in --> 转换流 --> BufferedReader的readline

InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);

while (true) {
System.out.println("请输入字符串(e或exit退出):");
String data = br.readLine();

if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)) {
System.out.println("程序结束...");
break;
}

System.out.println(data.toUpperCase());
}

br.close();

6.2 打印流

  • 种类:
    • PrintStream
    • PrintWriter
  • 说明:
    • 打印流,将基本数据类型的数据格式转化为字符串输出
  • 操作:
    • (1)创建一个输出流
    • (2)PrintStream ps = new PrintStream(输出流, autoFlush:true/false)//创建一个打印流
    • (3)System.setOut(打印流):修改了System.out的打印位置,从控制台打印,变成到输出流所在位置
    • (4)调用System.out.print()System.out.println() 来使用打印流

6.3 数据流

  • 种类:
    • DataInputStream
    • DataOutputStream
  • 说明:
    • 数据流:用于读写或写出基本数据类型的变量或字符串
    • 用数据流写入的文件,也只能用数据流读取,其他方式读取会出现乱码。并且读取的顺序,要按照写入顺序就行读取
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
例:演示写入以及读取
@Test //写入
public void testDataOutputStream() throws IOException {
FileOutputStream fos = new FileOutputStream("data.txt");
DataOutputStream dos = new DataOutputStream(fos);

dos.writeUTF("马化腾");
dos.writeInt(21);
dos.writeBoolean(true);
dos.flush();//将内存中的数据,写入文件中

dos.close();
}

@Test //读取
public void testDataInputStream() throws IOException{
FileInputStream fis = new FileInputStream("data.txt");
DataInputStream dis = new DataInputStream(fis);

String name = dis.readUTF();
int age = dis.readInt();
boolean isMale = dis.readBoolean();

System.out.println("name = " + name);// name = 马化腾
System.out.println("age = " + age);// age = 21
System.out.println("isMale = " + isMale); // isMale = true
}

七、对象流

7.1 定义

  • 作用:用于存储和读取基本数据类型数据 或 对象的处理流
  • 方法:
    • ObjectInputStream:(序列化)保存基本数据类型数据或对象的机制
    • ObjectOutputStream:(反序列化)读取基本数据类型或对象的机制

7.2 使用方法

  • 序列化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
public void testObjectOutputStream() throws IOException{
//1.创建对象流
File file = new File("ObjectStream.dat");
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);

//2.写入对象数据
oos.writeObject(new String("对象流测试!"));
oos.flush();//刷新操作

//3.关闭资源
oos.close();

//4.省略try-catch-finally
}
  • 反序列化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void testObjectInputStream() throws IOException, ClassNotFoundException {
//1.创建对象流
File file = new File("ObjectStream.dat");
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);

//2.读取数据
Object o = ois.readObject();
String str = (String)o;
System.out.println(str);

//3.关闭资源
ois.close();

//4.省略try-catch-finally
}

7.3 自定义类序列化要求

  • (1)实现Serializable (标识接口,不需要重写任何方法)
  • (2)提供全局常量,标识UID public static final long serivalVersionUID = 1231231L;
  • (3)除了当前Person类需要实现Serializeable,其类中属性,除了基本数据类型,其余属性也要实现Serializable接口

八、RandomAccessFile

8.1 定义

  • 1.RandomAccessFile直接继承与java.lang.Object类,实现了DataInput和DataOutput接口
  • 2.RandomAccessFile即可以作为一个输入流,又可以作为一个输出流

8.2 构造器

  • public RandomAccessFile(File file, String mode)

  • public RandomAccessFile(String name, String mode)

  • mode参数:

    • r:以只读方式打开
    • rw:打开以便读取和写入
    • rwd:打开以便读取和写入;同步文件内容的更新
    • rws:打开以便读取和写入;同步文件内容和元数据的更新

8.3 使用方法

  • 使用的方法和 FileInputStream 和 FileOutputStream区别不大
  • 如果RandomAccessFile作为输出流,写出到的文件如果不存在,则在执行过程中自动创建,如果写出到文件存在,则对原有文件内容进行覆盖
  • RandomAccessFile类,额外新增的方法
    • public void seek(int pos) //将指针调到角标为pos的位置,可通过此方法实现从文件末尾添加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
通过seek()方法,来实现插入效果
@Test
public void test2() throws Exception{
File file = new File("RandomAccessFile.txt");
RandomAccessFile raf = new RandomAccessFile(file, "rw");

raf.seek(3);

StringBuilder sb = new StringBuilder((int) file.length());
byte[] bbuf = new byte[10];
int len;
while((len = raf.read(bbuf)) != -1){
sb.append(new String(bbuf, 0, len));
}

raf.seek(3);
raf.write("xyz".getBytes());
raf.write(sb.toString().getBytes());

raf.close();
//省略了try-catch-finally处理
}