流
流(IO),在计算机系统中,将不同的输入输出源统一抽象为流,流是一种实现数据交换技术的核心,比较常见的流的使用在于:文件操作,网络数据传输等;流由两大核心组成:1.input(输入),2.output(输出);即读和写。Java中所有关于刘的类都来自Java。io包
流的分类
java–IO将刘分为几种类别
- 按流向分为;输入流和输出流
- 按类型分为:字节流和字符流
- 按功能分为:节点流和处理流
字节流
所谓字节流,其实就是将数据以字节为单位进行读写相关操作,字节流一般用于对于一些二进制文件(图片,音频,视频等)进行读写操作,java中的字节流都是来自以下两个抽象类:
- InputStream(字节输入流)
- OutputStream(字节输出流)
Inputstream类
inputStream类是所有字节输入流的父类,是一个抽象类,常用方法包含以下:
- available():获取当前流中的可读字节数
- close():关闭此流
- read():从流中读取一个字节,返回读取到的字节
- read(byte[] b):将流中读取到的字节存入到指定的字节数组中,返回真实读取的长度,读取不到返回-1
- read(byte[] b,int offset,int len):将流中读取到的字节存入到指定的字节数组中(跳过offset个字节存储,存储长度为len),读取不到返回-1
InputStream类的常见子类有FileInputStream,ByteArrayInputStream,ObjectInputStream,FilterInputStream等,以FileInputStream类为例:
public class ReadFileDemo {
public static void main(String[] args) throws IOException {
//创建File对象
File f = new File("C:\\Users\\Desktop\\a.txt");
//基于File对象创建文件字节输入流
FileInputStream fis = new FileInputStream(f);
//获取当前输入流的可读字节数
int len = fis.available();
//获取文件对象的可读字节数
byte[] b = new byte[len];
fis.read(b);
System.out.println(new String(b));
}
}
通过以上代码看出可以根据流中有效字节长度创建字节数组,在下一次读取时,直接将所有读取的内容存储到数组中,但是如果流中数据过大,将会需要消耗大量空间存储,考虑到空间的限制,一般不会一次性读完,而是采用循环的方式每次读取固定长度的数据并缓存到字节数组中,以减少内存空间的消耗,以上程序修改后,如下:
public class ReadFileDemo2 {
public static void main(String[] args) throws IOException {
//创建File对象
File f = new File("C:\\Users\\Desktop\\a.txt");
//基于File对象创建文件字节输入流
FileInputStream fis = new FileInputStream(f);
//声明一个指定长度的字节缓冲区(太大会消耗大量运行时内存)
byte[] b = new byte[1024];
//声明临时变量用于存储每次读取的字节的真实长度
int len = 0;
//循环读取
while((len = fis.read(b)) != -1){
String s = new String(b,0,len);
System.out.println(s);
}
fis.close();
}
}
OutputStream类
OutputStream类是所有字节输出流的父类,是一个抽象类,常用方法如下:
- write(byte[] b):将字节数组中的内容写入输出源(文件,网络,内存)
- write(byte[] b,int offset,int len):将字节数组中的内容从offset开始写入len长到输出源
- write(int b):将一个字节写入输出源
- flush():将流中的数据强制刷新到输出源
- close():关闭此流
OutputStream类的常见子类有:FileOutputStream,ByteArrayOutputStream,ObjectOutputStream,FilterOutputStream等,以FileOutputStream类为例:
public class WriteFileDemo {
public static void main(String[] args) throws IOException {
//准备目标文件对象
File file = new File("C:\\Users\\mrchai\\Desktop\\a.txt");
//创建一个基于目标文件的字节输出流
FileOutputStream fos = new FileOutputStream(file,true);
String msg = "床前明月光,疑似地上霜!";
//将需要通过输出流输出的内容转换为字节数组并输出
fos.write(msg.getBytes());
//关闭资源
fos.close();
}
}
文件拷贝
通过以上字节流(输入输出)的使用,可以实现系统中的文件拷贝功能,文件拷贝原理即:获取源文件的输入流,获取目标文件的输入流,通过读取输入流中的数据并写入输出流中,如下:
public class FileCopy {
/**
* 将源文件拷贝到目标目录中
* @param sourceFile 源文件(标准文件)
* @param targetDir 目标目录(目录)
*/
public void copy(File sourceFile,File targetDir){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//获取源文件的输入流
fis = new FileInputStream(sourceFile);
//获取目标文件的输出流
fos = new FileOutputStream(new File(targetDir,sourceFile.getName()));
//声明字节缓冲区
byte[] b = new byte[1024];
//声明变量存储真实读取长度
int len = 0;
//循环读取以及写入
while((len = fis.read(b)) != -1){
fos.write(b,0,len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
//关闭资源
if(fos != null){
fos.close();
}
if(fis != null){
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
File source = new File("D:\\素材\\视频\\larva\\35.avi");
File target = new File("C:\\Users\\mrchai\\Desktop");
new FileCopy().copy(source, target);
}
}
字符流
字符流,顾名思义,是以字符的形式对输入输出源操作,通常情况下一个字符表示两个字节,但是在一些unicode编码,如UTF-8则使用3个字节表示,但是由于字符流的特殊性,一般用字符流主要操作一些文本输入输出源(文本文档,记事本文件等字符数据);java中所有的字符流都从以下两个抽象类继承:
- Reader 字符输入流
- Writer 字符输出流
Reader
字符输入流,是所有字符输入流的父类,是一个抽象类,内部的常见方法如下:
- read():读取并返回一个字符
- read(char[] c):将从流中读取的字符存储到字符数组
- read(char[] c,int offset,int len):将从流中读取的字符存入字符数组(跳过offset个字节,写入len长)
- ready():返回此流是否准备好被读取的状态
- close():关闭此流
Reader类的常见子类包含:InputStreamReader,BufferedReader,FilterReader,PipedReader等,常见的间接子类有FileReader,使用如下:
public class ReaderDemo1 {
public static void main(String[] args) throws IOException {
File f = new File("C:\\Users\\mrchai\\Desktop\\3周疑问.txt");
//根据给定的文件对象构建一个文件字符输入流对象
FileReader fr = new FileReader(f);
// System.out.println(fr.ready());
// int i = fr.read();
// System.out.println((char)i);
char[] c = new char[512];
int len = 0;
while((len = fr.read(c)) != -1){
String s = new String(c,0,len);
System.out.println(s);
}
}
}
Writer
字符输出流,是所有字符输出流的父类,是一个抽象类,内部的常见方法如下:
- append(char c):向流中追加一个字符
- append(CharSequence c):向流中追加一个字符序列(字符串)
- witer(String s):写入一个字符串到目标输出源
- witer(char[] c):写入字符数组到目标输出源
Writer类的常见子类包含:OutStreamReader,BufferedWriter,FilterWriter,PipedWriter等,常见的间接子类有FileWriter,使用如下:
public class WriteDemo {
public static void main(String[] args) throws IOException {
File f = new File("C:\\Users\\mrchai\\Desktop\\test.txt");
//获取文件的字符输出流,使用追加模式
FileWriter fw = new FileWriter(f,true);
fw.write("飞流直下三千尺,疑是银河落九天");
//使用字符流执行写入操作时,由于内置的字符缓冲区可能不能及时将内容输出
//因此,需要手动调用flush强制将内容通过输出流输出或者执行close
// fw.flush();
fw.close();
}
}
转换流、缓冲流打印流
由于以上所提到的流,按照功能来说都属于节点流(直接跟输入输出源打交道),而在实际开发中有些需求可能会涉及到需要将字节流转换为字符流,或者将字符流转换为字节流等一些转换操作;另外也有可能需要将这些低级的节点流提高读取和写入效率,因此还需要一些高级流来进行处理,因此这些高级流也被称之为处理流,比如:转换流,缓冲流,打印流等。
转换流
java-io中的转换流主要分为两个:
- InputStreamReader:将字节流转换为字符流的桥梁
- OutputStreamWriter:将字符流转换为字节流的桥梁
缓冲流
缓冲流的出现主要为了提高节点流的读取和写入效率,使用方式通常为将其他节点流包装起来,io包中的缓冲流分为以下几个:
- BufferedReader
- BufferedWriter
- BufferedInputStream
- BufferedOutputStream
转换流&缓冲流综合使用1
public class StreamDemo {
public static void main(String[] args) throws IOException {
//获取标准输入流
InputStream is = System.in;
//将字节流转换为字符流(装饰器模式) gbk utf-8 gb2312 gb18030
InputStreamReader isr = new InputStreamReader(is,"gbk");
//将低级字符流转换为高级字符缓冲流
BufferedReader br = new BufferedReader(isr);
String s = br.readLine();
System.out.println(s);
}
}
转换流&缓冲流综合使用2
public class StreamDemo3 {
public static void main(String[] args) throws IOException {
String msg = "你好世界";
OutputStream os = System.out;
//将字符流转字节流的桥梁
OutputStreamWriter osw = new OutputStreamWriter(os);
//将低级字符输出流转换为缓冲流
BufferedWriter bw = new BufferedWriter(osw);
bw.write(msg);
bw.close();
}
}
打印流
另外在IO包中还提供了两个特殊的流,这两个流只有输出,没有输入:
- PrintStream 字节打印流
- PrintWriter 字符打印流
打印流通常可以对其他输出流(Writer,OutputStream)以及文件(File)进行包装,然后通过提供的相关API操作这些流,常见构造器:
- PrintStream
- PrintStream(File file)
- PrintStream(String fileName)
- PrintStream(OutputStream os)
- PrintStream(OutputStream os,boolean autoFlush)
- PrintWriter
- PrintWriter(File file)
- PrintWriter(String fileName)
- PrintWriter(OutputStream os)
- PrintWriter(OutputStream os,boolean autoFlush)
- PrintWriter(Writer w)
- PrintWriter(Writer w,boolean autoFlush)
使用如下:
public class PrintDemo {
public static void main(String[] args) throws FileNotFoundException {
//创建文件字节输出流(追加模式)
OutputStream os = new FileOutputStream("C:\\Users\\mrchai\\Desktop\\test.txt",true);
// PrintStream ps = new PrintStream(os);
// ps.println("谁知盘中餐");
PrintWriter pw = new PrintWriter(os,true);
pw.println("粒粒皆辛苦");
// pw.flush();
}
}
实现目录拷贝
FileCopyUtils.java
/**
* 文件拷贝工具
*/
public class FileCopyUtils {
/**
* 将一个源file对象拷贝到另一个目标file
* @param source 源文件(可能是目录)
* @param target 目标目录
* @throws IOException
*/
public static void copy(File source,File targetDir) throws IOException{
//判断当前需要被拷贝对象是目录还是标准文件
if(source.isDirectory()){
//在目录中创建子目录(源目录中不存在该子目录)
targetDir = new File(targetDir,source.getName());
if(!targetDir.exists()){
//如果目录不存在则试图创建
if(!targetDir.mkdirs()){
throw new FileNotFoundException("目录创建失败,请检查权限");
}
}
//读取目录
File[] files = source.listFiles();
if(Objects.nonNull(files)){
for (int i = 0; i < files.length; i++) {
copy(files[i],targetDir);
}
}
}else{
//文件拷贝
copyFile(source,targetDir);
}
}
private static void copyFile(File source, File targetDir) throws IOException {
BufferedInputStream bis = null;
BufferedOutputStream bow = null;
try{
//获取源文件的输入流并包装为缓冲流
bis = new BufferedInputStream(new FileInputStream(source));
//根据源文件文件名组合目标目录成为新文件对象
File target = new File(targetDir,source.getName());
//获取目标文件的输出流
bow = new BufferedOutputStream(new FileOutputStream(target));
//声明字节缓冲区,缓存读取的字节数据
byte[] b = new byte[1024];
int len = 0;
//循环读写
while((len = bis.read(b)) != -1){
bow.write(b, 0, len);
}
}finally{
//关闭资源
if(bow != null)bow.close();
if(bis != null)bis.close();
}
}
}
测试类:
public class TestCopy {
public static void main(String[] args) throws IOException {
//源file
File f1 = new File("D:/javacode");
//目标file
File f2 = new File("C:/Users/mrchai/Desktop");
//拷贝
FileCopyUtils.copy(f1, f2);
}
}