本节我们讲解与File有关的IO流
程序的主要任务是操作数据,程序从输入流中读取数据,从输出流中写入数组
通过程序读取和写入数据
流:有序的数据序列
数据流中最小的数据单元划分(操作数据类型):字节流和字符流
字节流的划分:InputStream(字节输入流)和OutputStream(字节输出流)
字符流的划分:Reader(字符输入流)和Writer(字符输出流)
字节输出流----OutputStream---抽象基类
向输出流写入数据的三种重载形式
方式1
void write(int b):向输出流写入一个字节
方式2
public void write(byte[] b)
功能说明:把参数b指定的字节数组中的所有的字节写入到输出流
方式3
public void write(byte[] b, int off,int len)
功能:把参数b指定的字节数组的若干字节写入到输出流
开发中:该方法和读数据一块使用
参数:off--字节数组的起始索引,len--从指定位置开始指定字节的数目
常用的方法
void flush()
功能:把缓冲区内的数据写入到输出流中
说明:一些带缓冲区的子类(BufferedOutputStreeam和PrintStream)覆盖了flush()方法
带缓冲区的输出流的特点:写数据时,数据先保存在缓冲区中,积累到一定程度才会真正写入到输入流中
缓冲区说明:通常是字节数组实现,实际上是一块内存空间
void close()
说明:关闭输出流,释放资源
注意:流一旦关闭,不能再写入数据了,并且该流对象输出完毕之后,不指向这个文件了,所以需要将它关闭掉
补充:Java语言不能创建系统资源,通过C或C++间接创建系统资源
向输入流写入数据的三种重载形式
方式1
int read()
说明:从输出流读取一个8位的字节,把它转换成0-255的整数,返回这一整数
疑问:字节转换过程?
方式2
int read(byte [] b)
说明:从输入流读取若干字节,把它们保存到参数b指定的字节数组中
返回值:读取的实际字节数
注意:参数b指定的字节数组(实际上是一个字节数组缓冲区),一般数组的大小是1024
方式3
int read(byte [] b,int off,int len)
说明:从输出流中读取若干个字节,把它们保存到参数b指定的数组中
参数:off--保存数据的起始下标;len--从起始下标读取的字节数
返回值:实际读取的字节数
注意:对于read()方法如果遇到输入流的结尾,则返回-1
字节输入流子类---FileInputStream--文件输入流
FileInputStream的两种构造方式
实例1
package org.westos_01;
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
//1)创建文件输入流对象
FileInputStream fis = new FileInputStream("fis.txt") ;
//2)读数据
//读取,赋值,判断---三合一
//一次读取一个字节的模板代码
int by = 0 ;
while((by=fis.read())!=-1){
//a--->97
System.out.print(by);//转换成ASCII码表中的0-255的整数
System.out.print((char)by);//转换为原来的字符形式}//释放资源fis.close() ;}}
练习1
package org.westos_01;
import java.util.Arrays;
public class Stringdemo {
public static void main(String[] args) {
String s = "我爱你中国" ;
byte[] bys = s.getBytes() ;//编码---GBK模式
System.out.println(Arrays.toString(bys));
//将字节数组转化成字符串
//[-50, -46, -80, -82, -60, -29, -42, -48, -71, -6]
}
}
//说明:中文在GBK编码下的对应字节
练习2
package org.westos_02;
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamDemo2 {
public static void main(String[] args) throws IOException {
//1)创建文件字节输入流对象
FileInputStream fis = new FileInputStream("fis2.txt") ;
//2)读数据
//指定的字节数组的长度是1024或者是1024的倍数
//如果使用模板带去去读取数据:一次读取一个字节数组
//定义缓冲区大小:指定长度为1024
byte[] bys = new byte[1024] ;
int len = 0 ; //读取字节数的实际长度
while((len=fis.read(bys))!=-1){
System.out.println(new String(bys)); //解码:将字节数组转化成对应的字符串--看得懂的形式
//从指定的索引开始(指定0),读取的是实际长度
System.out.println(new String(bys,0,len));//推荐使用这种格式读取数据:从0开始,读取实际长度
}
}
}
字节输出流子类---FileOutputStream--文件输出流
构造方法--1
public FileInputStream(File file)
构造方法--2
public FileInputStream(String name)
构造方法--3
public FileInputStream(String name,boolean append)
构造方法说明:如果文件路径不存早或者文件路径存在(但是代表一个文件目录,不是文件),会抛出FileNotFoundException异常,其它情况没有的话,自己会创建文件
关于构造方法3说明: 默认写入数据时,将覆盖文件中的原内容,如果append为true,将在文件的末尾写入数据---追加数据
实例2
package org.westos_01;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamDemo {
public static void main(String[] args) throws IOException {
//创建一个文件输出流对象
FileOutputStream fos = new FileOutputStream("fos2.txt",true) ;//方式3
fos.write(98) ;
byte[] bys = {97,98,99,100,101} ;
fos.write(bys) ;
fos.write(bys, 1, 3) ;
//关闭资源
fos.close() ;
}
}
说明:常用构造方式2、3,注意二者写入数据后看文件的区别
写入了这个数据, 发现数据和数据之间没有换行?
需要写入换行符号,各种操作系统对应IO这块换行符号是不一样的
对于 windows操作系统来说:换行符号--\r\n(两个字节)
对于Linux操操作系统来说:\n
对于Mac操作系统来说:\r
实例3
package org.westos_01;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamDemo2 {
public static void main(String[] args) throws IOException {
//创建一个输出流对象
FileOutputStream fos = new FileOutputStream("fos3.txt",true) ;
//写数据
for(int x = 0 ; x <10 ; x ++){
fos.write(("helo"+x).getBytes());
/* 等价
* String s="hello"+x;
* byte [] by=s.getBytes()
* fos.write(by);
*/
//写入一个换行符号
fos.write("\r\n".getBytes());
}
//关闭资源
fos.close() ;
}
}
实例4 文件路径名不存在和异常情况
package org.westos_01;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* IO流中加入异常操作
* @author Apple
*/
public class FileOutputStreamDemo3 {
public static void main(String[] args) {
//方式3:加入标准格式:try...catch...finally
//IO流中加入异常操作的标准用法:
//声明一个变量
FileOutputStream fos = null ;
try {
fos = new FileOutputStream("fos4.txt") ;
//写数据
fos.write("hello,Javaweb".getBytes()) ;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
//释放资源的
//由于是流对象,要对流对象做非空判断
if(fos !=null){
try {
fos.close() ;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
实例5 文件复制
需求:在当前项目下有一个a.txt文件,将a.txt文件的内容赋值到b.txt文件中
方式1 一次读取一个字节
package org.westos_03;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 分析:
* 1)数据源:a.txt------->FileInputStream:输入流------->读数据
* 2)目的地:b.txt------->FileOutputStream:输出流------>写数据
* @author Apple
*/
public class CopyFileDemo {
public static void main(String[] args) throws IOException {
//封装数据源:
//创建一个文件字节输入流对象
FileInputStream fis = new FileInputStream("a.txt") ;
//封装目的地
//创建文件输出流对象
FileOutputStream fos = new FileOutputStream("b.txt") ;
//一次读取一个字节:模板代码
int by = 0 ;
while((by=fis.read())!=-1){
//一次读一个字节,使用输出流给b.txt文件写一个字节
fos.write(by) ;
}
//释放资源
fos.close() ;
fis.close() ;
}
}
说明:文本、音频、视频、图片与上雷同
方式2 一次读取一个字节数组
需求:E盘下有一个高圆圆.jpg文件,将它图片内容复制到当前项目下的mm.jpg
package org.westos_04;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyImageDemo {
public static void main(String[] args) throws IOException {
//封装数据源和目的地
FileInputStream fis = new FileInputStream("E:\\高圆圆.jpg") ;
FileOutputStream fos = new FileOutputStream("mm.jpg") ;
//一次读取一个字节数组
byte[] bys = new byte[1024] ;//字节数组缓冲区
int len = 0 ;
while((len=fis.read(bys))!=-1){
//写数据的
fos.write(bys, 0, len) ;
}
//释放资源
fis.close() ;
fos.close() ;
}
}
注意:其它文件雷同,对于向输出流写入数据的第三种重载形式好处:不用输出空白,换行等
说明:一次写入一个字节数组比写入一个字节快的原因?减少了 物理写数据的次数
由于字节数组比一次读取一个字节更快,从而引出类更高效的一种流:字节缓冲流
缓冲流---BufferedInputStream(字节缓冲输入流)
package org.westos_05;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class BufferedInputStreamDemo {
public static void main(String[] args) throws IOException {
//创建一个字节缓冲输入流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt")) ;
/**
* 通过构造方法的参数可以看出
* 节点流:FileInputStream---InputStream的子类
* 处理流来装饰节点流
*/
//一次读取一个字节数组或者一次读取一个字节都可以
byte[] bys = new byte[1024] ;
int len = 0 ;
while((len=bis.read(bys))!=-1){
System.out.println(new String(bys,0,len));
}
//释放资源
bis.close() ;
}
}
缓冲流---BufferedOutputStream(过滤输出流的子类)
说明:此类覆盖了被装饰的输出流(节点流)的写数据行为,利用缓冲区提高写数据的效率;默认情况下,缓冲区满的时候,才把缓冲区的数据写入到输出流
常用构造方式:
public BufferedOutputStream(OutputStream out):默认缓冲区大小(默认缓冲大小已经足够大了)
package org.westos_05;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferedOutputStreamDemo {
public static void main(String[] args) throws IOException {
//创建字节缓冲输出流对象
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("bos.txt"));//Java的一种设计模式
//写数据
bos.write(97) ;
//刷新该缓冲区的流
bos.flush() ;//迫使将这写字节强制输出流中
bos.write(98) ;
//释放资源
bos.close() ;
bos.flush() ;
// bos.write(99) ;
}
}
问题1:为什么字节缓冲流不能
直接针对文件进行操作呢?
答:缓冲流只是提供一个缓冲区,针对IO进行实现数据的传输实际是通过底层基本流进行实现的,所以缓冲流不能直接对文件操作--装饰
计算机是如何识别中文的?
1)每个中文左边对应的字节一定是负数
2)GBK格式:右边的字节可以是正数,可以是0也可以是负数
package org.westos_05;
import java.util.Arrays;
public class StringDemo {
public static void main(String[] args) {
String s = "我爱你中国" ;
//当前平台默认的编码集:GBK---一个中文对应两个字节
//[-50, -46, -80, -82, -60, -29, -42, -48, -71, -6]
//将当前字符串转换成字节数组
byte[] bys = s.getBytes() ;
System.out.println(Arrays.toString(bys));
}
}
对几种字节流
复制文件速率的比较
package org.westos_06;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
*
* 对于图片文件,音频文件,视频文件,优先采用字节缓冲输入流(高效流)一次读取一个字节数组!
*
*
* 字节流复制文件:
* 基本字节流(FileInputStream)一次读取一个字节:共耗时:253820毫秒
* 基本字节流一次读取一个字节数组:共耗时:396毫秒
* 高效字节流(字节缓冲输入流)一次读取一个字节:共耗时:98020毫秒
* 高效字节流(字节缓冲输入流)一次读取一个字节数组:共耗时:317毫秒
*
* 复制e:\\abc.mp4文件---->当前项目下的Copy.mp4文件
* @author Apple
*/
public class CopyFileDemo {
public static void main(String[] args) throws IOException {
long startTime = System.currentTimeMillis() ;
method1("E:\\abc.mp4","Copy.mp4") ;
method2("E:\\abc.mp4","Copy.mp4") ;
method3("E:\\abc.mp4","Copy.mp4") ;
method4("E:\\abc.mp4","Copy.mp4") ;
long endTime = System.currentTimeMillis() ;
System.out.println("共耗时:"+(endTime-startTime)+"毫秒");
}
//高效字节流一次读取一个字节数组
private static void method4(String SrcString, String destString) throws IOException{
//封装数据源和目的地
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(SrcString)) ;
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destString)) ;
//一次读取一个字节数组
byte[] bys = new byte[1024] ;
int len = 0 ;
while((len=bis.read(bys))!=-1){
bos.write(bys, 0, len) ;
bos.flush() ;//刷新缓冲区的流
}
//释放资源
bis.close() ;
bos.close() ;
}
//字节缓冲输入流一次读取一个字节
private static void method3(String SrcString, String destString) throws IOException {
//创建字节缓冲输入对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(SrcString)) ;
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destString)) ;
//一次读取一个字节
int by = 0 ;
while((by=bis.read())!=-1){
bos.write(by) ;
bos.flush() ;
}
//释放资源
bis.close() ;
bos.close() ;
}
//基本的字节流一次读取一个字节数组
private static void method2(String SrcString, String destString) throws IOException {
//封装数据源
FileInputStream fis = new FileInputStream(SrcString) ;
//封装目的地
FileOutputStream fos = new FileOutputStream(destString) ;
//一次读取一个字节数组
byte[] bys = new byte[1024] ;
int len = 0 ;
while((len=fis.read(bys))!=-1){
//边读边写
fos.write(bys, 0, len) ;
}
//释放资源
fis.close() ;
fos.close() ;
}
//基本字节流一次读取一个字节
private static void method1(String StrString, String destString) throws IOException {
//封装数据源
FileInputStream fis = new FileInputStream(StrString) ;
//封装目的地
FileOutputStream fos = new FileOutputStream(destString) ;
//一次读取一个字节
//并且一次写一个字节
int by = 0 ;
while((by=fis.read())!=-1){
//写数据
fos.write(by) ;
}
//释放资源
fis.close() ;
fos.close() ;
}
}
字节流一次读取一个字节,出现了中文乱码
原因:给字节进行强制类型转换,代码的注释中有中文,并且平台默认编码GBK格式:一个中文对应的两个字节
代码
package org.westos_07;
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
//创建字节输入流对象
FileInputStream fis = new FileInputStream("FileInputStreamDemo2.java") ;
//一次读取一个字节
int by = 0 ;
while((by=fis.read())!=-1){
System.out.print((char)by);
}
//释放资源
fis.close() ;
}
}
解决方案:
使用字符流来进行操作;说明:字符流必须指定编码格式
回顾编码和解码
package org.westos_07;
import java.io.IOException;
import java.util.Arrays;
/**
* 编码:就是能看懂的字符串转换成看不懂的字节数组
* public byte[] getBytes(Charset charset):将字符串转换成字节数组,指定编码格式(字符集)
* 解码:就是将看不懂的字节数组----->能看懂的:将字节数组--->字符串
* public String(byte[] bytes, Charset charset)通过使用指定的 编码格式 解码指定的 byte 数组,构造一个新的 String
*
* 举例:谍战片
* 老地方见...
*
* 编码
* "老"----->字节数组----->二进制数据---->
* 解码:二进制数据----->十进制数据----->字节数组----->构造方式:字符串
*
*
* @author Apple
*/
public class StringDemo {
public static void main(String[] args) throws IOException {
String s = "爱你" ;
//编码
//如果不写编码格式:默认GBK
byte[] bys = s.getBytes() ;//[-80, -82, -60, -29]
System.out.println(Arrays.toString(bys));
//解码
//public String(byte[] bytes, Charset charset)
String str = new String(bys) ;//默认GBK
System.out.println(str);//爱你
}
}
处理流中的转换流--InputStreamReader
说明:采用了 适配器模式,将字节输入流转化成字符输入流
常用构造方式:
字符输入流:字节输入流+编码格式(默认GBK)
public InputStreamReader(InputStream in,charset sc) ;
package org.westos_08;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
//创建字符输入流对象
/*InputStreamReader isr = new InputStreamReader(new FileInputStream(
"osw.txt"), "GBK");*/
InputStreamReader isr = new InputStreamReader(new FileInputStream(
"osw.txt"));//以GBK格式读数据
//读数据:一次读取一个字符
/*int ch = 0 ;
while((ch=isr.read())!=-1){
System.out.print((char)ch);
}*/
char[] chs = new char[1024] ;
int len = 0;
while((len=isr.read(chs))!=-1){
System.out.println(new String(chs, 0, len));
}
//释放资源
isr.close() ;
}
}
I/O流的分类
总结:
1.Java IO是采用的是装饰模式,即采用处理流来包装节点流的方式,来达到代码通用性。
2.处理流和节点流的区分方法,节点流在新建时需要一个数据源(文件、网络)作为参数,而处理流需要一个节点流作为参数。
3.处理流的作用就是提高代码通用性,编写代码的便捷性,提高性能。
4.节点流都是对应抽象基类的实现类,它们都实现了抽象基类的基础读写方法。其中read()方法如果返回-1,代表已经读到数据源末尾。
图