-----------------------------android培训,java培训期待与您交流!---------------------------
Java I/O流系统
1. 流:流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中“流”动一样。
IO流的概述:IO流用来处理设备之间的数据传输。Java对数据的操作是通过流的方式。Java用于操作流的对象都在io包中。流按操作数据分为两种:字节流和字符流。流按流向分为:输入流,输出流。字符流是为了处理文本格式编码而存在的。
IO流常用的基类:字节流的抽象基类:InputStream,OutputStream 字符流的抽象基类:Reader,Writer.这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
2. 字符流:Writer,Reader
Writer
FileWriter的特点
package io;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo {
public static void main(String[] args) throws IOException {
// 创建一个FileWriter对象,该对象被初始化的时候要求必须明确要操作的文件。也就是说没有无参的构造函数。
//创建的文件放在指定的目录下,如果该目录下有同名的文件,那么原文件将被新文件覆
FileWriter fw = new FileWriter("D:\\heimaio\\filedemo.txt");
// 调用write方法将字符串写入到流中
fw.write("abcd");
// 将数据刷到目的地中。
fw.flush();
//关闭流资源,但是关闭之前会刷新一次内部缓存中的数据。
// 和flush的区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
fw.close();
}
}
io异常的专业处理方式:
package io;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo2 {
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("d:\\heimaio\\filedemo2.txt");
fw.write("gekuillwf");
} catch (IOException e) {
} finally {
try {
// 在关闭流之前,需要判断下流是否被创建成功,如果fw没有被创建成功,那么就会抛空指针异常。
if (fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文件的续写:
考虑从哪里入手。如果再开启一个流对象对指定的文件进行续写,那么就得从构造函数入手。
FileWriter("d:\\heimaio\\filedemo2.txt",true);如果目录下的文件已经存在,那么就在原文件的末尾处进行续写,如果该文件不存在,那么就直接创建一个新的文件。
Reader
文件文本读取方式一:
FileReader fr= null;
try {
//在读取硬盘上的文件的时候,首先硬盘上面得存在这个文件。
//也就是说,在创建FileReader对象的时候,必须在构造函数上有相应的关联硬盘文件的实参。
//肯定也会抛异常,因为可能给的实参信息不正确造成FileReader对象没有被创建出来。
fr = new FileReader("d:\\heimaio\\filereaderdemo.txt");
//read()方法的特点:每次读取一个字符,而且自动往下读。0-65535,也就是两个字节0000 0000-1111 1111,转成一个int整型数。
//当读取的整数值是-1时候,读到了文件的末尾。此时结束。
int ch=0;
while((ch=fr.read())!=-1){
System.out.println((char)ch);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
} finally{
if(fr!=null)
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
文件文本读取方式二:
fr = new FileReader("d:\\heimaio\\filereaderdemo2.txt");
// 定义一个缓存char[],每次读取的时候,根据缓存的最大容量进行读取返回读取的num。并将读取的数存入到char[]中。
// 如果读取的字符的个数小于缓存的容量,读取几个是几个。
// 如果文件中字符个数大于缓存的容量,每次读取的数据会将上次读取后存在缓存的数据给覆盖,覆盖顺序从前往后。
char[] cbuf = new char[1024];
int num = 0;
while ((num = fr.read(cbuf)) != -1) {
System.out.println(new String(cbuf, 0, num));
}
文件的拷贝:
用字符流读取硬盘上的文件。然后再用FileWriter写入到其他目录下的同名文件。
private static void copyText() {
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader("d:\\heimaio\\filedemo.txt");
fw = new FileWriter("c:\\filedemo.txt");
char[] cbuf = new char[1024];
int num = 0;
while ((num = fr.read(cbuf)) != -1) {
String str = new String(cbuf, 0, num);
// 每读取一次,就将数据写入到指定的文本中。
fw.write(str);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3. 字符流的缓冲区
概述:缓冲区的出现提高了对数据的读写效率(提高流的读写效率,避免了重复的对硬盘进行擦写而浪费的大量时间)。对应的类:BufferedWriter BufferedReader,缓冲区要结合流才可以使用。在流的基础上对流的功能进行增强。
BufferWriter
FileWriter fw = null;
BufferedWriter bw = null;
try {
// 创建一个字符写入流。
fw = new FileWriter("d:\\heimaio\\bufferedwriterdemo.txt");
// 为了提高流的效率加入了缓冲区技术。
bw = new BufferedWriter(fw);
bw.write("huanchongqu shuju ");
//该缓冲区提供了一个跨平台的换行符方法。
bw.newLine();
// 只要用到缓冲区就要记得刷新
bw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭缓冲区,就是在关闭缓冲区中的流对象。
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
BufferedWriter
//创建一个读取流对象和文件相关联。
fr = new FileReader("d:\\heimaio\\BufferedReaderDemo.txt");
//为了提高效率加入缓冲技术
br = new BufferedReader(fr);
String str = null;
int num = 0;
//该缓冲区提供了一个读一行的方法readLine(),方便于对文本数据的获取,当返回null时候,表示读到文件末尾。
while ((str = br.readLine()) != null) {
num++;
System.out.println(str + num);
}
通过缓存区复制一个文件
fr = new FileReader("d:\\heimaio\\CopyTextByBuf.txt");
fw = new FileWriter("c:\\CopyTextByBuf.txt");
br = new BufferedReader(fr);
bw = new BufferedWriter(fw);
String line = null;
// 读取源文件中的每一行数据,一行一行地写给指定目录下的同名文件。readLine()方法返回的时候只返回回车符之前的数据,並不反回回车符。其实底层用的还是read()方法,读取每一行的数据存储在内存中。读到\r\n为一行。
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
自己做的缓冲区实现readLine():
public String myReadline() throws IOException {
int ch = 0;
StringBuilder sb = new StringBuilder();
while ((ch = r.read()) != -1) {
if ((char) ch == '\r')
continue;
if ((char) ch == '\n')
break;
// 不应该在这个地方返回,因为返回的时候只有读到\r\n时候才能有数据返回。
sb.append((char) ch);
}
// 只要sb里面有数据就要返回。
if (sb.toString().equals(""))
return null;
return sb.toString();
}
装饰设计模式:
当想要对已有的对象进行功能的增强时,可以定义类,将已经有的对象传入,基于已经有的功能,并提供加强功能,那么自定义的该类成为装饰类,装饰类通常会通过构造方法接收被装饰的对象并基于被装饰对象的功能提供更强的功能。记着在面试答到该设计模式时候,举例:流和缓冲流。典型的是缓冲流中readLine()方法对read()方法的加强。
装饰和继承的区别:
装饰模式比继承要灵活。避免了继承体系臃肿。而且降低了类与类之间的关系。
装饰类因为增强了已有对象,具备的功能和已有的是相同。只不过是提供了更强的功能。所以装饰类和被装饰类通常都是属于一个体系中的。
LineNumberReader:
FileReader fr = new FileReader("d:\\heimaio\\LineNumberReaderDemo.txt");
// 利用了装饰模式的类,将fr作为参数被装饰。
LineNumberReader lnr = new LineNumberReader(fr);
// 里面增强了一个功能就是:获取行号。
String line = null;
while ((line = lnr.readLine()) != null) {
System.out.println(lnr.getLineNumber()+line);
}
4.字节流:OutputStream InputStream
//当想要操作图片的数据时候。这时候一定要使用字节流进行操作。
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileStream {
public static void main(String[] args) {
// writeFile();
// readFile_1();
// readFile_2();
readFile_3();
}
private static void readFile_3() {
FileInputStream fis = null;
try {
fis = new FileInputStream("d:\\heimaio\\FileStream.txt");
// available方法用来显示文件的字符个数,对于数据量较小的文件。可以使用该方法。因为它直接给出了缓存的大小,不需要再进行循环操作。
// 但是当数据量很大的时候,比如一个电影,那么这时候byte[] buf=new byte[fis.available()];
// 会出现内存溢出。这个方法应该慎用。
int len = fis.available();
byte[] buf= new byte[fis.available()];
fis.read(buf);
// int len1 = fis.available();
System.out.println(new String(buf,0,len));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void readFile_2() {
FileInputStream fis = null;
try {
fis = new FileInputStream("d:\\heimaio\\FileStream.txt");
int len =0;
// 通过定义一个缓存进行读取,和字符流读取方式类似。
byte[] buf = new byte[1024];
while((len=fis.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void readFile_1() {
FileInputStream fis = null;
try {
fis = new FileInputStream("d:\\heimaio\\FileStream.txt");
int ch=0;
// 一个一个字符的读,和字符流读取方式类似。
while((ch=fis.read())!=-1){
System.out.println((char)ch);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void writeFile() {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("d:\\heimaio\\FileStream.txt");
fos.write("gekui".getBytes());
// 为什么通过字节流写数据的时候不需要刷新。flush();
// 因为字节是计算机操作的最小基本单位,它不需要进行缓存而直接写到目标文件中
// 而字符流需要调用flush()的原因:是因为它需要进行缓存,因为它里面存的是字符
// 需要在内存中对字符进行特定的操作,如编码等。如果缓存的话,那么它读一个字节
// 就直接写入到目标文件里,也就是说读了一个字符的一般,这就会带来很多麻烦。
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
5.字节流的缓冲区
与字节流的缓冲区基本一样。都是利用装饰设计模式。首先得先定义一个流对象FileOutputStream fos = new FileOutputStream("d:\\heimaio\\FileStream.txt");将它作为参数传递给装饰类
BufferedOutputStream,然后再使用缓冲类(装饰类)里的加强方法对文件进行操作。例如:拷贝一段媒体文件。先将媒体文件读取到流中去,然后再将读取的数据写入到目标目录下。
注意:在进行数据读取时,read()方法其实是在对读取byte类型的数据进行int提升。内部用了b&255,将int型的前24位补零(用“&”的原因防止出现连续8个1的造成数据没有被有效读取就终止了读取操作)。而write(int)方法在写数据的时候,用了int的最低8位一个字节进行写操作。
读取键盘的录入:
InputStreamReader字节通向字符的桥梁,接收一个字节流参数。通过指定的编码将字节解码成字符。
OutputStreamWriter字符通向字节的桥梁,把一个字节写入流作为参数传递给该构造函数。此时,就有了要写入的文件。想往该文件中通过字符流写入数据,需要将字符流转换成字节流,硬盘上存在了字节数据,再通过文件的自行解码生成相应的文本文件。
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
read()方法是阻塞式方法,当键盘录入数据回车时候read方法才读取数据。如果没有数据的话,就在那里等待.
流操作的基本规律:
通过两个明确来完成。
1,明确源和目的。
源:输入流。InputStream Reader 目的:输出流。OutputStream Writer
2,操作的数据是否是纯文本。
是:字符流。 不是:字节流。
3,当体系明确后,在明确要使用哪个具体的对象。
通过设备来进行分区,源设备:内存,硬盘,键盘 目的设备:内存,硬盘,控制台。
改变标准输入输出设备:
System.setIn()可以将键盘源改变成其他源。System.setOut()可以将控制台目的改变成其他目的。
异常的日志信息:
日志信息的建立,我们在写程序的时候,经常会遇到一些异常信息。而一个完美的程序,往往会把一些关键性的异常信息,存储在一个特定的日志文件中以便程序员对系统的进一步完善。这个时候就需要用到流,将日志信息准确地存到指定日志文件中。示例代码:
void |
printStackTrace() |
void |
printStackTrace(PrintStream s) |
通过api提供的方法可以知道,系统默认的是将异常信息通过调用printStackTrace方法写入到控制台上面。我们可以通过流的特点,将信息存储到指定的文件上去。有两种方法:
1. System.setOut();改变标准输出流
2. printStackTrace(PintStream s )方法,s=new PrintStream(“文件”);