1、缓冲流
首先我们要明确一个概念,对文件或其他目标频繁的读写操作,效率是很低的,性能也是会变差的,所以我们的缓冲流就来了,使用缓冲流的好处是,能够高效的读写信息,原理是将数据先缓冲起来,然后一起写入或读取出来
BufferedInputStream: 为另一个输入流添加一些功能,在创建BufferedInputStream时,会创建一个内部缓冲区数组,用于缓冲数据
BufferedOutputStream: 通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统
BufferedReader: 从字符输入流中读取文本,缓冲各个字符,从而实现字符,数组和行的高效读取
BufferedWriter: 将文本写入字符输出流,缓冲各个字符,从而提供单个字符,数组和字符串的高效写入
/***
* 缓存的目的
* 解决在写入文件操作时,频繁的操作文件所带来的性能降低的问题
* 内部默认缓存大小是8kb,每次写入使先缓存到byte数组里,当数组存满时,会把数组中的数据写入文件,缓存下标归零
*
*
* 字符流
* 1、加入字符缓存流,增强读取功能(readline)
* 2、更高效的读取数据
* FileReader :内部使用InputStreamReader(sun。nio。cs.StreamDecoder-》解码过程 byte->char 默认有8kb的缓冲,大小不可变
* BufferReader 默认缓存大小8K,可以手动指定缓存大小 ,把数据直接读取到缓存中,减少每次转换过程;效率更高
*/public class BufferStreamDemo {
public static void charReader() throws IOException {
File file=new File("D:\test\a.txt");
Reader reader=new FileReader(file);
//为字符流提供缓冲,以达到高效读取的目的
BufferedReader bufferedReader=new BufferedReader(reader);
char[] cs=new char[1024];
int len=-1;
while ((len=bufferedReader.read(cs))!=-1){
System.out.println(new String(cs,0,len));
}
bufferedReader.read();
bufferedReader.close();
}
public static void charWriter() throws IOException {
File file=new File("D:\test\a.txt");
Writer writer=new FileWriter(file);
BufferedWriter bufferedWriter=new BufferedWriter(writer);
bufferedWriter.write("teststr");
bufferedWriter.close();
}
private static void testWeitr() throws IOException {
File file=new File("D:\test\a.txt");
OutputStream outputStream=new FileOutputStream(file);
//构造一个字节缓冲流
//默认缓存大小是8kb,
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(outputStream);
String info="孤独";
bufferedOutputStream.write(info.getBytes());//
bufferedOutputStream.close();//关闭流刷新缓存
}
private static void testRead() throws IOException {
File file=new File("D:\test\a.txt");
InputStream inputStream=new FileInputStream(file);
BufferedInputStream bufferedInputStream=new BufferedInputStream(inputStream);
byte[] bytes=new byte[1024];
int len=-1;
while ((len=bufferedInputStream.read(bytes))!=-1){
}
bufferedInputStream.close();
}
public static void main(String[] args) {
}
}
复制代码
在关闭流的时候,只需要关闭缓冲流就可以,在缓冲流的关闭方法里会自动关闭包裹的那一层非缓冲流
2、打印流
打印流的主要功能是用于输出,在整个IO包中打印流分为两种类型
字节打印流:PrintStream
字符打印流:PrintWriter
打印流可以很方便的进行输出
public class PrintStreamDemo {
public static void bytePrint() throws FileNotFoundException {
File file=new File("D://test//a.txt");
OutputStream outputStream=new FileOutputStream(file);
//加缓存
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(outputStream);
//增强打印功能,提供很多Print方法,方便我们输出
PrintStream printStream=new PrintStream(bufferedOutputStream);
printStream.println("testStr");
printStream.close();
}
public static void charPrint() throws IOException {
File file=new File("D://test//a.txt");
Writer writer=new FileWriter(file);
BufferedWriter bufferedWriter=new BufferedWriter(writer);
PrintWriter printWriter=new PrintWriter(bufferedWriter);
printWriter.println("testStr");
printWriter.close();
}
}
复制代码
3、对象流与序列化
对象流的两个类:
ObjectOutputStream 将Java对象的基本数据类型和图形写入OutputStream
ObjectInputStream 对以前使用ObjectOutputStream写入的基本数据对对象进行反序列化
序列化一组对象
序列字面意思有序的排列,在序列化操作中,同时序列化多个对象时,反序列化也必须按顺序操作,如果想要序列化一组对象该如何操作呢?
序列化一组对象可采用,对象数组的形式,因为对象数组可以向Object进行转型操作
transient关键字 如果用transient声明一个实例对象,当对象存储时,它的值不需要维持,
我们什么时候才去序列化对象呢?
- 对象需要存储到物理介质中(文件)
- 对象需要在网络中传输的时候
我们都知道对象是一个类的实例,只有在程序运行的时候,对象才存在,我们序列化的时候,只是把对象的属性,类名,属性类型,和属性值写入流中,并不是把这个对象写入流中,在反序列化的时候,也是在程序中新建一个对象,将流中的数据再设置到新对象中
public class Dog implements Serializable {
private static final long serialVersionUID=1l;
private transient int id;//在序列化中被忽略
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Dog() {
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}
复制代码
public class ObjectStreamDemo {
/***
* 序列化
* 把对象写入文件:实际写入的是类名,属性名,属性类型,属性的值
* @throws IOException
*/ public static void writeObject() throws IOException {
Dog dog=new Dog("凉白开",23);//实现Serializable接口,
File file=new File("D://test//dog.obj");
OutputStream outputStream=new FileOutputStream(file);
ObjectOutputStream objectOutputStream=new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(dog);
objectOutputStream.close();
}
/***
* 反序列化
* 从文件中把对象的内容读取出来,还原成对象
* @throws IOException
*/ public static void readObject() throws IOException, ClassNotFoundException {
Dog dog=new Dog("凉白开",23);//实现Serializable接口,
File file=new File("D://test//dog.obj");
InputStream inputStream=new FileInputStream(file);
ObjectInputStream objectInputStream=new ObjectInputStream(inputStream);
Dog dog1= (Dog) objectInputStream.readObject();
objectInputStream.close();
System.out.println(dog1);
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
writeObject();
readObject();
}
}
复制代码
4、字节数组流
ByteArrayInputStream: 包含一个内部缓冲区,该缓冲区包含从流中读取的字节,内部计数器跟踪 read方法要提供的下一个字节。关闭ByteArrayInputStream流无效,此类中的方法在关闭流后仍可被调用,而不会产生任何IO异常
ByteArrayOutputStream: 此类实现了一个输出流,其中的数据被写入一个byte数字,缓冲区随着数据的不断写入而自动增长,可使用toByteArray和toString方法进行获取数据,关闭ByteArrayOutputStream流无效,此类中的方法在关闭流后仍可被调用,而不会产生任何IO异常
用于对数据进行一个字节一个字节的分析,给定一个字符串,输出里边的所有字母
/***
* 字节数组流
* 基于内存操作,内部维护着一个数组,我们可以利用流的读取机制来处理字符串
* 无需关闭
*/public class ByteArrayStreamDemo {
private static void byteArray(){
String str="AAAAAAAAbbbbbbbbbbC123$$$$$$$$$$";
ByteArrayInputStream bais=new ByteArrayInputStream(str.getBytes());
ByteArrayOutputStream baos=new ByteArrayOutputStream();
int current=-1;//每次读取的字节
while ((current=bais.read())!=-1){
if(current>=65&¤t<=90||(current>=97&¤t<=122)){
baos.write(current);
}
}
//无需关闭流, 字节数组流基于内存操作,和文件无关
System.out.println(baos.toString());
}
public static void main(String[] args) {
byteArray();
}
}
复制代码
5、数据流
DataInputStream: 数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本的Java数据类型,应用程序可以使用数据输出流写入,稍后由数据输入流读取的数据。DataInput对于多线程访问不一定是安全的
DataOutputStream: 数据输出流允许应用程序以适当的方式将数据将Java基本数据类型写入输出流中,然后应用程序可以使用数据输入流将数据读入
/***
* 数据流
* 与机器无关的操作Java的基本数据类型
*/public class DataStreamDemo {
private static void write() throws IOException {
File file=new File("D://test//a.txt");
OutputStream out= new FileOutputStream(file);
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(out);//加入缓存
DataOutputStream dout=new DataOutputStream(bufferedOutputStream);
dout.writeBoolean(true);
dout.writeInt(88);
dout.writeUTF("我是个单身狗儿");
dout.close();
}
private static void read() throws IOException {
File file=new File("D://test//a.txt");
InputStream in= new FileInputStream(file);
BufferedInputStream bufferedInputStream=new BufferedInputStream(in);//加入缓存
DataInputStream dataInputStream=new DataInputStream(bufferedInputStream);
System.out.println(dataInputStream.readBoolean());//读取要按顺序读
System.out.println(dataInputStream.readInt());
System.out.println(dataInputStream.readUTF());
dataInputStream.close();
}
public static void main(String[] args) throws IOException {
write();
read();
}
}
复制代码
6、文件分割、合并,合并流
文件分割:
/***
* 文件分割
* @param targetFile 切割的File
* @param cutSize 每个文件的大小
*/private static void division(File targetFile,long cutSize) throws IOException {
if(targetFile==null) return;
//计算总分割的文件数
int number=targetFile.length()%cutSize==0?(int)(targetFile.length()/cutSize):(int)(targetFile.length()/cutSize+1);
//构造文件输入流
BufferedInputStream bufferedInputStream=new BufferedInputStream(new FileInputStream(targetFile));
BufferedOutputStream bufferedOutputStream=null;//new BufferedOutputStream(new FileOutputStream(new ));
byte[] bytes=null;//每次要读取的字节数
int len=-1;//读取的实际长度
int count=0;//每个文件要读取的字数
//循环的次数为生成文件的个数
for (int i=0;i<number;i++){
bufferedOutputStream=new BufferedOutputStream(new FileOutputStream(new File("D://test//test//"+(i+1)+"-temp-"+targetFile.getName()) ));
if(cutSize<=1024){
bytes=new byte[(int)cutSize];
count=1;
}else{
bytes=new byte[1024];
count=(int)cutSize/1024;
}
while (count>0 && (len=bufferedInputStream.read(bytes))!=-1){
bufferedOutputStream.write(bytes,0,len);
bufferedOutputStream.flush();
count--;
}
//计算每个文件大小除以cutsize的余数来判断是否再读取一次
if(cutSize%1024!=0){
bytes=new byte[(int)cutSize%1024];
len = bufferedInputStream.read(bytes);
bufferedOutputStream.write(bytes,0,len);
bufferedOutputStream.flush();
bufferedOutputStream.close();
}
}
bufferedInputStream.close();
}
复制代码
合并流: SequenceInputStream表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止
/***
* 文件合并
*/private static void merge(String filePath) throws IOException {
Vector<InputStream> v=new Vector<>();//内部实现是数组
for (int i=0;i<5;i++){
InputStream in=new FileInputStream(new File("test"+i));
v.add(in);
}
Enumeration<InputStream> enumeration= v.elements();
//构造一个合并流
SequenceInputStream sequenceInputStream=new SequenceInputStream(enumeration);
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(new File(filePath)));
byte[] bytes=new byte[1024];
int len=-1;
while ((len=sequenceInputStream.read(bytes))!=-1){
bos.write(bytes,0,len);
bos.flush();
}
bos.close();
sequenceInputStream.close();
}
复制代码
7、字符串流
StringReader:起源为一个字符串的字符流
StringWriter:一个字符流,可以用其回收在字符串缓冲区中的输出来构造字符串。关闭StringWriter无效,不会产生任何IO异常
统计一个句子有多少个单词!
private static void strReader() throws IOException {
String info ="Hello Andorid";
StringReader sr=new StringReader(info);
//流标记器
StreamTokenizer streamTokenizer=new StreamTokenizer(sr);
int count=0;
while (streamTokenizer.ttype!=StreamTokenizer.TT_EOF){
if(streamTokenizer.nextToken()==StreamTokenizer.TT_WORD){
count++;
}
}
sr.close();
}
复制代码
8、管道流
管道输入流应该连接到管道输出流,管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从PipedInputStream对象读取,并由其他线程将其写入相应的PipedOutputStream.不建议对着两个对象尝试使用单线程,因为这样可能会产生死锁,管道输入流包含一个缓冲区,可在缓冲区限定的范围将读写操作分离,如果向连接管道输出流提供字节的线程不再存在,则认为该管道已损坏
/***
* 管道流:一个线程写入,一个线程读取
* 用于线程之间的数据通讯
*/public class PipedStreamDemo {
public static void main(String[] args) throws IOException {
PipedInputStream pin=new PipedInputStream();
PipedOutputStream pout=new PipedOutputStream();
//两个管道进行连接
pin.connect(pout);//进行连接
ReadRunnable readRunnable=new ReadRunnable(pin);
WriterRunnable writerRunnable=new WriterRunnable(pout);
new Thread(readRunnable).start();
new Thread(writerRunnable).start();
}
}
class ReadRunnable implements Runnable{
PipedInputStream pin;
public ReadRunnable(PipedInputStream pin) {
this.pin=pin;
}
@Override
public void run() {
byte[] bytes=new byte[1024];
try {
int len=pin.read(bytes);
System.out.println(new String(bytes,0,len));
pin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class WriterRunnable implements Runnable{
PipedOutputStream pout;
public WriterRunnable(PipedOutputStream pout) {
this.pout=pout;
}
@Override
public void run() {
try {
pout.write(new String("当初要不是女朋说不学安卓就分手,他就不会陷入这个无底洞,结果安卓学成了,女朋友却跟隔壁的PHP走了").getBytes());
pout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
复制代码
9、总结
Java中的各种流都有各自的特性,和应用场景,其实也并不复杂,多写多练掌握了就好,领导再也不怕我写与IO的业务了,如果对兄弟们有收获,一键三连哦!