您的位置:首页 > 职场人生

黑马程序员----IO流(字符流、字节流、转换流)

2015-08-15 21:39 483 查看
——- android培训java培训、期待与您交流! ———-

IO流

        1.IO流用来处理设备之间的数据传输。

        2.java对数据的操作是通过流的方式。

        3.java用于操作流的对象都在IO包中。

        流对象的分类:

                1.按照操作数据分类:字节流和字符流(可以在内部融合编码表,可以指定到底是按照GBK的码表还是UTF-8的码表);    

                2.按照流向分类:输入流和输出流。

        IO流的常用基类:

                字节流的基类:InputStream(读)和OutputStream(写);

                字符流的基类:Reader和Writer.

        注意:由这四个类派生出来的子类的名称都是以其父类名作为子类名的后缀。如:InputStream的子类FileInputStream,Reader的子类FileReader.前缀名就是这个子类对象的功能。

        这四个基类都是抽象的,里面有抽象方法,需要被子类分别实现。

字符流

        既然IO流是用于操作数据的,那么数据的最常体现形式是文件,先以操作文件为主来演示。

        需求:在硬盘上创建一个文件,并写入数据。

        找到一个专门用于操作文件的Writer对象FileWriter(没有空参数的构造函数,想要操作文件,就必须先有文件),该对象一被初始化,就要有被操作的文件存在。

 

        close()和flush()的区别:

                 flush()刷新后流可以继续使用,close()刷新后流被关闭,不能再使用。

     

        java靠系统内部的方式来完成数据的书写,会使用Windows的资源,使用完以后要释放资源。

FileWriter(写)

public class IO_FileWriter
{
public static void main(String[] args) throws IOException //抛出IO异常,因为文件路径可能会写错,会判断文件有没有问题;
{
//第一步:创建一个FileWriter对象,该对象一被初始化就要明确被操作的文件;而且该文件会被创建到指定的目录下;
//如果该目录下已经有同名文件,将被覆盖;其实该步就是在明确数据要存放的目的地
FileWriter fw=new FileWriter("c:\\File.txt");

//第二步:往文件中写内容。调用write方法,将内容写到流当中;
fw.write("zxcvbnm");

//第三步:刷新该流的缓冲,缓冲里面临时地存放数据,调用flush方法,将缓冲区里面的数据放到目的地中;
fw.flush();

//第四步:关闭流资源,关闭前会刷新一下内部的缓冲中的数据,将数据刷到目的地中;
fw.close();
fw.write("sdfghjk");//流关闭,不能再往里面写数据;
}
}
FileReader(读)

文件的读取方式一:单个字符读取;

read():一次只读取一个字符,而且会自动往下读,该方法返回作为整数读取的字符,如果已经到达流末尾,则返回-1;

文件的读取方式二:通过字符数组进行读取;

read(char[] cbuf):返回的是读到的字符个数。

public class IO_FileReader
{
public static void main(String[] args) throws IOException
{
//方式一:
//创建一个文件读取流对象,和指定名称的文件相关联;要保证该文件是已经存在的,如果不存在,会发生异常FileNotFound;
FileReader fr = new FileReader("c:\\IO_Exception");

//调用读取流对象的read方法;
/*
int ch1=fr.read();
System.out.println("ch="+(char)ch1);

int ch2=fr.read();
System.out.println("ch="+(char)ch2);

int ch3=fr.read();
System.out.println("ch="+(char)ch3);
*/
/*
while(true)
{
int ch=fr.read();
if(ch==-1)
break;
System.out.println("ch:"+(char)ch);
}
*/
int ch=0;
while((ch=fr.read())!=-1)
{
System.out.println("ch:"+(char)ch);
}
//关闭资源;
fr.close();

System.out.println("-----------------------------");

//方式二:
FileReader fr1=new FileReader("c:\\IO_Exception");

//定义一个字符数组,用于存储读到的字符。
char[] buf=new char[1024];//数组的长度定义为1024的整数倍;
/*
int num1=fr1.read(buf);
System.out.println("num"+num1+":"+new String(buf));//3:abc

int num2=fr1.read(buf);
System.out.println("num"+num2+":"+new String(buf));//3:def

int num3=fr1.read(buf);
System.out.println("num"+num3+":"+new String(buf,0,num3));//1:g;读取数组中的一部分转换成字符串输出。

int num4=fr1.read(buf);
System.out.println("num"+num4+":"+new String(buf));//-1:gef
*/
int num=0;
while((num=fr1.read(buf))!=-1)
{
System.out.println(new String(buf,0,num));//读到几个就打印几个;
}
fr1.close();
}
}


