-----------android培训、java培训、java学习型技术博客、期待与您交流!------------
IO流
一、概述
1.IO流是用来处理设备之间的数据传输。
2.Java对数据的操作时通过流的方式。
3.Java用于操作流的对象都在IO包中。
4.流按操作数据分为两种:字节流和字符流。流按流向分为:输入流和输出流。
5.流常用的基类:
1)字节流的抽象基类:ImputStream和OutputStream。
2)字符流的抽象基类:Reader和Writer。
6.体系架构:
字符流体系架构:
Reader:用于读取字符流的抽象类。
|--BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
|--LineNumberReader:跟踪行号的缓冲字符输入流。
|--InputStreamReader:转换流,是字节流通向字符流的桥梁。
|--FileReader:用来读取字符文件的便捷类。
Writer:写入字符流的抽象类。
|--BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
|--OutputStreamWriter:转换流,是字符流通向字节流的桥梁。
|--FileWriter:用来写入字符文件的便捷类。
字节流体系架构:
ImputStream:此抽象类是表示字节输入流的所有类的超类。
|--FileInputStream:从文件系统中的某个文件中获得输入字节, 用于读取诸如图像数据之类的原始字节流。
|--FilterInputStream:
|--BufferedInputStream:字节输入流缓冲区。
OutputStream:此抽象类是表示输出字节流的所有类的超类,输出流接受输出字节并将这些字节发送到某个接收器。
|--FileOutputStream:文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流,用于写入诸如图像数据之类的原始字节的流。
|--FilterOutputStream:此类是过滤输出流的所有类的超类。
|--BufferedOutputStream:该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
|--PrintStream:为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。打印的所有字符都使用平台的默认字符编码转换为字节。PrintStream 永远不会抛出 IOException,而是,异常情况仅设置可通过 checkError 方法测试的内部标志。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
问题思考:字节流和字符流有什么不同?
答:字节流可以用于进行任何数据类型的操作,而字符流带编码表,只能用于进行纯文本数据类型的操作。
二、格式示例
IO异常格式的处理:
import java.io.*;
class FileWriterDemo2
{
public static void main(String[] args)
{
//需要将fw引用定义在外部,因为在finally中需要调用close方法
FileWriter fw=null;
try
{
//创建对象的时候,需要定义在try里面,因为抛出了IO异常。
fw=new FileWriter("E:\\heima\\deme2.txt");
//write方法抛出了IO异常。
fw.write("hello world");
}
catch (IOException e)
{
System.out.println("cacht="+e.toString());
}
finally
{
//关闭流资源一定要执行,需定义在finally内部,且close方法抛出了
//IO异常,需要进行try处理。
try
{
//需要对fw是否为null进行判断,因为如果fw为null,则再调用close方法
//发发生NullPoterException异常。如果有多个流资源需要关闭,应进行多次
//判断并关闭,不要写在一个判断里。
if (fw!=null)
{
fw.close();
}
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
}
}
}
三、字符流
字符流常用对字符文件的操作。对于字符流的操作,应熟练掌握以下几个内容:
1.在指定目录下创建一个纯文本文件,并在这个文件中写入指定内容:
示例1:在"E:\\heima"的目录下创建纯文本文件demo.txt,并向该文件demo.txt写入内容。
import java.io.*;
class FileWriterDemo
{
public static void main(String[] args) throws IOException
{
FileWriter fw=null;
try
{
//fileWriter对象一被初始化就必须明确被操作的文件的对象。
//而且该文件会被创建到指定目录下,如果该目录下已有同名文件存在,
//则将被覆盖。由于创建的目录可能不存在,因此抛出了IOException。
fw=new FileWriter("E:\\heima\\demo.txt");
//调用write方法,将字符串写入到流中。
fw.write("nihao");
//刷新流对象中的缓冲区的数据。将数据刷到目的地中。
fw.flush();
//可重复进行写入数据到文件中。
fw.write("hello");
fw.flush();
}
catch (IOException e)
{
System.out.println("cacht="+e.toString());
}
finally
{
try
{
if (fw!=null)
{
//关闭流对象,在关闭前会刷新流一次。关闭后不能再向流中写入数据,
//否则将发生IO异常。该动作一定做,因为流在调用window资源,需要进行关闭。
fw.close();
}
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
}
}
}
程序运行后在"E:\\heima"的目录下的创建了一个Demo.txt文件,文件的内容如下图:
2.对指定目录下的一个纯文本文件进行内容续写:
示例2:对示例1中创建的文件demo.txt进行内容续写。
import java.io.*;
class FileWriterDemo3
{
public static void main(String[] args)
{
FileWriter fw=null;
try
{
//传递一个true穿参数,代表不覆盖已有文件,并在已有文件末尾处添加。
//如果没有已有文件,则会新创建一个文件。
fw=new FileWriter("E:\\heima\\demo2.txt",true);
//添加内容
fw.write("zhangsan");
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
if (fw!=null)
{
fw.close();
}
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
}
}
}
程序运行后,在示例1中的demo.txt文件内容中续写了一些指定内容,截图如下:
3.写入内容的方法总结:
1)写入单个字符。
void write(int c):将int类型的字符c写入到指定目的对象中。
2)写入字符数组或其中的某一部分。
void write(char[] cbuf):将字符数组cbuf中的全部字符写入到指定对象中。
void write(char[] cbuf,int off,int len):将字符数组cbuf中从索引off开始写入到指定对象,写入的字符总数为len。
3)写入字符串或其中的某一部分。
void write(String str):将整个字符串内容写入到指定对象中。
void write(String str,int off,int len):将字符串str中从索引off开始写入到指定对象,写入的字符总数为len。
注:上述所有写入的方法都会抛出IOException异常。
4.对指定目录下的一个纯文本文件进行内容读取:
示例3:对示例2续写的文件demo.txt进行内容读取并打印到控制台。
import java.io.*;
class FileReaderDemo1
{
public static void main(String[] args)
{
FileReader fr=null;
try
{
//创建一个文件读取流对象,和指定的文件demo.txt相关联。
//要保证该文件是存在的,如果不存在则会发生FileNotFoundException异常。
fr=new FileReader("E:\\heima\\demo.txt");
int ch=0;
//read()方法读取文件的内容,一次读一个字符,而且会自动往下读。
//循环读取文件内容,如果读到文件末尾则会返回-1,可根据此条件
//来判断是否继续读取。
while ((ch=fr.read())!=-1)
{
System.out.print((char)ch); //将读取到int类型数据转换成字符类型输出。
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
if (fr!=null)
{
fr.close();
}
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
}
}
}
程序运行后的结果如下图:
5.读取内容的方式总结:
1)读取单个字符。
int read():返回值为读取的int类型字符。如果读取的字符已到达流的末尾,则返回 -1。
2)将字符读入数组或数组中的某一部分。
int read(char[] cbuf):将字符读入数组。在某个输入可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。
int read(char[] cbuf,int offset,int length):将字符读入字符数组cbuf中,从索引offset开始存储,存入的字符个数限制为length。
注:返回值为实际存储到字符数组的个数。如果读取的字符数已到达流的末尾,则返回 -1。
3)将字符读入指定的字符缓冲区。了解charBuffer更多用法,参加AIP文档。
int read(CharBuffer target):返回值为添加到缓冲区的字符数量,如果此字符源位于缓冲区末端,则返回 -1。
6.字符流缓冲区:
1)BufferedWriter:字符流写入缓冲区。
写入数据的方法有:
a)写入单个字符:
void write(int c):该方法抛出了IO异常。
b)写入字符数组:
void write(char[] c):该方法抛出了IO异常。
c)写入字符数组的某一部分:
void write(char[] cbuf, int off, int len) :该方法抛出了IO异常。
d)写入字符串:
void write(String str):该方法抛出了IO异常。
e)写入字符串的某一部分:
write(String s, int off, int len):如果 len 参数的值为负数,则不写入任何字符。这与超类中此方法的规范正好相反,它要求抛出 IndexOutOfBoundsException。该方法抛出了IO异常。
示例4:使用缓冲区技术,在"E:\\heima"的目录下创建纯文本文件test.txt,并向该文件test.txt写入内容。
<pre name="code" class="java">import java.io.*;程序运行后在"E:\\heima"的目录下生成了test.txt文件,文件内容如下截图所示:
class BufferedWriterDemo
{
public static void main(String[] args)
{
//创建字符流读取缓冲区引用
BufferedWriter bufw=null;
try
{
//创建字符流读取缓冲区对象,并传入文件读取流对象
bufw=new BufferedWriter(new FileWriter("E:\\heima\\test.txt"));
//将字符串内容写入到字符流读取缓冲区中
bufw.write("nihao");
//刷新缓冲区中的数据,在写入内容不多的情况下,可以不用flush方法,但一定要close方法关闭
//资源,即将写入的内容最后关闭的时候一次性刷入文件中。如果写入内容很多,应加flush,防止
//在还没有刷新的情况下,出现停电,导致数据丢失。当加了flush后,可以即时将数据刷入文件中。
bufw.flush();
//换行
bufw.newLine();
bufw.write("nihao");
}
catch (IOException e)
{
throw new RuntimeException("文件写入失败");
}
finally
{
if (bufw!=null)
{
try
{
bufw.close();
}
catch (IOException ex1)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
}
2)BufferedReader:字符流读取缓冲区。
读取数据的方法有:
a)读取单个字符:
int read():返回字符的int类型值。如果已到达流末尾,则返回 -1。
b)将字符读入数组:
int read(char[] cbuf):返回读入的字符数。如果已到达流的末尾,则返回 -1。在某个输入可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。
c)将字符读入数组的某一部分:
int read(char[] c,int off,int len):返回读取的字符数。如果已到达流末尾,则返回 -1。
d)读取一个文本行:
int readLine():返回包含该行内容的字符串,不包含任何行终止符。如果已到达流末尾,则返回 null。
e)试图将字符读入指定的字符缓冲区:
int read(charBuffer target):返回添加到缓冲区的字符数量,如果此字符源位于缓冲区末端,则返回 -1。
注:上述方法都抛出了IO异常。
示例5:使用缓冲区技术,对示例4创建的test.txt文件进行内容读取并打印输出到控制台。
import java.io.*;程序运行后的结果如下截图所示:
class BufferedReaderDemo
{
public static void main(String[] args)
{
//创建字符流读取缓冲区引用
BufferedReader bufr=null;
try
{
//创建字符流读取缓冲区对象,并传入文件读取流对象
bufr=new BufferedReader(new FileReader("E:\\heima\\test.txt"));
String line=null;
//调用字符流读取缓冲区对象的readLine方法,如果判断不为空,则继续读下一行
while ((line=bufr.readLine())!=null)
{
System.out.println(line);
}
}
catch (IOException e)
{
throw new RuntimeException("文件读取失败");
}
finally
{
if (bufr!=null)
{
try
{
bufr.close();
}
catch (IOException ex1)
{
throw new RuntimeException("读取关闭失败");
}
}
}
}
}
7.装饰设计模式:
1)概述
当想要对已有的对象进行功能增强时,可以定义类,将已有的功能,并提供加强功能。那么自定义的类就称为装饰类。
装饰类通常会通过构造方法接受被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
示例6:对已存在的Person类进行功能增强,并用SuperPerson类描述。
<pre name="code" class="java">class PersonDemo程序运行后的结果如下图所示:
{
public static void main(String[] args)
{
SuperPerson sp=new SuperPerson(new Person());
sp.superEat();
}
}
class Person
{
public void eat()
{
System.out.println("吃饭");
}
}
//装饰类
class SuperPerson
{
private Person p;
SuperPerson(Person p)
{
this.p=p;
}
//对eat功能进行增强
public void superEat()
{
System.out.println("开胃酒");
p.eat();
System.out.println("甜点");
}
}
2)装饰与继承的区别:
继承结构:
MyReader
|--MyTextReader
|--MyBuffferedTextReader:继承增强类。
|--MyMediaReader
|--MyBufferedMediaReader:继承增强类。
|--MyDataReader
|--MyBufferedDataReader:继承增强类。
装饰结构:
MyReader
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferedReader:装饰类,用于给MyReader子类的功能进行增强。
区别:装饰模式比继承要灵活,避免了继承体系的臃肿,而且降低了类与类之间的关系。装饰类因为增强已有对象,具备的功能和已有功能是相同的,只不过提供了更强的功能。所以装饰类和被装饰类通常属于一个体系中的。但装饰类只能在被装饰类的父类的基础上进行增强,而继承可以在被继承类的基础上直接增强。
3)自定义装饰类:
示例7:模拟BufferedReader类。
//自定义装饰类
class MyBufferedReader extends Reader
{
private FileReader r;
MyBufferedReader(FileReader r)
{
this.r=r;
}
//定义读取行功能
public String myReadLine() throws IOException
{
StringBuilder sb=new StringBuilder();
int ch=0;
while ((ch=r.read())!=-1)
{
if (ch=='\r')
continue;
else if (ch=='\n')
return sb.toString();
else
sb.append((char)ch);
}
if (sb.length()!=0)
{
return sb.toString();
}
return null;
}
//直接用传进来的子类进行覆盖,因为该子类一定实现了父类的方法
public int read(char buf,int off,int len)
{
r.read(buf,off,len);
}
//覆盖父类的close抽象方法
public void close()
{
r.close();
}
}
8.LinenumberReader类:
1)概述
LineNumberReader是BufferedReader的子类,用于跟踪行号的缓冲字符输入流。该类的setLineNumber和getLineNumber方法可以用于设置和获取行号。
示例8:对示例4创建的test.txt文件进行内容读取并打印输出到控制台,且要求打印的内容带有行号,并要求从100行开始输出。
import java.io.*;程序运行后的结果如下图:
class LineNumberReaderDemo
{
public static void main(String[] args)
{
LineNumberReader lnr=null;
try
{
lnr=new LineNumberReader(new FileReader("E:\\heima\\test.txt"));
String line=null;
//设置行号为100
lnr.setLineNumber(100);
while ((line=lnr.readLine())!=null)
{
//连同行号一起打印
System.out.println(lnr.getLineNumber()+"::"+line);
}
}
catch (IOException e)
{
throw new RuntimeException("文件读取失败");
}
finally
{
if (lnr!=null)
{
try
{
lnr.close();
}
catch (IOException ex1)
{
throw new RuntimeException("读取关闭失败");
}
}
}
}
}
2)自定义LineNumberReader类:
/*
自定义LineNumberReader类:
思路:
1.定义MyLineNumberReader类,并继承BufferedReader。
2.复写readLine方法,使其没读取一次该方法,行号计数加1。
3.对外提供设置行号和获取的方法。
*/
import java.io.*;
//继承BufferedReader类,可以使类更加简单,只需定义特有方法即可。
class MyLineNumberReader extends BufferedReader
{
//定义行号属性
private int lineNumber;
MyLineNumberReader(Reader r)
{
super(r);
}
public String readLine() throws IOException
{
//每调用一次行号自增1
lineNumber++;
return super.readLine();
}
//设置行号
public void setLineNumber(int lineNumber)
{
this.lineNumber=lineNumber;
}
//获取行号
public int getLineNumber()
{
return lineNumber;
}
}
9.代码练习:
练习1:完成对示例4创建的test.txt文件的复制。
/*程序运行后在"E:\\heima"的目录下创建了一个testCopy.txt文件,该文件的内容的截图如下:
需求:完成<span style="font-size:14px;">对示例4创建的test.txt文件</span>的复制。
思路:
1.在指定目录下创建一个文件,用来存储复制文本内容。
2.创建一个文件读取流对象,与要复制文本文件相关联。
3.将要复制的文本文件的内容读取到数组中。
4.将数组中的内容写入到新创建的文件中。
*/
import java.io.*;
class FileCopyDemo1
{
public static void main(String[] args)
{
FileWriter fw=null;
FileReader fr=null;
try
{
fw=new FileWriter("E:\\heima\\testCopy.txt");
fr=new FileReader("E:\\heima\\test.txt");
//定义字符数组用来存储
char[] buf=new char[1024];
int num=0;
while ((num=fr.read(buf))!=-1)
{
//将数组中的内容写入到新创建的文本文件中,不需要刷新。
fw.write(buf,0,num);
}
}
catch (IOException e)
{
throw new RuntimeException("文件读取失败");
}
finally
{
if (fr!=null)
{
try
{
fr.close();
}
catch (IOException ex1)
{
throw new RuntimeException("读取关闭失败");
}
}
if (fw!=null)
{
try
{
fw.close();
}
catch (IOException ex2)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
}
练习2:使用字符流缓冲区技术完成练习1。
/*程序运行后在"E:\\heima"的目录下创建了test_Copy.txt文件,该文件的内容截图如下:
思路:
1.在指定目录下创建一个文件,用来存储复制文本内容。定义一个字符写入流缓冲区,并与新创建的文本文件相关联。
2.创建一个文件读取流对象,与要复制文本文件相关联,定义一个字符读取流缓冲区,并与文件读取流对象相关联。
3.用readLine方法循环取出字符读取缓冲区的内容。
4.将readLine取出的内容写入到字符写入流缓冲区中。
5.用newLine方法换行,并刷新到新创建的文件中。
*/
import java.io.*;
class FileCopyDemo2
{
public static void main(String[] args)
{
BufferedWriter bufw=null;
BufferedReader bufr=null;
try
{
bufw=new BufferedWriter(new FileWriter("E:\\heima\\test_Copy.txt"));
bufr=new BufferedReader(new FileReader("E:\\heima\\test.txt"));
String line=null;
while ((line=bufr.readLine())!=null)
{
bufw.write(line);
bufw.newLine();
bufw.flush();
}
}
catch (IOException e)
{
throw new RuntimeException("文件读取失败");
}
finally
{
if (bufr!=null)
{
try
{
bufr.close();
}
catch (IOException ex1)
{
throw new RuntimeException("读取关闭失败");
}
}
if (bufw!=null)
{
try
{
bufw.close();
}
catch (IOException ex2)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
}
四、字节流
字节流常用于对非字符文件的操作,如图象、视频等,但不表示字节流不能操作字符文件,而是用字符流来操作字符文件更为便捷。字节流应掌握的内容基本同字符流一致。
1.写入内容的方法总结:
1)将指定字节写入此文件输出流。
void write(int b):该方法抛出了IO异常。
2)将 b.length 个字节从指定 byte 数组写入此文件输出流中。
void write(byte[] b):该方法抛出了IO异常。
3)将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。
void write(byte[] b,int off,int len):该方法抛出了IO异常。
注:上述所有方法都是FileOutputStream类特有方法,且写入文件时,不需要进行刷新操作。
2.读取内容的方法总结:
1)从此输入流中读取一个数据字节。
int read():返回下一个数据字节。如果已到达文件末尾,则返回 -1。
2)从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
int read(byte[] b):返回读入缓冲区的字节总数。如果因为已经到达文件末尾而没有更多的数据,则返回 -1。在某些输入可用之前,此方法将阻塞。
3)从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。
int read(byte[] b,int off,int len):读入缓冲区的字节总数,如果因为已经到达文件末尾而没有更多的数据,则返回 -1。如果 len 不为 0,则在输入可用之前,该方法将阻塞;否则,不读取任何字节并返回 0。
3.复制图片(复制视频等原理类似):
示例9:复制指定目录的文件。
/*程序运行后,复制图像文件与被复制图形文件对比图如下:
思路:
1.用字节读取流对象与图片关联。
2.用字节写入流对象创建一个新的图片文件,用来进行存储获取到的文件数据。
3.通过循环读写完成数据的存储。
4.关闭资源。
注:字节流写入数据时不需要刷新,但仍需关闭资源。
*/
import java.io.*;
class CopyPicDemo
{
public static void main(String[] args)
{
FileInputStream fis=null;
FileOutputStream fos=null;
try
{
fos=new FileOutputStream("E:\\heima\\2.png"); //创建字节写入流对象,生成复制图像文件。
fis=new FileInputStream("E:\\heima\\1.png"); //创建字节读取流对象,并与要被复制的图像文件关联。
byte[] by=new byte[1024];
int len=0;
while ((len=fis.read(by))!=-1)
{
fos.write(by,0,len);
}
}
catch (IOException e)
{
throw new RuntimeException("文件复制失败");
}
finally
{
if (fis!=null)
{
try
{
fis.close();
}
catch (IOException ex1)
{
throw new RuntimeException("读取关闭失败");
}
}
if (fos!=null)
{
try
{
fos.close();
}
catch (IOException ex1)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
}
4.字节流缓冲区
字节流缓冲区有两个:BufferedInputStream(字节流读取缓冲区)和BufferedOutputStream(字节流写入缓冲区)。
从输入流读取数据的方法总结:
1)从输入流中读取数据的下一个字节:
int read():返回下一个数据字节。如果到达流末尾,则返回 -1。
2)从此输入流中将 byte.length 个字节的数据读入一个 byte 数组中:
int read(byte[] b):返回读入缓冲区的字节总数,如果因为已经到达流末尾而没有更多的数据,则返回 -1。在某些输入可用之前,此方法将阻塞。
3)从此字节输入流中给定偏移量处开始将各字节读取到指定的 byte 数组中:
int read(byte[] b, int off, int len):返回读取的字节数。如果已到达流末尾,则返回 -1。
注:上述方法都抛出了IO异常。
将数据写入缓冲的输出流的方法:
1)将指定的字节写入此缓冲的输出流:
void write(int b):该方法抛出了IO异常。
2)将 b.length
个字节写入此输出流:
void write(byte[] b):该方法抛出了IO异常。
3)将指定 byte 数组中从偏移量 off
开始的 len
个字节写入此缓冲的输出流:
void write(byte[] b,int off,int len):该方法抛出了IO异常。
示例10:使用缓冲区技术完成对示例9的复制文件操作。
import java.io.*;程序运行后,复制图像文件与被复制图形文件对比图如下:
class BufferedStreamDemo
{
public static void main(String[] args)
{
BufferedInputStream bufis=null;
BufferedOutputStream bufos=null;
try
{
//创建字节流写入缓冲区对象
bufos=new BufferedOutputStream(new FileOutputStream("E:\\heima\\2.png"));
//创建字节流读取缓冲区对象
bufis=new BufferedInputStream(new FileInputStream("E:\\heima\\1.png"));
int num=0;
while ((num=bufis.read())!=-1)
{
bufos.write(num);
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
if (bufis!=null)
{
try
{
bufis.close();
}
catch (IOException ex1)
{
throw new RuntimeException("读取关闭失败");
}
}
if (bufos!=null)
{
try
{
bufos.close();
}
catch (IOException ex1)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
}
5.读取键盘录入:
示例11:通过键盘录入,当录入一行的数据后,就将该行数据转换成大写并进行打印。如果录入的数据是over,那么停止录入。
/*程序运行后的结果如下图:
System.out:对应的标准输出设备,控制台。
System.in:对应的标准输入设备,键盘。
*/
import java.io.*;
class ReadInDemo
{
public static void main(String[] args)
{
InputStream in=System.in;
StringBuilder sb=new StringBuilder();
try
{
while (true)
{
int ch=in.read();
if (ch=='\r')
{
continue;
}
else if (ch=='\n')
{
String s=sb.toString();
if (s.equals("over"))
{
break;
}
System.out.println(s.toUpperCase());
sb.delete(0,sb.length());
}
else
sb.append((char)ch);
}
}
catch (IOException ex)
{
throw new RuntimeException("发生异常");
}
finally
{
try
{
in.close();
}
catch (IOException e)
{
throw new RuntimeException("关闭失败");
}
}
}
}
6.转换流
转换流包括两个:InputStreamReader(字节流通向字符流)和OutpuStreamWriter(字符流通向字节流)。
示例12:通过转换流完成对示例11的操作。
import java.io.*;程序运行后的结果如下图:
class TransStreamDemo
{
public static void main(String[] args)
{
BufferedReader bufr=null;
BufferedWriter bufw=null;
try
{
//将字节流转换成字符流输入
bufr=new BufferedReader(new InputStreamReader(System.in));
//将字符流转换成字节流输出
bufw=new BufferedWriter(new OutputStreamWriter(System.out));
String line=null;
while ((line=bufr.readLine())!=null)
{
if (line.equals("over"))
{
break;
}
bufw.write(line.toUpperCase()); //将读取内容转换成大写,并写入字符流写入缓冲区
bufw.newLine(); //换行
bufw.flush(); //刷新字符流写入缓冲区的数据到控制台
}
}
catch (IOException ex)
{
throw new RuntimeException("发生异常");
}
finally
{
if (bufr!=null)
{
try
{
bufr.close();
}
catch (IOException e)
{
throw new RuntimeException("读取关闭失败");
}
}
if (bufw!=null)
{
try
{
bufw.close();
}
catch (IOException e)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
}
五、自定义缓冲区
1.自定义字符流缓冲区:
字符流缓冲区提供了一个一次读一行的方法readline,方便于对文本数据的读取。当返回null时,表示读到文件末尾。
readLine方法的原理:无论是读一行,获取读取的多个字符。其实最终都是在硬盘上一个一个读取,所以最终使用的还是read方法一次读一个的方法。
注:readLine方法返回的时候,只返回回车符之前的数据内容,并不返回回车符。
示例13:自定义字符流读取缓冲区。
<pre name="code" class="java">import java.io.*;文件BufferedDemo.txt内容截图如下:
class MyBufferedReaderDemo
{
public static void main(String[] args)
{
MyBufferedReader bufr=null;
try
{
//创建自定义字符流读取缓冲区对象
bufr=new MyBufferedReader(new FileReader("E:\\heima\\BufferDemo.txt"));
String line=null;
while ((line=bufr.myReadLine())!=null)
{
System.out.println(line);
}
}
catch (IOException e)
{
throw new RuntimeException("文件读取失败");
}
finally
{
if (bufr!=null)
{
try
{
bufr.myClose();
}
catch (IOException ex1)
{
throw new RuntimeException("读取关闭失败");
}
}
}
}
}
//自定义字符读取流缓冲区
class MyBufferedReader
{
private FileReader r;
MyBufferedReader(FileReader r)
{
this.r=r;
}
//定义读取行功能
public String myReadLine() throws IOException
{
StringBuilder sb=new StringBuilder();
int ch=0;
while ((ch=r.read())!=-1)
{
if (ch=='\r')
continue;
else if (ch=='\n')
return sb.toString();
else
sb.append((char)ch);
}
if (sb.length()!=0)
{
return sb.toString();
}
return null;
}
//定义关闭字符读取流功能
public void myClose() throws IOException
{
r.close();
}
}
程序运行后的结果如下图:
2.自定义字节流缓冲区:
示例14:自定义字节流读取缓冲区。
import java.io.*;程序运行后的截图如下:
class MyBufferedInputStreamDemo
{
public static void main(String[] args)
{
MyBufferedInputStream mybufis=null;
BufferedOutputStream bufos=null;
try
{
//创建自定义字节流写入缓冲区对象
bufos=new BufferedOutputStream(new FileOutputStream("E:\\heima\\2.mp3"));
//创建字节流读取缓冲区对象
mybufis=new MyBufferedInputStream(new FileInputStream("E:\\heima\\1.mp3"));
//byte[] by=new byte[1024];
int num=0;
while ((num=mybufis.myRead())!=-1)
{
//num是int类型,那么写入文件的字节数是不是4个字节呢?不是的,因为write方法
//里对int类型做了强制类型转换,只保留了int类型的最后八位。所以还是只写入了
//一个字节的数据。
bufos.write(num);
}
}
catch (IOException e)
{
throw new RuntimeException("文件复制失败");
}
finally
{
if (mybufis!=null)
{
try
{
mybufis.myClose();
}
catch (IOException ex1)
{
throw new RuntimeException("读取关闭失败");
}
}
if (bufos!=null)
{
try
{
bufos.close();
}
catch (IOException ex1)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
}
//自定义字节流读取缓冲区
class MyBufferedInputStream
{
private InputStream is;
private byte[] by=new byte[1024*4];
private int pos=0,count=0;
MyBufferedInputStream(InputStream is)
{
this.is=is;
}
//一次读一个字节,从缓冲区(字节数组)获取
public int myRead() throws IOException
{
if (count==0)
{
//通过is对象获取硬盘上的数据,并存储在by数组中
count=is.read(by);
pos=0;
}
if (count>0)
{
int b=by[pos];
pos++;
count--;
//为什么要与上255?因为字节流的字节数据都是二进制,如果读取的一个字节数据为1111-1111,即十进制为-1,
//那么提升为int类型时,还是-1,此时这个字节数据的-1就会和循环读取的判断条件-1一致,导致循环停止,复
//制文件失败,所以需与上255,从而保证int类型的最后一个八位不变,前面三个八位都为0,此时就不会出现返回
//-1的现象。这也是返回类型不是byte而是int的原因所在。
return b&255;
}
return -1;
}
public void myClose() throws IOException
{
is.close();
}
}