1、输入与输出操作传递的是一种数据流,即字节数据,这种流的处理形式在java.io包中提供了两类支持:
字节处理流(abstract):OutputStream(输出字节流)、InputStream(输入字节流)
字符处理流(abstract):Writer(输出字符流)、Reader(输入字符流)
2、OutputStream类定义:public abstract class OutputStream extends Object implements Closeable, Flushable
OutputStream类定义的是一个公共的输出操作标准,在这个操作标准中共定义有三个内容输出的方法:
输出单个字节数据:public abstract void write(int b) throws IOException
输出一组字节数据:public void write(byte[] b) throws IOException
输出部分字节数据:public void write(byte[] b,int off,int len) throws IOException
OutputStream类是一个抽象类,若想获得实例化对象,一般应该通过子类实例向上转型,如果进行的是文件处理操作,则可以使用FileOutputStream子类:public class FileOutputStream extends OutputStream
因为要进行FileOutputStream子类的向上转型,所以就需要关注其中的两类构造方法:
覆盖public FileOutputStream(File file) throws FileNotFoundException
追加public FileOutputStream(File file,boolean append) throws FileNotFoundException
import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; public class FileDemo { public static void main(String[] args) throws Exception { //1.指定要操作的文件路径 File file = new File("D:" + File.separator + "JavaDemo" + File.separator + "Demo" + File.separator + "d.txt") ; if(!file.getParentFile().exists()) { //文件不存在 file.getParentFile().mkdirs() ; //创建父目录,要操作的文件系统可以自动创建 } //2.通过子类实例化 OutputStream output = new FileOutputStream(file) ; //3.将要输出的文件内容转变为字节数组 output.write("一只瓶子a".getBytes()); //4.关闭资源 output.close(); } }
由于OutputStream子类实现了Closeable接口,而Closeable接口又继承了AutoCloseable接口,即OutputStream子类也属于AutoCloseable接口子类,所以对close()方法可以简化处理。
//1.指定要操作的文件路径 File file = new File("D:" + File.separator + "JavaDemo" + File.separator + "Demo" + File.separator + "d.txt") ; if(!file.getParentFile().exists()) { //文件不存在 file.getParentFile().mkdirs() ; //创建父目录,要操作的文件系统可以自动创建 } //2.通过子类实例化 try(OutputStream output = new FileOutputStream(file,true)) { //修改为追加的方式 //3.将要输出的文件内容转变为字节数组 output.write("一只瓶子a\r\n".getBytes()); //\r换行相当于回车,\n新行 }catch(IOException e) { e.printStackTrace(); }
3、字节输入流:InputStream
InputStream类定义:public abstract class InputStream extends Object implements Closeable
读取单个字节数据:public abstract int read() throws IOException(每次读一个字节,读到最后一个字节的下一个字节为null时返回-1)
读取一组字节数据:public int read(byte[] b) throws IOException(每次读“一小组”数据,此时返回的是“小组”的字节数,若没有需读取数据了则返回-1)
读取一组字节数据的部分内容:public int read(byte[] b,int off,int len) throws IOException
读取数据示例:
import java.io.File; import java.io.FileInputStream; import java.io.InputStream; public class FileDemo { public static void main(String[] args) throws Exception { File file = new File("D:" + File.separator + "JavaDemo" + File.separator + "Demo" + File.separator + "d.txt") ; InputStream input = new FileInputStream(file) ; byte data [] = new byte [1024] ; //开辟一个缓冲区读取数据 input.read(data); System.out.println(new String(data)) ; input.close(); } }
注:byte data [] = input.readAllBytes() ; //读取全部数据,此方法并不适用于数据量较大的文件的读取
4、Writer字符输出流(JDK1.1)
类定义:public abstract class Writer extends Object implements Appendable, Closeable, Flushable
写入字符数组的方法:public void write(char[] cbuf) throws IOException
写入字符串的方法:public void write(String str) throws IOException
代码示例:
import java.io.File; import java.io.FileWriter; import java.io.Writer; public class FileDemo { public static void main(String[] args) throws Exception { File file = new File("D:" + File.separator + "JavaDemo" + File.separator + "Demo" + File.separator + "d.txt") ; if(!file.getParentFile().exists()) { //文件不存在 file.getParentFile().mkdirs() ; } //Writer out = new FileWriter(file) ; //覆盖之前内容 Writer out = new FileWriter(file, true) ; //追加 out.append("再次追加内容") ; out.write("一只瓶子aoe"); out.close(); } } 使用Writer输出的最大优势在于可以直接利用字符串完成(中文数据)。
5、Reader字符输入流
类定义:public abstract class Reader extends Object implements Readable, Closeable
读入数据:public int read(char[] cbuf) throws IOException
代码示例:
import java.io.File; import java.io.FileReader; import java.io.Reader; public class FileDemo { public static void main(String[] args) throws Exception { File file = new File("D:" + File.separator + "JavaDemo" + File.separator + "Demo" + File.separator + "d.txt") ; if(file.exists()) { Reader in = new FileReader(file) ; char data [] = new char[1024] ; int len = in.read(data) ; System.out.print(new String(data, 0, len)); //截取data中,第0位置开始的len个字符串 in.close(); } } }
字符流读取时只能按照数组的形式处理。
6、字节流与字符流的区别
在使用OutputStream类输出的时候如果现在没有使用close()方法关闭输出流内容依然可以实现正常输出,但是在使用Writer类时如果没有使用close()方法关闭输出流,那么此时内容无法正常输出(写入)。原因在于Writer使用到了缓冲区,使用Write()方法时会有强制刷线缓冲区的情况,这个时候会将内容进行输出,如果没有关闭,就无法进行输出操作,所以如果在不关闭的情况下要想将全部的内容输出可以使用flush()方法强制刷新。
即,字节流在进行处理的时候并不会使用到缓冲区,而字符流会使用到缓冲区,另外,使用缓冲区的字符流更适合于中文数据的处理。
7、转换流
转换流是指实现字节流与字符流操作的功能转换。为此在java.io包里提供有两个类:InputStreamReader类和OutPutStreamWriter类
类定义:
public class InputStreamReader extends Reader
public class OutputStreamWriter extends Writer
构造方法:
public InputStreamReader(InputStream in)
public OutputStreamWriter(OutputStream out)
通过观察类定义与构造方法可以发现转换处理就是将接收到的字节流对象通过向上转型变为字符流对象。
OutputStream output = new FileOutputStream(file) ; //接收字节流 Writer out = new OutputStreamWriter(output) ; //将字节流转为字符流 out.Write("一只瓶子a") ; //字符串写入(字符流适合处理中文) out.close();
8、文件读取流程
通过CPU访问内存,内存访问磁盘上的文件。程序通过字节流读取数据时会直接访问CPU,而使用字符流读取数据时会经过缓冲区进行先期处理(方便处理中文数据),进而能保证用户读取到的是中文数据。其中缓存指的就是程序中间的一道处理缓冲区。
9、文件拷贝示例:
分析:由于可能拷贝各种类型的文件,所以选用字节流较为合适,也可能会进行大文件的拷贝
方案一:通过InputStream将要拷贝的内容读取到程序之中,而后输出到目标文件
方案二:部分拷贝,读取一部分拷贝一部分
(原始实现:) import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; public class FileDemo { public static void main(String[] args) throws Exception { if(args.length != 2) { System.out.println("命令执行错误,执行结构:java FileDemo 拷贝源文件目录 拷贝目标文件目录") ; System.exit(1); //系统退出 } long start = System.currentTimeMillis() ; FileUtil fu = new FileUtil(args[0], args[1]) ; System.out.println(fu.copy() ? "文件拷贝成功!" : "文件拷贝失败!") ; long end = System.currentTimeMillis() ; System.out.println("拷贝完成所用时间:" + (end - start)) ; } } //一个文件操作的工具类 class FileUtil { private File srcFile ; //源文件路径 private File desFile ; //目标文件路径 public FileUtil(String src, String des) { this(new File(src), new File(des)) ; } public FileUtil(File srcFile, File desFile) { this.srcFile = srcFile ; this.desFile = desFile ; } //文件拷贝处理 public boolean copy() throws Exception { if(!this.srcFile.exists()) { System.out.println("拷贝源文件不存在!") ; return false ; } if(!this.desFile.getParentFile().exists()) { this.desFile.getParentFile().mkdirs() ; //创建父目录 } byte data [] = new byte[1024] ; //开辟一个拷贝的缓冲区 InputStream input = null ; OutputStream output = null ; try { input = new FileInputStream(this.srcFile) ; output = new FileOutputStream(this.desFile) ; int len = 0 ; while((len = input.read(data)) != -1) { output.write(data, 0, len); } return true; }catch(Exception e) { throw e ; }finally { if(input != null) { input.close(); } if(output != null) { output.close() ; } } } }
从JDK1.9开始InputStream类中都追加有数组转存的处理操作方法:(开发过程中注意版本问题,比较新) InputStream:public long transferTo(OutputStream out) throws IOException
// byte data [] = new byte[1024] ; //开辟一个拷贝的缓冲区 InputStream input = null ; OutputStream output = null ; try { input = new FileInputStream(this.srcFile) ; output = new FileOutputStream(this.desFile) ; // int len = 0 ; // while((len = input.read(data)) != -1) { // output.write(data, 0, len); // } input.transferTo(output) ; return true; }
【重要】10、目录拷贝示例:
package com.demo; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; public class FileDemo { public static void main(String[] args) throws Exception { if(args.length != 2) { System.out.println("命令执行错误,执行结构:java FileDemo 拷贝源文件目录 拷贝目标文件目录") ; System.exit(1); //系统退出 } long start = System.currentTimeMillis() ; FileUtil fu = new FileUtil(args[0], args[1]) ; if(new File(args[0]).isFile()) { System.out.println(fu.copyFile() ? "文件拷贝成功!" : "文件拷贝失败!") ; }else { System.out.println(fu.copyDir() ? "文件拷贝成功!" : "文件拷贝失败!") ; } long end = System.currentTimeMillis() ; System.out.println("拷贝完成所用时间:" + (end - start)) ; } } //一个文件操作的工具类 class FileUtil { private File srcFile ; //源文件路径 private File desFile ; //目标文件路径 public FileUtil(String src, String des) { this(new File(src), new File(des)) ; } public FileUtil(File srcFile, File desFile) { this.srcFile = srcFile ; this.desFile = desFile ; } //目录拷贝处理 public boolean copyDir() throws Exception { try { this.copyDirImpl(this.srcFile) ; return true; } catch (Exception e) { throw e ; } } private void copyDirImpl(File file) throws Exception { if(file.isDirectory()) { //是目录 File result[] = file.listFiles() ; if(result != null) { for(int x = 0; x < result.length; x ++) { copyDirImpl(result[x]) ; } } }else { //是文件(需要获取文件的新目录this.desFile与文件名newFilePath) /* * file.getPath() D:\src\NewDemoCopy.java * this.srcFile.getPath() D:\src * 该语句的执行结果是获取文件名 */ String newFilePath = file.getPath().replace(this.srcFile.getPath() + File.separator, "") ; File newFile = new File(this.desFile, newFilePath) ; //两个参数分别是目录与文件名 this.copyFileImpl(file, newFile) ; } } //文件拷贝处理 public boolean copyFile() throws Exception { if(!this.srcFile.exists()) { System.out.println("拷贝源文件不存在!") ; return false ; } return this.copyFileImpl(this.srcFile, this.desFile) ; } private boolean copyFileImpl(File srcFile, File desFile) throws Exception { if(!desFile.getParentFile().exists()) { desFile.getParentFile().mkdirs() ; //创建父目录 } // byte data [] = new byte[1024] ; //开辟一个拷贝的缓冲区 InputStream input = null ; OutputStream output = null ; try { input = new FileInputStream(srcFile) ; //实例化输入流,输入源文件内容 output = new FileOutputStream(desFile) ; //实例化输出流,输出至目标文件 // int len = 0 ; // while((len = input.read(data)) != -1) { // output.write(data, 0, len); // } input.transferTo(output) ; //将源文件的内容复制到目标文件 return true; }catch(Exception e) { throw e ; }finally { if(input != null) { input.close(); } if(output != null) { output.close() ; } } } }