黑马程序员 java基础<三>--IO流(1)

时间:2023-02-16 13:07:46

                                                                     -------android培训java培训java学习型技术博客、期待与您交流! ----------

概述:

1、IO流:即Input Output的缩写。

2、特点:

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

2)Java对数据的操作是通过流的方式。

3)Java用于操作流的对象都在IO包中。

4)流按操作数据分为两种:字节流和字符流。

5)流按流向分为:输入流和输出流。

注意:流只能操作数据,而不能操作文件。

3、IO流的常用基类:

1)字节流的抽象基流:InputStream和OutputStream

2)字符流的抽象基流:Reader和Writer

注:此四个类派生出来的子类名称都是以父类名作为子类名的后缀,以前缀为其功能;如InputStream子类FileInputStream和Reader子类FileReader

用两张图来说明Java中IO流操作有关的类和IO流类图结构

黑马程序员  java基础<三>--IO流(1)

黑马程序员  java基础<三>--IO流(1)

                                                                                            

                                                  知识点一   字符流

一、简述:

1、字符流中的对象融合了编码表。使用的是默认的编码,即当前系统的编码。

2、字符流Writer,Reader只是用于处理文字数据,而字节流InputStream,OutputStream可以处理媒体数据,像图片,音频和视频等。

3、既然IO流是用于操作数据的,那么数据的最常见体现形式是文件。专门用于操作文件的Writer子类对象:FileWriter   --->  后缀名为父类名,前缀名为流对象的功能。

二、字符流的读写

1、写入字符流:

1)创建一个FileWriter对象,该对象一被初始化,就必须要明确被操作的文件。且该目录下如果已有同名文件,则同名文件将被覆盖。其实该步就是在明确数据要存放的目的地。

2)调用write(String s)方法,将字符串写入到流中。

3)调用flush()方法,刷新该流的缓冲,将数据刷新到目的地中。

4)调用close()方法,是关闭流资源。但是关闭前会刷新一次内部的缓冲数据,并将数据刷新到目的地中。

特别要注意几点:

①close()和flush()区别:flush()刷新后,流可以继续使用;而close()刷新后,将会关闭流,不可再写入字符流。

②其实java自身不能写入数据,而是调用系统内部方式完成数据的书写,使用系统资源后,一定要关闭资源。

③数据的续写是通过构造函数 FileWriter(String s,boolean append),根据给定文件名及指示是否附加写入数据的boolean值来构造FileWriter对象。

2、读取字符流:

1)创建一个文件读取流对象,和指定名称的文件相关联。要保证该文件已经存在,若不存在,将会发生异常FileNotFoundException。

2)调用读取流对象的read()方法。read():一次读一个字符,且会继续往下读。

      第一种方式:读取单个字符。第二种方式:通过字符数组进行读取。

3)读取后要将流资源关闭。

举例说明一下:

/*
那么先以操作文件为主来演示

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

找到一个专门用于操作文件的Writer子类对象。FileWriter 后缀名是父类名,前缀名是该流对象的功能
*/
import java.io.*;
class FileWriteDemo
{
public static void main(String []args) throws IOException
{
//创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件
//而且该文件会被创建到指定的目录下。如果该目录下已有同名文件,将被覆盖
//其实该步就是在明确数据要存放的目的地。
FileWriter fw =new FileWriter("C:/demo.txt");

fw.write("abcde");
//刷新流对象中缓冲中的数据
//将数据刷到目的地中
fw.flush();

fw.write("分工哈哈哈哈");
//将数据刷到目的地中
fw.flush();

//关闭流资源,但是关闭之前会刷新一次缓冲中的数据
//讲数据刷到目的地中,flush刷新后,流可以继续使用,close刷新后,会将流关闭。
fw.close();
}

}

三、IO异常的处理方式:

IO异常的处理方式
凡是与设备上的数据发生关系的,进行处理的都会发生IO异常,无论读还是写

