该学习笔记是根据阿玮老师《黑马程序员Java零基础视频教程》总结出来的,非常感谢阿玮老师。
一. File类
- 简单介绍
-
路径分为两种:相对路径和绝对路径。相对路径不带盘符,默认到当前项目下去找;绝对路径带盘符。
-
java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。
(1)File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容,必须使用IO流来完成。Java-File类详解(一篇讲透)
(2)想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录。File对象可以作为参数传递给流的构造器,指明读取或写入的"终点"。
(3) File类的对象表示一个路径,可以是文件的路径、也可以是文件夹的路径,其中这个路径可以是存在的,也可以是不存在的。File类常见的构造方法有三个,如下:
-
//第一种构造方法
String str = "D:\\JAVA\\project\\SImpleTest\\data1.txt";
File file1 = new File(str); //创建File对象file1.
System.out.println(file1);
// 第二种构造方法
String parent = "D:\\JAVA\\project\\SImpleTest";
String child = "data1.txt";
File file2 = new File(parent,child);
System.out.println(file2);
//第三种构造方法
File parent1 = new File(parent);
File file3 = new File(parent1,child);
System.out.println(file3);
/*
输出:
D:\JAVA\project\SImpleTest\data1.txt
D:\JAVA\project\SImpleTest\data1.txt
D:\JAVA\project\SImpleTest\data1.txt
*/
* 关于File类常见的方法大家可以自行网上查询,反正大致上分为五类:判断、获取、创建、删除、获取并遍历。
二. IO流
- 简单介绍
-
由于File类只能对文件本身进行操作,不能读写文件里面存储的数据,因此我们需要借助IO流来实现文件数据的读写操作。"I"表示input输入流,"O"表示output输出流,"IO"流就是输入输出流。我们可以把IO流看成是程序(内存)和文件之间,用于传输数据的通道。那有一个问题,什么是输入流呢?什么是输出流呢?
-
输入流:从文件中读取数据到程序(内存)中,这个就是输入流;输出流:从程序(内存)中写出数据到文件中,这个就是输出流。我们可以这样区分输入流和输出流:
数据读取到程序中---输入流;把数据写出文件--输出流
。关于输入流和输出流的划分如下图所示,其中字节流一次读取一个字节(byte)的数据;字符流一次读取一个字符的数据。字节流是万能的,什么类型的文件都可以读取,而字符流只能读取纯文本文件。
-
三. 字节流
- FileOutputStream(字节输出流):操作本地文件的字节输出流,可以把程序中的数据写出到本地文件中。书写步骤:(1)首先创建字节输出流对象;(2)然后写出数据;(3)最后释放资源。
// FileOutputStream:操作本地文件的字节输出流,可以把程序中的数据写到本地文件中.
String path1 = ".\\data1.txt";
File file = new File(path1);
System.out.println(file.exists());
FileOutputStream fos = new FileOutputStream(path1);//会让程序和path1路径下的文件之间产生传输通道
fos.write(97);
fos.write(98);// 在上面通道中写出数据
byte[] bt = {99,100,101,102,103,104};
fos.write(bt);
fos.write(bt,1,2);//从索引为1开始写出数据,然后一共写出2个.
fos.close(); // 关闭上述通道
// 字节输出流的细节:
//1.创建字节输出流对象.细节(1)传递的参数可以是字符串表示的路径,也可以是File对象
// 细节(2)如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的;
// 细节(3)如果文件已经存在,则会清空文件(也就是把原有的数据覆盖了).
//2.写出数据. 细节(1)如果write方法传递的参数是整数,那么实际上写到本地文件中的是ASCII上整数对应的字符
//3.释放资源: 细节(1)每次使用完输出流之后都要释放资源--关闭通道
//换行写. Windows系统:\r\n--回车换行--换行符; linux系统: \n--换行符; Mac: \r-换行符
// 在Windows操作系统中,java对回车换行进行了优化,虽然完整是\r\n,但是我们只写其中一个\r或者\n也可以实现换行,
// 因为java会在底层实现补全.但是还是建议写全
String path2 = ".\\data2.txt";
FileOutputStream fos1 = new FileOutputStream(path2);
String str = "helloWorld";
byte[] bytes = str.getBytes(); // getBytes()获取字符串对应的字节数组(也就是将字符串转换为字节数组)
System.out.println(Arrays.toString(bytes));
fos1.write(bytes);
// 写换行符
String str1 = "\r\n";
fos1.write(str1.getBytes());
String str2 = "666";
fos1.write(str2.getBytes());
fos1.close();
// 续写(追加写):
// 如果想要续写,打开续写开关即可.开关位置:创建对象的第二个参数,默认false表示关闭续写,此时创建对象会清空原有的文件;
// 手动传递true,表示打开续写,此时创建对象不会清空原有的文件
FileOutputStream fos2 = new FileOutputStream(path2,true); // 后面写true表示续写,默认是false
fos2.write("\r\n".getBytes());
fos2.write("111".getBytes());
/*
输出:
true
[104, 101, 108, 108, 111, 87, 111, 114, 108, 100]
*/
- FileInputStream(字节输入流):操作本地文件的字节输入流,可以把本地文件中的数据读取到程序中来。书写步骤:(1)首先创建字节输入流对象;(2)然后读取数据;(3)最后释放资源。
// FileInputStream:操作本地文件的字节输入流,可以把本地文件中的数据读取到程序中来.
String path = ".\\data1.txt"; // data1.txt对应的数据为:abcdefghde
//1.创建对象
FileInputStream fis = new FileInputStream(path);
//2.读取数据
int read = fis.read();
System.out.println((char) read);
//释放资源
fis.close();
// 字节输入流的细节:
//1.创建字节输入流对象. 细节(1)如果文件不存在,就直接报错.
//2.读取数据. 细节(1)一次读一个字节,读出来的是数据在ASCII码上对应的数字;
// 细节(2)读到文件末尾了,read方法返回-1;
//3.释放资源: 细节(1)每次使用完输入流之后都要释放资源--关闭通道
// 字节输入流循环读取
FileInputStream fis1 = new FileInputStream(path);
int b;
while ((b = fis1.read()) != -1){
System.out.print((char) b);
}
/*输出:
a
abcdefghde
*/
(1)文件拷贝代码练习:
String path1 = ".\\data.txt"; // 已经存在的文件
String path2 = ".\\copyData.txt"; //要拷贝的文件
// 创建对象
FileInputStream fis = new FileInputStream(path1); //将文件中的数据读取到程序中,需要创建输入流.
FileOutputStream fos = new FileOutputStream(path2); //将程序中的数据写出到文件中,需要创建输出流.
// 拷贝:边读边写
int b;
while ((b = fis.read()) !=-1){
fos.write(b);
}
// 关闭资源:规则是先打开的最后关闭.
fos.close();
fis.close();
(2)FileInputStream一次读写一个字节,如果拷贝的文件过大,那么上述的文件拷贝方法速度会非常慢,因此我们需要一次读取多个字节数据。
(3)一次读取多个字节代码示例:
String path1 = ".\\data.txt"; // 数据为:hello world java--16字节
// 创建字节流对象
FileInputStream fis = new FileInputStream(path1);
// 读取数据
byte[] bytes = new byte[10]; //每次读取尽可能把字节数组装满
int len1 = fis.read(bytes);
//一次读取多个字节数据,具体读取多少跟字节数组的长度有关.len表示本次读取了多少个字节数据.
// 第一次往字节数组bytes里面读取10个字节数据:hello worl,因此len1=10,bytes为[hello worl].
System.out.println(len1);
String str = new String(bytes);
System.out.println(str);
int len2 = fis.read(bytes);
// 因为一共有16字节大小的数据,第一次读取了10字节数据,还剩下6字节数据.所以第二次只能读取6字节数据,len2=6.
//但是第一次bytes里面为[hello worl],第二次读取的"d java"会将"hello "覆盖,但"worl"不会覆盖.因此第二次读取到bytes里面为[d javaworl]
System.out.println(len2);
System.out.println(new String(bytes));
System.out.println(new String(bytes,0,len2)); // 这样写可以过滤上次没有被覆盖的元素,也就是读取多少就输出多少
fis.close();
/*输出:
10
hello worl
6
d javaworl
d java
*/
(4)文件拷贝代码优化:
String path1 = ".\\data.txt"; // 数据为:hello world java--16字节
String path2 = ".\\dataCopy.txt";
// 创建对象
FileInputStream fis = new FileInputStream(path1);
FileOutputStream fos = new FileOutputStream(path2);
//拷贝
int len;
byte[] bytes = new byte[1024*1024*5]; //5MB大小
while ((len = fis.read(bytes)) != -1){
fos.write(bytes,0,len); //表示读多少写多少
}
//释放资源
fos.close();
fis.close();
四. 字符集
- ASCII码表
- GBK字符集
(1)GBK存储英文的规则
(2)GBK存储中文(汉字)的规则
(3)总结
- Unicode(万国码)。Unicode编码方式有:UTF-16编码规则----用2~4个字节进行存储;UTF-32编码规则----固定使用四个字节进行存储;UTF-8编码规则----使用1~4个字节进行存储;Unicode Transfer Format(Unicode转换格式)–UTF,其中UTF-8编码规则是实际开发中最为常见的。在UTF-8编码规则中文使用3个字节表示,英文使用1个字节表示。记住UTF-8不是字符集,而是Unicode字符集的一种编码方式
(1)Unicode存储英文的规则
(2)Unicode存储中文(汉字)的规则
(3)总结
- 为什么会有乱码呢?
答:(1)读取数据时没有读完整个汉字;(2)编码和解码的方式不统一。
那如何不产生乱码呢?
答:(1)不要使用字节流读取文本文件;(2)编码和解码使用同一个码表,同一种编码方式。
编码和解码的方式不统一
- java中编码和解码
String str = "ai你哟";
//默认方式进行编码
byte[] bytes = str.getBytes();
//IDEA默认编码方式为UTF-8,中文3字节,英文1字节;eclipse默认编码方式为GBK,中文2字节,英文1字节.
System.out.println(Arrays.toString(bytes)); //'a':1字节; 'i':1字节; '你':3字节; '哟':3字节,共8字节.
System.out.println(bytes.length);
// 指定编码方式
byte[] gbk = str.getBytes("GBK"); //'a':1字节; 'i':1字节; '你':2字节; '哟':2字节,共6字节.
System.out.println(Arrays.toString(gbk));
System.out.println(gbk.length);
//解码
String str1 = new String(bytes);
String str2 = new String(gbk,"GBK");
System.out.println(str1);
System.out.println(str2);
/*输出:
[97, 105, -28, -67, -96, -27, -109, -97]
8
[97, 105, -60, -29, -45, -76]
6
ai你哟
ai你哟
*/
五. 字符流
- FileReader(字符输入流)书写步骤:(1)创建字符输入流对象;(2)读取数据;(3))释放资源。
代码示例
//data.txt数据为: 你好世界
// hello World
// 你好java
// hello java
// 文件中的数据每一行末尾都有一个隐藏换行符(除了最后一行)--\r\n(两个字符'\r'和'\n'),其中回车符'\r'在打印的时候没有打印出来.
// 换行符'\n'打印的时候会直接换行.
String path = ".\\data.txt";
// 1.创建对象并关联本地文件
FileReader fr = new FileReader(path);
//空参读取的read.(1)read默认也是一个字节一个字节的读取,如果遇到中文就会一次读取多个字节;
// (2)在读取之后,方法底层还会进行解码并转成十进制,最终把这个十进制作为返回值.
int ch;
while ((ch=fr.read()) != -1){
System.out.print((char) ch);
}
fr.close();
//1.创建对象
FileReader fr1 = new FileReader(path);
//2.带参数的read:把读取数据、解码、强转三步合并了,把强转之后的字符放到字节数组中.
char[] chars = new char[6]; //一次读6个字符,'你'、'好'、'J'、'v'这些都是一个字符.
int len;
System.out.println();
while ((len=fr1.read(chars)) != -1){
System.out.print(new String(chars,0,len)); //把字节数组中的数据变成字符串再进行打印
}
fr1.close();
/*输出:
你好世界
hello World
你好java
hello java
你好世界
hello World
你好java
hello java
*/
- FileWriter(字符输出流)
(1)构造方法
(2)成员方法
(3)细节处理
(4)代码
String path = ".\\data1.txt";
//1.创建字符输出流对象.
FileWriter fw = new FileWriter(path);
//写一个字符:'我'--25105
fw.write(25105);
//写一个字符串 "hello world".
fw.write("hello World");
// 写一个字符串的一部分
fw.write("!!!!!!",0,3);
// 写一个字符数组
char[] chars = {'你','好','世','界','\r','\n','A'};
fw.write(chars);
// 释放资源
fw.close();
//续写:FileWriter fw = new FileWriter(path,true);设置true
六. 缓冲流
- 字节缓冲流。
- 字符缓冲流。
代码示例
String path1 = ".\\data.txt";
// 1.创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader(path1));
// readLine方法在读取数据的时候,一次读取一行数据,遇到回车换行结束,但是该方法不会把回车换行读取到内存中.
// (也就是readLine方法不会读取回车换行符)
String s;
while ((s= br.readLine()) != null){
System.out.println(s);
}
br.close();
//1.创建字符缓冲输出流对象
String path2 = ".\\dataCopy.txt";
BufferedWriter bw = new BufferedWriter(new FileWriter(path2));
bw.write("你骚气的样子,我无法形容");
bw.newLine();
bw.write("但是我好喜欢");
bw.close();
/*输出:
你好世界
hello World
你好java
hello java
*/