文章目录
- 字节流和字符流
- 1流操作简介
- 2、字节输出流(OutputStream)
- AutoCloseable的自动关闭支持
- 3字节输入流:(InputStream)
- 4、字符输出流:(Writer)
- 5、字符输入流:Reader
- 6、字节流VS字符流
字节流和字符流
1流操作简介
File类不支持文件内容处理,如果要处理文件内容,必须要通过流的操作模式来完成。流分为输入流和输出流。在java.io包中,流分为两种:字节流与字符流
字节流(byte):(针对字节的处理,比较底层)InputStream、OutputStream
字符流(char):(针对于字符的处理)Reader、Writer
字节流与字符流的区别只有一个,字节流是原生操作,字符流是处理后的操作。
在进行网络数据传输,磁盘数据保存所保存所支持的数据类型只有:字节。
而所有磁盘中的数据必须先读取到内存后才能进行操作,而内存中会帮助我们把字节变为字符。字符更加适合处理中文。
流操作流程:
无论是字符流还是字节流,操作流程几乎一样,以文件操作为例
1:取得File对象
2:取得file对象的输入、输出流
3:进行数据的读取或写入操作
4:关闭流(close()).
IO处理属于资源操作,所有资源处理(IO、数据库、网络)使用后都必须关闭。
2、字节输出流(OutputStream)
如果要想通过程序进行内容输出,则可以使用java.io.OutoutStream
来观察OutputStream类的定义结构
public abstract class OutputStream implements Closeable,Flushable
OutputStream类实现了Closeable、Flushable两个接口,这两个接口中的方法:
Closeable:public void close() throws IOException;
Flushable:public void flush() throws IOException;
在OutputStream类中还定义有其他方法:
将指定的字节数组全部输出:public void write(byte[] b)throws IOException;
将部分字节数组输出:public void write(byte[] ,int offset,int len)throws IOException;
输出单个字节:public abstract void write(int b)throws IOException;
由于OutputStream是一个抽象类,所以要想为父类实例化,就必须使用子类。由于方法名称都由父类声明好了,所以我们在此处只需要关心子类的构造方法。如果要进行文件的操作,可以使用FileOutputStream类来处理。这个类的构造方法如下:
文件内容覆盖:public FileOutputStream(File file)throws FileNotFoundException ;
文件内容追加:public FileOutputStream(File file,boolean append);
实现文件的内容输出:
//实现我们文件内容的输出
import java.io.*;
public class OutputStreamTest {
public static void main(String[] args) throws IOException {
File file=new File("C:"+File.separator+"Users"+File.separator+"白乐荣"+
File.separator+"Desktop"+File.separator+"Tset.txt");
//如果文件不存在就创建
if(!file.getParentFile().exists()){//必须保证父目录存在
file.getParentFile().mkdirs();//创建多级目录
}
//OutputStream是一个抽象类,所以需要通过子类进行实例话,此时自能操作File类
OutputStream outputStream=new FileOutputStream(file);
//要求输入到文件的内容
String msg="我爱编程,我爱java";
//将内容变为字节数组
outputStream.write(msg.getBytes());
//关闭输出
outputStream.close();
}
}
执行上面的代码,你将会发现桌面上出现了一个Tset.txt文件,里面的内容就是我爱编程,我爱Java。
在进行我们文件输出的时候,所有的文件会自动帮助用户创建,不需要调用createFile()手工创建。这个程序如果重复执行,并不会出现追加的情况而是一直覆盖,如果需要文件内容追加,则需要调用FileOutputStream提供的另外一种构造方法。
实现我们的文件内容的追加
//实现我们的追加程序
import java.io.*;
public class FileAddTest {
public static void main(String[] args) throws IOException {
File file=new File("C:"+File.separator+"Users"+File.separator+"白乐荣"+
File.separator+"Desktop"+File.separator+"Tset.txt");
//如果文件不存在就创建
if(!file.getParentFile().exists()){//必须保证父目录存在
file.getParentFile().mkdirs();//创建多级目录
}
//OutputStream是一个抽象类,所以需要通过子类进行实例化,此时只能操作File类
OutputStream outputStream=new FileOutputStream(file,true);
//要求输出到文件的内容
String msg="我爱我自己";
//将内容变为字节数组
outputStream.write(msg.getBytes());
//关闭输出
outputStream.close();
}
}
我们将上述代码执行几遍,就会出现几次我们的“我爱我自己”。
这就是我们的文件内容追加。
接下来,我们在实现一个文件部分内容输出的代码
部分输出有时会产生乱码
AutoCloseable的自动关闭支持
从JDK1.7开始追加一个AutoCloseable接口,这个接口的主要目的是自动进行关闭处理,但是这种处理一般不用,因为它必须结合try…catch
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class TestIO {
public static void main(String[] args) throws Exception{
File file = new File(File.separator + "Users" + File.separator + "白乐荣" +
File.separator + "Desktop"
+ File.separator + "hello.txt");
if (!file.getParentFile().exists()) { // 必须保证父目录存在
file.getParentFile().mkdirs() ; // 创建多级父目录
}
// OutputStream是一个抽象类,所以需要通过子类进行实例化,此时只能操作File类
try(OutputStream output = new FileOutputStream(file,true)){
// 要求输出到文件的内容
String msg = "我爱我自己\n" ;
// 将内容变为字节数组
output.write(msg.getBytes());
}catch (Exception e) {
e.printStackTrace();
}
}
}
必需要在try的括号里面调用对象。比较鸡肋,一般不建议使用这种方法。
3字节输入流:(InputStream)
我们已经实现了利用OutputStream将程序内容输出到文件的处理,下面使用InputStream类在程序中读取文件内容。
//InputStream类的定义如下:
public abstract class InputStream implements Closeable
发现InputStream类只实现了Closeable接口,在InputStream类中提供有入下方法:
1、读取数据到字节数组中,返回数据的读取个数。如果此时开辟的字节数组大小大于读取的数据大小,则返回的就是读取个数;如果要读取的数据大于数组内容,那么这个时候返回的就是数组长度;如果没有数据了还在读,则返回-1:public int read(byte b[])throws IOException.最常用的方法
2、读取部分数据到字节中,每次只读取传递数组的部分内容,如果读取满了则返回长度(len),如过没有读取满则返回读取的数据个数,如果读到最后没有数据了返回-1:public int ready(byte b[],int off,int len)throws IOException
3、读取单个字节,每次读取一个字节的内容明知道没有数据了返回-1:public abstract int read()throws IOException;
同OutputStream的使用一样,InputStream是一个抽象类,如果要对其实例化,同样也需要使用子类,如果文件进行处理,则使用FileInputStream类。
import java.io.*;
public class PartOutTest {
public static void main(String[] args) throws IOException {
//定义文件路径
File file=new File("C:"+File.separator+"Users"+File.separator+"白乐荣"+
File.separator+"Desktop"+File.separator+"Tset.txt");
//必须保证文件存在才能进行处理
if(file.exists()){
InputStream inputStream=new FileInputStream(file);
byte[] data=new byte[1024];
int len=inputStream.read(data);
//将数据读取到字符数组中,这个字符数组相当于一个缓冲区
String result=new String(data,0,len);
System.out.println("读取内容【"+result+"】");
inputStream.close();
}
}
}
我们从我们的整个操作流程可以发现OutputStream、InputStream类的使用形式上是非常类似的。
流程整理
1:取得File对象
2:取得file对象的输入、输出流
3:进行数据的读取或写入操作
4:关闭流(close()).
4、字符输出流:(Writer)
字符适合于处理中文数据,Writer是字符输出流的处理类,这个类的定义如下:
public abstract class writer implements Appendable,Closeable,Flushable
与我们的OutputStream相比多了一个Appendable接口
在Writer类里面也提供write()方法,而且该方法接收的类型都是char型,要注意的是,Writer类提供了一个直接输出字符串的方法:
//直接输出字符串
public void write(String str) throws IOException
如果要操作文件使用FileWriter子类
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class WriterTest {
public static void main(String[] args) throws IOException {
//取得文件对象
File file=new File("C:"+File.separator+"Users"+File.separator+"白乐荣"+
File.separator+"Desktop"+File.separator+"Hello.txt");
//如果文件不存在,就创建
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
String msg="我爱我家";
//获取输出流
Writer writer=new FileWriter(file);
//写入文件
writer.write(msg);
//关闭流
writer.close();
}
}
Writer类的结构与方法的使用与OutputStream非常相似,只是Writer类对于中文的支持很好并且提供了直接写入的String的方法而已。
5、字符输入流:Reader
Reader依然也是一个抽象类。如过要进行文件读取,同样的,使用FileReader。
Writer类中提供有方法直接向目标元写入字符串,而在Reader类中没有方法可以直接读取字符串类型,这个时候只能通过字符数组进行读取操作。
import java.io.*;
/*
* 这是一个将文件内容读取到输入流的程序
* */
public class ReadTest {
public static void main(String[] args) throws IOException {
File file=new File("C:"+File.separator+"Users"+File.separator+"白乐荣"+
File.separator+"Desktop"+File.separator+"Hello.txt");
//取得文件按后保证文件存在
if(file.exists()){
//获取输入流
Reader in=new FileReader(file);
char [] data=new char[1024];
int len=in.read(data);
//进行数据读取
String result=new String(data,0,len);
System.out.println("读取内容【"+result+"】");
in.close();
}
}
}
6、字节流VS字符流
使用字节流和字符流在代码的形式上差距不大,但是在实际的开发过程中,字节流一定是优先考虑的只有在处理中文的时候才会考虑字符流。因此所有的字符都需要通过内容缓冲来进行处理。所有的字符都需要通过内存缓冲来进行所有字符流的操作,无论写入还是输出,数据都先保存在缓存中。
//如果字符流不关闭,数据就有可能保存在缓存中没有输出到目标元。这种情况下就必须强制刷新才能够得到完整的数据。
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
/*
* 字符流的刷新操作*/
public class FlushTest {
public static void main(String[] args) throws IOException {
File file=new File("C:"+File.separator+"Users"+File.separator+"白乐荣"+
File.separator+"Desktop"+File.separator+"Hello.txt");
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
String msg="我爱你";
Writer out=new FileWriter(file);
out.write(msg);
out.flush();//表示强制清空缓冲内容,所有内容输出
}
}