异常是针对方法来说的,抛出、声明抛出、捕获和处理异常都是在方法中进行的。        Java异常处理通过5个关键字try、catch、throw、throws、finally进行管理。基本过程是用try语句块包住要监视的语句,如果在try语句块内出现异常,则异常会被抛出,你的代码在catch语句块中可以捕获到这个异常并做处理;还有以部分系统生成的异常在Java运行时自动抛出。你也可以通过throws关键字在方法上声明该方法要抛出异常,然后在方法内部通过throw抛出异常对象。异常中要记得在finally中判断流对象是否为空,并在使用完后关闭所有的流对象。

举例说明:

import java.io.*;

class FileWriteDemo2
{
public static void main(String []args)
{
//在外面建立引用,在try内初始化
FileWriter fw=null;
try{

//try里面定义的变量在外面访问不到
//FileWriter fw=new FileWriter("demo.txt");

//向磁盘中写入文件
fw=new FileWriter("c:\\demo.txt");
//向文件中写入内容
fw.write("abcdefg");


}catch(IOException e)
{
System.out.println("catch:"+e.toString());
//finally内关闭资源,一定会被执行的代码往里面放入
}finally{

try{
// 对流对象进行判断,如果不为空就关闭
if(fw!=null)
{
fw.close();
}


}catch(IOException e)
{
System.out.println(e.toString());
}

}

}
}
四:文件的续写

  /*
演示对已有文件的数据续写。
*/
import java.io.*;

class FileWriteDemo3
{
public static void main(String []args)
{
//在外面建立引用,在try内初始化
FileWriter fw=null;
try{

//try里面定义的变量在外面访问不到
//FileWriter fw=new FileWriter("demo.txt");

//向磁盘中写入文件
//传递一个true参数,代表不覆盖已有的文件,并在已有文件的末尾处进行数据的续写
fw=new FileWriter("c:\\demo.txt",true);
//向文件中写入内容
fw.write("nihao\r\nxiexie");


}catch(IOException e)
{
System.out.println("catch:"+e.toString());
//finally内关闭资源,一定会被执行的代码往里面放入
}finally{

try{
// 对流对象进行判断,如果不为空就关闭
if(fw!=null)
{
fw.close();
}


}catch(IOException e)
{
System.out.println(e.toString());
}

}

}
}

五、文本文件读取方式


/*第一种方式:一次读取一个字符*/import java.io.*;

class FileReaderDemo
{
public static void main(String []args) throws IOException
{
//创建一个文件读流对象,和指定名称的文件相关联
//要保证该文件时已经存在的,如果不存在,会发生异常FileNotFoundException
FileReader fr=new FileReader("C:/demo.txt");
//调用读取流对象的read方法
//raed()方法一次读入一个字符,而且会自动往下读。
/*
int ch=fr.read();

System.out.println("ch="+(char)ch);

int ch1=fr.read();

System.out.println("ch="+(char)ch1);

*/
//read方法作为整数读取的字符,范围在0到65535之间,如果达到流的末尾,则返回-1

/*
while(true)
{
int ch=fr.read();
if(ch==-1)
break;
System.out.println("ch="+(char)ch);
}
fr.close();
*/
int ch=0;

while((ch=fr.read())!=-1)
{
System.out.println("ch="+(char)ch);
}
}
}

/*
第二种方式,通过字符数组进行读取。

这种读取方式最有效
*/

import java.io.*;

class FileReaderDemo2
{
public static void main(String []args) throws IOException
{
FileReader fr=new FileReader("C:/demo.txt");

//定义一个字符数组。用于存储读到字符
//该read(char[])返回的是读到的字符个数

char [] buf=new char[1024];
/*
int num=fr.read(buf);

System.out.println("num="+num+"..."+new String(buf));

int num1=fr.read(buf);

System.out.println("num1="+num1+"..."+new String(buf));

int num2=fr.read(buf);

System.out.println("num2="+num2+"..."+new String(buf,0,1));
*/
int num=0;
while((num=fr.read(buf))!=-1)
{
System.out.println(new String(buf,0,num));
}
fr.close();
}
}

六、拷贝文本文件:

