再回首Java第二十六天

推回输入流
在Java输入、输出流体系中有两个特殊的流与众不同,就是PushbackInputStream/PashbackReader,它们都提供了如下三个方法:
?void unread(byte[]/char[] buf):将以一个字节/字符数组内容推回到推回缓冲区里,从而允许重复读取刚刚读取的内容。
?void unread(byte[] /char[] buf, int off,int ben):把一个字节/字符数组从off开始,长度为len字节/字符的内容推回到推回缓冲区里,从而允许重复读取刚刚读取的内容
?void unread(int b):将一个字节/字符推回到推回缓冲区里,从而允许重复读取刚刚读取的内容
这两个输入了都带有一个推回缓冲区,当成程序调用者两个推回输入流的unread方法时,系统将会把指定数组里的内容推回到该缓冲区里,而推回输入流每次调用read方法时总是先从推回缓冲区读取,只有在完全读取了推回缓冲区里的内容,而且还没装满read方法中的数组形参时才会从源输入流中读取。
当我们创建PushbackInputStream/PushbackReader时需要制定推回缓冲区的大小,默认推回缓冲区的大小为1。如果推回缓冲区的大小超过了推回缓冲区的大小,程序就会引发Pushback buffer overflow的IOException

下面程序试图找出PushbackTest.java文件中的new PushbackTest字符串,找到该字符串后,程序只是打印出目标字符串之前的内容

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PushbackReader;