字符流缓冲区

提高了对数据的读写效率;缓冲区要结合流才可以使用,在流的基础上对流的功能进行增强。

缓冲区的出现是为了提高流的操作效率而出现的,所以在创建缓冲区之前,必须要先有流对象。

对应类:

        1.BufferedWriter:将文本写入字符流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

        2.BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

该缓冲区中提供了一个跨平台的换行符:newLine();

BufferedWriter

public class IO_BufferedWriter
{
public static void main(String[] args) throws IOException
{
//创建一个字符写入流对象:
FileWriter fw=new FileWriter("c:\\buf.txt");

//为了提高字符写入流的效率,加入缓冲技术,原理是这个对象里面封装了数组。只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
BufferedWriter bufw=new BufferedWriter(fw);
for(int x=1;x<5;x++)
{
bufw.write("sdfghjkl"+x);

bufw.newLine();//相当于bufw.write("\r\n");写入一个换行符;但是newLine方法具有跨平台性;

//记住,只要用到缓冲区,就要记得刷新;
bufw.flush();
}
//缓冲区的关闭其实关闭的是它所提高效率的流对象;
bufw.close();//fw.close();不用再写
}
}
BufferedReader

该缓冲区提供了一个一次读一行的方法readLine(),方便于对文本数据的获取。

该方法返回该行内容的字符串,不包含任何行终止符,如果已经到达流末尾,则返回null.

    

readLine方法的原理:

        无论是读一行,获取读取多个字符,其实最终都是在硬盘上一个一个读取,所以最终使用的还是read方法一次读一个。

public class IO_0_BufferedReader
{
public static void main(String[] args) throws IOException
{
//创建一个读取流对象和文件相关联;
FileReader fr=new FileReader("c:\\buf.txt");

//为了提高效率,加入缓冲技术,将字符读取流对象作为参数传递给缓冲对象的构造函数;
BufferedReader bufr=new BufferedReader(fr);
/*
String s1=bufr.readLine();
System.out.println("s1:"+s1);

String s2=bufr.readLine();
System.out.println("s1:"+s2);
*/
String line=null;
while((line=bufr.readLine())!=null)
{
System.out.println(line);
}
bufr.close();
}
}

装饰设计模式

        当想要对已有的对象进行功能增强时可以定义一个类将已有对象传入,基于已有对象的功能并提供加强功能,那么自定义的该类就称为装饰类。

        装饰类:装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能提供更强的功能。
class Person
{
public void eat()
{
System.out.println("吃饭");
}
}
class SuperPerson
{
private Person p;
SuperPerson(Person p)
{
this.p=p;
}
public void supereat()
{
System.out.println("开胃酒");
//System.out.println("吃饭");
p.eat();
System.out.println("甜点");
System.out.println("来一根");
}
}
public class IO_0_DecorationClass
{
public static void main(String[] args)
{
Person p=new Person();
p.eat();

System.out.println("-----------------");

SuperPerson sp=new SuperPerson(p);
sp.supereat();
}
}

MyReader(类)

专门用于读取数据的对象。

对不同类型的数据进行读取,出现一个继承体系:

MyReader

    --MyTextReader:

        --MyBufferedTextReader:

    --MyMediaReader:

        --MyBufferedMediaReader:

    --MyDataReader:

        --MyBufferedDataReader:

    ......

对体系进行优化;

class MyBufferedReader

{

    MyBufferedReader(MyTextReader text){}

    MyBufferReader(MyMediaReader media){}

    ......

}

上面这个类扩展性极差,找到其参数的共同类型,通过多态的形式,可以提高扩展性。