将C盘一个文本文件复制到D盘。
复制的原理:
其实就是将C盘下的文件数据存储到D盘的一个文件中。
步骤:
1,在D盘创建一个文件。用于存储C盘文件中的数据。
2,定义读取流和C盘文件关联。
3,通过不断的读写完成数据存储。

方式一:读取一个字符,存入一个字符
方式二:先将读取的数据存入到内存中,再将存入的字符取出写入D盘
4,关闭资源:输入流资源和输出流资源。
举例:

import java.io.*;

class CopyText
{
public static void main(String[] args) throws IOException
{
copy_2();
}


public static void copy_2()
{
FileWriter fw = null;
FileReader fr = null;
try
{
fw = new FileWriter("SystemDemo_copy.txt");
fr = new FileReader("SystemDemo.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)
{
}
}
}

//从C盘读一个字符,就往D盘写一个字符。
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();

}
}

示意图:

黑马程序员  java基础<三>--IO流(1)

七、BufferedWriter和BufferedReader:字符流缓冲区

1、缓冲区的出现:提高了流的读写效率,所以在缓冲区创建前,要先创建流对象,即先将流对象初始化到构造函数中。

2、缓冲技术原理:此对象中封装了数组,将数据存入,在一次性取出。

3、写入流缓冲区BufferedWriter的步骤:

1)创建一个字符写入流对象

2)为提高字符写入流效率,加入缓冲技术,只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。

      注意,只要用到缓冲去就需要刷新。

3)其实关闭缓冲区就是在关闭缓冲区中的流对象。

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

4、读取流缓冲区BufferedReader的步骤:

1)创建一个读取流对象和文件关联

2)为提高效率,加入缓冲技术,将字符读取流对象作为参数传递给缓冲对象的构造函数。

3)该缓冲区提供了一个一次读一行的方法readLine(),方便与对文本数据的获取,当返回null时,表示读到文件末尾。

      readLine()方法返回的时只返回回车符之前的数据内容,并不返回回车符,即读取的内容中不包含任何行终止符(回车符和换行符)。

--->readLine()方法原理:无论是读一行,或读取多个字符,其实最终都是在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个。

5、使用缓冲技术拷贝文件,提高效率:

/*
缓冲区的出现是为了提高流的操作效率而出现的

所以在创建缓冲区之前,必须要先有流对象

\r\n是在windows下换行 \n在linux是换行

该缓冲区中提供了一个跨平台的换行符
newLine();
*/
import java.io.*;

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

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

for(int x=1;x<5;x++)
{
bufw.write("abcd"+x);
bufw.newLine();
bufw.flush();
}
//bufw.write("abcde");
//bufw .write("\r\n");
//newLine()在任何系统平台下都是换行
// bufw.newLine();

//记住,只要用到缓冲区,就要记得刷新
//bufw.flush();

//其实关闭缓冲区,就是在关闭缓冲区中的流对象
bufw.close();

}
}

/*
字符读取流缓冲区:

该缓冲区提供了一个一次读一行的方法readLine,方便于对文本数据的获取。
当返回null时,表示读到文件末尾。
*/
import java.io.*;
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();

System.out.println("s1:"+s1);

String s2=bufr.readLine();

System.out.println("s2:"+s2);
*/
bufr.close();
}

}

示意图:


黑马程序员  java基础<三>--IO流(1)

八、MyBufferedReader( 自定义BufferedReader):

原理:可根据BufferedReader类中特有发那个发readLine()的原理,自定义一个类中包含相同功能的方法

步骤:1.初始化自定义的类,加入流对象。

            2.定义一个临时容器,原BufferedReader封装的是字符数组,此类中可定义一个StringBuilder的容器,最终可实现字符串的提取。

/*
明白了BufferedReader类中特有方法readLine的原理后,
可以自定义一个类中包含一个功能和readline一致的方法
来模拟一下BufferedReader
*/
import java.io.*;

class MyBufferedReader extends Reader
{

//装饰设计模式
private Reader r=null;

MyBufferedReader(Reader r)
{
this.r=r;
}
//可以一次读一行数据的方法
public String myReadLine() throws IOException
{
//定义一个临时容器,员BufferedReader封装的是字符数组
//为了演示方便,定义一个StringBulilder容器,因为最终还是要将数据变成字符串。
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);
}
//如果没有遇到\n,sb中而且有数据的话也要返回
if(sb.length()!=0)
return sb.toString();
return null;
}
/*
覆盖类中的抽象方法
*/
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();
}
}



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();
}

}

