字节流
字节流 文件的写入方式 FileInputStream
read()一次读取一个字节
public static void throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("aaa.txt"); //创建一个文件输入流对象,并关联aaa.txt
int b; //定义变量,记录每次读到的字节
while((b = fis.read()) != -1) { //将每次读到的字节赋值给b并判断是否是-1
System.out.println(b); //打印每一个字节
}
fis.close(); //关闭流释放资源
}
输出结果:aaa.txt内容为 abc
console输出:97 98 99
read()方法读取的是一个字节,为什么返回是int,而不是byte
int为32位 byte为8位
因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制形式的存储的,如果每次读取都返回byte,有可能在读到中间的时候遇到111111111那么这11111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果11111111会在其前面补上24个0凑足4个字节,那么byte类型的-1就变成int类型的255了这样可以保证整个数据读完,而结束标记的-1就是int类型.
在write的时候,会自动去掉前面的24个0。
字节流 文件的输出方式 FileOutputStream
write()一次写出一个字节
public static void throws FileNotFoundException, IOException {
FileOutputStream fos = new FileOutputStream("yyy.txt"); //创建字节输出流对象,如果没有就自动创建一个,如果有这个文件就会先将文件清空
fos.write(97); //虽然写出的是一个int数,但是到文件上的是一个字节,会自动去除前三个8位
fos.write(98);
fos.write(99);
fos.write(100);
fos.close();
}
FileOutputStream续写
如果我们想在一个文件中添加字节怎么办?
FileOutputStream fos = new FileOutputStream("yyy.txt",true); //如果想续写就在第二个参数传true,否则源文件会被覆盖
fos.write(97);
文件拷贝
拷贝图片
public static void throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("1.jpg"); //创建输入流对象,关联1.jpg
FileOutputStream fos = new FileOutputStream("copy.jpg"); //创建输出流对象,关联copy.jpg
int b;
while((b = fis.read()) != -1) { //在不断的读取每一个字节
fos.write(b); //将每一个字节写出
}
fis.close(); //关流释放资源
fos.close();
}
拷贝mp3
public static void demo2() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("致青春.mp3"); //创建输入流对象,关联致青春.mp3
FileOutputStream fos = new FileOutputStream("copy.mp3"); //创建输出流对象,关联copy.mp3
int b;
while((b = fis.read()) != -1) { //在不断的读取每一个字节
fos.write(b); //将每一个字节写出
}
fis.close(); //关流释放资源
fos.close();
}
字节流一次读写一个字节复制音频,这样效率很低
这里就可以引出缓冲区的概念
缓冲区
A:缓冲思想
*字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,
*这是加入了数组这样的缓冲区效果,java本身在设计的时候,
*也考虑到了这样的设计思想(装饰设计模式后面讲解),所以提供了字节缓冲区流
B.BufferedInputStream
*BufferedInputStream内置了一个缓冲区(数组)
*从BufferedInputStream中读取一个字节时
*BufferedInputStream会一次性从文件中读取8192个, 存在缓冲区中, 返回给程序一个
*程序再次读取时, 就不用找文件了, 直接从缓冲区中获取
*直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个
C.BufferedOutputStream
*BufferedOutputStream也内置了一个缓冲区(数组)
*程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中
*直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里
public static void demo1() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("致青春.mp3"); //创建输入流对象,关联致青春.mp3
FileOutputStream fos = new FileOutputStream("copy.mp3"); //创建输出流对象,关联copy.mp3
BufferedInputStream bis = new BufferedInputStream(fis); //创建缓冲区对象,对输入流进行包装让其变得更加强大
BufferedOutputStream bos = new BufferedOutputStream(fos);
int b;
while((b = bis.read()) != -1) {
bos.write(b);
}
bis.close();
bos.close();
}
flush和close方法的区别
flush()方法
*用来刷新缓冲区的,刷新后可以再次写出,一般用于实时刷新
close()方法
*用来关闭流释放资源的的,如果是带缓冲区的流对象的close()方法,不但会关闭流,还会再关闭流之前刷新缓冲区,关闭后不能再写出
流的标准处理异常代码
1.6版本及其以前
try finally嵌套
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("aaa.txt");
fos = new FileOutputStream("bbb.txt");
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
} finally {
try {
if(fis != null)
fis.close();
}finally {
if(fos != null)
fos.close();
}
}
1.7版本
try close
try(
FileInputStream fis = new FileInputStream("aaa.txt");
FileOutputStream fos = new FileOutputStream("bbb.txt");
MyClose mc = new MyClose();
){
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
}
在try()中创建的流对象必须实现了AutoCloseable这个接口,如果实现了,在try后面的{}(读写代码)执行后就会自动调用,流对象的close方法将流关掉
例题
1.在控制台录入文件的路径,将文件拷贝到当前项目下
public class Test2 {
/**
* 在控制台录入文件的路径,将文件拷贝到当前项目下
*
* 分析:
*
* 1,定义方法对键盘录入的路径进行判断,如果是文件就返回
* 2,在主方法中接收该文件
* 3,读和写该文件
* @throws IOException
*/
public static void main(String[] args) throws IOException {
File file = getFile(); //获取文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file.getName()));
int b;
while((b = bis.read()) != -1) {
bos.write(b);
}
bis.close();
bos.close();
}
/*
* 定义一个方法获取键盘录入的文件路径,并封装成File对象返回
* 1,返回值类型File
* 2,参数列表无
*/
public static File getFile() {
Scanner sc = new Scanner(System.in); //创建键盘录入对象
System.out.println("请输入一个文件的路径:");
while(true) {
String line = sc.nextLine(); //接收键盘录入的路径
File file = new File(line); //封装成File对象,并对其进行判断
if(!file.exists()) {
System.out.println("您录入的文件路径不存在,请重新录入:");
}else if(file.isDirectory()) {
System.out.println("您录入的是文件夹路径,请重新录入:");
}else {
return file;
}
}
}
}
2.将键盘录入的数据拷贝到当前项目下的text.txt文件中,键盘录入数据当遇到quit时就退出
public class Test3 {
/**
* 将键盘录入的数据拷贝到当前项目下的text.txt文件中,键盘录入数据当遇到quit时就退出
*
* 分析:
* 1,创建键盘录入对象
* 2,创建输出流对象,关联text.txt文件
* 3,定义无限循环
* 4,遇到quit退出循环
* 5,如果不quit,就将内容写出
* 6,关闭流
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//1,创建键盘录入对象
Scanner sc = new Scanner(System.in);
//2,创建输出流对象,关联text.txt文件
FileOutputStream fos = new FileOutputStream("text.txt");
System.out.println("请输入数据:");
//3,定义无限循环
while(true) {
String line = sc.nextLine(); //将键盘录入的数据存储在line中
//4,遇到quit退出循环
if("quit".equals(line)) {
break;
}
//5,如果不quit,就将内容写出
fos.write(line.getBytes()); //字符串写出必须转换成字节数组
fos.write("\r\n".getBytes());
}
//6,关闭流
fos.close();
}
}