----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
IO流
定义:IO流用来处理设备之间的数据传输,java对数据的操作是通过流的方式,java用于操作流的对象都在IO包中.
分类:
1) 流按操作数据分为两种:字节流和字符流
字节流:处理字节数据的流对象。设备上的数据无论是图片或者dvd,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。
字符每个国家都不一样,涉及到了字符编码问题,那么GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时+指定的编码表才可以解析正确数据。为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流。只要操作字符数据,优先考虑使用字符流体系。
2) 流按流向分为:输入流,输出流
流的操作只有两种:读和写。
流的体系因为功能不同,但是有共性内容,不断抽取,形成继承体系。该体系一共有四个基类,而且都是抽象类。
字节流的抽象基类: InputStream, OutputStream
字符流的抽象基类:Reader ,Writer
注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。即子类名后缀都是父类名,前缀名都是这个子类的功能名称。
字符流和字节流
字节流两个基类:InputStream OutputStream
字符流两个基类:Reader Writer
范例:
import java.io.*;
public class filewritedemo {
public static void main(String[] args) throws IOException //读、写都会发生IO异常
{
//创建一个filewriter对象,该对象一被初始化就必须要明确被操作的文件。
//而且该文件会被创建到指定目录下, 如果该目录下已有同名文件,将被覆盖
//其实该步就是在明确数据要存放的目的地
FileWriter fw=new FileWriter("demo.txt");
//调用writer方法,将字符串写入到流中
fw.write("agwewetg");
//刷新流对象中的缓冲中的数据,将数据刷到目的地中
fw.flush();
//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据
//将数据刷到目的地中,
//和flush区别,flush刷新后,流可以继续使用,close刷新后,会将流关闭。
fw.close();// 关闭流,其实关闭的就是java调用的系统底层资源。在关闭前,会先刷新该流。
}
}
close()和flush()的区别:
flush():将缓冲区的数据刷到目的地中后,流可以使用。
close():将缓冲区的数据刷到目的地中后,流就关闭了,该方法主要用于结束调用的底层资源。这个动作一定做。
io异常的处理方式:io一定要写finally;
FileWriter写入数据的细节:
1:window中的换行符:\r\n两个符号组成。 linux:\n。
2:续写数据,只要在构造函数中传入新的参数true。
3:目录分割符:window \\ /
4:new FileWriter("demo.txt",true)//文件续写
import java.io.*;
public class filereaderdemo {
public static void main(String[] args) throws IOException
{
// 创建一个文件读取流对象,和指定名称的文件相关联
//要保证该文件是已经存在的,如果补存在,会发生异常fileNotFoundException
FileReader fr=new FileReader("demo.txt");
//调用读取流对象的read方法
//read():一次读一个字符,而且会自动往下读。
//读取方式的第一种。
int ch=0;
while((ch=fr.read())!=-1)//条件是没有读到结尾。
{
System.out.println((char)ch);//调用读取流的read方法,读取一个字符。
}
/*while(true)
{
int ch1=fr.read();
if(ch1==-1)
{
break;
}
System.out.println("ch="+(char)ch);
}*/
}
}
读取数据的第二种方式:第二种方式较为高效,自定义缓冲区。
第二种方式,通过字符数组进行读取
import java.io.FileReader;
import java.io.IOException;
public class FileReaderDemo2
{
public static void main(String[] args) throws IOException
{
FileReader fr=new FileReader("demo.txt");//创建读取流对象和指定文件关联。
//定义一个字符数组,用于存储读到字符。
//该read(char[])返回的是读到字符个数。
//因为要使用read(char[])方法,将读取到字符存入数组。所以要创建一个字符数组,一般数组的长度都是1024的整数倍。
char[]buf=new char[1024];
int num=0;
while((num=fr.read(buf))!=-1)
{ System.out.println(new String(buf,0,num));
}
fr.close();
}
}
读取一个.java文件,并打印在控制台上
import java.io.*;
public class filereaderTest {
public static void main(String[] args)throws IOException
{
FileReader fr=new FileReader("DateDemo.java");
char[] buf=new char[1024];
int num=0;
while((num=fr.read(buf))!=-1)
{
System.out.print(new String(buf,0,num));
}
fr.close();
}
}
经典例题:复制文件
步骤1,在D盘创建一个文件,用于存储C盘文件中的数据
2,定义读取流和C盘文件关联
3,通过不断的读写完成数据存储
4,关闭资源
import java.io.*;
public class CopyTest {
public static void main(String[] args) throws IOException {
copy_1();
}
public static void copy_2()
{
FileWriter fw=null;
FileReader fr=null;
try {
fw = new FileWriter("demo_copy.txt");
fr = new FileReader("demo.java");
char[] buf=new char[1024];
int len=0;
while((len=fr.read(buf))!=-1)
{
fw.write(buf,0,len);
}
} catch (IOException e) {
throw new RuntimeException("读写失败");
}
finally{
if(fr!=null)
try{fr.close();}
catch(IOException e){}
if(fw!=null)
try{fw.close();}
catch(IOException e){}
}
}
public static void copy_1()throws IOException
{
//创建目的地
FileWriter fw=new FileWriter("Runtimedemo_copy.txt");
//与已有文件关联
FileReader fr=new FileReader("Runtimedemo.java");
int ch=0;
while((ch=fr.read())!=-1)
{
fw.write(ch);
}
fw.close();
fr.close();
}
}
字符流:
Reader:用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int)和 close()。
|-- -BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
|---LineNumberReader:跟踪行号的缓冲字符输入流。此类定义了方法setLineNumber(int) 和getLineNumber(),它们可分别用于设置和获取当前行号。
|---InputStreamReader:是字节流通向字符流的桥梁:它使用指定的charset读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
|---FileReader:用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。
|---CharArrayReader:
|---StringReader:
-------------------------------------------------
Writer:写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush()和 close()。
|---BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
|---OutputStreamWriter:是字符流通向字节流的桥梁:可使用指定的charset将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
|---FileWriter:用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。
|---PrintWriter:
|---CharArrayWriter:
|---StringWriter:
---------------------------------
字节流:
InputStream:是表示字节输入流的所有类的超类。
|--- FileInputStream:从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。FileInputStream用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。
|---FilterInputStream:包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。
|--- BufferedInputStream:该类实现缓冲的输入流。
|--- Stream:
|---ObjectInputStream:
|---PipedInputStream:
-----------------------------------------------
OutputStream:此抽象类是表示输出字节流的所有类的超类。
|---FileOutputStream:文件输出流是用于将数据写入File或FileDescriptor的输出流。
|---FilterOutputStream:此类是过滤输出流的所有类的超类。
|---BufferedOutputStream:该类实现缓冲的输出流。
|--- PrintStream:
|--- DataOutputStream:
|---ObjectOutputStream:
|---PipedOutputStream:
--------------------------------
缓冲区是提高效率用的,给谁提高呢?
字符流的缓冲区
a) 缓冲区的出现提高了对数据的读写效率,对应类:BufferedWriter BufferdeReader
b) 缓冲区要结合流才可以使用,在流的基础上对流的功能进行了增强
c) 缓冲区的出现是为了提高流的操作效率而出现的,所以在创建缓冲区之前,必须要先有流对象
d) 该缓冲区中提供了一个跨平台的换行符:newLine();
BufferedWriter:是给字符输出流提高效率用的,那就意味着,缓冲区对象建立时,必须要先有流对象。明确要提高具体的流对象的效率。
字符读取流缓冲区
该缓冲区提供了一个一次读一行的方法readline。方便于对文本数据的获取,当返回null时,表示读到文件末尾
readline方法返回的时候只返回回车符之前的数据内容,并不返回回车符。
import java.io.*;
public class BufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
//创建一个读取流对象和文件相关联
FileReader fr=new FileReader("buf.txt");
//为了提高效率,加入缓冲技术,将字符读取流对象作为参数传递给缓冲对象的构造函数
BufferedReader bufr=new BufferedReader(fr);
String line=null;
while((line=bufr.readLine())!=null)
{
System.out.println(line);
}
/*String s1=bufr.readLine();//readLine方法返回的时候是不带换行符的。
System.out.println("s1:"+s1);*/
bufr.close();
}
}
范例2
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));//输出到控制台
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
bufw.write(line.toUpperCase());//将输入的字符转成大写字符输出
bufw.newLine();
bufw.flush();
}
bufw.close();
bufr.close();
利用缓冲区来复制文件:范例
import java.io.*;
public class copyTextByBuf {
public static void main(String[] args)
{
BufferedReader br=null;
BufferedWriter bw=null;
try {
br=new BufferedReader(new FileReader("bufferedWritedemo.java"));
bw=new BufferedWriter(new FileWriter("buffered_copy.txt"));
String line=null;
while((line=br.readLine())!=null)
{
bw.write(line);
bw.newLine();
bw.flush();
}
} catch (IOException e) {
throw new RuntimeException("读写失败");
} finally{
try{ if(br!=null)
br.close();
}
catch(IOException e)
{
throw new RuntimeException("读取关闭失败");
}
try{ if(bw!=null)
bw.close();
}
catch(IOException e)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
模拟一个readerline原理范例:这个逻辑性很强
import java.io.*;
public class MyBufferedReaderDemo
{
public static void main(String[] args) throws IOException
{ //创建一个读取流对象和文件相关联
FileReader fr=new FileReader("buf.txt");
MyBufferedReader mybuf=new MyBufferedReader(fr);
String line=null;
while((line=mybuf.myReadLine())!=null)
{
System.out.println(line);
}
mybuf.myclose();
}
}
class MyBufferedReader extends Reader
{
private /* FileReader */Reader r;
MyBufferedReader(Reader/*FileReader*/ r)
{
this.r=r;
}
//可以一次读一行数据的方法
public String myReadLine() throws IOException
{
//定义一个临时容器,原bufferedreader封装的是字符数组
//为了演示方便,定义一个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);
}
return null;
}
/*
* 覆盖reader类中的抽象方法*/
public int read(char[] cbuf,int off,int len)throws IOException
{
return r.read(cbuf,off,len);
}
public void close()throws IOException
{
r.close();
}
public void myclose() throws IOException
{
r.close();
}
}
__________________________________________________________________________________________________________
设计模式:装饰设计模式
定义:对一组类进行功能的增强。
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能,那么自定义的该类称为装饰类。装饰类通常会通过构造方法接受被装饰的对象并基于被装饰的对象的功能,提供更强的功能。
public class personDemo {
public static void main(String[] args) {
person p=new person();
p.chifan();
superperson p1=new superperson(p);
p1.superchifan();
}
}
class person
{
public void chifan()
{
System.out.println("吃饭");
}
}
class superperson //装饰设计模式
{ private person p;
superperson(person p)
{
this.p=p;
}
public void superchifan()
{
System.out.println("开胃酒");
p.chifan();
System.out.println("甜点");
}
}
MyReader//专门用于读取数据的类
|--MyTextReader
|---MyBufferTextReader
|--MyMediaReader
-- MyBufferMediaReader
|---MyDataReader
|--MyBufferDataReader
class MyBufferReader
{
MyBufferReader(MyTextReader text)
{}
MyBufferReader(MyMediaReader media)
{}
}
上面这个类扩展性很差
找到其参数的共同类型,通过多态的形式,可以提高扩展性。
class MyBufferReader extends MyReader
{private MyReader r;继承结构变成组合结构。
MyBufferReader(MyReader r)
{}
} 装饰设计模式。
MyReader//专门用于读取数据的类 、、、装饰
|--MyTextReader
|--MyMediaReader
|---MyDataReader
|--MyBufferReader
装饰模式比继承要灵活,避免了继承体系臃肿。而且降低了类与类之间的关系
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能
所以装饰类和被装饰类通常是都属于一个体系中的。
LineNumberReader:
跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。
import java.io.*;
public class LineNumberReaderDemo {
public static void main(String[] args)throws IOException
{
FileReader fr=new FileReader("persondemo.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 outputstream
在复制图片或者视频时用的是字节流
范例:复制图片
import java.io.*;
public class copyPic {
public static void main(String[] args) {
FileOutputStream fos=null;
FileInputStream fis=null;
try {
fos=new FileOutputStream("c:\\2. jpg");
fis=new FileInputStream("c:\\1. jpg");//源
byte[] buf=new byte[1024];//定义缓冲区
int len=0;
while((len=fis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
} catch (IOException e) {
throw new RuntimeException("复制文件失败");
}
finally
{
try {
if(fis!=null)
fis.close();
} catch (IOException e) {
throw new RuntimeException("读取文件失败");
}
try {
if(fos!=null)
fos.close();
} catch (IOException e) {
throw new RuntimeException("写入文件失败");
}
}
}
}
用字符流可以复制,但打开时看不了,因为图片数据读完一段以后去查看编码表,到编码表里面找到对应的数字给查出来了。如果找到对应数据编码没变,如果没找到对应的数据编码就找编码表的未知区域,最后编码变了。所以生成新图片的编码和老图片的编码不一样的。
通过键盘录入数据:
import java.io.*;
public class ReadIn {
/**读取键盘录入
* system.out:对应的是标准输出设备,控制台
* system.in:对应的标准输入设备,键盘
* 需求:通过键盘录入数据
* 当录入一行数据后,就将该行数据进行打印,如果录入的数据是over,那么停止录入
* @param args
*/
public static void main(String[] args)throws IOException
{
InputStream in=System.in;
StringBuilder sb=new StringBuilder();
while(true)
{
int ch=in.read();
if(ch=='\r')
continue;
if(ch=='\n')
{
String s=sb.toString();
if("over".equals(s))
break;
System.out.println(s.toUpperCase());
sb.delete(0, sb.length());
}
else
sb.append((char)ch);
}
/*int ch=0;
while((ch=in.read())!=-1)
{
System.out.println(ch);
}
in.close();*/
/*int by=in.read();
System.out.println(by);
System.out.println('\r'+0);*/
}
}
readLine方法是字符流BufferedReader类中的方法而键盘录入的read方法是字节流InputStream的方法
那么能不能将字节流转成字符流再使用字符流缓冲区的readLine方法呢?
InputStreamReader是字节流通向字符流的桥梁,是将字节流转换成字节流。
InputStreamReader(InputStream in)要接受一个指定的字节流。
创建一个使用默认字符串的InputStreamReader。
OutputStreamWriter 是字符流通向字节流的桥梁。可使用指定的charset将要写入流中的字符编码成字节,它使用的字符集可以由名称指定或显示给定,否则将接受平台默认的字符集。
为了提高效率,可考虑将OutputStreamWriter包装到BufferedWriter中,以避免频繁调用转换器。
OutputStreamWriter(OutputStream out)它在构造的时候要接收一个输出流。
import java.io.*;
public class TransStreamDemo {
public static void main(String[] args) throws IOException
{
/*//获取键盘录入对象
InputStream in=System.in;
//将字节流对象转成字符流对象,使用转换流,InputStreamReader
InputStreamReader isr=new InputStreamReader(in);
//为了提高效率,将字符串进行缓冲区技术高效操作,使用BufferedReader
BufferedReader bufr=new BufferedReader(isr);*/
//键盘录入的最常见写法。
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
/*
*OutputStream out=System.out;
*OutputStreamWriter osw=new OutputStreamWriter(out);
*BufferedWriter bufw=new BufferedWriter(osw);
*OutputStreamWriter不具有newLine方法。newLine是BufferedWriter的方法。
**/
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));
String line=null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());//可以不用写system.out.println();
bufw.newLine();
bufw.flush();
}
bufr.close();
}
}
InputStreamReader(InputStream in,String charsetName)
创建使用指定字符集的InputStreamReader
下面有一儿子FileReader操作字符文件。固定的编码表GBK,指定编码表时要看爹InputStreamReader。
如果是GBK的可以用FileReader,如果是utf-8则用InputStreamReader来读。
字符流的好处是可以直接返回来一个中文。
___________________________________________________________________________________________________________
流对象:
其实很简单,就是读取和写入。但是因为功能的不同,流的体系中提供N多的对象。那么开始时,到底该用哪个对象更为合适呢?这就需要明确流的操作规律。
流的操作规律:
1,明确源和目的。
数据源:就是需要读取,可以使用两个体系:InputStream、Reader;
数据汇:就是需要写入,可以使用两个体系:OutputStream、Writer;
2,操作的数据是否是纯文本数据?
如果是:数据源:Reader
数据汇:Writer
如果不是:数据源:InputStream
数据汇:OutputStream
3,虽然确定了一个体系,但是该体系中有太多的对象,到底用哪个呢?
明确操作的数据设备。
数据源对应的设备:硬盘(File),内存(数组),键盘(System.in)
数据汇对应的设备:硬盘(File),内存(数组),控制台(System.out)。
4,需要在基本操作上附加其他功能吗?比如缓冲。
如果需要就进行装饰。
转换流特有功能:转换流可以将字节转成字符,原因在于,将获取到的字节通过查编码表获取到指定对应字符。
转换流的最强功能就是基于字节流 + 编码表。没有转换,没有字符流。
发现转换流有一个子类就是操作文件的字符流对象:
InputStreamReader
|--FileReader
OutputStreamWriter
|--FileWrier
想要操作文本文件,必须要进行编码转换,而编码转换动作转换流都完成了。所以操作文件的流对象只要继承自转换流就可以读取一个字符了。
但是子类有一个局限性,就是子类中使用的编码是固定的,是本机默认的编码表,对于简体中文版的系统默认码表是GBK。
FileReader fr = new FileReader("a.txt");
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"gbk");
以上两句代码功能一致,
如果仅仅使用平台默认码表,就使用FileReader fr = new FileReader("a.txt"); //因为简化。
如果需要制定码表,必须用转换流。
转换流 = 字节流+编码表。
转换流的子类File = 字节流 + 默认编码表。
凡是操作设备上的文本数据,涉及编码转换,必须使用转换流。
----------------------- android培训、java培训、java学习型技术博客、期待与您交流! ----------------------