九、装饰设计模式:

1、简述:当想对已有对象进行功能增强是,可定义类:将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该类称之为装饰类。即对原有类进行了优化。

装饰设计模式:
当想要对已有的对象进行功能增强时,
可以定义一个类,将已有对象传入,基于已有的功能,并提供加强功能
那么自定义的该类称为装饰类
装饰类通常会通过构造方法接受被装饰的对象
并基于被装饰的对象的功能,提供更详细的功能。

2、特点:装饰类通常都会通过构造方法接收被装饰的对象,并基于被装饰的对i型那个的功能提供更强的功能。

3、装饰和继承的区别:

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

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

3)从继承结构转为组合结构。

注:在定义类的时候,不要以继承为主;可通过装饰设计模式进行增强类功能。灵活性较强,当装饰类中的功能不适合,可再使用被装饰类的功能。

        要继承相应的父类,就需要将所有的抽象方法实现,或交给子类实现。其中MyBufferedReader的例子就是最好的装饰设计模式的例子,就是                 对BufferedReader的增强。

举例:

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("甜点");
System.out.println("吸根烟");
}

}
class PersonDemo
{
public static void main(String[] args)
{
Person p=new Person();
// p.chifan();
SuperPerson sp=new SuperPerson(p);

sp.superChifan();
}

}

/*
明白了BufferedReader类中特有方法readLine的原理后,
可以自定义一个类中包含一个功能和readline一致的方法
来模拟一下BufferedReader
*/
import java.io.*;

class MyBufferedReader extends Reader
{

//装饰设计模式
private Reader r=null;

MyBufferedReader(Reader r)
{
this.r=r;
}
//可以一次读一行数据的方法
public String myReadLine() throws IOException
{
//定义一个临时容器,员BufferedReader封装的是字符数组
//为了演示方便,定义一个StringBulilder容器,因为最终还是要将数据变成字符串。
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);
}
//如果没有遇到\n,sb中而且有数据的话也要返回
if(sb.length()!=0)
return sb.toString();
return null;
}
/*
覆盖类中的抽象方法
*/
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();
}
}



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();
}

}
十、 LineNumberReader和MyLineNumberReader
在BufferedReader中有个直接的子类LineNumberReader,其中有特有的方法获取和设置行号:

setLineNumber()和getLineNumber()

原理:LineNumberReader中有一个计数器,定义一个Reader流对象,当读到流对象的时候才能进行行号的添加

示例:

import java.io.*;

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();
}

}

/*

*/
import java.io.*;

class MyLineNumberReader extends MyBufferedReader
{
private Reader r;
private int lineNumber;
MyLineNumberReader(Reader r)
{
super(r);
}
public String myReadLine() throws IOException
{

lineNumber++;
return super.myReadLine();
}

public void setLineNumber(int lineNumber)
{
this.lineNumber=lineNumber;
}
public int getLineNumber()
{
return lineNumber;
}
}

/*
class MyLineNumberReader
{
private Reader r;
private int lineNumber;
MyLineNumberReader(Reader r)
{
this.r=r;
}
public String myReadLine() throws IOException
{

lineNumber++;
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;
}

public void setLineNumber(int lineNumber)
{
this.lineNumber=lineNumber;
}
public int getLineNumber()
{
return lineNumber;
}

public void myClose()throws IOException
{
r.close();
}

}
*/
class MyLineNumberReaderDemo
{
public static void main(String[] number) throws IOException
{
FileReader fr=new FileReader("copyTextByBuf.java");

MyLineNumberReader mylnr=new MyLineNumberReader(fr);

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

最新最全的的java学习视频教程: http://pro.net.itcast.cn/View-22-1458.aspx

                                          -------android培训java培训java学习型技术博客、期待与您交流! ----------

详细请查看:http://edu.csdn.net/heima