1.字节流
1.1 什么是字节流
计算机中都是二进制数据,一个字节是8个2进制位.字节可以表示所有的数据,比如文本,音频,视频.图片,都是作为字节存在的.也就是说字节流处理的数据非常多。
在文本文件中存储的数据是以我们能读懂的方式表示的。而在二进制文件中存储的数据是用二进制形式表示的。我们是读不懂二进制文件的,因为二进制文件是为了让程序来读取而设计的。例如,Java的源程序(.java源文件)存储在文本文件中,可以使用文本编辑器阅读,但是Java的类(字节码文件)存储在二进制文件中,可以被Java虚拟机阅读。二进制文件的优势在于它的处理效率比文本文件高。
我们已经知道File对象封装的是文件或者路径属性,但是不包含向(从)文件读(写)数据的方法。为了实现对文件的读和写操作需要学会正确的使用Java的IO创建对象。
字节流的抽象基类:
输入流:java.io.InputStream
输出流:java.io.OutputStream
特点:字节流的抽象基类派生出来的子类名称都是以其父类名作为子类名的后缀。如:FileInputStream, ByteArrayInputStream等。
说明:字节流处理的单元是一个字节,用于操作二进制文件(计算机中所有文件都是二进制文件)
1.2 InputStream:输入流
案例:读取”c:/a.txt”文件中的所有内容并在控制台显示出来。
注意:事先准备一个a.txt并放到c:/下,不要保存中文。
1. 使用read()方法实现。
2. 使用int read(byte[] b)方法实现。
写代码读取”c:/a.txt”文件中的所有的内容并在控制台显示出来
实现:查看api文档
InputStream 有read方法,一次读取一个字节,OutputStream的write方法一次写一个int。发现这两个类都是抽象类。意味着不能创建对象,那么需要找到具体的子类来使用。
实现;显示指定文件内容。
明确使用流,使用哪一类流?使用输入流,FileInputStream
1.2.1 流程
第一步:
1:打开流(即创建流)
第二步:
2:通过流读取内容
第三步:
3:用完后,关闭流资源
1.2.2 输入流读取方式一
private static void read() throws IOException{
String path = "E:\\我的学习\\java web\\代码\\IOProject\\Files\\1.txt";
File file = new File(path);
//创建一个文件字节流对象
FileInputStream inputStream = new FileInputStream(file);
int b ;
while((b = inputStream.read())!= -1){
System.out.println((char)b);
}
//关闭流
inputStream.close();
}
文件图
问题:读取乱码,因为一个汉子占两个字节,不能拿一个字节来读
1.2.3 输入流读取方式二
private static void read_v2() throws IOException{
String path = "E:\\我的学习\\java web\\代码\\IOProject\\Files\\1.txt";
File file = new File(path);
//创建一个文件字节流对象
FileInputStream inputStream = new FileInputStream(file);
//创建一个数据,存储文件数据
byte[] byteArray = new byte[(int)file.length()];
byte[] array = new byte[3];
inputStream.read(byteArray);
String str = new String(byteArray);
//String对中文会识别,对中文编码进行转换
System.out.println(str);
inputStream.close();
}
文件图:
1.3 OutputStream:输出流
1.3.1 流程:
1:打开文件输出流,流的目的地是指定的文件
2:通过流向文件写数据
3: 用完流后关闭流
1.3.2 输出流写出方式1
使用write(int b)方法,一次写出一个字节
private static void writeTextFile() throws IOException{
String path = "E:\\我的学习\\java web\\代码\\IOProject\\Files\\2.txt";
//1.打开文件输出流,流的目的地是指定的文件
FileOutputStream fos = new FileOutputStream(path);
//2.通过流文件写数据
fos.write('a');
fos.write('b');
fos.write('c');
fos.write('Z');
//3.关闭流
fos.close();
}
文件图:
当c盘下的a.txt不存在会怎么样?
测试:将c盘下的a.txt文件删除,发现当文件不存在时,会自动创建一个,但是创建不了多级目录。
注意:使用write(int b)方法,虽然接收的是int类型参数,但是write 的常规协定是:向输出流写入一个字节。要写入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。
1.3.3 输出流写出方式2
使用write(byte[] b),就是使用缓冲.提高效率.
上述案例中的使用了OutputStram 的write方法,一次只能写一个字节。成功的向文件中写入了内容。但是并不高效,如和提高效率呢?是否应该使用缓冲,根据字节输入流的缓冲原理,是否可以将数据保存中字节数组中。通过操作字节数组来提高效率。查找API文档,在OutputStram类中找到了write(byte[] b)方法,将 b.length 个字节从指定的 byte 数组写入此输出流中。
如何将字节数据保存在字节数组中,以字符串为例,”\n我叫苏振宇—-zhenyusu” 如何转为字节数组。显然通过字符串的getBytes方法即可。
private static void writeTextFile2() throws IOException{
String data = "\n我叫苏振宇----zhenyusu";
byte[] dataArray = data.getBytes();//字符串转化为字节
String path = "E:\\我的学习\\java web\\代码\\IOProject\\Files\\3.txt";
//1.创建输出流,指向指定的文件
FileOutputStream fos = new FileOutputStream(path);
//2.通过流文件写数据
fos.write(dataArray);
//3.关闭流
fos.close();
}
文件图
仔细查看txt文本文件发现上述程序每运行一次,老的内容就会被覆盖掉。那么如何不覆盖已有信息,能够往txt里追加信息呢。查看API文档,发现FileOutputStream类中的构造方法中有一个构造可以实现追加的功能FileOutputStream(File file, boolean append) 第二个参数,append - 如果为 true,则将字节写入文件末尾处,而不是写入文件开始处。
private static void writeTextFile3() throws IOException{
String data = "\n我叫苏振宇----zhenyusu";
byte[] dataArray = data.getBytes();//字符串转化为字节
String path = "E:\\我的学习\\java web\\代码\\IOProject\\Files\\4.txt";
//1.创建输出流,指向指定的文件
FileOutputStream fos = new FileOutputStream(path,true);
//2.通过流文件写数据
fos.write(dataArray);
//3.关闭流
fos.close();
}
文件图:
1.4 字节流文件拷贝
流程:
(1)从源文件读取,放入到读取流中,从读取流中读取到数组中。
(2)创建输出流到目的文件,将数组中的字节数据写入到文件中。
private static void copy(String src,String desc) throws IOException{
File file = new File(src);
//创建一个字节流对象
InputStream input = new FileInputStream(file);
byte[] arr = new byte[(int) (file.length())];
input.read(arr);
//关闭读取流
input.close();
//写
File descFile = new File(desc);
//文件不存在创建
if(!descFile.exists()){
descFile.createNewFile();
}
OutputStream fos = new FileOutputStream(descFile);
fos.write(arr);
//关闭写入流
fos.close();
}
2 字符流
2.1 Reader
方法:
1,int read():读取一个字符。返回的是读到的那个字符。如果读到流的末尾,返回-1.
2,int read(char[]):将读到的字符存入指定的数组中,返回的是读到的字符个数,也就是往数组里装的元素的个数。如果读到流的末尾,返回-1.
3,close():读取字符其实用的是window系统的功能,就希望使用完毕后,进行资源的释放由于Reader也是抽象类,所以想要使用字符输入流需要使用Reader的实现类。查看API文档。
找到了FileReader:
1,用于读取文本文件的流对象。
2,用于关联文本文件。
构造函数:在读取流对象初始化的时候,必须要指定一个被读取的文件。
如果该文件不存在会发生FileNotFoundException.
private static void read() throws IOException{
String path = "E:\\我的学习\\java web\\代码\\IOProject\\Files\\4.txt";
File file = new File(path);
Reader reader = new FileReader(file);
System.out.println(file.length());
char []array = new char[36];
reader.read(array);
for(char c:array){
System.out.print(c);
}
String str = new String(array);
System.out.println(str);
reader.close();
}
2.2 Writer
Writer中的常见的方法:
1,write(ch): 将一个字符写入到流中。
2,write(char[]): 将一个字符数组写入到流中。
3,write(String): 将一个字符串写入到流中。
4,flush():刷新流,将流中的数据刷新到目的地中,流还存在。
5,close():关闭资源:在关闭前会先调用flush(),刷新流中的数据去目的地。然流关闭。
发现基本方法和OutputStream 类似,有write方法,功能更多一些。可以接收字符串。
同样道理Writer是抽象类无法创建对象。查阅API文档,找到了Writer的子类FileWriter
private static void write() throws IOException{
String str = "我叫苏振宇-zhenyusu";
String path = "E:\\我的学习\\java web\\代码\\IOProject\\Files\\6.txt";
//创建流,指向目的文件
Writer writer = new FileWriter(path);
//写入数据
writer.write(str);
writer.close();
}
2.3字符流拷贝文件
private static void copyFile(String path1,String path2) throws IOException{
char []array = new char[1024];
int ch = -1;
// 读取path1的文件内容
Reader reader = new FileReader(path1);
//写入到path2的文件中去
Writer writer = new FileWriter(path2);
while((ch =reader.read(array)) != -1){
writer.write(array,0,ch);
}
//关闭流
reader.close();
writer.close();
}
字节流可以拷贝视频和音频等文件,那么字符流可以拷贝这些吗?经过验证拷贝图片是不行的。发现丢失了信息,为什么呢?
计算机中的所有信息都是以二进制形式进行的存储(1010)图片中的也都是二进制。在读取文件的时候字符流自动对这些二进制按照码表进行了编码处理,但是图片本来就是二进制文件,不需要进行编码。有一些巧合在码表中有对应,就可以处理,并不是所有的二进制都可以找到对应的。信息就会丢失。所以字符流只能拷贝以字符为单位的文本文件(以ASCII码为例是127个,并不是所有的二进制都可以找到对应的ASCII,有些对不上的,就会丢失信息。)
3 缓冲流
3.1 字节缓冲流
上述程序中我们为了提高流的使用效率,自定义了字节数组,作为缓冲区.Java其实提供了专门的字节流缓冲来提高效率.
BufferedInputStream和BufferedOutputStream
BufferedOutputStream和BufferedOutputStream类可以通过减少读写次数来提高输入和输出的速度。它们内部有一个缓冲区,用来提高处理效率。查看API文档,发现可以指定缓冲区的大小。其实内部也是封装了字节数组。没有指定缓冲区大小,默认的字节是8192。
显然缓冲区输入流和缓冲区输出流要配合使用。首先缓冲区输入流会将读取到的数据读入缓冲区,当缓冲区满时,或者调用flush方法,缓冲输出流会将数据写出。
注意:当然使用缓冲流来进行提高效率时,对于小文件可能看不到性能的提升。但是文件稍微大一些的话,就可以看到实质的性能提升了。
//字节缓冲流实现复制
private static void copy(String src,String desc) throws IOException{
File file = new File(src);
File descFile = new File(desc);
if(!descFile.exists()){
descFile.createNewFile();
}
//字节写出流
InputStream in = new FileInputStream(file);
//字节写入流
OutputStream out = new FileOutputStream(desc);
//写入缓冲流
BufferedOutputStream bufferOut = new BufferedOutputStream(out);
//写出缓冲流
BufferedInputStream bufferIn = new BufferedInputStream(in);
int b;
while((b = bufferIn.read()) != -1){
bufferOut.write(b);
}
bufferIn.close();
in.close();
bufferOut.close();
out.close();
}
3.2 字符缓冲流
public static void copy(String src,String desc) throws IOException{
File file = new File(src);
File descFile = new File(desc);
//字符流
Reader in = new FileReader(file);
Writer out = new FileWriter(descFile);
//字符缓冲流
BufferedReader bufferRead = new BufferedReader(in);
BufferedWriter bufferWrite = new BufferedWriter(out);
String line;
while((line = bufferRead.readLine())!=null){
bufferWrite.write(line);
//换行
bufferWrite.newLine();
}
bufferRead.close();
in.close();
bufferWrite.close();
out.close();
}
4 字节字符转换流
public static void test() throws IOException{
String name = "E:\\我的学习\\java web\\代码\\IOProject\\Files\\7.txt";
File file = new File(name);
FileInputStream in = new FileInputStream(file);
//字节流转化为字符流
InputStreamReader reader = new InputStreamReader(in);
//BufferedReader 对象来缓冲字符流
BufferedReader buffer = new BufferedReader(reader);
String line = null;
while((line = buffer.readLine())!=null){
System.out.println(line);
}
buffer.close();
in.close();
reader.close();
}