class MyBufferedReader extends MyReader

{

    MyBufferedReader(MyReader r){}

}

上面的体系变成:

MyReader

    --MyTextReader:

    --MyMediaReader:

    --MyDataReader:

    ......

    --MyBufferedReader:

    

装饰和继承的区别:

    装饰模式比继承要灵活,避免了继承体系的臃肿,而且降低了类与类之间的关系。

    装饰类因为增强了已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。所以,装饰类和被装饰类通常是属于同一个体系中。

带行号的装饰类LineNumberReader:

    可以跟踪行号的缓冲字符输入流,此类定义了方法setLineNumber(int)和getLineNumber(),它们用于设置和获取当前行号。

    默认情况下,行编号从0开始,该行号随数据流读取每个行结束符处递增,并且可以通过setLineNumber(int)更改行号,

    但要注意的是,setLineNumber(int)不会实际更改流中的当前位置,它只更改将由getLineNumber()返回的值。
// 自定义LineNumberReader类:
class MyLineNumberReader //extends MyBufferedReader
{
private Reader r;
private int lineNumber;

MyLineNumberReader(Reader r)
{
this.r=r;
/*
super(r);
*/
}
public String myReadLine() throws IOException
{
lineNumber++;//行号自增;
/*
return super.myReadLine();
*/
StringBuilder sb=new StringBuilder();

int ch=0;
while((ch=r.read())!=-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 setLineNumber(int lineNumber)
{
this.lineNumber=lineNumber;
}
public int getLineNumber()
{
return lineNumber;
}
public void myClose() throws IOException//继承父类后,此方法不写;
{
r.close();
}
}
public class IO_0_DecorateAndExtends
{
public static void main(String[] args) throws IOException
{
FileReader fr=new FileReader("c:\\Regex_0.java");

LineNumberReader lnr=new LineNumberReader(fr);

String line=null;
lnr.setLineNumber(0);//设置起始行号;
while((line=lnr.readLine())!=null)
{
System.out.println(lnr.getLineNumber()+":"+line);
}

lnr.close();

System.out.println("-------------------------");

//自定义LineNumberReader:
FileReader fr1=new FileReader("c:\\Regex_0.java");

MyLineNumberReader mylnr=new MyLineNumberReader(fr1);

String myline=null;
mylnr.setLineNumber(100);
while((myline=mylnr.myReadLine())!=null)
{
System.out.println(mylnr.getLineNumber()+":"+myline);
}

mylnr.myClose();
}
}

字节流

        InputStream(读)和OutputStream(写);

        使用字节流复制一个图片。

        思路:

                1.用字节读取流对象和图片关联;

                2.用字节写入流创建一个图片文件,用于存储获取到的图片资源。

                3.通过循环读写,完成数据的存储。

                4.关闭资源。

        其实用字符流也可以复制,但是复制完的图片有可能打不开,因为图片数据在读的时候,每读一段都会去查编码表,如果找到对应的数字,则码不会变,如果没有找到相应的数字,则会找一个类似于相似代码,就是走了编码表的位置数据区域,这个时候编码会发生变化,导致拷贝失败。

public class IO_CopyPicture
{
public static void main(String[] args)
{
FileOutputStream fos=null;
FileInputStream fis=null;
try
{
fos=new FileOutputStream("c:\\2.png");
fis=new FileInputStream("c:\\1.png");

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(fos!=null)
fos.close();
}
catch (IOException e)
{
throw new RuntimeException("写入关闭失败");
}
try
{
if(fis!=null)
fis.close();
}
catch (IOException e)
{
throw new RuntimeException("读取关闭失败");
}
}
}
}

字节流缓冲区

        该类实现缓冲的流,通过设置这种流,应用程序就可以将各个字节写入底层流中,而不必针对每次字节写入调用底层系统。

        BufferedOutputStream:

        BufferedInputStream:

