1 概述
2 计算机中一切均为字节
8个比特位就是1个字节,1个二进制位就是一个比特位。
在数据传输过程中,一切数据(文本、图像、声音等)最终存储的均为一个个字节,即8个二进制数字。所以数据传输过程中使用二进制数据可以完成任意数据的传递。
我们向一个文件中存储一定的数据(一些数字),如果使用文本方式打开,则会以文本的方式解释数据。如果以视频的方式打开,则会以视频的方式解释数据。音频、可行执行文件等亦是如此。所以,在文件传输过程中,我们要时刻明确,传输的始终为二进制数据。
3 字节流
3.1 字节输出流OutputStream
OutputStream是所有字节输出流的顶层父类。并且他是一个抽象类。
我们一般使用它的子类叫做FileOutputStream。
输出流用来写,可以将java程序中的数据写入到文件中。
构造方法:
FileOutputStream(File file): 传递一个File类型的文件路径。
FileOutputStream(String name): 传递一个字符串类型的文件路径。
其他方法:
void write(int b):向文件中写入一个字节。如果传递的是一个数字,最终写入进行的会是对应ASCII码表中的字符。
void write(byte[] b): 向文件中写入一个字节数组。
void write(byte[] b, int off, int len): 向文件中写入字节数组的一部分。b表示要写入的字节数组。 off表示要从这个位置开始写。 len写入的个数。
写入的具体步骤:
1. 创建字节输出流对象,绑定一个目的地,用来写入。
2. 调用write方法进行写入。
3. 释放资源。
注意:只有字符流写需要刷新,字节流写是不需要刷新的。
字符在java中占两个字节。但是如果这个字符是ASCII码表中的字符,那么这个字符在操作系统中是占一个字节的。
注意:在操作系统中,中文默认是占2个字节。所以不能使用一次写入一个字节的方式写入一个中文字符。
3.2 字符串与字节数组的互转
字符串->字节数组
byte[] getBytes(): 把字符串转成字节数组。
字节数组->字符串
String(byte[] bytes): 将字节数组转成字符串。
String(byte[] bytes, int offset, int length) :将字节数组的一部分转成字符串。bytes字节数组。 offset从那个位置开始转。 length个数。
gbk中文占2个字节。
utf中文占3个字节。
3.3 使用字节输出流向文件中写入字节数组
void write(byte[] b): 向文件中写入一个字节数组。
void write(byte[] b, int off, int len): 向文件中写入字节数组的一部分。b表示要写入的字节数组。 off表示要从这个位置开始写。 len写入的个数。
字节数组推荐通过字符串调用getBytes方法获取到,因为这样更加的简便。
写入中文,可以先把对应的中文字符串转成字节数组
3.4 IO流释放资源的方式
无论如何,一定要保证流最终释放掉。
把close的代码放入到finally,因为finally里面的代码无论如何都会执行到。
package cn.itcast.demo01.outputstream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
IO流的异常处理。
无论如何,一定要保证流最终释放掉。
把close的代码放入到finally,因为finally里面的代码无论如何都会执行到。
*/
public class Demo04OutputStreamException {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
//创建字节输出流对象
fos = new FileOutputStream("c.txt");
//调用方法写入
fos.write(100);//d
} catch(IOException e) {
e.printStackTrace();
} finally {
//释放资源
try {
if(fos != null) {
fos.close();
}
} catch(IOException e) {
e.printStackTrace();
}
}
}
}
3.5 多个流对象的异常处理
shift + alt + z 直接生成try…catch…
先定义个工具类
package cn.itcast.demo01.outputstream;
import java.io.Closeable;
import java.io.IOException;
/*
定义的工具类,里面的方法可以释放IO流对象。
所有的IO流都实现了一个接口,这个接口叫做Closeable
并且这个接口中有一个方法,可以释放资源。
void close()
*/
public class IOUtils {
public static void close(Closeable... cs) {
//遍历c
for(Closeable c : cs) {
//把每个c给释放掉
try {
if(c != null) {
c.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
关闭方法
package cn.itcast.demo01.outputstream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
多个流对象的异常处理。
shift + alt + z
*/
public class Demo05OutputStreamException {
public static void main(String[] args) {
FileOutputStream fos1 = null;
FileOutputStream fos2 = null;
try {
//创建两个流对象
fos1 = new FileOutputStream("d.txt");
fos2 = new FileOutputStream("e.txt");
//向这两个文件中写数据.
fos1.write(65);
fos2.write(66);
} catch (IOException e) {
e.printStackTrace();
} finally {
//释放资源
/*
try {
if(fos1 != null) {
fos1.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fos2 != null) {
fos2.close();
}
} catch (IOException e) {
e.printStackTrace();
}
*/
IOUtils.close(fos1, fos2);
}
}
}
3.6 追加写以及换行写
如果要写换行,需要使用换行符。
windows:\r\n
linux:\n
mac:\r
文件续写:
调用有两个参数的构造方法创建对象。
FileOutputStream(File file, boolean append):如果第二个参数是true表示续写。
FileOutputStream(String name, boolean append) :如果第二个参数是true表示续写。
3.7 字节输入流InputStream
InputStream 是所有字节输入流的顶层父类。这个类是一个抽象类。
一般使用它的子类FileInputStream。
这个类的作用是用来读取,可以将文件中的字节读取到java程序中。
构造方法:
FileInputStream(File file):传递一个File类型的文件名。如果要读取的文件不存在,就会报错。
FileInputStream(String name): 传递一个字符串类型的文件名。如果读取结束返回-1.
其他方法:
int read(): 一次读取一个字节。会将读取到的字节返回。如果读取结束,返回的是-1
使用这个read方法一次只能读取一个字节。如果读取中文,就会产生问题。因为中文占两个字节。
他会把这两个字节拆开读取并转换。
int read(byte[] b): 一次读取一个字节数组。 返回值为读取到的个数。如果读取结束返回-1.
字节流不建议操作中文,因为可能会出现各种奇妙的问题。
如果操作中文,要使用字符流。
使用FileInputStream去文件中读取字节。
采用一次读取一个字节的方式。
步骤:
1. 创建FileInputStream对象。
2. 调用read方法进行读取。
3. 释放资源。
3.8 一次读取一个字节的方式复制文件
package cn.itcast.demo02.inputstream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
字节流复制文件。
一次读取一个字节的方式。
文件复制其实就是一边读一边写。每读取到一个字节,就把读取到的字节写入到文件中。
思路:
1. 创建输入流FileInputStream,用来读取。
2. 创建输出流FileOutputStream, 用来写入
3. 开始读取,一次读取一个字节。
4. 每读取到一个字节,就把读取到的这个字节写入到目的地文件中。
5. 释放资源
*/
public class Demo01Copy {
public static void main(String[] args) throws IOException {
//创建输入流FileInputStream,用来读取。
FileInputStream fis = new FileInputStream("aa.jpg");
//创建输出流FileOutputStream,用来写入
FileOutputStream fos = new FileOutputStream("dest01.jpg");
//开始读取,一次读取一个字节。
int i;//保存每次读取到的字节
while((i = fis.read()) != -1) {
//每读取到一个字节,就把读取到的这个字节写入到目的地文件中。
fos.write(i);
}
//释放资源
fos.close();
fis.close();
}
}
3.9 一次读取一个字节数组的方式复制文件
package cn.itcast.demo02.inputstream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
一次读取一个字节数组的方式复制文件,效率高。
思路:
1. 创建FileInputStream用来读取。
2. 创建FileOutputStream用来写入。
3. 开始读取,一次读取一个字节数组。
4. 每读取一次字节数组,就把这个读取到的字节数组写入到文件中。
5. 释放资源。
*/
public class Demo02Copy {
public static void main(String[] args) throws IOException {
//创建FileInputStream用来读取。
FileInputStream fis = new FileInputStream("aa.jpg");
//创建FileOutputStream用来写入。
FileOutputStream fos = new FileOutputStream("dest02.jpg");
//开始复制
//开始读取,一次读取一个字节数组。
byte[] bArr = new byte[1024]; //定义字节数组用来保存读取到的字节
int len;//用来保存每次读取到的字节的个数。
//循环读取
while((len = fis.read(bArr)) != -1) {
//读取到的内容会放入到bArr这个字节数组中。
//每读取一次字节数组,就把这个读取到的字节数组写入到文件中。
fos.write(bArr, 0, len);//读取几个,就写几个
}
//释放资源
fos.close();
fis.close();
}
}
4 缓冲流
字节缓冲流根据流的方向,共有2个
写入数据到流中,字节缓冲输出流 BufferedOutputStream
读取流中的数据,字节缓冲输入流 BufferedInputStream
它们的内部都包含了一个缓冲区,通过缓冲区读写,就可以提高了IO流的读写速度
4.1 BufferedOutputStream字节输出缓冲流
BufferedOutputStream 字节输出缓冲流,用来进行写入。
特点:
效率高,因为内部有一个缓冲区。
构造方法
BufferedOutputStream(OutputStream out): 传递一个字节输出流。
BufferedOutputStream的使用
1. 创建一个FileOutputStream对象。
2. 创建BufferedOutputStream对象。
3. 调用方法,进行写入。
4. 释放资源
其实缓冲流本身并不具备读或者写的功能,他的作用其实是为其他流提供加速。
package cn.itcast.demo03.bufferedstream;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo01BufferedOutputStream {
public static void main(String[] args) throws IOException {
//创建一个FileOutputStream对象。
FileOutputStream fos = new FileOutputStream("h.txt");
//创建BufferedOutputStream对象。
BufferedOutputStream bos = new BufferedOutputStream(fos);//表示要对fos这个流进行加速。
//调用方法,进行写入。
bos.write("今天天气不错, 孔子东游".getBytes());
//释放资源。
bos.close();
}
}
4.2 BufferedInputStream 字节输入缓冲流
BufferedInputStream 字节输入缓冲流,可以读取数据。
内部有一个缓冲区,可以进行加速,所以他的效率比较高。
构造方法:
BufferedInputStream(InputStream in):传递一个字节输入流。可以传递子类FileInputStream。
使用步骤:
1. 创建一个FileInputStream对象
2. 创建BufferedInputStream,并且在构造方法中传递FileInputStream对象,表示要对这个流进行加速。
3. 调用方法,进行读取。
4. 释放资源。
package cn.itcast.demo03.bufferedstream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Demo02BufferedInputStream {
public static void main(String[] args) throws IOException {
//创建一个FileInputStream对象
FileInputStream fis = new FileInputStream("source03.txt");
//创建BufferedInputStream,并且在构造方法中传递FileInputStream对象,表示要对这个流进行加速。
BufferedInputStream bis = new BufferedInputStream(fis);//表示要对fis这个流进行加速。
//调用方法,进行读取。
//一次读取一个字节
/*
int i;
while((i = bis.read()) != -1) {
//把读取到的内容进行打印
System.out.print((char)i);
}
*/
//一次读取一个字节数组。
byte[] bArr = new byte[1024];
int len;
while((len = bis.read(bArr)) != -1) {
System.out.println(new String(bArr, 0, len));//打印
}
//释放资源
bis.close();
}
}
5 四种方式复制文件的效率比较。
四种方式复制文件,并比较效率。
使用基本字节流一次读取一个字节复制文件 7091ms
使用基本字节流一次读取一个字节数组复制文件 18ms
使用缓冲流一次读取一个字节复制文件 145ms
使用缓冲流一次读取一个字节数组复制文件 6ms
package cn.itcast.demo03.bufferedstream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo03Copy {
public static void main(String[] args) throws IOException {
//复制之前记录一下时间
long start = System.currentTimeMillis();
//开始复制
//copyFile1("aa.jpg", "copytest01.jpg");
//copyFile2("aa.jpg", "copytest02.jpg");
//copyFile3("aa.jpg", "copytest03.jpg");
copyFile4("aa.jpg", "copytest04.jpg");
//记录时间
long end = System.currentTimeMillis();
System.out.println(end - start);
}
/*
* 使用缓冲流一次读取一个字节数组
*/
public static void copyFile4(String source, String dest) throws IOException {
//创建BufferedInputStream,用来读取
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
//创建BufferedOutputStream,用来写入
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest));
//一次读取一个字节数组。
byte[] bArr = new byte[1024];
int len;
//循环读取
while((len = bis.read(bArr)) != -1) {
bos.write(bArr, 0, len);//读取几个,就写几个
}
//释放资源
bos.close();
bis.close();
}
/*
* 使用缓冲流一次读取一个字节复制文件
*/
public static void copyFile3(String source, String dest) throws IOException {
//创建BufferedInputStream,用来读取
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
//创建BufferedOutputStream,用来写入
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest));
//开始读取,每读取一个字节,就把读取到的字节写入到文件中
int i;
//一次读取一个字节
while((i = bis.read()) != -1) {
//写入到文件
bos.write(i);
}
//释放资源
bos.close();
bis.close();
}
/*
* 基本的字节流一次读取一个字节数组
*/
public static void copyFile2(String source, String dest) throws IOException {
FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(dest);
byte[] bArr = new byte[1024];
int len;
//循环读取
while((len = fis.read(bArr)) != -1) {
fos.write(bArr, 0, len);//读取几个,就写几个
}
//释放资源
fos.close();
fis.close();
}
/*
* 使用基本字节流一次读取一个字节复制文件
*/
public static void copyFile1(String source, String dest) throws IOException {
//创建字节输入流用来读取
FileInputStream fis = new FileInputStream(source);
//创建字节输出流用来写入
FileOutputStream fos = new FileOutputStream(dest);
//开始复制
int i;
//一次读取一个字节
while((i = fis.read()) != -1) {
//写入到文件
fos.write(i);
}
//释放资源
fos.close();
fis.close();
}
}
5 复制多级文件夹
复制文件夹,如果这个文件夹中还有子文件夹,也可以复制过去。
源文件夹:
d:\source
aa.txt
bb.doc
cc文件夹
目标文件夹:
d:\dest
要把source文件夹复制到dest下面,source文件夹下面的所有的东西也会过去。
复制之后source里面的所有的东西都会放到下面文件夹中。
结果路径:d:\dest\source
思路:
1. 定义方法,用来将源文件夹中所有的东西复制到目标文件夹。
2. 先根据源文件夹和目标文件夹拼接一个结果路径。
3. 创建这个结果路径对应的文件夹。
4. 拿到源文件夹中的所有文件和文件夹。然后遍历。
5. 如果遍历到的是一个文件夹。递归调用把当前遍历到的这个文件夹作为源复制到结果文件夹中
如果遍历到的是一个文件,就复制文件。(使用输入流输出流一边读一边写。)
package cn.itcast.demo04.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
复制文件夹,如果这个文件夹中还有子文件夹,也可以复制过去。
源文件夹:
d:\source
aa.txt
bb.doc
cc文件夹
目标文件夹:
d:\dest
要把source文件夹复制到dest下面,source文件夹下面的所有的东西也会过去。
复制之后source里面的所有的东西都会放到下面文件夹中。
结果路径:d:\dest\source
思路:
1. 定义方法,用来将源文件夹中所有的东西复制到目标文件夹。
2. 先根据源文件夹和目标文件夹拼接一个结果路径。
3. 创建这个结果路径对应的文件夹。
4. 拿到源文件夹中的所有文件和文件夹。然后遍历。
5. 如果遍历到的是一个文件夹。递归调用把当前遍历到的这个文件夹作为源复制到结果文件夹中
如果遍历到的是一个文件,就复制文件。(使用输入流输出流一边读一边写。)
*/
public class Demo01CopyTest {
public static void main(String[] args) throws IOException {
copyDirToDir(new File("d:\\source"), new File("d:\\dest"));
}
/*
* 定义方法,用来将源文件夹中所有的东西复制到目标文件夹。
*/
public static void copyDirToDir(File source, File dest) throws IOException {
// 先根据源文件夹和目标文件夹拼接一个结果路径。
//父路径是目标文件夹。 子路径是源文件夹的名字
File result = new File(dest, source.getName());
//创建这个结果路径对应的文件夹。
result.mkdirs();
//拿到源文件夹中的所有文件和文件夹
File[] files = source.listFiles();
//遍历
for(File thisFile : files) {
//thisFile表示的是源文件夹中的每一个文件或者文件夹。
//如果当前遍历到的是一个文件夹, 源文件夹就是当前遍历到的文件夹, 目的地文件夹就是结果文件夹result
if(thisFile.isDirectory()) {
copyDirToDir(thisFile, result);
} else {
//如果是一个文件,就要复制文件
//创建输入流
FileInputStream fis = new FileInputStream(thisFile);
//拼接一个最终复制过去文件的路径
File destFile = new File(result, thisFile.getName());
//创建一个输出流
FileOutputStream fos = new FileOutputStream(destFile);
//开始读写
//一次读取一个字节数组
byte[] bArr = new byte[1024];
int len;
while((len = fis.read(bArr)) != -1) {
fos.write(bArr, 0, len);
}
//释放资源
fos.close();
fis.close();
}
}
}
}