public class PushbackTest{
public static void main(String[] args){
PushbackReader pr=null;
try {
pr=new PushbackReader(new FileReader(".\\src\\PushbackTest.java"),128);
char[] cbuf=new char[64];
String lastContent="";
int hasRead=0;
while((hasRead=pr.read(cbuf))>0){
String content=new String(cbuf,0,hasRead);
int targetIndex=0;
if((targetIndex=(lastContent+content).indexOf("new PushbackReader"))>0){
pr.unread((lastContent+content).toCharArray());
pr.read(cbuf,0,targetIndex);
System.out.println(new String(cbuf,0,targetIndex));
return;
}else{
System.out.println(lastContent);
lastContent=content;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(pr!=null){
try {
pr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

程序第一步先创建指定缓冲大小为128的PushbackReader对象,并创建一个长度为64的字符数组来保存每次读取的字符内容再申请一个为空的字符变量用来保存上一次读取的字符串内容,hashRead用来保存每次读取的字符个数。进入while方法先看判断条件:只要没把文件读完就执行;while条件中就执行了一次读的操作,并将读取的内容截取到恰好长度的字符保存到cbuf数组中,并将读取的字符个数返回给hasRead,一次读取完毕,一次while条件判断也完成了,接着执行while循环中的语句,首先将本次读取的字符数组cbuf从下标0开始长度为hasRead转换成字符串,接着申请一个targetIndex变量用来记录目标字符串在上次读取内容和这次读取内容出现的位置,前提是本次读取内容中包含了目标子字符串,其实记录的也是推回缓冲区后目标字符串出现的位置,
下面就是if判断了看这次读取的内容是否包含目标字符串,如果没有就执行else中的内容输出上次打印测内容,并将这次读入的内容复制给lastRead作为下次打印内容的上次读取内容,如果if条件满足那么就将上次读取内容和本次读取内容作连接运算,并求出目标字符串的位置赋值给targetIndex变量,紧接着
上次读取的字符串和本次读取的字符串作连接运算后退回缓冲区,然后再次调用read方法这次read的内容就是缓冲区的内容了,读取的内容刚好从0开始长度为target刚好把cbuf放满接着就处理读取的数组并打印完成程序,由于两次读取的字符长度刚好等于缓冲区长度而没有超过所以不会发生发生Pushback buffer overfow的IOException异常。
总的来看上面程序就是不断的读取固定长度的内容并打印出来,直到读取到目标字符串,满足if条件后就做两件事打印上次读取的内容,并推回推回输入流缓冲区。
while条件里面每次read方法后都有一个隐式的记录指针记录这次读取到的位置,作为下次读取的开始位置

时间: 2024-10-10 20:25:19

再回首Java第二十六天的相关文章

再回首Java第二十五天

流的分类按照流的流向来分,可以分为输入流和输出流?输入流:只能从中读取数据,不能向其写数据?输出流:只能向其写数据,不从能从中读数据 Java输入流主要有InputStream和Reader作为基类,Java输出流主要以OutputStream和Writer作为基类 字节流和字符流字节流和字符流的用法几乎完全一样,区别在于字节流和字符流所操作的数据单元不一样:字节流操作的最小数据单元是8位的字节,而字符流操作的最小数据单元是16位的字符 节点流和处理流可以从向一个特定的IO设备(如磁盘.网络)读

再回首Java第二十四天

Callable和FutureJava1.5开始,Java提供了Callable接口,Callable接口提供了一个call()方法作为线程的执行体,但call()方法run()方法的功能更强大:?call()方法可以有返回值?call()方法可以声明抛出异常因此我们完全可以提供一个Callable对象作为Thread的target,而该线程的执行体就是该Callable对象的call方法.问题是:Callable对象时JDK1.5开始新增的接口,而它并不是Runnable的子接口,所以Call

再回首Java第二十二天

类加载器的种类: 1.Bootstrap ClassLoader: 负责加载Java核心类,即$JAVA_HOME/jre/lib/rt.jar,由C++实现 2.Extension ClassLoader: 负责加载Java平台扩展功能的一些jar,包括$JAVA_HOME/jre/lib/*.jar和$JAVA_HOME/jre/lib/ext/*.jar 3.System(App) ClassLoader:负责加载classpath中指定的jar或.class 4.Custom Class

再回首Java第十五天

类的加载 当程序主动使用某个类是,如果该类还没有被加载到内存中,系统就会通过加载.链接.初始化三个步骤对该类进行初始化,如果没有意外,JVM就会完成这三个步骤,所以有时也把这三个步骤称为类的加载和类的初始化. 类的加载是指将类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说当程序中使用任何类时,系统都会为之创建一个java.lang.Class对象 类的加载由类的加载器完成,类加载器通常由JVM提供,这些类加载器是程序运行的基础,JVM提供的加载器其称为系统加

再回首Java第二十七天

泛型与数组 JDK1.5还有一个很重要的设计原则:如果一段代码在编译时系统没有产生:”unchecked未经检测的转换“,则程序在运行时不会引发”ClassCastException“异常.正是基于这个原因,所以数组元素的类型不能包含类型变量或类型形参,除非是无上限的类型通配符.但可以声明这样的数组,即使声明元素类型包含类型变量或类型形参的数组.也就是说:只能声明List<String>[]数组,但不能创建ArrayList<String>[10]这样的数组对象 假设Java能支持

再回首Java第二十三天

序列化版本问题由于反序列化Java对象时必须提供该对象的class文件,现在的问题是随着项目的升级,系统class文件也会升级,Java如何保证两个class文件的兼容性呢Java序列化机制允许为序列化类提供一个private static final 的serialVersionUID属性值,该属性用于该Java类的序列化版本,也就是说如果一个类升级后,只要它的serialVersionUID属性值保持不变,序列化机制也会把它们当成同一个序列化版本为了在反序列化时,确保序列化版本的兼容性,最好

再回首Java第十四天

Calendar的实例方法setLenient(boolean bool)用于设置其容错性 Calendar两种的模式:lenient模式和no-lenient模式,当Calendar处于lenient模式时,每个时间字段可以接受超出它允许范围的值.当处于no-lenient模式时,当某个时间字段设置的值超出了它允许的范围,程序将抛出异常. set方法延迟修改 set(f,value)方法将日历字段f更改为value,此外它还设置了一个内部成员变量,以指示日历字段已被修改.尽管日历字段f是立即修

再回首Java第十天

内部类 大部分时候,我们把类定义成一个独立的程序单元.在某些情况下,我们需要把一个类放在另一个类的内部定义,这个定义在其它类内部的类被称为内部类(嵌套类),包含内部类的类称为外部类(宿主类).内部类有如下作用 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同包的其它类访问.假设创建Cow这个类,需要组合CowLeg的属性,CowLeg属性只有在Cow类中有效,离开了Cow类就没有意义,这种情况下可以把CowLeg定义成Cow的内部类,不允许其它外部的类访问 内部类成员可以直接访问外

再回首Java第十九天

类加载机制 JVM的类加载机制主要有一下三种加载机制 全盘负责:所谓全盘负责就是说当一个类加载器负责加载某个Class的时候,该Class所依赖和引用的其它的Class都由该类加载器负责加载,除非显式使用另一个类加载器来载入 父类委托:所谓父类委托则是先让父类加载器试图加载该Class,只有在父类加载器无法加载时才从自己的类路径中加载该类 缓存机制:缓存机制保证所有被加载的Class都会被缓存,当程序中使用某个Class时,类加载器先从缓存中寻找该Class,只有当缓存中不存在该Class时,系