目录
File类
概述:
1.File:翻译是文件,用于表达java中的路径名。
2.路径:用于表示文件或者文件夹在当前系统中的位置
3.路径的分类:
绝对路径,没有任何的歧义,也没有任何的前提条件,Windows,从盘符开始的路径,例如"D:/a/b/c/d/a.txt"
相对路径,相对于某个文件的路径。
构造方法:
1.File(String path)
将一个字符串描述的路径,封装成一个 File对象。
2.File(String parent,String Child)
将两个字符串(父级路径, 子级路径),拼接之后形成的路径封装成一个File对象。
3.File(File parent, String child)
将File类型的父级路径和String类型的字节路径拼接成一个新路径,封装成File对象
注意:
创建好File对象后,只是封装了一个路径, 和磁盘上是否有这个路径无关。
创建功能
1、说明:
最终创建出来的是一个文件还是文件夹,不取决于路径名称
取决于调用的什么方法去创建
2、创建文件:
createNewFile()
3、创建文件夹:
mkdir()
创建文件夹,如果父级路径不存在,则文件夹创建失败
mkdirs()
创建文件夹,如果父级路径不存在,则自动创建父级路径,再创建子级路径
练习:在D盘下的a/b/c/d文件夹中创建一个HelloWorld.txt。
package File类;
import java.io.File;
import java.io.IOException;
public class Test01 {
public static void main(String[] args) throws IOException {
File f = new File("D:/a/b/c/d");
f.mkdirs();
File ff = new File(f,"HelloWorld.txt");
ff.createNewFile();
}
}
删除:
1、方法名称:
delete()
2、既可以删除文件,也可以删除文件夹
3、注意事项:
1、方法的调用者,是一个File对象,方法的参数是另外一个File对象
2、调用者是当前修改之前的路径对象,参数是要修改为的路径对象
3、如果改了父级路径,就是剪切,如果不改父级路径就是重命名
示例代码
import java.io.File;
public class Demo05_File的重命名功能 {
public static void main(String[] args) {
File ori = new File("x/a.txt");
File dest = new File("a/b/c/a.txt");
ori.renameTo(dest);
File f = new File("a/b/c/f.txt");
dest.renameTo(f);
}
}
判断功能
1、exists(),判断调用者路径是否存在
2、isFile(),判断调用者是否是一个文件
3、isDirectory(),判断调用者是否是一个文件夹
示例代码:
import java.io.File;
public class Demo06_File的判断功能 {
public static void main(String[] args) {
File a = new File("a");
System.out.println(a.exists());
System.out.println(a.isFile());
System.out.println(a.isDirectory());
}
}
获取功能
1、获取路径三个方法:
getName()
获取最短的那个文件或者文件夹名称
getPath()
获取相对路径,构造方法中传入的那个字符串
getAbsolutePath()
获取绝对路径
2、获取文件或者文件夹属性的方法
length()
获取文件的字节个数
只能针对文件使用,不能针对文件夹使用
lastModified()
获取文件的最后修改时间
3、获取文件夹中内容的方法
String[] list()
返回文件夹中的所有内容的名称(不包含子文件夹中的内容)
File[] listFiles()
返回文件夹中的所有内容的File对象(不包含子文件夹中的内容)
注意事项:
这两个方法只能针对文件夹使用,不能针对文件使用
练习:定义一个方法:键盘录入一个字符串,表示一个文件夹路径,如果不是文件夹路径则提示重新录入
package File类;
import java.io.File;
import java.util.Scanner;
public class Test02 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (true){
String path = sc.next();
File f = new File(path);
if (f.isDirectory()&&f.exists()){
System.out.println("录入成功");
break;
}else {
System.out.println("请重新录入");
}
}
}
}
IO流
介绍
1、IO:I/O,Input/Output,输入/输出
这是根据流向来进行描述的方式,究竟是输入还是输出,站在内存的角度看待问题。所有从其他设备到内存的过程,称为“输入”;所有从内存到其他设备的过程,称为“输出”。
2、java中操作输入输出使用的都是流对象,都是在io包中的类。
IO分类
1、IO流有非常多,非常复杂庞大的一个体系,需要对这些类型进行分类,分类方式有两种:按照流向分类;按照能处理的数据的类型分类
2、按照流向:
输入流
输出流
3、按照操作数据:
字节流:可以直接操作字节数据(byte数据)的流对象
字符流:可以直接操作字符数据(char数据)的流对象
4、四种顶层抽象父类:
字节流:
字节输入流:InputStream
字节输出流:OutputStream
字符流:
字符输入流:Reader
字符输出流:Writer
字节流
概述
1、可以直接处理字节信息的流对象
2、计算机中一切数据都是字节数据
无论是文字、音频、图片、视频、网页等内容,底层都是字节数据
3、字节流可以操作计算机中一切数据
4、所有其他流对象,底层都需要依赖字节流
5、顶层抽象父类:
InputStream
OutputStream
6、顶层父类是抽象类
将来会根据不同的交互设备,有不同的实现类型
InputStream
1、是字节输入流的顶层抽象父类,定义了字节输入流应该具有的功能
2、常用方法:
int read()
返回一个字节
int read(byte[] arr)
将读取到的数据,存储在arr数组中
返回的数据表示本次读取到的数据的个数
available()
从该流中,还能读多少字节出来
close()
关闭流对象
FileInputStream
1、InputStream是抽象类,不能直接创建对象;根据不同的设备,有不同的具体子类
2、其中用于和磁盘交互的字节输入流:FileInputStream
3、常用构造方法:
FileInputStream(File f)
FileInputStream(String pathname)
将File对象或者String描述的文件路径,封装成一个输入流对象
4、常用成员方法:
使用父类继承而来的即可
示例代码:
ackage File类;
import java.io.FileInputStream;
import java.io.IOException;
public class Demo1_字节流的输入流 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E:/T/b.txt");//定义一个输入字节流。
int len ;
while ((len = fis.read()) != -1){
System.out.print((char) len);
}
fis.close();
}
}
为什么读取的一个字节返回值是int
1、需要有一个读取到末尾的标记,通知我们结束循环
2、如果返回值类型是byte,范围就是-128~127,如果返回值是-1,那么表示的就是一个正常读取到的数字
3、读取到的字节,前面加上24个0,成为32位的int类型,就可以保证这些int全都是正数,如果返回值是负数,那就一定不是从文件中读取到的字节,可以使用负数作为文件结束的标记。
补码形式表示数据
-1
10000001
11111110
11111111(补码)
255
00000000 00000000 00000000 11111111
10000000 00000000 00000000 00000001 肯定不是从文件中读取的数据
InputStream中的另外两个方法
1、int read(byte[] arr)
2、available()
代码示例:
package File类;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
public class Demo2_Fis的另外两个方法 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E:/T/b.txt");
System.out.println( fis.available());
byte[] b = new byte[5];
int n = fis.read(b);
System.out.println(n);
System.out.println(Arrays.toString(b));
}
}
OutputStream
1、是字节输出流的顶层抽象父类,至少应该有一个写出一个字节的方法
2、常用方法:
write(int b)
将一个字节存储到目标文件中
write(byte[] arr)
将参数指定的字节数组中的全部字节,写出到目标文件中
write(byte[] arr, int offset, int len)
将参数指定的字节数组的一部分,写出到目标文件中
从offset索引开始,一共len个字节
close()
关流
FileOutputStream
1、OutputStream是一个抽象的父类,不能创建对象
2、根据不同的输出设备,有不同的具体子类,写出到文件系统的文件的流对象:
FileOutputStream
3、构造方法:
FileOutputStream(String path)
FileOutputStream(File f)
创建一个输出到指定文件中的字节输出流,如果指定文件存在,则清空
FileOutputStream(String path, boolean append)
FileOutputStream(File f, boolean append)
创建一个输出到指定文件中的字节输出流,如果指定文件存在,append值为true, 则在末尾追加
4、对于文件内容的解释
1、字节流中的write方法,将字节数据进行输出,没有编码没有解码,直接将字节写出到磁盘中。
2、使用文本编辑器打开一个文件:先读取字节信息;再将字节信息解码形成字符
代码示例:
package File类;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo3_FileOutPutStream {
public static void main(String[] args) throws IOException {
File f = new File("E:/c.txt");
f.createNewFile();
FileOutputStream fos = new FileOutputStream(f,true);
fos.write('a');
fos.write('b');
fos.write('c');
fos.write('d');
fos.close();
}
}
FileOutputStream的另外两个方法
1、write(byte[] arr)
2、write(byte[] arr, int offset, int len)
将字节数组的一部分写出到目标文件中
注意,第二个方法是为了使用数组写入的时候出现数组为清空导致录入不应当录入的数据。
代码示例:
package File类;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo4_OutputStream的另外两个方法 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("e:/c.txt",true);
byte[] b = {'a','b','?',2};
fos.write(b);
fos.write(b,1,2);
fos.close();
}
}
文件拷贝
1、本质:
将源文件中的一个字节,使用字节输入流读取到内存中
将内存中的一个字节,使用字节输出流,写出到目标文件中
2、操作:
1、准备一个字节输入流,关联源文件
2、准备一个字节输出流,关联目标文件
3、定义循环,读取源文件,每次读取的字节,写出到目标文件去
代码示例:
package File类;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo5_文件拷贝 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E:/c.txt");
FileOutputStream fos = new FileOutputStream("E:/copy_c.txt");
int n ;
while ((n = fis.read()) != -1){//如果文件中还存在就写到n,如果不存在就返回-1
fos.write(n);//把n写入
}
fis.close();
fos.close();
}
}
文件拷贝效率提升
1、一个字节一个字节的拷贝,速度太慢
大量的时间浪费在寻找字节上
2、提升效率的思路:
一次读取写出多个字节
一次读取多个:读取到一个较大的数组中
一次写出多个:将数组中的内容一次性写出到目标文件去
3、一次性拷贝:
1、好处:效率高
2、坏处:占用内存太多,磁盘中的文件非常大,无法申请相同大小的数组
4、使用小数组:
1、准备一个大小为1024正数倍的数组
2、循环读取源文件到数组中,将本次读取到的内容写出到目标文件去
代码示例
先用一个一个读取的字节流试试读取的时间:
目标文件信息如下:
再在刚才的代码中添加个计时器
package File类;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo5_文件拷贝 {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();//计时器
FileInputStream fis = new FileInputStream("E:/T/a.wmv");
FileOutputStream fos = new FileOutputStream("E:/copy_a.wmv");
int n ;
while ((n = fis.read()) != -1){//如果文件中还存在就写到n,如果不存在就返回-1
fos.write(n);//把n写入
}
fis.close();
fos.close();
long end = System.currentTimeMillis();
System.out.println(end - start);//输出结果
}
}
结果如下:
现在我们再试试准备一个很大很大的数组,把里面的信息都存进去然后一次性写出去,也就是说一次读,一次写。
package File类;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo6_大数组文件拷贝 {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();//计时器
FileInputStream fis = new FileInputStream("E:/T/a.wmv");
FileOutputStream fos = new FileOutputStream("E:/copy_a.wmv");
byte[] b = new byte[fis.available()];//准备的数组大小刚好等于文件长度。
fis.read(b);//一次读
fos.write(b);//一次写
fis.close();
fos.close();
long end = System.currentTimeMillis();
System.out.println(end - start);//输出结果
}
}
结果只有38秒,对比之前单个字节的读写快的夸张(也可能是我上次跑的出问题了)。
但是数组的长度和文件的大小相等是效率最快的方法吗?
下面来做个测试,准备一个100MB以上的文件。
先来测试一次读写代码同上,就不再粘出来了。
结果1:
这次来测试第二种方式,数组长度为1024的整数倍。
以上是修改的代码。
结果如下:
比一次存取快了不少,所以最合适的方法还是使用长度为1024的正整数倍的小数组来读写。
高效缓冲流
1、BufferedInputStream和BufferedOutputStream
2、是包装类,本身自己不能作为单独的字节流读写字节,用于加强基础的字节流对象,加强之后,读写的效率就会提升
3、身份:
无论是被加强的对象,还是当前用于加强的高效缓冲流对象,都是顶层抽象父类的具体子类对象
BufferedInputStream和FileInputStream,都是InputStream的子类
BufferedOutputStream和FileOutputStream,都是OutputStream的子类
4、使用:
BufferedInputStream(InputStream is)
将一个普通的字节输入流,加强为一个高效字节输入流
BufferedOutputStream(OutputStream os)
将一个普通的字节输出流,加强为一个高效字节输出流
代码示例:
package File类;
import java.io.*;
public class Demo8_高效缓冲流 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E:/T/a.wmv");
FileOutputStream fos = new FileOutputStream("E:/copy_bos.wmv");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int c ;
while ((c = bis.read()) != -1){
bos.write(c);
}
bis.close();
bos.close();
}
}
高效原因
1、BufferedInputStream
当调用read方法时,不是只读取了一个字节,而是一次读取了8192个字节,存储在流对象自己内部维护的缓冲区数组中,拿出了其中第一个字节返回给调用者;如果第二次再调用read方法,不需要再次去磁盘中读取字节信息了,只需要从内存的数组中取出第二个字节返回即可,减少了和磁盘交互次数8191次,提升了效率
如果数组中的数据全被读完,8193次读取时,就再次到磁盘中读取下一个8192个字节
2、BufferedOutputStream
当调用write方法时,不是直接将一个字节写出到磁盘,将该字节写出到BufferedOutputStream自己对象维护的一个私有字节数组中,数组的大小为8192字节,等到数组被写满,自动将数组中的数据一次性刷新到目标文件去,减少了和磁盘交互的次数,提升了效率。
流的刷新和关闭
1、刷新:
flush()
针对有缓冲区数组的,输出流,缓冲区中的内容,写出到其他设备中
2、关流:
close()
将流对象关闭,释放资源
3、关系:
1、在带缓冲的输出流的close方法中,包含一个flush
2、close方法执行之后,流对象就不能继续使用了;flush方法执行之后,流对象可以继续使用
3、不要在循环中频繁使用刷新,否则就丧失了缓冲区的意义
字符流
1、使用字节流写出字符
可以完成
比较麻烦,因为需要我们自己,转成字节数组
2、使用字节流读取字符
1、每次读取一个字节,无法读取到完整的字符信息
2、每次读取2个字节到一个数组中,可能碰到英文,多读取字节信息,造成后面汉字的混乱
问题总结:不知道每次读取几个字节,才是一个完整的字符信息
3、解决方式:
1、将字节流作为一个私有的成员变量,定义在某个类型中
2、如果需要写出字符,则由当前类型先在内部转成字节数组,再让私有字节流写出到目标文件
3、该类型中,可以定义一个读取完整字符的方法:根据中文、英文的字节特点来使用私有的字节流动态决定每次读取几个字节(在gbk编码表中,英文占一个字节,全都是正数,中文占2个字节,并且其中的第一个字节全是负数),可以读取到一个完整字符的全部字节信息,再该类型的内部进行解码形成字符,将字符返回给调用者
字符流概述
1、用于直接操作(读写)字符的流对象
2、分类:
字符输入流
Reader
字符输出流
Writer
Reader
1、字符输出流的顶层抽象父类
2、常用方法:
int read()
读取一个完整的字符,如果返回-1表示到达文件末尾
int read(char[] chs)
读取一系列字符到一个字符数组中
close()
关闭流对象
FileReader
1、Reader是一个顶层抽象父类,根据不同的读取设备,有不同的具体子类,其中读取文件系统的具体子类:FileReader
2、构造方法:
FileReader(String path)
FileReader(File f)
将路径封装成字符文件输入流对象
示例代码:
package File类;
import java.io.FileReader;
import java.io.IOException;
public class Demo9_字符流输入 {
public static void main(String[] args) throws IOException {
FileReader fr =new FileReader("E:\\程序\\8.17笔记总结\\src\\File类\\a.txt");
int c;
while ((c = fr.read()) != -1){
System.out.print((char) c);
}
}
}
Writer
1、字符输出流的顶层抽象父类
2、常用功能:
writer(int c)
将一个字符写出到目标文件
write(char[] chs)
将一个字符数组写出到目标文件
write(char[] chs, int offset, int len)
将一个字符数组中的从offset开始一共len个字符写出到目标文件
write(String str)
写出一个字符串
write(String str, int offset, int len)
将字符串的一部分写出
flush()
刷新缓冲区
close()
关闭流对象
FileWriter
1、Writer是一个顶层的抽象父类,根据输出设备不同,有不同的具体子类,输出到文件系统的,就是用FileWriter
2、构造方法:
FileWriter(String path)
FileWrtier(File f)
将一个路径封装成字符输出流
如果文件存在,则先清空
FileWriter(String path, boolean append)
FileWriter(File f, boolean append)
将一个路径封装成字符输出流
如果第二个参数为true,则在文件存在的情况下,可以追加
代码示例:
import java.io.FileWriter;
import java.io.IOException;
public class Demo03_FileWriter {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("a.txt");
fw.write("你好你好");
fw.write('a');
fw.close();
}
}
字符流的拷贝
1、使用字符输入流读取数据,写出到目标文件中
2、必要性:
不需要使用字符流拷贝 读取数据的过程中,先将字节信息读取到内存,将字节信息转成了字符信息 在写出数据的过程中,先把字符信息转成字节信息,再写出到目标文件中 在内存中,先把字节信息转成了字符信息,再将字符信息转成了字节信息,这个转换浪费资源,并且容易出错
4、字符流的使用场景
1、如果单纯读取一些纯文本文件时,需要使用字符流
2、如果单纯将一些字符信息写出到目标文件时,也需要使用字符流
3、既需要读取,也需要写出,在读取和写出之间,还要对字符信息做编辑或者处理,就需要使用字符 流读取和写出(如果需要对字符操作,就使用字符流)
字符流是否可以拷贝非纯文本文件
1、答案:
不能拷贝
2、原因:
读取非纯文本文件时,读取到字节信息,字符流要对字节信息进行解码,由于是非纯文本文件,所以读取的字节可能无法找到任何对应的字符,只能使用英文的?来代替这个字节信息(这一步就对源文件的字节信息篡改),输出过程中,把?转成一个字节,写出到目标文件中,写出的字节已经和当初读取的字节不是同一个字节了。字符流拷贝纯文本文件,读取的每个字节,都会有对应的字符信息,所以不需要使用其他字符代替,不会对信息做篡改
字符流拷贝效率提升
1、使用字符类型的小数组,分批拷贝源文件的内容
2、使用到的方法:
Reader中的
int read(char[] arr)
Writer中的
write(char[] arr, int offset, int len)
代码示例:
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Demo06_字符流小数组拷贝 {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("a_copy2.txt");
char[] chs = new char[1024 * 8];
int len;
while ((len = fr.read(chs)) != -1) {
fw.write(chs, 0, len);
}
fr.close();
fw.close();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
使用带缓冲的字符流
1、BufferedReader和BufferedWriter
2、介绍:
也是包装类,将没有缓冲功能的普通字符流,包装加强之后形成高效的缓冲字符流包装类也是顶层抽象父类的子类
3、构造方法:
BufferedReader(Reader r)
将一个普通的Reader加强为BufferedReader
BufferedWriter(Writer w)
将一个普通的Writer加强为BufferedWriter
4、使用:
仍然使用的是顶层抽象父类Reader和Writer中的方法
代码示例:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Demo07_带缓冲的字符流 {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("a_copy3.txt"));
int c;
while ((c = br.read()) != -1) {
bw.write(c);
}
br.close();
bw.close();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
带缓冲的字符流特有方法
1、BufferedReader中:
readLine()
可以从源文件中一次读取一行
2、BufferedWriter中:
newLine()
跨平台的换行符
转换流
编码表
1、GBK编码表
只定义了英文和中文的各种字符和数字的对应关系,最多使用2个字节就可以表示一个字符。
英文使用1个字节表示一个字符,中文使用2个字节表示1个字符
2、UTF-8编码表
定义了全世界所有语言的符号和数字的对应关系,最多使用3个字节就可以表示一个字符。
英文使用1个字节表示一个字符,中文使用3个字节表示1个字符
转换流
1、使用字节流和字符流拷贝不同编码的文件的问题:
1、目标文件将来的解码方式是UTF-8,现在却使用GBK的编码结果,写出到了目标文件,写出时的编码形式(GBK),和将来读取的解码格式(UTF-8)不同
2、希望写出时使用的编码表和将来读取后解码使用的编码表相同,只能使用能指定编码形式的“转换流”
2、转换流:仍然是字符流,只不过是能通过指定的编码表在字节和字符之间转换
InputStreamReader
是字节流到字符流的转换
OutputStreamWriter
是字符流到字节流的转换
3、InputStreamReader:
InputStreamReader(InputStream is, String charSetName)
is:用于读取磁盘字节信息的字节流
charSetName:用于解码读取到的字节信息使用的编码表名字
4、OutputStreamWriter
OutputStreamWriter(OutputStream os, String charSetName)
os:用于将编码之后的字节信息写出到目标文件去
charSetName:用于解码当前流对象要写出的字符,使用的编码表
示例代码:
package 转换流;
import java.io.*;
public class GbktoUtf {
public static void main(String[] args) throws IOException {
File f =new File("E:/Times.txt");
InputStreamReader isr = new InputStreamReader(new FileInputStream(f),"Gbk");
OutputStreamWriter osr = new OutputStreamWriter(new FileOutputStream("y.txt"),"utf-8");
int n;
while ((n = isr.read()) != -1){
osr.write(n);
}
isr.close();
osr.close();
}
}
这个就是把GBK编码形式的文本文档转换成了UTF-8形式。
示例代码2
package 转换流;
import java.io.*;
public class UtftoGbk {
public static void main(String[] args) throws IOException {
File f = new File("y.txt");
InputStreamReader isr = new InputStreamReader(new FileInputStream(f),"utf-8");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("x.txt"),"gbk");
int n;
while ((n = isr.read()) != -1){
osw.write(n);
}
isr.close();
osw.close();
}
}
这个是UTF-8转为GBK
内存输出流
1、ByteArrayOutputStream
1、是OutputStream的子类,可以直接使用字节输出流的全部方法
2、本身是内存中的流,不算真正意义上的I/O
3、本质:用于缓存不完整字节信息的一个缓冲区
4、底层:就是一个字节数组,只不过提供了自动增长空间的方法
2、方法:
toString():将底层缓冲区中的字节解码返回字符串
toByteArray():返回底层缓冲区数组中,有效的那部分字节
代码示例:
package 内存输出流;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws IOException {
File f = new File("y.txt");
FileInputStream fis = new FileInputStream(f);
ByteOutputStream bos = new ByteOutputStream();
byte[] b = new byte[3];
int len;
while ((len = fis.read(b)) != -1){
bos.write(b,0,len);
}
System.out.println(bos.toString());
System.out.println(Arrays.toString(bos.toByteArray()));
}
}
对象操作流
概述
1、就是可以直接写出和读取对象的流
2、分类:
对象输出流:ObjectOutputStream
对象输入流:ObjectInputStream
对象输出流
1、ObjectOutputStream
2、使用方式:
1、是OutputStream的子类,本身也是字节输出流
2、是可以直接将一个内存中的对象,写出到目标文件的输出流
3、构造方法:
ObjectOutputStream(OutputStream os)
将一个没有对象输出能力的os,加强为具有一次写出一个对象的功能
4、常用方法:
writeObject(Object obj)
3、注意事项:
1、在将对象写出到目标文件时,写出的对象所属的类型必须实现java.io.Serializable接口
2、写出对象时既没有编码也没有解码(在内存中,对象就是二进制的字节信息,写出到目标文件之后,仍然是二进制的字节信息),而使用文本编辑器打开文件时,强行解码,所以出现了乱码
3、将来能使用对象输入流将文件中的对象重新读取到内存中即可,不需要看懂
对象输入流
1、ObjectInputStream
2、使用方式:
1、是InputStream的子类
2、可以将文件中的一个完整对象,读取到内存中
3、构造方法:
ObjectInputStream(InputStream is)
将一个普通的字节流,包装成具有一次读取一个对象的流
4、常用方法:
Object readObject()
代码示例:
package 对象操作流;
import java.io.*;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
File f = new File("Person.txt");
Person p = new Person("xiaoming",12);
Person p2 = new Person("xiaohong",13);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f));//对象输出流
oos.writeObject(p);//分别将两个对象写入文件
oos.writeObject(p2);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f));//对象输入流
Object o = ois.readObject();//读取对象,读取为Object类型
System.out.println(o.toString());//因为子类重写了toString方法
o = ois.readObject();
System.out.println(o.toString());
ois.close();
}
}
注意事项
1、概念:
序列化:对象,从内存到其他设备的过程
反序列化:对象,从其他设备到内存的过程
2、条件:
无论序列化和反序列化,都需要实现Serializable接口
3、Serializable接口:
1、是一个标记性接口,没有任何需要实现的抽象方法
2、标记性接口:一旦实现某个接口,就相当于属于了这个类型
4、写出的对象和读取对象数量不一致:
1、EOFException:End Of File Exception
到达了文件的末尾异常
2、出现的原因:文件中已经没有对象了,但是仍然在读取
3、避免:写出对象时,就不要零散的写出,而是将所有要序列化的对象存储在一个集合中,将集合写出;将来读取时只需要读取一个集合,在内存中遍历集合
5、有关序列化的版本ID
1、用于描述流中的对象类型,是否和本地新类型相融的一个标记
2、如果序列化版本ID,在流中和本地不一致,就会报出无效的类型异常
3、以后的操作:
在定义任何一个序列化类型时,手动指定该类型的序列化版本id,将来无论是否修 改了这个类型,只要不变化序列化版本号,就表示类型仍然没变
代码:private static final long serialVersionUID = 1L;
代码示例:
首先是Person类
package 对象操作流;
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 7188899827739954982L;
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
然后是序列化:
package 对象操作流;
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
public class Test2_集合序列化 {
public static void main(String[] args) throws IOException {
ArrayList<Person> al = new ArrayList<>();
Collections.addAll(al, new Person("xiaoming",10),new Person("xiaohong",20));
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("pers.txt")));
oos.writeObject(al);
oos.close();
}
}
接着是反序列化:
package 对象操作流;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
public class Test2_集合反序列化 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("pers.txt")));
Object o = ois.readObject();
ArrayList<Person> al = (ArrayList<Person>) o ;
ois.close();
System.out.println(al.get(0).toString());
System.out.println(al.get(1).toString());
//System.out.println(al.get(0).getName());
}
}
随机读写流
1、RandomAccessFile
2、特点:
1、不属于流的体系
2、不仅可以读,还可以写
3、支持随机读写
3、构造方法:
RandomAccessFile(String path, String mode)
mode:访问模式(r:只读;rw:读写)
4、成员方法:
seek(long position)
随机的设定要访问的文件位置
代码示例:
package 随机访问流;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class Demo2_随机访问流 {
public static void main(String[] args) throws IOException {
File f = new File("y.txt");
RandomAccessFile rf = new RandomAccessFile(f,"rw");
rf.seek(2);
int n = rf.read();
System.out.println((char) n);
rf.seek(20);
rf.write('A');
rf.close();
}
}
打印流
概述
1、分类:
打印字节流
打印字符流
2、打印字节流:
PrintStream
3、打印字符流:
PrintWriter
4、说明:
虽然分成了字节流和字符流,但是并不关注字节流还是字符流
更多关注的是打印流中,定义的print和println方法
对于这两个方法,在这两个类中,没有什么功能上的区别
打印字节流
1、PrintStream
2、是OutputStream的子类,具有普通的字节输出流中的全部方法,并不关注继承而来的这些输出字节的方法,关注的是PrintStream中的print和println的方法
3、构造方法:
1、获取PrintStream:System.out,默认关联到控制台上
2、PrintStream(String pathname):关联一个指定的文件进行输出
4、常用方法:
print方法和println方法,可以将一切数据类型进行输出,在输出之前,先获取该数据的字符串表示形式
代码示例:
import java.io.FileNotFoundException;
import java.io.PrintStream;
public class Demo08_打印字节流 {
public static void main(String[] args) throws FileNotFoundException {
PrintStream ps = System.out;
ps.println(97);
PrintStream ps2 = new PrintStream("c.txt");
ps2.write(97);
ps2.println(97);
ps2.close();
}
}
打印字符流
1、PrintWriter
2、是Writer的子类,具有普通的字符输出流的全部方法,并不关注继承而来的这些输出字符的方法,关注的是PrintWriter中的print和println这两个特有方法
3、常用方法:
print和println方法,和PrintStream中的方法作用一样
4、和PrintStream的不同点:
PrintStream是字节流,没有缓冲区,PrintWriter是Writer的子类,因此具有缓冲区,有了刷新的操作;
PrintWriter具有自动刷新的功能:
1、使用具有刷新功能的构造方法:
PrintWriter(OutputStream os, boolean autoFlush)
PrintWriter(Writer w, boolean autoFlush)
可以将一个普通的字节输出流或者字符输出流,加强为一个具有自动刷新 功能的打印流对象(第二个参数必须为:true)
2、使用带有自动刷新的写出方法
Println
代码示例:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
public class Demo09_打印字符流 {
public static void main(String[] args) throws FileNotFoundException {
PrintWriter pw = new PrintWriter(new FileOutputStream("d.txt"), true);
pw.write("abc");
pw.print("def");
pw.println("xyz");
}
}
本章总结
1.File类用与访问文件或目录的属性。
2.流是指一连串流动的字符,是以先进先出的方式发送信息的通道。程序和数据源之间是通过流联系起来的。
3.流可以分为输入流和输出流,也可以分为字节流和字符流。
4.FileInputStream和FileOutputStream以字节流的方式读写文本文件。
5.BufferedReader和BufferedWriter以字符流的方式读写文本文件,而且效率更高。
6.DataInputStream和DataOutputStream可用于读写二进制文件。
File文件操作
操作的是文件的身体
创建 删除 判断是否存在 判断是否文件夹。。。
File f = new File("路径");
f.createNewFile();
if(f.exists()){
f.delete();
}
流操作的是文件的内容
流按方向分:
输入流
字节流 会把内容拆成字节
字符流 会把内容当成字符 包装流BufferedReader //字符输入流
二进制流 会把内容拆成01
输出流
字节流
字符流 包装流BufferedWriter //输出流
二进制流
练习小作业:
1.操作文件或目录属性
实现查看文件属性,创建和删除文件。
2.复制文本文件到指定盘符
3.替换文本文件内容
4.复制图片到指定盘符