        通过字节流缓冲区演示MP3的复制public class IO_BufferedOutputStream
{
public static void main(String[] args) throws IOException
{
long start=System.currentTimeMillis();
copy_1();
long end=System.currentTimeMillis();

System.out.println((end-start)+"毫秒");
}
//通过字节流的缓冲区完成复制;
public static void copy_1() throws IOException
{
BufferedInputStream bufis=new BufferedInputStream(new FileInputStream("f:\\what more can I give.mp4"));
BufferedOutputStream bufos=new BufferedOutputStream(new FileOutputStream("f:\\0.mp4"));

int by=0;
while((by=bufis.read())!=-1)
{
bufos.write(by);
}
bufos.close();
bufis.close();
}
}自定义字节流缓冲区

问题:

byte:-1  ---->int:-1;

11111111 11111111 11111111 11111111

00000000 00000000 00000000 11111111

11111111 ---->提升了一个int类型,还是-1.是-1的原因是在8个1前面补的是1导致的。

那么只要在前面补0,既可以保留原字节数据不变,又可以避免-1的出现。

但是,怎么补0呢?

   11111111   11111111   11111111  11111111

& 00000000 00000000 00000000 11111111

---------------------------------------------------------------

  00000000 00000000 00000000 11111111    (前面补了0)

class MyBufferedInputStream
{
private InputStream in;
private byte[] buf=new byte[1024*4];
private int pos=0,count=0;

MyBufferedInputStream(InputStream in)
{
this.in=in;
}

//一次读一个字节,从缓冲区(字节数组)中获取;
public int myRead() throws IOException//返回的是int类型,对byte b进行类型提升,在前面补0即可,取数的最低8位:&255;
{
//通过in对象读取硬盘上的数据,并存储在buf中。
if(count==0)
{
count=in.read(buf);
if(count<0)
return -1;
pos=0;
byte b=buf[pos];

count--;
pos++;
return b&255;//补0;
}
else if(count>0)
{
byte b=buf[pos];

count--;
pos++;
return b&255;//补0;
}
return -1;
}
//关闭流:
public void myclose() throws IOException
{
in.close();
}
}
public class IO_MyBufferedInputStream
{
public static void copy_2() throws IOException
{
MyBufferedInputStream bufis=new MyBufferedInputStream(new FileInputStream("f:\\what more can I give.mp4"));
BufferedOutputStream bufos=new BufferedOutputStream(new FileOutputStream("f:\\1.mp4"));

int by=0;

//System.out.println("第一个字节:"+bufis.myRead());
while((by=bufis.myRead())!=-1)//读到了连续8个1,返回了-1,所以停下来了;
{
bufos.write(by);//写最低8位,保证了数据的原样性;
}
bufos.close();
bufis.myclose();
}
public static void main(String[] args) throws IOException
{
long start=System.currentTimeMillis();
copy_2();
long end=System.currentTimeMillis();

System.out.println((end-start)+"毫秒");
}
}

转换流

        专门用来操作字节流的字符流对象。

        1.InputStreamReader:

                字节流通向字符流的桥梁。

        2.OutputStreamWriter:

                字符流通向字节流的桥梁。

        读取键盘录入:

                System.out:对应的是标准的输出设备,默认是控制台;

                System.in:对应的是标准的输入设备,默认的是键盘。

        需求:通过键盘录入数据,当录入一行数据后,就将该行数据进行打印。如果录入的数据时over,那么就停止录入。通过键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理,也就是readLine方法。

        那么,能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?

        readLine方法是字符流BufferedReader类中的方法,而键盘录入的read方法是字节流InputStream的方法。

        将字节流转换成字符流,再使用字符流缓冲区的readLine方法。

public class IO_KeyboardInput
{
public static void main(String[] args) throws IOException
{
//KeyBoardIn();

//使用readLine将代码简化;
//第一步:获取键盘录入对象:
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);

//BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));

String line=null;
while((line=bufr.readLine())!=null)
{
//定义结束标记:
if("over".equals(line))
break;
//System.out.print(line.toUpperCase());
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();

}
public static void KeyBoardIn() throws IOException
{
InputStream in=System.in;
/*
int by=in.read();
System.out.println(by);
*/
/*
int ch=0;
while((ch=in.read())!=-1)
{
System.out.println(ch);
}
in.close();//不关也可以,程序结束,录入也就结束了。
*/
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);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息