目录
1.3.1、FileInputStream&BufferedInputStream
1.3.2、FileOutputStream&BufferedOutputStream
一、流的概念【记忆】
计算机在运行程序时都是在内存中运行,会将程序需要的数据都加载到内存中,CPU处理数据是和内存进行交互。而内存在断电后存储的数据会消失,所以要长期存储的数据都会存放在硬盘上。硬盘是不能和CPU直接做交互的,那么需要把硬盘的数据先复制到内存中,复制到内存的操作就需要流,反过来长期保存数据也要通过流的操作。流就是用来做数据传输的类,流也只是个名字,不需要纠结为什么叫流。
IO流就是输入输出流,I代表InputStream,O代表OutputStream。
IO流的体系结构
二、流的分类【记忆】
1、方向
相对于内存,分为输入流和输出流。
1.1、输入流
向内存中输入数据是输入流。
1.2、输出流
从内存中把数据输出到硬盘等设备上叫做输出流。
2、处理最小单位
流在处理数据时,按照每次处理的最小单位,可以将流分为字节流和字符流。
2.1、字节流
一次处理最小单位是字节,字节流可以处理所有文件,一般字节流的类都是以Stream结尾的。
2.2、字符流
每次处理的最小单位是字符,因为人直接能读懂的就是字符,所以为了方便开发,做了一个处理字符的流,这种流只能处理纯文本文件,纯文本文件就是用记事本打开能看明白的文件,例如:txt、java、html等。非纯文本文件例如:class、jpg、png、MP3、MP4。
3、级别
3.1、节点流
就是最普通的流,它只有基础功能。
3.2、高级流
在节点流的基础上,进行了拓展和优化,例如增加缓存区、增加更简单操作的方法等,所以在实际开发中应用最多的就是高级流,一般节点流的作用是为了创建高级流。
3.3、高级流为什么效率高
可以对比去水房打水,想装满一个杯子,假如杯子容量是246滴水。用节点流打水相当于每次取一滴水,需要进行246次才能完成,大部分的时间都浪费到路上了。用缓冲区相当于拿了一个小容器,每次可以取30滴水,效率明细提高了。而用高级流相当于拿了一个容量是.100甚至更大的容器取水,是效率最高的方式。
三、File类【掌握】
File表示一个文件或目录,它可以和硬盘上的一个文件或目录建立关系。这个文件或目录可以存在也可以不存在,因为流可以读文件,也可以创建文件。可以通过操作这个File对象获取或者修改对应文件或目录的属性等。
1、常用构造方法
File类有4个构造方法,其中最常用的是参数为一个字符串的构造方法。
File(path):创建一个文件或目录,path是这个文件或目录的路径。
2、常用方法
exists():判断文件或目录是否存在。
getName():获取文件或目录的名字,名字是包含文件拓展名的。
getPath():获取文件或目录的路径。
isDirectory():判断File对象是否是一个目录。
isFile():判断File对象是否是一个文件。
length():返回文件的大小。
listFiles():返回一个File[],表示这个目录下的所有文件。
renameTo(file):将当前的文件对象重命名为file表示的文件。
3、考题方法
delete():删除文件或目录,但是如果目录不为空,那么删除不掉这个目录,如果目录中有目录,那删除不掉这个目录,需要把这个目录中所有的文件删除掉才能删除这个目录。
mkdir():创建这个目录,如果这个目录的父级路径不存在,那么这个目录无法创建。
mkdirs():创建这个目录,无论父级路径(不是单指一层父级,多少层都可以)是否存在,都会创建这个目录,不存在的父级目录会一起创建出来。
4、例子
public static void main(String[] args) {
System.out.println("两个属性:");
System.out.println("File.pathSeparator = " + File.pathSeparator);
System.out.println("File.separator = " + File.separator);
System.out.println("2、常用方法");
System.out.println("exists():判断文件或目录是否存在");
File exists1 = new File("D:/code/test/1");
System.out.println(exists1.exists());
File exists2 = new File("D:/code/test/a.txt");
System.out.println(exists2.exists());
System.out.println("getName():获取文件或目录的名字");
File getName = new File("D:\\code\\test\\a.txt");
System.out.println(getName.getName());
System.out.println("getPath():获取文件或目录的路径");
System.out.println(getName.getPath());
System.out.println("isDirectory():判断File对象是否是一个目录");
System.out.println(getName.isDirectory());
System.out.println("isFile():判断File对象是否是一个文件");
System.out.println(getName.isFile());
System.out.println("length():返回文件的大小");
System.out.println(getName.length());
File file = new File("D:\\code\\test");
System.out.println(file.length());
System.out.println("listFiles():返回一个File[],表示这个目录下的所有文件");
File[] fileArray = file.listFiles();
for(File f : fileArray){
System.out.println(f.getName());
}
System.out.println("renameTo(file):将当前的文件对象重命名为file表示的文件");
File img123d = new File("D:\\code\\test\\1\\123的.png");
File img123 = new File("D:\\code\\test\\1\\123.png");
img123d.renameTo(img123);
System.out.println("3、考题方法");
System.out.println("delete():删除文件或目录,但是如果目录不为空,那么删除不掉这个目录,如果目录中有目录,那删除不掉这个目录,需要把这个目录中所有的文件删除掉才能删除这个目录");
File dir23 = new File("D:\\code\\test\\1\\23");
dir23.delete();
File dir23_png = new File("D:\\code\\test\\1\\23\\1.png");
dir23_png.delete();
System.out.println("mkdir():创建这个目录,如果这个目录的父级路径不存在,那么这个目录无法创建");
File dirMk = new File("D:\\code\\test\\1\\aaa\\bbb");
dirMk.mkdir();
System.out.println("mkdirs():创建这个目录,无论父级路径(不是单指一层父级,多少层都可以)是否存在,都会创建这个目录,不存在的父级目录会一起创建出来");
File dirMks = new File("D:\\code\\test\\1\\ccc\\ddd");
dirMks.mkdirs();
}
两个属性:
File.pathSeparator = ;
File.separator = \
2、常用方法
exists():判断文件或目录是否存在
true
true
getName():获取文件或目录的名字
a.txt
getPath():获取文件或目录的路径
D:\code\test\a.txt
isDirectory():判断File对象是否是一个目录
false
isFile():判断File对象是否是一个文件
true
length():返回文件的大小
7
0
listFiles():返回一个File[],表示这个目录下的所有文件
1
a.txt
renameTo(file):将当前的文件对象重命名为file表示的文件
3、考题方法
delete():删除文件或目录,但是如果目录不为空,那么删除不掉这个目录,如果目录中有目录,那删除不掉这个目录,需要把这个目录中所有的文件删除掉才能删除这个目录
mkdir():创建这个目录,如果这个目录的父级路径不存在,那么这个目录无法创建
mkdirs():创建这个目录,无论父级路径(不是单指一层父级,多少层都可以)是否存在,都会创建这个目录,不存在的父级目录会一起创建出来
四、常用流【掌握】
1、字节流
1.1、常用流
FileInputStream:处理文件的输入节点流。
BufferedInputStream:高级输入节点流,有缓冲区的。
FileOutputStream:处理文件的输出节点流。
BufferedOutputStream:高级输出节点流,有缓冲区的。
1.2、构造方法
FileInputStream(file):创建一个文件输入流,操作的文件是file
BufferedInputStream(inputStream):通过一个输入流,创建一个高级流,根据多态参数可以传FileInputStream。
FileOutputStream(file):创建一个文件输出流,输出的文件是file。
FileOutputStream(file,append):创建一个文件输出流,和上一个方法不同之处在于append代表了操作这个文件是追加还是覆盖,true代表追加的意思。
BufferedOutputStream(outputStream):通过输出流创建一个高级流,参数是outputStream,输出是否是追加由outputStream决定。
1.3、常用方法
1.3.1、FileInputStream&BufferedInputStream
read(array,int1,int2):读取数据,通过流读取一些数据存放到array数组中,起始位置是int1,读取int2个字节,一般int1都是0,int2会变化。
close():关闭流方法,关闭方法一般写在finally中,要先判断一些是否为空,关闭流要从外向内。
1.3.2、FileOutputStream&BufferedOutputStream
write(array,int1,int2):通过流写数据,将array中int1开始,共int2个字节写出去。
write(array):通过流写数据,相当于write(array,0,array.length)。
close():关闭流。
flush():清空缓存,如果不执行,那么缓存输出流可能有缓存没有写出去,调用这个方法会强制将缓存写出去。如果调用了close方法,会自动调用这个方法。
1.4、例子
public class TestStream {
public static void main(String[] args) {
File inFile = new File("D:\\code\\test\\a.txt");
String str = in(inFile);
System.out.println(str);
File outFile = new File("D:\\code\\test\\b.txt");
out(outFile, "lsdjfslkjflsfj你好世界");
File inCopy = new File("D:\\code\\test\\123.png");
File outCopy = new File("D:\\code\\test\\456.png");
copy(inCopy, outCopy);
}
/**
* 复制文件
*
* @param in 原文件
* @param out 复制生成的文件
*/
public static void copy(File in, File out) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(in));
bos = new BufferedOutputStream(new FileOutputStream(out));
byte[] array = new byte[10];
int length = bis.read(array, 0, array.length);
while (length >= 0) {
bos.write(array, 0, length);
length = bis.read(array, 0, array.length);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 读取文件方法
*
* @param file 目标文件
* @return 文件内容
*/
public static String in(File file) {
String result = "";
FileInputStream fis = null;
BufferedInputStream bis = null;
try {
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
List<Object> list = new ArrayList<>();
byte[] array = new byte[10];
int length = bis.read(array, 0, array.length);
while (length >= 0) {
for (int i = 0; i < length; i++) {
list.add(array[i]);
}
length = bis.read(array, 0, array.length);
}
byte[] arr = new byte[list.size()];
for (int i = 0; i < list.size(); i++) {
arr[i] = (byte) list.get(i);
}
result = new String(arr, "GBK");
System.out.println(result.length());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result;
}
/**
* 创建文件
*
* @param out 输出文件
* @param str 输出内容
*/
public static void out(File out, String str) {
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try {
fos = new FileOutputStream(out, true);
bos = new BufferedOutputStream(fos);
bos.write(str.getBytes());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1.5、注意事项
-
流操作结束后一定要关闭流,在关闭流之前判断是否为空。
-
读取文件时,在最后可能会因为文件大小导致byte数组没有被填充满,byte数组因为上次读取的数据仍然在数组内,转换的时候会多转换几个byte,文件就不完全一致了。所以每次要存储读取了多少个字节,根据读取到多少自己动态的处理数组中数据。
-
写文件时也要注意类似于第二点的问题,也要确定输出长度再输出,否则输出的文件和源数据就不一致了。
-
中文乱码问题。一般导致中文乱码问题的原因有两个,第一个是编码集不统一,需要通过修改编码集解决。第二种是中文占两个字节,读取时这两个字节被分开了,导致转字符串时乱码,这种解决方案只能通过一次性将读取的内容转化成字符串解决。
-
纯文本文件建议使用字符流,非纯文本文件必须使用字节流。
2、字符流
2.1、常用流
FileReader:文件输入字符流。
FileWriter:文件输出字符流。
BufferedReader:文件输入高级字符流。
BufferedWriter:文件输出高级字符流。
2.2、构造方法
FileReader(file):读取file文件。
FileWriter(file,append):将内容写入到file中,append为true时会做追加操作。
BufferedReader(reader):读取reader流中数据。
BufferedWriter(writer):文件输出。
2.3、常用方法
readLine():输入流,读取一行,返回这行数据的字符串。
write(str):输出流,直接将字符串输出到文件中。
newLine():换行。
2.4、例子
2.4.1、读文件
public class TestReader {
public static void main(String[] args) {
System.out.println(readGBK(new File("D:\\code\\testReader\\a.txt")));
}
/**
* 读取文件
*
* @param file 目标文件
* @return 文件内容
*/
public static String read(File file) {
StringBuilder result = new StringBuilder();
FileReader fr = null;
BufferedReader br = null;
try {
fr = new FileReader(file);
br = new BufferedReader(fr);
String str = br.readLine();
while (str != null) {
result.append(str + "\n");
str = br.readLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result.toString();
}
/**
* 读取GBK编码集文件
*
* @param file 目标文件
* @return 文件内容
*/
public static String readGBK(File file) {
StringBuilder result = new StringBuilder();
FileInputStream fis = null;
InputStreamReader isr = null;
BufferedReader br = null;
try {
fis = new FileInputStream(file);
isr = new InputStreamReader(fis, "GBK");
br = new BufferedReader(isr);
String str = br.readLine();
while (str != null) {
result.append(str + "\n");
str = br.readLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (isr != null) {
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result.toString();
}
}
2.4.2、写文件
public class TestWriter {
public static void main(String[] args) {
String str = "asdfasdfasljsdlfjslkfjlsf5154654654654\n" +
"35ds44sf46sdf645sd456\n" +
"sd45fs123d\n" +
"sf2.s12\n" +
"今天星期六\n" +
"13sf";
write(new File("D:\\code\\testReader\\b.txt"), str);
}
/**
* 写文件
*
* @param file 输出文件
* @param str 输出内容
*/
public static void write(File file, String str) {
FileWriter fw = null;
BufferedWriter bw = null;
try {
fw = new FileWriter(file);
bw = new BufferedWriter(fw);
bw.write(str);
bw.newLine();
bw.newLine();
bw.newLine();
bw.newLine();
bw.newLine();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
五、对象流【了解】
对象流是用于针对对象输入或者输出的,输入可能从硬盘中读取数据,也可能从内存中读取数据。输出同理,可能输出到硬盘上,也可能输出到内存中。
1、常用流
ObjectInputStream:对象输入流。
ObjectOutputStream:对象输出流。
ByteArrayInputStream:字节数组输入流。
ByteArrayOutputStream:字节数组输出流。
2、原型模式
原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
2.1、Score
public class Score implements Serializable {
private String name;
private int score;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Score{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
}
2.2、Student
public class Student implements Cloneable, Serializable{
private String name;
private Score score;
private int age;
@Override
public Student clone() throws CloneNotSupportedException {
ObjectInputStream ois = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bais = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
oos = new ObjectOutputStream(baos);
oos.writeObject(this);
bais = new ByteArrayInputStream(baos.toByteArray());
ois = new ObjectInputStream(bais);
return (Student) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Score getScore() {
return score;
}
public void setScore(Score score) {
this.score = score;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
", age=" + age +
'}';
}
}
2.3、Test
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Student s1 = new Student();
s1.setAge(10);
s1.setName("zhangsan");
Score score = new Score();
score.setName("math");
score.setScore(90);
s1.setScore(score);
System.out.println(s1);
System.out.println(s1.hashCode());
Student s2 = s1.clone();
System.out.println(s2);
System.out.println(s2.hashCode());
s1.getScore().setScore(50);
System.out.println(s1.getScore());
System.out.println(s2.getScore());
System.out.println(s1.getScore() == s2.getScore());
}
}
六、数据流【了解】
包括DataInputStream、DataOutputStream等,用来操作基本数据类型和字符串的。
DataInputStream:将文件中存储的基本数据类型和字符串写入内存的变量中。
DataOutputStream:将内存中的基本数据类型和字符串的变量写出文件中。
1、利用DataOutputStream向外写出变量。
public class Test01 {
/*这是一个main方法,是程序的入口*/
public static void main(String[] args) throws IOException {
/*
* DataOutputStream:将内存中的基本数据类型和字符串的变量写出文件中
* File f = new File("d:\\Demo2.txt");
* FileOutputStream fos = new FileOutputStream(f);
* DataOutputStream dos = new DataOutputStream(fos);
*/
DataOutputStream dos = new DataOutputStream(new FileOutputStream(new File("d:\\Demo2.txt")));
/*向外将变量写到文件中去*/
dos.writeUTF("你好");
dos.writeBoolean(false);
dos.writeDouble(6.9);
dos.writeInt(82);
/*关闭流*/
dos.close();
}
}
在Demo2.txt文件中,我们看到:
这个内容我们看不懂,是给程序看的。
2、利用DataInputStream开始读取的程序
public class Test02 {
/*这是一个main方法,是程序的入口*/
public static void main(String[] args) throws IOException {
/*DataInputStream:将文件中存储的基本数据类型和字符串写入内存的变量中*/
DataInputStream dis = new DataInputStream(new FileInputStream(new File("d:\\Demo2.txt")));
/*将文件中内容读取到程序中来*/
System.out.println(dis.readUTF());
System.out.println(dis.readBoolean());
System.out.println(dis.readDouble());
System.out.println(dis.readInt());
/*关闭流*/
dis.close();
}
}
结果:
验证:那个文件,我们看不懂,程序看得懂。
要求:写出的类型跟读入的类型必须要匹配!
七、try-with-resources【掌握】
JDK1.7开始增加了对需要关闭的资源管理处理的特殊语法try-with-resources。
try(资源变量=创建资源对象){
} catch(XXXException e){
}
其中资源对象需要实现AutoCloseable接口,例如:inputStream、outputStream、Connection、statement、Result等接口都实现了AutoCloseable,使用try-with-resources可以不写finally语句,编译器会帮助生成关闭资源代码,更推荐这种写法,代码简洁,可读性也更强。
try (FileInputStream fis = new FileInputStream(file)){
fis.read(fileContent);
} catch (IOException e) {
e.printStackTrace();
}
八、注意事项【掌握】
-
字符流不能处理非纯文本文件,字节流可以处理所有文件。纯文本文件就是记事本打开可以看明白的文件,例如txt、html、css、Java,非纯文本文件例如:MP3、MP4、jpg、png、doc等。
-
流用完一定要记得关闭,尤其是高级流,如果不关闭会出现问题,缓冲区的数据可能不会被处理到。flush方法可以处理缓存,close调用了它,我们常用close方法,彻底关闭不用的流。
-
处理纯文本文件建议用字符流,比较简单。