流的基本概念
服务器与客户端传输的就是数据流,或者说比特流。
java.io包中的字节处理流:
- 输出字节流:OutputStream
- 输入字节流:InputStream
字符处理流:
- 输出字符流:Writer
- 输出字节流:Reader
对文件的读写流程:
- 通过File类找到文件路径
- 通过字节流或字符流对对象进行实例化
- 利用字节流与字符流的方法实现数据的输入输出
- 流操作属于资源操作,必须进行关闭处理
OutputStream字节输出流
类的声明:public abstract class OutputStream extends Object implements Closeable, Flushable
有两个接口:
Closeable | Flushable |
---|---|
jdk1.5 | jdk1.5 |
void close(); | void flush(); |
Closeable接口实现了AutoCloseable接口(jdk1.7,出现了try…catch)
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; | 输出部分字节数据 |
(off参数表示偏移量,一般为0.)
OutputStream类是抽象类,实现其方法需要通过子类向上转型。那么子类(FileOutputStream)的关注点主要放在构造方法上即可。
例: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 OutputTest {
public static void main(String[] args) throws Exception {
File file=new File("F:\\course\\output.txt");
if(!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
OutputStream out=new FileOutputStream(file);
String str="hello first file 操作";
out.write(str.getBytes());
out.close();
}
}
由于OutputStream是AutoCloseable的子类,所以可以执行自动关闭处理。
但是否执行自动关闭取决于项目的整体结构。
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class OutputTest {
public static void main(String[] args) throws Exception {
File file=new File("F:\\course\\output.txt");
if(!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
try(OutputStream out=new FileOutputStream(file,true)){
String str="hello first file 操作\r\n";//\n是换行,\r\n是标准换行
out.write(str.getBytes());
}catch(IOException e) {
e.printStackTrace();
}
}
}
InputStream字节输入流
类的声明:public abstract class InputStream extends Object implements Closeable
主要方法:
方法名 | 作用 |
---|---|
public abstract int read(int b)throws IOException; | 输出单个字节数据(如果数据读取到末尾,返回“-1”) |
*public int read(byte b[])throws IOException; | 输出一组字节数据(返回值为读取数据的个数,如果没有数据(已经读取到底)返回“-1”) |
public int read(byte b[],int off,int len)throws IOException; | 输出一组字节数据的部分 |
InputStream是抽象类,依靠子类FileInputStream向上转型实现。
例:
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class InputTest {
public static void main(String[] args) throws Exception {
File file=new File("F:\\course\\Output.txt");
if(file.exists()) {
InputStream in=new FileInputStream(file);
byte bt[]=new byte[1024];
int len = in.read(bt);
System.out.println("["+new String(bt,0,len)+"]");
in.close();
}
}
}
字节输入流的问题,使用read()方法只能以字节数组为主进行接收。
jdk1.9中出现了一个方法:public byte[] readAllBytes()throws IOException;
如果要读取的内容很大,会使程序崩溃。最多10K。
Writer字符输出流
jdk1.1,java.io包
类声明:public abstract class Writer extends Object implements Appendable, Closeable, Flushable
接口名 | 版本 | 方法 |
---|---|---|
Closeable | jdk1.5 | void close(); |
Flushable | jdk1.5 | void flush(); |
Appendable | jdk1.5 | void append(); |
输出方法:
方法 | 作用 |
---|---|
public void write(char[] cbuf)throws IOException; | 输出字符数组 |
public void write(String str)throws IOException; | 输出字符串 |
范例:使用write输出:
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
public class WriterTest {
public static void main(String[] args) throws Exception {
File file = new File("F:/course/file.txt");
if(!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
Writer out=new FileWriter(file,true);
String str="?????????我被冷落了QAQ\n";
out.append(str);
out.close();
}
}
使用Writer输出的最大优势在于可以使用字符串完成,字符流最大的优势在处理中文数据上。
Reader字符输入流
类声明:public abstract class Reader extends Object implements Readable, Closeable
接口名 | 版本 | 方法 |
---|---|---|
Closeable | jdk1.5 | void close(); |
Readable | jdk1.5 | void read(); |
Reader里面没有提供整个字符串的输入处理操作,只能进行字符数组进行接收。
方法名 | 作用 |
---|---|
public abstract int read()throws IOException; | 输出单个字节数据(如果数据读取到末尾,返回“-1”) |
*public int read(char[] cbuf )throws IOException; | 输出一组字节数据(返回值为读取数据的个数,如果没有数据(已经读取到底)返回“-1”) |
public int read(char[] cbuf ,int off,int len)throws IOException; | 输出一组字节数据的部分 |
范例:实现数据读取
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
public class ReaderTest {
public static void main(String[] args) throws Exception {
File file=new File("F:/course/file.txt");
if(file.exists()) {
Reader in=new FileReader(file);
char ch[]=new char[1024];
int len=in.read(ch);
System.out.println("["+new String(ch,0,len)+"]");
in.close();
}
}
}
字节流与字符流的区别
输出流比较:
OutputStream类中,不close()关闭文件内容正常输出。
Writer类中,不使用close()文件内容无法输出。
原因:Writer使用了缓存区,使用close方法会强制刷新缓冲区,close时会将内容进行刷新,否则不刷新。如果想要在不使用close的情况下使结果输出可以使用flush方法强制清空。
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
public class WriterTest {
public static void main(String[] args) throws Exception {
File file = new File("F:/course/file.txt");
if(!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
Writer out=new FileWriter(file,true);
String str="?????????我被冷落了QAQ\n";
out.append(str);
out.flush();//强制刷新
//out.close();
}
}
字节流处理的时候不会使用缓冲区,但是字符流在进行处理的时候会使用到缓冲区。使用到缓冲区的字符流更加适合中文的处理。
字节流和字符流的处理方式相似,IO很多情况下是使用二进制,所以大都以字节流为主。
转换流
实现字节流和字符流的功能转换。
两个转换流的类:InputStreamReader,OutputStreamWriter
类的声明:
OutputStreamReader | InputStreamWriter |
---|---|
public class OutputStreamWriter extends Writer | public class InputStreamReader extends Reader |
public OutputStreamWriter(OutputStream out) | public InputStreamReader(InputStream in) |
通过类的结构和构造方法可以发现,转换处理本质是对象的转型和构造方法的接收,就是通过字节流对象通过向上转型变成字符流对象。
转换流主要是为了让我们明确的对结构分析处理。
FileReader和FileWriter的继承关系:
FileReader | FileWriter |
---|---|
public class FileReader extends InputStreamReader | public class FileWriter extends OutputStreamWriter |
案例:文件拷贝
需求分析:
- 实现各种文件的拷贝,使用字节流。
- 大文件的拷贝问题。
方案:
- 使用InputStream将文件中所有的内容读到程序中,才一次性写入目标文件。(如果文件很大会让程序崩溃)
- 采用部分拷贝。
使用方法:
- public void write(byte b[],int off,int len)throws IOException;
- public void read(byte b[])throws IOException;
实现文件拷贝处理:(原始方法)
package wzr.study09.io;
import java.io.*;
public class FileUtilTest {
public static void main(String[] args) throws Exception {
if(args.length!=2) {
System.out.println("格式:java filename str1 str2");
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(File src,File des) {
srcfile=src;
desfile=des;
}
public FileUtil(String src,String des) {
this(new File(src), new File(des));
}
public boolean copy() throws Exception {
if(!srcfile.exists()) {
System.out.println("拷贝的源文件不存在");
return false;
}
if(!desfile.getParentFile().exists()) {
desfile.getParentFile().mkdirs();
}
byte bt[]=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(bt))!=-1) {
output.write(bt,0,len);
}
return true;
}catch(Exception e) {
throw e;
}finally {
if(input!=null) {
input.close();
}
if(output!=null) {
output.close();
}
}
}
}
jdk1.9开始,输入流InputStream和Reader都追加有缓存的处理方法
InputStream:public long transferTo(OutputStream out)throws Exception;
OutputStream:public long transferTo(Writer out)throws Exception;
使用转存的方式处理:
package wzr.study09.io;
import java.io.*;
public class FileUtilTest {
public static void main(String[] args) throws Exception {
if(args.length!=2) {
System.out.println("格式:java filename str1 str2");
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(File src,File des) {
srcfile=src;
desfile=des;
}
public FileUtil(String src,String des) {
this(new File(src), new File(des));
}
public boolean copy() throws Exception {
if(!srcfile.exists()) {
System.out.println("拷贝的源文件不存在");
return false;
}
if(!desfile.getParentFile().exists()) {
desfile.getParentFile().mkdirs();
}
InputStream input=null;
OutputStream output=null;
try {
input=new FileInputStream(srcfile);
output=new FileOutputStream(desfile);
input.transferTo(output);//没有避开文件过大的情况
return true;
}catch(Exception e) {
throw e;
}finally {
if(input!=null) {
input.close();
}
if(output!=null) {
output.close();
}
}
}
}
文件夹拷贝:
package wzr.study09.io;
import java.io.*;
public class FileUtilTest {
public static void main(String[] args) throws Exception {
if(args.length!=2) {
System.out.println("格式:java filename str1 str2");
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(File src,File des) {
srcfile=src;
desfile=des;
}
public FileUtil(String src,String des) {
this(new File(src), new File(des));
}
public boolean copyDir() throws Exception{
try {
copyDirImpl(this.srcfile);
return true;
}catch(Exception e) {
return false;
}
}
public boolean copyFile() throws Exception {
if(!srcfile.exists()) {
System.out.println("拷贝的源文件不存在");
return false;
}
return copyFileImpl(this.srcfile,this.desfile);
}
private void copyDirImpl(File file) throws Exception{
if(file.isDirectory()) {
File result[]=file.listFiles();
if(result!=null) {
for(File obj:result) {
copyDirImpl(obj);
}
}
}else {
System.out.println(file.getPath().replace(this.srcfile.getPath()+File.separator, ""));
// String newfilepath=file.getPath().replace(this.srcfile.getPath()+File.separator, "");
// File newfile=new File(this.desfile,newfilepath);
// copyFileImpl(file,newfile);
}
}
private boolean copyFileImpl(File srcfile,File desfile) throws Exception{
if(!desfile.getParentFile().exists()) {
desfile.getParentFile().mkdirs();
}
byte bt[]=new byte[1024];
InputStream input=null;
OutputStream output=null;
try {
input=new FileInputStream(srcfile);
output=new FileOutputStream(desfile);
//input.transferTo(output);//没有避开文件过大的情况
int len=0;
while((len=input.read(bt))!=-1) {
output.write(bt,0,len);
}
return true;
}catch(Exception e) {
return false;
}finally {
if(input!=null) {
input.close();
}
if(output!=null) {
output.close();
}
}
}
}