IO<一>
来源:互联网 发布:广州淘宝摄影基地 编辑:程序博客网 时间:2024/05/22 12:50
IO概述。
IO流用来处理设备之间的数据传输
Java对数据的操作是通过流的方式
Java用于操作流的对象都在IO包中
流按操作数据分为两种:字节流与字符流。
流按流向分为:输入流,输出流。
如何判断是输入流,还是输出流:
以内存为参照,如果数据向内存流动,则是输入流,反之则为输出流
字节流的抽象基类:
InputStream ,OutputStream。
字符流的抽象基类:
Reader , Writer。
注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
如:InputStream的子类FileInputStream。
如:Reader的子类FileReader。
学习IO流体系:看顶层(父类共性功能,用底层(子类具体对象)。
该体系的一个好处就是:
每个子类的后缀名都是所属体系的父类的名称,很容易区分所属的体系。
而且每个子类的后缀名都是该子类对象的功能体现
这样我们在使用IO体系中的对象时,就非常容易查找了。
字符流
字符流的由来:
以前处理数据都是字节数据,使用字节流技术就可以完成了。
因为后期编码表的不断出现,识别某一文字的码表不唯一。比如中文,GBK,Unicode都可以识别就出现了编码问题
中文字节数据gbk——》流处理——》gbk解析可以了。
后期:容器出现了这样的问题:
中文字节数据gbk——》流处理unicode来处理——》数据错误。
为了处理文字数据,就需要通过字节流技术+编码表相结合来完成。注意:只有文字是这样的,因为文字涉及编码问题。其他数据比如dvdMP3 图片等是不涉及这个问题的。
虽然字节流+编码表可以解决文字数据处理问题,但是较为麻烦。
为了便于使用,将字节流和编码表进行了封装,就出现了便于文字操作的流技术:字节流。
其实字符流就是:字节流+编码表
Writer
体系结构:
|-----------Writer
|----OutputStreamWriter:字符流通向字节流的桥梁,可以指定编码。
|--FileWriter:文件写入流,会创建一个文件,通过构造函数定义是续写还是覆盖。
|----BufferedWriter:字符写入流,定义了缓冲区,可以提高效率;
缓冲区为字符数组,可定义缓冲区大小,默认缓冲区大小为8k。
Reader
|-----------Reader
|----InputStreamReader:字节流通向字符流的桥梁,可以指定编码。
|--FileReader:文件读取流,文件需存在,否则抛出异常。
|----BufferedReader:字符读取流,定义了缓冲区,可以提高效率;缓冲区为字符数组,可定义缓冲区大小,默认缓冲区大小为8k。
Reader
文件读取的第一种方式(一次读一个字节读:
read()方法,一个字符一个字符的读取,返回值是字符的编码值,如果到流结束处,则返回-1,它能读取到换行符。
Writer 和 Reader的综合练习:文件拷贝
import java.io.*;
class CopyDemo
{
public staticvoid main(String [] args)
{
FileReaderfr = null;
FileWriterfw = null;
try{
fr =new FileReader("E:\\Dmeo.txt");
fw =new FileWriter("D:\\Dmeo.txt");
char[]ch = new char[1024];
int len= 0;
//用ch将读取的文件和写入的文件关联起来
while((len=fr.read(ch))!=-1)
{
fw.write(ch,0,ch.length);
}
}
catch(IOException e){
thrownew RuntimeException("文件读写失败");
}
finally{
if(fr!=null){
try{
fr.close();
}
catch(IOException e){
thrownew RuntimeException("读取流资源关闭失败。");
}
}
if(fw!=null){
try{
fw.close();
}
catch(IOException e){
thrownew RuntimeException("写入流资源关闭失败。");
}
}
}
}
}
字符流的缓冲区
BufferedReader
BufferedWriter
缓冲区给给流的操作动作(读写)提高效率.所以缓冲区的对象建立必须要有流对象。
缓冲区的出现:提高了流的读写效率,所以在缓冲区创建前,要先创建流对象,即先将流对象初始化到构造函数中。
缓冲技术原理:此对象中封装了数组,将数据存入,在一次性取出。
写入流缓冲区BufferedWriter的步骤:
n 创建一个字符写入流对象
n 为提高字符写入流效率,加入缓冲技术,只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。注意,只要用到缓冲去就需要刷新。
n 其实关闭缓冲区就是在关闭缓冲区中的流对象。
-->该缓冲区中提供了一个跨平台的换行符:newLine()。
读取流缓冲区BufferedReader的步骤:
l 创建一个读取流对象和文件关联
l 为提高效率,加入缓冲技术,将字符读取流对象作为参数传递给缓冲对象的构造函数。
l 该缓冲区提供了一个一次读一行的方法readLine(),方便与对文本数据的获取,当返回null时,表示读到文件末尾。
readLine()方法返回的时只返回回车符之前的数据内容,并不返回回车符,即读取的内容中不包含任何行终止符(回车符和换行符)。
--->readLine()方法原理:无论是读一行,或读取多个字符,其实最终都是在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个。
import java.io.*;
classCopyBufferedDemo{
public static void main(String[] args) {
BufferedReader bufr = null;
BufferedWriter bufw = null;
try{
//创建缓冲区,将读写流对象作为参数传入
bufr = new BufferedReader(newFileReader("D:\\JAVA\\IO\\buffered\\myJava.txt"));
bufw = new BufferedWriter(newFileWriter("D:\\JAVA\\IO\\文件拷贝\\myJava.txt"));
//定义字符串,通过readLine一次读一行,并存入缓冲区
String line = null;
while((line=bufr.readLine())!=null)
{
//将读取到缓冲区的数据通过line存入写入流中
bufw.write(line);
//换行符方法
bufw.newLine();
//将数据刷新到指定文件中
bufw.flush();
}
}
catch (IOException e){
throw new RuntimeException("读写文件失败。");
}
finally{
if(bufr!=null){
try{
bufr.close();
}
catch (IOException e){
throw newRuntimeException("读取流资源关闭失败。");
}
}
if(bufw!=null){
try{
bufw.close();
}
catch (IOException e){
throw newRuntimeException("写入流资源关闭失败。");
}
}
}
}
}
装饰设计模式。
简述:当想对已有对象进行功能增强是,可定义类:将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该类称之为装饰类。即对原有类进行了优化。
特点:装饰类通常都会通过构造方法接收被装饰的对象,并基于被装饰的对i型那个的功能提供更强的功能。
装饰和继承的区别:
l 装饰模式比继承要灵活,通过避免了继承体系的臃肿,且降低了类与类间的关系。
l 装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰的类通常都是属于一个体系。
l 从继承结构转为组合结构。
注:在定义类的时候,不要以继承为主;可通过装饰设计模式进行增强类功能。灵活性较强,当装饰类中的功能不适合,可再使用被装饰类的功能。
解决的问题:给已有的对象提供增强额外的功能。还不用对原有对象进行修改。
比继承更为灵活。
Writer
|--TextWriter
|--MediaWriter
现有一个体系用于各种数据的写入。
但是,发现写入效率有点低。想要对其进行效率的提高。
可以使用缓冲技术来完成的。
已有对象中的写入方法,不够高效,可以通过派生子类的形式对其进行复写,定义
高效的写入动作。
Writer
|--TextWriter
|--BufferTextWriter
|--MediaWriter
|--BufferMediaWriter
|--DataWriter
|--BufferDataWriter
通过继承的方式提高了效率。
但是对于扩展性是一个问题。而且所需的功能越多,子类就越多。
一旦加入新类,就需要为它提供高效。麻烦!
如何解决这个问题呢?优化!
既然都需要缓冲,对数据写入效率进行提高 。
可以转变一下思想,这样来做,将缓冲技术单独进行封装。
哪个对象需要缓冲,就把哪个对象传递给缓冲对象即可。
class Buffer{
Buffer(TextWriter w){
}
Buffer(MediaWriter w){
}
}
为了便于扩展,可以对一组对象进行缓冲。
class BufferWriter extends Writer{
Buffer(Writer w){
}
public void write(){
}
}
体系就变成了这样:
Writer
|--TextWriter
|--MediaWriter
|--BufferWriter
BufferWriter的出现,增强了Writer体系中的功能。
这种设计方式比原理更为灵活,避免了继承的臃肿。
将这样解决方式就定义了一个名称方便于后人使用:装饰设计模式。
记住:装饰类和被装饰类都所属于同一个体系。
class Person{
publicvoid eat(){
System.out.println("吃饭");
}
}
class SuperPerson{
privatePerson p;
SuperPerson(Person p){
this.p= p;
}
publicvoid eat(){
System.out.println("开胃酒");
// System.out.println("吃饭");
p.eat();
System.out.println("甜点");
}
}
importjava.io.*;
classMyBufferedReader extends Reader
{
private Reader r;
MyBufferedReader(Reader r)
{
this.r = r;
}
//可以一次读一行数据的方法。
public String myReadLine()throws IOException
{
//定义一个临时容器。原BufferReader封装的是字符数组。
//为了演示方便。定义一个StringBuilder容器。因为最终还是要将数据变成字符串。
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch=r.read())!=-1)
{
if(ch=='\r')
continue;
if(ch=='\n')
return sb.toString();
else
sb.append((char)ch);
}
if(sb.length()!=0)
return sb.toString();
return null;
}
/*
覆盖Reader类中的抽象方法。
*/
public int read(char[] cbuf, int off, intlen) throws IOException
{
return r.read(cbuf,off,len) ;
}
public void close()throws IOException
{
r.close();
}
public void myClose()throws IOException
{
r.close();
}
}
class MyBufferedReaderDemo
{
public static void main(String[] args)throws IOException
{
FileReader fr = newFileReader("abc.txt");
MyBufferedReader myBuf = newMyBufferedReader(fr);
String line = null;
while((line=myBuf.myReadLine())!=null)
{
System.out.println(line);
}
myBuf.myClose();
}
}
LineNumberReader
可以用来读取特殊文本,添加行号,比如说程序源文件
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("tempfile\\demo.java");
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
lnr.setLineNumber(100);
while((line=lnr.readLine())!=null){
System.out.println(lnr.getLineNumber()+":"+line);
}
lnr.close();
}
字节流
基本操作与字符流类相同
但它不仅可以操作字符,还可以操作其他媒体文件
InputStream类是字节输入流的抽象类,是所有字节流输入流的父类,InputStream类的具体层次结构
InputStream
|--AudioInputStream
|--ByteArrayInputStream
|--FileInputStream
|--BufferedInputStream
|--DataInputStream
|--、、、、、、
|--FilterInputStream
|--InputStream
|--ObjectInputStream
|--PipedInputStream
|--SequenceInputStream
|--StringBufferInputStream
常用方法:
int |available() 返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。
void |close() 关闭此输入流并释放与该流关联的所有系统资源。
void |mark(int readlimit) 在此输入流中标记当前的位置。
boolean | markSupported() 测试此输入流是否支持 mark 和 reset 方法。
abstract int| read() 从输入流中读取数据的下一个字节。
int |read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b中。
int |read(byte[] b, int off, int len)将输入流中最多 len个数据字节读入 byte 数组。
void|reset() 将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。
long| skip(long n) 跳过和丢弃此输入流中数据的 n 个字节。
OutputStream类是字节输入流的抽象类,次抽象表示输出字节流的所有类的超类.OutputStream
OutputStream
|--ByteArrayOutputStream
|--FileOutputStream
|--BufferedOutputStream
|--DataOutputStream
|--等等.详情参阅API java.io包中
|--FilterOutputStream
|--ObjectOutputStream
|--OutputStream
|--PipedOutputStream
常用方法:
void |close() 关闭此输出流并释放与此流有关的所有系统资源。
void |flush() 刷新此输出流并强制写出所有缓冲的输出字节。
void |write(byte[] b) 将 b.length 个字节从指定的 byte 数组写入此输出流。
void |write(byte[] b, int off, int len)将指定 byte数组中从偏移量 off 开始的 len 个字节写入此输出流。
abstract void |write(int b) 将指定的字节写入此输出流。
FileInputStream 与 FileOutputStream 类
FileInputStream类与FileOutputStream类都是用来操作磁盘文件的,如果用户的文件读取需求比较简单,测可以使用FileInputStream类.该类继承自InputStream类
FlieOutputSream类与FileInputStreamLEI对应提供了基本的文件写入能力,FileOutputStream类是OutputStream类的子类.
import java.io.*;
class CopyPic
{
publicstatic void main(String[] args)
{
//创建流对象引用
FileOutputStreamfos = null;
FileInputStreamfis = null;
try{
//创建读写流对象
fos= new FileOutputStream("2.gif");
fis= new FileInputStream("1.gif");
intlen = 0;
//定义字节数组,存储读取的字节流
byte[]arr = new byte[1024];
//循环读写流,完成数据存储
while((len=fis.read(arr))!=-1){
fos.write(arr,0,len);
}
}catch(IOException e){
thrownew RuntimeException("复制图片失败");
}
//最终关闭资源
finally{
if(fos!=null){
try{
fos.close();
}catch(IOException e){
thrownew RuntimeException("写入流关闭失败");
}
}
if(fos!=null){
try{
fis.close();
}catch(IOException e){
thrownew RuntimeException("读取流关闭失败");
}
}
}
}
}
字节流缓冲区:
读写特点:
read():会将字节byte型值提升为int型值
write():会将int型强转为byte型,即保留二进制数的最后八位。
原理:将数据拷贝一部分,读取一部分,循环,直到数据全部读取完毕。
l 先从数据中抓取固定数组长度的字节,存入定义的数组中,再通过然后再通过read()方法读取数组中的元素,存入缓冲区
l 循环这个动作,知道最后取出一组数据存入数组,可能数组并未填满,同样也取出包含的元素
l 每次取出的时候,都有一个指针在移动,取到数组结尾就自动回到数组头部,这样指针在自增
l 取出的时候,数组中的元素再减少,取出一个,就减少一个,直到减到0即数组取完
l 到了文件的结尾处,存入最后一组数据,当取完数组中的元素,就会减少到0,这是全部数据就取完了
自定义字节流缓冲区:
思路:
1、定义一个固定长度的数组
2、定义一个指针和计数器用于读取数组长度,和计数数组元素是否取完为0
3、每次将字节数据存入元素要先将数组中的元素取完
注:取出的是byte型,返回的是int型,这里存在提升的动作,
当byte中的八位全为1的时候是byte的-1,提升为int类型,就变为int型的-1,,read循环条件就结束了
变为-1的原因是由于在提升时,将byte的八位前都补的是1,即32位的数都是1,即为int型的-1了。
如何保证提升后的最后八位仍为1呢?就需要将前24位补0,就可以保留原字节数据不变,又可以避免转为int型出现-1的情况;
那么要如何做呢?
这就需要将提升为int的数据和前24位为0,后八位仍为原字节数据的这个值做与运算。即和255做与运算即可
importjava.io.*;
classMyBufferedInputStream
{
private InputStream in;
byte[] by = new byte[1024*4];
private int pos=0,count=0;
//传入加强的类
MyBufferedInputStream(InputStream in)
{
this.in = in;
}
//自定义读取方法
public int myRead()throws IOException
{
//先判断计数器
if(count==0)
{
//计数器为0则存入数据
count = in.read(by);
//计数器为负则返回-1,说明结束数据读取
if(count<0)
return -1;
//每次从数组中读取数据完,指针要归零,重新移动指针
pos = 0;
//获取存入数组的元素,并需要让指针和计数器相应变化
byte b = by[pos];
count--;
pos++;
//返回读取的值,需要与运算
return b&255;
}
//计数器大于零时,不需要存数据,只需读取
else if(count>0)
{
byte b = by[pos];
count--;
pos++;
return b&0xff;
}
//为-1时即到数据结尾
return -1;
}
public void myClose()throws IOException
{
in.close();
}
}
流的操作规律:
在进行数据操作时,IO包中提供了N多对象不同功能来操作设备上的数据。
在实际开发时,到底用哪个流对象来完成数据处理呢?
这是我们最为苦恼的事情。
如何明确具体用哪个流对象呢?
通过该规律就哦了。
规律就是四个明确?
1,明确源和目的。
源:InputStream Reader一定是被读取的。
目的:OutputStream Writer 一定是被写入的。
2,处理的数据是否是纯文本的数据?
是:使用字符流。Reader Writer
否:使用字节流。 InputStreamOutputStream
如果是源并且是纯文本,Reader
如果是目的并且是纯文本,Writer
到这里,两个明确确定完,就可以确定出要使用哪个体系。
接下来,就应该明确具体这个体系要使用哪个具体的对象。
3,明确数据所在的设备:
源设备:
键盘(System.in)
硬盘(FileXXX)FileReader FileInputStream
内存(数组)ByteArrayInputStreamCharArrayReader StringReader
网络(Socket)
目的设备:
显示器(控制台System.out)
硬盘(FileXXX)FileWriter FileOutputStream
内存(数组)ByteArrayOutputStreamCharArrayWriter StringWriter
网络(Socket)
具体使用哪个对象就可以明确了。
4,明确是否需要额外功能?
1,是否需要高效?缓冲区Buffered四个。
2,是否需要转换?转换流 InputStreamReaderOutputStreamWriter
3,是否操作基本数据类型? DataInputStream DataOutputStream
4,是否操作对象(对象序列化)? ObjectInputStream ObjectOutputStream
5,需要对多个源合并吗? SequenceInputStream
6,需要保证数据的表现形式到目的地吗? PrintWriter
后面会学到更多。
如果数据有规律,并且源和目的都是file,需要随机访问时,可以使用RandomAccessFile工具类。
-------------------
例子:
实际需求:
需求1:复制一个文本文件。
1,明确源和目的:既有源,又有目的。
源:InputStream Reader
目的:OutputStream Writer.
2,明确是否是纯文本?是!
源:Reader
目的:Writer
3,明确具体设备:
源:
硬盘(file)
目的:
硬盘(file)
源对应的体系Reader中可以操作硬盘设备的对象是FileReader
目的对应的体系Writer中可以操作硬盘设备的对象是FileWriter
直接明确具体对象并创建。
FileReader fr = newFileReader("a.txt");
FileWriter fw = newFileWriter("b.txt");
//就是频繁的读写操作。自己补上。
4,需要额外功能吗?
需要,高效。 使用缓冲区。
BufferedReader bufr = newBufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = newBufferedWriter(new FileWriter("b.txt"));
需求2:复制一个图片
1,明确源和目的:既有源,又有目的。
源:InputStream Reader
目的:OutputStream Writer.
2,明确是否是纯文本?不是!
源:InputStream
目的:OutputStream
3,明确设备:
源:硬盘
目的:硬盘:
FileInputStream fis = newFileInputStream("1.jpg");
FileOutputStream fos = newFileOutputStrema("2.jpg");
需求3:读取键盘录入,存储到一个文件中。
1,明确源和目的:既有源,又有目的。
源:InputStream Reader
目的:OutputStream Writer.
2,明确是否是纯文本?一般键盘录入的都是文字,所以是纯文本的。
源:Reader
目的:Writer
3,明确设备:
源:键盘。
目的:硬盘。
具体对象
源是:System.in.
目的是:FileWriter
InputStream in = System.in;
FileWriter fw = newFileWriter("a.txt");
对这个读写,应该这样完成,通过键盘录入读取字节数据,先不做写入操作,
而是将字节数据临时存储,转成字符串,然后在交给fw写入。
发现代码操作的起来很麻烦。有没有已有的功能可以解决这个问题啊?
4,需要额外功能吗?
需要。必须的。
需要将键盘录入的字节转成字符。
使用转换流。而且是 将字节-->字符的转换流对象。InputStreamReader
InputStreamReader isr = newInputStreamReader(System.in);
FileWriter fw = new FileWriter("a.txt");
还需要其他功能吗?
需要,高效。
BufferedReader bufr = newBufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = newBufferedWriter(new FileWriter("a.txt"));
需求4:读取一个文本文件,显示到显示器上。
1,明确源和目的:既有源,又有目的。
源:InputStream Reader
目的:OutputStream Writer.
2,明确是否是纯文本?是。
源:Reader
目的:Writer
3,明确设备:
源:硬盘。File
目的:显示器。System.out
FileReader fr = newFileReader("a.txt");
OutputStream out = System.out;
这已经可以完成读写了。
通过fr读取文本到字符数组,将字符数组转成字符串,然后在将字符串转成字节数组。
交给out输出。
就是这个转换动作都需要自己完成。烦!
有没有已有的功能可以解决这个问题啊?
4,需要额外功能吗?必须的。
转换。需要将已有的字符数据转成字节。字符-->字节的桥梁OutputStreamWriter
FileReader fr = newFileReader("a.txt");
OutputStreamWriter osw = newOutputStreamWriter(System.out);
需要高效。
BufferedReader bufr = new BufferedReader(newFileReader("a.txt"));
BufferedWriter bufw = newBufferedWriter(new OutputStreamWrier(System.out));
------------------------------
需求5:读取一个文本文件,将文件中文本按照指定的编码表UTF-8写入到另一个文件中。
1,明确源和目的:既有源,又有目的。
源:InputStream Reader
目的:OutputStream Writer.
2,明确是否是纯文本?是。
源:Reader
目的:Writer
3,明确设备:
源:硬盘。File
目的:硬盘。File
FileReader fr = newFileReader("a.txt");
FileWriter fw = newFileWriter("b.txt");
这样做不行,满足不了需求,为什么呢?
因为这个两个对象在操作文本数据,都是用了默认的编码表。在我的本机中默认码表是GBK.
而需求中希望写入到文件的数据是按照utf-8的码表。
其实这两个对象就是字节流+默认编码表。
源对象不变。
FileReader fr = newFileReader("a.txt");
需要目的为指定编码表。
这时就要用到转换流。因为转换流中可以指定具体的编码表。 需要往里传递一个字节流,而且要操作文件,FileOutputStream
OutputStreamWriter osw = newOutputStreamWriter(new FileOutputStream("b.txt"),"UTF-8");
需要高效
BufferedReader bufr = newBufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = newBufferedWriter(new OutputStreamWriter(newFileOutputStream("b.txt"),"UTF-8"));
以下部分的内容,要求必须理解!
c:\\1.txt源是一个文件也是字节数据,是不是应该使用字节流呢?
FileInputStreamfis = new FileInputStream("c:\\1.txt");
InputStreamReaderisr = new InputStreamReader(fis,"gbk");
isr.read();//字符。
既然是明确操作是文件,而且使用默认编码表。
可以使用InputStreamReader的子类。FileReader
FileReaderfr = new FileReader("c:\\1.txt");
- IO(一)
- IO(一)
- IO<一>
- IO(一)
- IO(一)
- JAVA【IO一】IO流
- 一、 异步IO
- IO流循序渐进一
- IO学习笔记(一)
- Java IO(一)
- java.io(一)
- IO (一)
- IO读写(一) java
- 标准IO实例一
- C#IO系统 一
- IO流一
- 文件IO(一)
- IO总结(一)
- 杭电 HDU 1258 Sum It Up
- c++ list 用法
- Android中ListView异步加载数据
- 自画 CComboBox 控件注意事项
- 学习hadoop--第一步
- IO<一>
- ns2 中MFlood协议的移植
- How debugger works
- s5pv210 LED
- 北邮赵玉平教授百家讲坛《曹操的启示》摘录
- 键盘、鼠标控制小球
- 归家偶感二
- mysql函数的使用
- JDK1.5新特性