------- android培训、java培训、期待与您交流! ----------
一.基本概念
处理数据的流的方式就是IO技术
流可以理解为数据流动
Java对数据的操作是通过流的方式
Java用于操作流的对象都在IO包中
流按操作数据分为两种:字节流与字符流
流按流向分为:输入流,输出流
输入流和输出流相对于内存设备而言.
将外设中的数据读取到内存中:输入
将内存的数写入到外设中:输出。
二.字符流
字符流的由来:
其实就是:字节流读取文字字节数据后,不直接操作而是先查指定的编码表。获取对应的文字。
在对这个文字进行操作。简单说:字节流+编码表
字节流的两个顶层父类:
1,InputStream 2,OutputStream.// 读和写
字符流的两个顶层父类:
1,Reader 2,Writer
这些体系的子类都以父类名作为后缀。
而且子类名的前缀就是该对象的功能。
字符流两个基类:
Reader Writer
既然IO流是用于操作数据的
那么数据的最常见体现形式是:文件
例子
//需求:在硬盘上,创建一个文件并写入一些文字数据
//找到一个专门用于操作文件的Writer子类对象 FileWriter 后缀名是父类名,前缀名是该流对象的功能
import java.io.*;
class FileWriteDemo
{
publicstatic void main(String[] args) throws IOException//因为创建文件时可能会创建到不存在的盘符下,所有要抛出或trycatch
{
//创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件
//而且该文件会被创建到指定目录下。如果该目录下已有同名文件,会覆盖。
//其实该步就是在明确数据要存放的目的地
FileWriterfw = new FileWriter("demo.txt");
//调用write方法,将字符串写入到流中。此时txt中无数据,在流的缓冲中
fw.writr("abcd");
//flush()刷新流对象的缓冲的数据,将数据刷到目的地中。此时txt有数据
fw.flush();
//close()关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据
//将数据刷到目的地中。
//和flush区别,flush刷新后,流可以继续使用,close刷新后,会将流关闭
fw.close();
}
}
对已有文件的数据续写
//传递一个true参数,代表不覆盖已有的文件,并在已有文件的末尾处进行数据续写。
FileWriter fw =newFileWriter("demo.txt",true);
IO流异常
例子
import java.io.*;
class FileDemo
{
publicstatic void main(String[] args)
{
FileWriterfw = null; //在外面建立引用,因为finally中的fw.close()无法
//读取到try中建立的fw,所以要先建立
try
{
fw= new FileWriter("demo.txt");
fw.write("abccccc");
}
catch(IOExceptione)
{
System.out.println(e.toString());
}
finally
{
try
{
if(fw!=null)//创建失败时,若fw为空,则无法调用close()
fw.close();//一定要执行,避免出错后浪费资源
//三句话都可能发生异常,这句话单独try
}
catch(IOExceptione)
{
System.out.println(e.toString());
}
}
}
}
读取数据
例子
import java.io.*;
class FileReaderDemo
{
publicstatic void main(String[] args) throws IOException
{
//创建一个文件读取对象,和指定名称的文件相关联
//要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
FileReaderfr = new FileReader("demo.txt");
/*
//"demo.txt"内写ab
//调用读取流对象的read方法
//read():一次读一个字符,而且会自动往下读
intch1 = fr.read();
System.out.println("ch1="+(char)ch1); //强转成char型,不写char会转成int类型,结果a
intch2 = fr.read();
System.out.println("ch2="+(char)ch2); //结果b
fr.close();
*/
//写成循环
intch = 0;
while((ch=fr.read())!=-1){ //返回-1就是读到结尾了
System.out.println((char)ch);
}
//读取一个字符数组
//read(char[])返回的是读到字符个数
char[] buf = new char[3];//缓冲区通常定义成1024的整数倍 如char[] buf = new char[1024]
int num = 0;
while((num=fr.read(buf))!=-1)
{
System.out.println(newString(buf,0,num));
}
fr.close();
拷贝文本文件
复制的原理:
其实就是将C盘下的文件数据存储到D盘的一个文件中
步骤:
1、在D盘创建一个文件,用于存储C盘文件中的数据
2、定义读取流和C盘文件关联
3、通过不断的读写完成数据存储
4、关闭资源
例子
public static void main(String[] args)throws IOException
{
//1,读取一个已有的文本文件,使用字符读取流和文件相关联。
FileReaderfr = new FileReader("IO流_2.txt");
//2,创建一个目的,用于存储读到数据。
FileWriterfw = new FileWriter("copytext_1.txt");
//3,频繁的读写操作。
intch = 0;
while((ch=fr.read())!=-1)
{
fw.write(ch);
}
//4,关闭流资源。
fw.close();
fr.close();
}
字符流缓冲区:
BufferedWriter
:newLine(); //换行
BufferedReader
:readLine(); //读一行文本
BufferedWriter: 使用缓冲区
例子
FileWriter fw = newFileWriter("buf.txt"); //创建一个文件
//为了提高写入的效率。使用了字符流的缓冲区。
//创建了一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联
BufferedWriter bufw = newBufferedWriter(fw);
//使用缓冲区的写入方法将数据先写入到缓冲区中。
//bufw.write("abcdefq"+LINE_SEPARATOR+"hahahha");
//bufw.write("xixiixii");
//bufw.newLine();
//bufw.write("heheheheh");
for(int x=1; x<=4; x++)
{
bufw.write("abcdef"+x);
bufw.newLine();
bufw.flush();
}
//使用缓冲区的刷新方法将数据刷目的地中。
//bufw.flush();
//关闭缓冲区。其实关闭的就是被缓冲的流对象(fw)。
bufw.close();
缓冲区的出现是为了提高流的操作效率而出现的
所有在创建缓冲区之前,必须要先有流对象
该缓冲区提供了一个跨平台的换行符
newLine();
readLine()方法的原理
无论是读一行,还是读取多个字符。其实最终都是在硬盘上一个一个读取。
所以最终使用的还是read方法一次读一个的方法。
使用了读取缓冲区的read方法,将读取到的字符进行缓冲并判断换行标记。将标记前的缓冲数据变成字符串返回。
装饰设计模式
当想要对已有的对象进行功能增强时,
可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。
那么自定义的该类称为装饰类
装饰类通常会通过构造方法接收被装饰的对象
并基于被装饰的对象的功能,提供更强的功能
装饰和继承的区别
继承:
首先有一个继承体系。
Writer
|--TextWriter:用于操作文本
|--MediaWriter:用于操作媒体。
装饰:
想要对操作的动作进行效率的提高。
按照面向对象,可以通过继承对具体的进行功能的扩展。
效率提高需要加入缓冲技术。
Writer
|--TextWriter:用于操作文本
|--BufferTextWriter:加入了缓冲技术的操作文本的对象。
|--MediaWriter:用于操作媒体。
|--BufferMediaWriter:
装饰模式比继承灵活,避免了继承体系臃肿
而且降低了类与类之间的关系
例子:
class PersonDemo {
publicstatic void main(String[] args){
Personx = new Person(); //写成X方便理解
NewPersonp1 = new NewPerson(x); //装饰类构造方法,传入被装饰类对象。此时this.p = x,把x所指对象的地址赋给了NewPerson中的p,所以NewPerson中的p.chifan();调用的是x所指对象的方法
p1.chifan();
}
}
class Person{
voidchifan(){
System.out.println("吃饭");
}
}
//这个类的出现时为了增强Person而出现的
class NewPerson{
privatePerson p;
NewPerson(Personp){
this.p= p;
}
publicvoid chifan(){
System.out.println("开胃酒");
p.chifan(); //假如这里写成chifan(); 结果就会变成不断循环开胃酒
System.out.println("甜点");
}
}
// 结果
// 开胃酒
// 吃饭
// 甜点
特点:装饰类和被装饰类都必须所属同一个接口或者父类。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能
所以装饰类和被装饰类通常都是属于一个体系中的
LineNumberReader
带行号的装饰类,基于readLine,但是带行号
三.字节流
基本操作与字符流相同
但它不仅可以操作字符,还可以操作其他媒体文件
字节流写数据直接写入目的地,没有必要调flush();
available():字节长度,如果abcds,就是5个字节,多大就是多少
如
FileInputStream fis = newFileInputStream("fos.txt");
int num = fis.available();
byte[] buf = newbyte[fis.available()];//定义一个刚刚好的缓冲区,不用再循环了
//但若数据过大,则不要用此方法,如一个视频几G,没必要定义这么大的缓冲区
//此时还是定义成1024的整数倍比较好,如byte[] buf = new byte[1024];
fis.read(buf);
System.out.println(new String(buf));
fis.close();
//复制一张图片
//思路:
/1、用字节读取流对象和图片关联
//2、用字节写入流对象创建一个图片文件,用于存储获取到的图片数据
//3、通过循环读写,完成数据的存储
//4、关闭资源
//例子
import java.io.*;
class CopyPic
{
publicstatic void main(String[] args)
{
FileOutputStreamfos = null;
FileOutputStreamfis = null;
try
{
fos= new FileOutputStream("c:\\2.bmp");
fis= new FileInputStream("c:\\1.bmp");// 这个是源
//建议使用这种读取数据的方法
byte[]buf = new byte[1024]; //定义一个缓冲区
intlen = 0;
while((len=fis.read(buf))!=1)
{
for.write(buf,0,len);
}
}
catch(IOExceptione)
{
thrownew RuntimeException("复制文件失败");
}
finally
{
try
{
if(fis!=null)
fis.close();
}
catch(IOExceptione)
{
thrownew RuntimeException("读取关闭失败");
}
try
{
if(fos!=null)//需要分别关闭(出现异常的情况下)
fos.close();
}
catch(IOExceptione)
{
thrownew RuntimeException("写入关闭失败");
}
}
}
}
读取键盘录入
System.out:对应的是标准输出设备,控制台
System.in:对应的标准输入设备,键盘
//需求:
//通过键盘录入数据
//当录入一行数据后,就将该行数据进行打印
//如果录入的数据时over,那么停止录入
publicstatic void readKey2() throws IOException
{
/*
* 获取用户键盘录入的数据,
* 并将数据变成大写显示在控制台上,
* 如果用户输入的是over,结束键盘录入。
*
* 思路:
* 1,因为键盘录入只读取一个字节,要判断是否是over,需要将读取到的字节拼成字符串。
* 2,那就需要一个容器。StringBuilder.
* 3,在用户回车之前将录入的数据变成字符串判断即可。
*
*/
//1,创建容器。
StringBuildersb = new StringBuilder();
//2,获取键盘读取流。
InputStreamin = System.in;
//3,定义变量记录读取到的字节,并循环获取。
intch = 0;
while((ch=in.read())!=-1)
{
//在存储之前需要判断是否是换行标记 ,因为换行标记不存储。
if(ch=='\r')
continue;
if(ch=='\n'){
Stringtemp = 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);
}
转换流
1 .字节流
InputStream ->FileInputStream
OutputStream ->FileOutputStream
2 . 字符流
Reader -> BufferedReader -> LineNumberReader
-> InputStreamReader -> FileReader
Writer -> BufferedWriter
-> OutputStreamWriter -> FileWriter
InputStreamReader OutputStreamWriter 为转换流,FileReader FileWriter为其子类
如何选用哪种流读取文件?
IO流分为字符流 与 字节流,根据读写文件确定使用哪一种流,比如
读取文件是否为文本:
是:用字符流
不是:用字节流
何时使用转换流?
1.
如果使用非默认编码保存文件或者读取文件时,需要用到转换流,因为字节流的重载构造方法中有指定编码格式的参数,而FielReader与 FileWriter 是默认编码的文本文件
比如:
当我们使用默认GBK编码保存文本时,下面2句代码其实是一样的效果,
new OutputStreamWriter(new FileOutputStream("out.txt"))
new FileWriter("out.txt")
当要求保存为其他编码比如UTF-8时,就要这样写
流的操作规律:
之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。
想要知道开发时用到哪些对象。只要通过四个明确即可。
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 = 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,是否是纯文本呢?
是
源:Reader
目的:Writer
3,明确设备
源:
键盘。System.in
目的:
硬盘。File
FileWriter fw = newFileWriter("b.txt");
这样做可以完成,但是麻烦。将读取的字节数据转成字符串。再由字符流操作。
4,需要额外功能吗?
需要。转换。
将字节流转成字符流。因为名确的源是Reader,这样操作文本数据做便捷。
所以要将已有的字节流转成字符流。使用字节-->字符。InputStreamReader
InputStreamReader isr = newInputStreamReader(System.in);
FileWriter fw = newFileWriter("b.txt");
还需要功能吗?
需要:想高效。
BufferedReader bufr = newBufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = newBufferedWriter(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= newFileReader("a.txt");
OutputStreamWriter osw = newOutputStreamWriter(System.out);
需要,高效。
BufferedReader bufr = newBufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = newBufferedWriter(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 = newInputStreamReader(System.in);
OutputStreamWriter osw = newOutputStreamWriter(System.out);
为了将其高效。
BufferedReader bufr = newBufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = newBufferedWriter(new OutputStreamWriter(System.out));
需求5,将一个中文字符串数据按照指定的编码表写入到一个文本文件中.
1,目的。OutputStream,Writer
2,是纯文本,Writer。
3,设备:硬盘File
FileWriter fw = newFileWriter("a.txt");
fw.write("你好");
注意:既然需求中已经明确了指定编码表的动作。
那就不可以使用FileWriter,因为FileWriter内部是使用默认的本地码表。
只能使用其父类。OutputStreamWriter.
OutputStreamWriter接收一个字节输出流对象,既然是操作文件,那么该对象应该是FileOutputStream
OutputStreamWriter osw = newOutputStreamWriter(new FileOutputStream("a.txt"),charsetName); //如 (newFileOutputStream("a.txt"),"GBK")
需要高效吗?
BufferedWriter bufw = newBufferedWriter(new OutputStreamWriter(newFileOutputStream("a.txt"),charsetName));
什么时候使用转换流呢?
1,源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换作为桥梁。
提高对文本操作的便捷。
2,一旦操作文本涉及到具体的指定编码表时,必须使用转换流 。
目前为止,10个流对象重点掌握。
字符流:
FileReader
FileWriter
BufferedReader
BufferedWriter
InputStreamReader
OutputStreamWrier
字节流:
FileInputStream
FileOutputStream
BufferedInputStream
BufferedOutputStream