【分类】
按操作数据分为两种:字节流和字符流
字节流的两个顶层父类:
1.inputstream 2.OutputStream
字符流的两个顶层父类:
1.Reader 2.Writer
这些体系的子类都以父类名作为后缀。
而且子类名的前缀就是该对象的功能
记住:如果操作文字数据,建议优先考虑字符流。
而且要将数据从内存写到硬盘上。要使用字符流中的输出流Writer
按流向分为:输入流和输出流
输入流和输出流相对于内存设备而言
将外设中的数据读取到内存中:输入流
将内存的数据写入到外设中:输出流
【字符流的由来】
字符流:
其实就是:字节流读取文字字节数据后,不直接操作而是先查指定的码表。获取对应的文字。
再对这个文字进行操作。简单说:字节流+编码表
由来:
以前其实没有字符流,是后面才有的。
这就要从编码表说起了,为了能让计算机能识别美国的生活中的文字,就产生了用二进制和它文字
的对应关系表,ASCII码表,而二进制转换成十进制就是数字,例如:97对应a,这张
码表记录了美国所有的文字和数字的对应关系。可是,其他国家也需要去做这样的事情
,所以就得让其他国家的文字和二进制对应起来,这样就产生了很多码表,中国有中国
的码表,日本有日本的码表等等,但是多张码表不利于数据的互通,所以Unicode(国际标准码表)
就产生了,java中内置的就是Unicode。Unicode会将各个国家的编码重新对应。因为相同的数字在不同的编码表中
表示的文字不一样,例如: 一个中文在GBK中有对应得数字,但是这个数字在其他编码里面表示的就是其他的国家对应的文字了,
这样在Unicode中就会造成冲突,所以,Unicode会将各个国家的编码重新对应。所以我们读数据的时候,
就需要指定码表。所以就将用于读取字节数据的字节流和编码表结合,封装成了字符流,这样字符流就产生了。
【例子】
//需求:将一些字符存储到硬盘中
如果文件不存在,则会自动创建,如果文件存在,则会覆盖
private static final String LINE_SEPARATOR=System.getProperty("line.separator");
FileWriter fw=null;
try{
//第二个参数为是否续写
// fw=new FileWriter("demo.txt");
fw=new FileWriter("demo.txt",true);
//调用Writer对象中的write(String)方法,写入数据。
//其实数据写入到临时存储缓冲区中。
fw.write("abcd");
//换行
fw.write("abcd"+LINE_SEPARATOR+"HHAHH");
//进行刷新,将数据直接写到目的地中
fw.flush();
//关闭流,释放资源
fw.close();
}catch(IOException e){
System.out.println(e.toString());
}finally {//要是创建fw的时候被try catch了fw就是null了 所以需要判空
if (fw != null)
try {
fw.close();
} catch (IOException e) {
// code....
throw new RuntimeException("关闭失败");
}
}
【例2】
读写,一次读一个字符
//1,创建读取字符数据的流对象。
/*
* 在创建读取流对象时,必须要明确被读取的文件。一定要确定该文件是存在的。
*
* 用一个读取流关联一个已存在文件。
*/
FileReader fr = new FileReader("demo.txt");
int ch = 0;
while((ch=fr.read())!=-1){
System.out.println((char)ch);
}
/*
//用Reader中的read方法读取字符。
int ch = fr.read();
System.out.println((char)ch);
int ch1 = fr.read();
System.out.println(ch1);
int ch2 = fr.read();
System.out.println(ch2);
*/
fr.close();
一次读一个数组:
FileReader fr = new FileReader("demo.txt");
/*
* 使用read(char[])读取文本文件数据。
*
* 先创建字符数组。
*/
char[] buf = new char[1024];
int len = 0;
while((len=fr.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
/*
int num = fr.read(buf);//将读取到的字符存储到数组中。num 返回的是数组的长度
System.out.println(num+":"+new String(buf,0,num));
*/
fr.close();
【例3】复制文本文件
既然是文本那就选择字符流
private void copyFile() throw IOException{
//读取一个已有的文本文件,使用字符流来读取
FileReader fr=new FileReader("copytext1.txt");
//创建一个目的,用于存储读到的数据
FileWriter fw=new FileWriter("copytext2.txt");
//一次读写一个字符
int ch=0;
while((ch=fr.read())!=-1){
w.write(ch);
}
fw.close();
fr.close();
}
一次读写一个数组
private void copyFile2(){
FileReader fr=null;
FileWriter fw=null;
try{
//创建一个临时容器,用于缓存读取到的字符
char[]buf=new char[1024];
//定义一个变量记录读取到的字符数,(其实就是往数组里装的字符个数)
int len=0;
while((len=fr.read(buf))!=-1){
fw.write(buf,0,len);
}
}catch(Exception e){
}finally{
if(fw!=null)
try{
fw.close();
}catch(IOException e){
}
if(fr!=null)
try{
fr.close();
}catch(IOEception e){
}
}
}
我们做程序的有两部分优化,第一部分就是设计优化,第二部分就是性能优化
设计优化就是对代码进行重构,让代码实现更强的扩展性,灵活性,以及复用性,
所以我们会用很多的设计模式来实现设计优化。提高性能优化的一个重要手段之一
就是缓冲。
【字符流缓冲区】
缓冲区的出现提高了对数据的读写效率。
对应类
BufferedWriter
BufferedReader
缓冲区要结合流才可以使用
在流的基础上对流的功能进行了增强
【字符流缓冲区写入例子】
FileWriter fw= new FileWriter("test.txt");
BufferedWriter bufw=new BufferedWriter(fw);
bufw.write("abdcef");
//bufw.newLine();//行分隔符
bufw.flush();
bufw.close();
【字符流缓冲区读例子】
缓冲区读的方法有读一个字符,数组,行(这个是字符特有的读取方式)
FileReader fr=new FileReader("buf.txt");
BufferedReader bufr =new BufferedReader(fr);
String line=null;
while((line=bufr.readLine())!=null){
System.out.println(line);
}
【字符流缓冲区复制文件】
FileReader fr =new FileReader("buf.txt");
BufferedReader bufr=new BufferedReader(fr);
FileWriter fw=new FileWriter("copy.txt");
BufferedWriter bufw=new BufferedWriter(fw);
String line=null;
while((line=bufr.readLine())!=null){
bufw.write(line);
bufw.newLine();
bufw.flush();
}
bufr.close();
bufw.close();
自定义缓冲区
package cn.itcast.p4.io.charstream.mybuffer;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
/**
* 自定义的读取缓冲区。其实就是模拟一个BufferedReader.
*
* 分析:
* 缓冲区中无非就是封装了一个数组,
* 并对外提供了更多的方法对数组进行访问。
* 其实这些方法最终操作的都是数组的角标。
*
* 缓冲的原理:
* 其实就是从源中获取一批数据装进缓冲区中。
* 在从缓冲区中不断的取出一个一个数据。
*
* 在此次取完后,在从源中继续取一批数据进缓冲区。
* 当源中的数据取光时,用-1作为结束标记。
*
*
* @author Administrator
*
*/
public class MyBufferedReader extends Reader {
private Reader r;
//定义一个数组作为缓冲区。
private char[] buf = new char[1024];
//定义一个指针用于操作这个数组中的元素。当操作到最后一个元素后,指针应该归零。
private int pos = 0;
//定义一个计数器用于记录缓冲区中的数据个数。 当该数据减到0,就从源中继续获取数据到缓冲区中。
private int count = 0;
MyBufferedReader(Reader r){
this.r = r;
}
/**
* 该方法从缓冲区中一次取一个字符。
* @return
* @throws IOException
*/
public int myRead() throws IOException{
if(count==0){
count = r.read(buf);
pos = 0;
}
if(count<0)
return -1;
char ch = buf[pos++];
count--;
return ch;
/*
//1,从源中获取一批数据到缓冲区中。需要先做判断,只有计数器为0时,才需要从源中获取数据。
if(count==0){
count = r.read(buf);
if(count<0)
return -1;
//每次获取数据到缓冲区后,角标归零.
pos = 0;
char ch = buf[pos];
pos++;
count--;
return ch;
}else if(count>0){
char ch = buf[pos];
pos++;
count--;
return ch;
}*/
}
public String myReadLine() throws IOException{
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch = myRead())!=-1){
if(ch=='\r')
continue;
if(ch=='\n')
return sb.toString();
//将从缓冲区中读到的字符,存储到缓存行数据的缓冲区中。
sb.append((char)ch);
}
if(sb.length()!=0)
return sb.toString();
return null;
}
public void myClose() throws IOException {
r.close();
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return 0;
}
@Override
public void close() throws IOException {
}
}
【LineNumberReader】
可以获取读取内容的行号
例:
FileReader r=new FileReader("abc.txt");
LineNumberReader lnr=new LineNumberReader(r)
String line=null;
lnr.setLineNumber(100);//从第100行开始读
while((line=lnr.readLine())!=null){
System.out.println(lnr.getLineNumer()+""+line);
}
lnr.close();
【字节流】
字节输出流
FileOutputStream fos =new FileOutputStream("123.txt");
fos.write("123".getBytes());
fos.close();
字节读取流,这种方式效率太低千万不要使用
FileInputStream fis=new FileInputStream("123.txt");
int len=0
//一次读取一个
//len = fis.read();
while((len=fis.read())!=-1){
System.out.println((char)ch);
}
fis.close();
一次读取一个数组
byte[] buf =new byte[1024];
int len=0;
while((len=fis.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
【字节复制】
public void copy_1() throw IOException{
FileInputStream fis =new FileInputStream("c:\\123.txt");
FileOutputStream fos = new FileOutputStream("c:\\234.txt");
int len=0;
byte [] buf=new byte[1024];
while((len=fis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
fis.close();
}
【字节流缓冲区】
public void copy_2() throw IOException{
FileInputStream fis =new FileInputStream("c:\\123.txt");
BufferedInputStream bufis= new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("c:\\234.txt");
BufferedOutputStream bufos=new BufferedOutStream(fos);
int len=0;
// byte [] buf=new byte[1024];//使用缓冲区的话,这里就多余了
// while((len=bufis.read(buf))!=-1){
// bufos.write(buf,0,len);
// bufos.flush();
while((len=bufis.read())!=-1){
bufos.write(buf,0,len);
bufos.flush();
}
bufos.close();
bufis.close();
}
【键盘录入】
InputStream in=System.in;
System.out.println(ch);
in.close();//这个一般不用,他会随着系统的消失而消失,它是唯一的,如果关了
InputStream in2= System.in;
in.read();
就会报异常
【读取键盘录入】
public static void readKey2() throws IOException {
/*
* 获取用户键盘录入的数据,
* 并将数据变成大写显示在控制台上,
* 如果用户输入的是over,结束键盘录入。
*
* 思路:
* 1,因为键盘录入只读取一个字节,要判断是否是over,需要将读取到的字节拼成字符串。
* 2,那就需要一个容器。StringBuilder.
* 3,在用户回车之前将录入的数据变成字符串判断即可。
*
*/
//1,创建容器。
StringBuilder sb = new StringBuilder();
//2,获取键盘读取流。
InputStream in = System.in;
//3,定义变量记录读取到的字节,并循环获取。
int ch = 0;
while((ch=in.read())!=-1){
// 在存储之前需要判断是否是换行标记 ,因为换行标记不存储。
if(ch=='\r')
continue;
if(ch=='\n'){
String temp = sb.toString();
if("over".equals(temp))
break;
System.out.println(temp.toUpperCase());
sb.delete(0, sb.length());
}
else
//将读取到的字节存储到StringBuilder中。
sb.append((char)ch);
// System.out.println(ch);
}
}
【转换流】
字节流转成字符流inputstreamreader
InputStream in=System.in;
InputStreamReader isr=new InputStreamReader(in);//这里变成了字符流
BufferedReader bufr =new BufferedReader(isr);
String line = bufr.readline();//读取字符串数据
字符流转成字节流outputStreamWriter
OutputStream out=System.out;//字节流
OutputStreamWriter osw=new OutputStreamWriter();
BufferedWriter bw=new BufferedWriter(osw);
【IO流的规律总结】
字节流的两个顶层父类:
1.InputStream 2.OutputStream
字符流的两个顶层父类
1.Reader 2.Writer
字节流:
InputStream
OutputStream
FileInputStream
FileOutputStream
BuffereInputStream
BuffereOutputStream
字符流:
Writer
Reader
FileReader
FileWriter
BufferedReader
BufferedWriter
转换流
InputStreamReader
OutputStreamWriter
流的操作规律:
之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。
想要知道开发时用到哪些对象。只要通过四个明确即可。
1,明确源和目的(汇)
源:InputStream Reader
目的:OutputStream Writer
2,明确数据是否是纯文本数据。
源:是纯文本:Reader
否:InputStream
目的:是纯文本 Writer
否:OutputStream
到这里,就可以明确需求中具体要使用哪个体系。
3,明确具体的设备。
源设备:
硬盘:File
键盘:System.in
内存:数组
网络:Socket流
目的设备:
硬盘:File
控制台:System.out
内存:数组
网络:Socket流
4,是否需要其他额外功能。
1,是否需要高效(缓冲区);
是,就加上buffer.
2,转换。
例子:
================================================================================
需求1:复制一个文本文件。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本?
是!
源:Reader
目的:Writer
3,明确具体设备。
源:
硬盘:File
目的:
硬盘:File
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
4,需要额外功能吗?
需要,需要高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
==================================================================================================
需求2:读取键盘录入信息,并写入到一个文件中。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备
源:
键盘。System.in
目的:
硬盘。File
InputStream in = System.in;
FileWriter fw = new FileWriter("b.txt");
这样做可以完成,但是麻烦。将读取的字节数据转成字符串。再由字符流操作。
4,需要额外功能吗?
需要。转换。 将字节流转成字符流。因为名确的源是Reader,这样操作文本数据做便捷。
所以要将已有的字节流转成字符流。使用字节-->字符 。InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter("b.txt");
还需要功能吗?
需要:想高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
===================================================
需求3:将一个文本文件数据显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确具体设备
源:
硬盘:File
目的:
控制台:System.out
FileReader fr = new FileReader("a.txt");
OutputStream out = System.out;//PrintStream
4,需要额外功能吗?
需要,转换。
FileReader fr= new FileReader("a.txt");
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要,高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
================================================================
需求4:读取键盘录入数据,显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备。
源:
键盘:System.in
目的:
控制台:System.out
InputStream in = System.in;
OutputStream out = System.out;
4,明确额外功能?
需要转换,因为都是字节流,但是操作的却是文本数据。
所以使用字符流操作起来更为便捷。
InputStreamReader isr = new InputStreamReader(System.in);
OutputStreamWriter osw = new OutputStreamWriter(System.out);
为了将其高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
============================================================
该使用哪个流?
看是否是纯文本
是: Reader Writer
否:InputStream OutputStream
看设备:
1.硬盘:File
2.键盘:system.in
3.内存:数组
4.网络:Socket流
java凡是涉及到String的一定会涉及编码表
如果没有指定,就会使用平台默认的编码格式
中文版windows的编码是gbk
只有FileInputStreamReader/FileOutputStreamWriter可以指定编码格式。
其他的都是使用默认的也就是平台的编码
【转换流的编码解码】
将一个中文字符串数据按照指定的编码表写入到一个文本中。
1、目的。OutputStream Writer
2、是否存文本.Writer
3、设备: 硬盘:File
FileWriter fr=new FileWriter("123.txt");
fr.writer("你好");
这里其实就是转换流指定了本机默认的编码表的体现。而且这个转换流的子类对象,可以方便操作文本文件。
简单说:操作文件的字节流+本机默认的编码表。这是按照默认码表来操作文件的边界类。
如果操作文本文件需要明确具体的编码,FileWriter就不行了,必须使用转换流
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStreamWriter("12.txt"),"gbk");
osw.writer("你好");
需要高效
BufferedWriter bufw =new BufferedWriter(osw);
什么时候使用转换流呢?
1.源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换作为桥梁
2.一旦操作文本设计到具体的指定编码表时,必须使用转换流
【打印流】
1。字节流
/*
* PrintStream:
* 1,提供了打印方法可以对多种数据类型值进行打印。并保持数据的表示形式。
* 2,它不抛IOException.
*
* 构造函数,接收三种类型的值:
* 1,字符串路径。
* 2,File对象。
* 3,字节输出流。
*/
PrintStream out = new PrintStream("print.txt");
// int by = read();
// write(by);
// out.write(610);//只写最低8位,
// out.print(97);//将97先变成字符保持原样将数据打印到目的地。
out.close();
2。字符流
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
/*
* PrintWriter:字符打印流。
* 构造函数参数:
* 1,字符串路径。
* 2,File对象。
* 3,字节输出流。
* 4,字符输出流。
*
*/
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(new FileWriter("out.txt"),true);
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
out.println(line.toUpperCase());
// out.flush();
}
out.close();
bufr.close();
}
【序列流】
/*
* 需求:将1.txt 2.txt 3.txt文件中的数据合并到一个文件中。
*/
//Vector已经不常用了,所以用Arraylist来代替。
// Vector<FileInputStream> v = new Vector<FileInputStream>();
// v.add(new FileInputStream("1.txt"));
// v.add(new FileInputStream("2.txt"));
// v.add(new FileInputStream("3.txt"));
// Enumeration<FileInputStream> en = v.elements();
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
for(int x=1; x<=3; x++){
al.add(new FileInputStream(x+".txt"));
}
Enumeration<FileInputStream> en = Collections.enumeration(al);//代替了下面的代码,其实实现原理是一样的
/*这个有点麻烦,所以用Collections工具类来代替
final Iterator<FileInputStream> it = al.iterator();
Enumeration<FileInputStream> en = new Enumeration<FileInputStream>(){
@Override
public boolean hasMoreElements() {
return it.hasNext();
}
@Override
public FileInputStream nextElement() {
return it.next();
}
};*/
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream("1234.txt");
byte[] buf = new byte[1024];
int len = 0;
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
IO包中的其它流
【RandomAccessFile】
public class RandomAccessFileDemo {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
/*
* RandomAccessFile
* 一看这个类名字,纠结。不是io体系中的子类。
*
* 特点:
* 1,该对象即能读,又能写。
* 2,该对象内部维护了一个byte数组,并通过指针可以操作数组中的元素,
* 3,可以通过getFilePointer方法获取指针的位置,和通过seek方法设置指针的位置。
* 4,其实该对象就是将字节输入流和输出流进行了封装。
* 5,该对象的源或者目的只能是文件。通过构造函数就可以看出。
*
*
*/
// writeFile();
// readFile();
randomWrite();
}
public static void randomWrite() throws IOException{
RandomAccessFile raf = new RandomAccessFile("ranacc.txt", "rw");
//往指定位置写入数据。
raf.seek(3*8);
raf.write("哈哈".getBytes());
raf.writeInt(108);
raf.close();
}
public static void readFile() throws IOException {
RandomAccessFile raf = new RandomAccessFile("ranacc.txt", "r");
//通过seek设置指针的位置。
raf.seek(1*8);//随机的读取。只要指定指针的位置即可。
byte[] buf = new byte[4];
raf.read(buf);
String name = new String(buf);
int age = raf.readInt();
System.out.println("name="+name);
System.out.println("age="+age);
System.out.println("pos:"+raf.getFilePointer());
raf.close();
}
//使用RandomAccessFile对象写入一些人员信息,比如姓名和年龄。
public static void writeFile() throws IOException{
/*
* 如果文件不存在,则创建,如果文件存在,不创建
*
*/
RandomAccessFile raf = new RandomAccessFile("ranacc.txt","rw");
raf.write("张三".getBytes());
raf.writeInt(97);
raf.write("小强".getBytes());
raf.writeInt(99);
//
raf.close();
}
}
【管道流】
package cn.itcast.io.p4.piped;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class PipedStream {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
PipedInputStream input = new PipedInputStream();
PipedOutputStream output = new PipedOutputStream();
input.connect(output);
new Thread(new Input(input)).start();
new Thread(new Output(output)).start();
}
}
class Input implements Runnable{
private PipedInputStream in;
Input(PipedInputStream in){
this.in = in;
}
public void run(){
try {
byte[] buf = new byte[1024];
int len = in.read(buf);
String s = new String(buf,0,len);
System.out.println("s="+s);
in.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}
class Output implements Runnable{
private PipedOutputStream out;
Output(PipedOutputStream out){
this.out = out;
}
public void run(){
try {
Thread.sleep(5000);
out.write("hi,管道来了!".getBytes());
} catch (Exception e) {
// TODO: handle exception
}
}
}
【基本数据的流】
public class DataSteamDemo {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// writeData();
readData();
}
public static void readData() throws IOException {
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
String str = dis.readUTF();
System.out.println(str);
}
public static void writeData() throws IOException {
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeUTF("你好");
dos.close();
}
}
【操作字节数组】
ByteArrayInputStream
ByteArrayOutputStream
public class ByteArrayStreamDemo{
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
ByteArrayInputStream bis =new ByteArrayInputStream("abcdef".getbytes());
ByteArrayOutputStream bos=new ByteArrayOutputStream();
int ch = 0;
while((ch=bis.read())!=-1){
bos.write(ch);
}
System.out.println(bos.toString());
}
}