Java IO流详解
IO体系中,子类后缀大部分是父类名称,而前缀大都是体现了类功能的名字。
//字符流
Reader(读) Wirter(写)
FileReader FileWriter
BufferedReader BufferedWriter
//转换流
InputStreamReader OutPutStreamWriter
//字节流
InputStream(读) OutputStream(写)
FileInputStream FileOutputStream
BufferedInputStream BufferedOutputStream
//对象序列化流
ObjectInput ObjectOutput
ObjectInputStream ObjectOutputStream
//数据流
DataInput DataOutput
DataInputStream DataOutputStream
//打印流
PrintStream 属于字节流,可以打印字节,也可以打印数组
写入文件不用close或flush
PrintWirter、BufferedWriter 属于字符流,只可以打印字符或字符串
因为有缓存,要想写入文件必须close或flush。
ByteArrayInputStream ByteArrayOutputStream
Reader中常用方法:
1.int read():读取一个字符,返回那个字符,遇到文件尾返回-1.
2.int read(char[]):将读到的字符存到指定的数组中,返回读到的字符
个数,即数组中元素数,遇到文件尾,返回-1.
3.close():关闭流,释放资源
Writer中常用方法:
1.write(ch):将一个字符写入到流中
2.write(char[]):将一个字符数组写入到流中
3.write(String):将一个字符串写入到流中
4.flush():刷新流,将流中的数据刷新到目的地,流还存在
5.close():关闭流,调用之前会先刷新流,然后把流关闭。
一、将文本数据写入到一个文件中
import java.io.*;
public class JAVA_IO {
public static void main(String[] args) throws Exception{ FileWriter fw = new FileWriter("E:\\Output.txt"); fw.write("abcdefg"); fw.write("\r\n"); //写入换行 fw.write("123456"); fw.flush(); fw.close(); } } |
二、读取文本文件数据,并打印出来
import java.io.*; public class JAVA_IO { public static void main(String[] args) throws Exception{ FileReader fr = new FileReader("E:\\Input.txt"); int ch = fr.read(); while(-1 != ch){ System.out.printf("%c", (char)ch); ch = fr.read(); } fr.close(); } } |
三、将字符读入一个缓冲区,满1KB就输出
import java.io.*;
public class JAVA_IO {
public static void main(String[] args) throws Exception{ FileReader fr = new FileReader("E:\\Input.txt"); //声明一个字符数组,读到1kb就输出 char[] buf = new char[1024]; int ch = fr.read(buf); while(-1 != ch){ System.out.printf("%s", new String(buf,0,buf.length)); ch = fr.read(buf); } fr.close(); } } |
使用缓冲区的字符流,其实质就是对数组的封装
BufferedWriter 特有方法:
newLine():跨平台的换行符
BufferedReader 特有方法:
readLine():以此读取一行,读到行标记时,将行标记之前的字符数据作为字符串返回,即字符串不包括换行符。当读到末尾是返回null。
缓冲区对象的使用是为了增强流的功能而存在的,所以在建立缓冲区对象时要有流对象的存在。
一、通过缓冲区对象向文件中写入数据
import java.io.*;
public class JAVA_IO { public static void main(String[] args) throws Exception{ FileWriter fw = new FileWriter("E:\\Output.txt"); BufferedWriter bufw = new BufferedWriter(fw); bufw.write("123456"); bufw.newLine(); bufw.write("Hello World"); bufw.write("\r\n"); bufw.write("GXU"); bufw.close(); fw.close(); } } |
二、通过缓冲区对象读取文件中数据,并打印出来
import java.io.*;
public class JAVA_IO { public static void main(String[] args) throws Exception{ FileReader fr = new FileReader("E:\\Input.txt"); BufferedReader bufr = new BufferedReader(fr); //按照行的形式读取数据,读取的数据不包括回车符 String line = bufr.readLine(); while(null != line){ System.out.println(line); line = bufr.readLine(); } bufr.close(); fr.close(); } } |
三、通过缓冲区的形式对文本进行拷贝
import java.io.*;
public class JAVA_IO { public static void main(String[] args) throws Exception{ BufferedReader bufr = new BufferedReader( new FileReader("E:\\Input.txt")); BufferedWriter bufw = new BufferedWriter( new FileWriter("E:\\Copy.txt")); //每次读取一行,不包括换行符 String line = bufr.readLine(); while(null != line){ bufw.write(line); bufw.newLine(); //添加一个换行符 bufw.flush(); line = bufr.readLine(); } bufr.close(); bufw.close(); } } |
四、实现readLine()方法:
原理:在缓冲区的内部使用的还是关联对象的read()方法,只不过每次读取一个字符,先不进行具体操作,而是把它临时存储起来,当读到回车符时,再把临时容器中存储的数据一次性返回。
import java.io.*;
public class JAVA_IO { public static void main(String[] args) throws Exception{ FileReader r = new FileReader("E:\\Input.txt"); MyBufferedReader bur = new MyBufferedReader(r); String line = bur.myReadLine(); while(null != line){ System.out.println(line); line = bur.myReadLine(); } bur.myClose(); } }
class MyBufferedReader{ private Reader r; public MyBufferedReader(Reader r){ this.r = r; } public String myReadLine() throws IOException{ StringBuilder sb = new StringBuilder(); int ch; ch =r.read(); while(-1 != ch){ if(ch == '\r'){ //回车 return sb.toString(); } if(ch == '\n'){ //换行 return sb.toString(); } else{ sb.append((char)ch); } //System.out.println(sb.toString()); ch =r.read(); } if(sb.length() != 0){ return sb.toString(); } return null; } public void myClose() throws IOException{ r.close(); } } |
以上使用一种叫做装饰设计模式的设计模式,对一组对象进行功能的增强,而且比继承有更好地灵活性。
通常装饰类和被装饰类都属于同一个父类或者接口。
在原先的功能上加上现实行号:
class MyLineNumberReader extends MyBufferedReader{ private int lineNumber = 0; public MyLineNumberReader(Reader r) { super(r); } public String myReadLine() throws IOException{ lineNumber ++; return super.myReadLine(); }
public int getLineNumber() { return lineNumber; }
public void setLineNumber(int lineNumber) { this.lineNumber = lineNumber; } } |
加上行号后的主函数:
import java.io.*;
public class JAVA_IO { public static void main(String[] args) throws Exception{ FileReader r = new FileReader("E:\\Input.txt"); MyLineNumberReader mr = new MyLineNumberReader(r); String line = mr.myReadLine(); while(null != line){ System.out.println(mr.getLineNumber() + " " + line); line = mr.myReadLine(); } mr.myClose(); } } |
字节流:
抽象基类:InputStream OutputStream
字节流可以操作任何类型的数据
注意:字符流使用的数组是字符数组,char[] buf.
字节流使用的数组是字节数组,byte[] buf.
一、FileOutputStream的使用,直接向文件中写入数据:
import java.io.*;
public class Java_IO { public static void main(String[] args) throws Exception{ FileOutputStream fos = null; fos = new FileOutputStream("E:\\output.txt"); fos.write("abc".getBytes()); //写入abc fos.write("\r\n".getBytes()); //写入换行 fos.write(new Integer(123).toString().getBytes()); //写入123 fos.flush(); fos.close(); } } |
二、FileInputStream的使用,从文件中读出内容并打印。要求文件不能太大。
import java.io.*;
public class Java_IO { public static void main(String[] args) throws Exception{ FileInputStream fis = null; //创建输入流对象 fis = new FileInputStream("E:\\input.txt"); byte[] buf = new byte[1024]; int count = fis.read(buf); while(-1 != count){ System.out.println(new String(buf,0,count)); count = fis.read(); } fis.close(); } } |
三、拷贝一张图片
import java.io.*;
public class Java_IO { public static void main(String[] args) throws Exception{ FileInputStream fis = null; FileOutputStream fos = null;
fis = new FileInputStream("E:\\1.jpg"); fos = new FileOutputStream("E:\\pic.wyg"); int b = fis.read(); while(-1 != b){ fos.write(b); b = fis.read(); } fis.close(); fos.close(); } } |
问题:字节流的read()方法读取一个字节,为什么反回的是一个int类型,而不是byte类型?
因为read方法读到末尾时返回的是-1,而在所操作的数据中很容易出现连续多个1的情况,比如连续8个1,结果就是-1,这会导致读取过程提前结束。将读到的数值提升为int类型的数值,但只保留原字节,并在剩余二进制为补0. 具体操作:byte & 255 或者 byte & 0xff 可以看出该操作中只保留数据的低8位,范围在0-255。 对于write方法,可以一次写入一个字节,但接收的是int型的数值,在写入时只写入int型的最低一个字节,即只写入低8位。 简单的说:read方法对读到的数据进行提升,write方法对所操作的数据进行转换。 |
转换流:
InputStreamReader OutputStreamWriter
特点:(1)是字节流和字符流之间转换的桥梁
(2)该流对象中可以对读到的字节数据进行指定编码表的编码转换
什么时候使用?
1、当字节和字符之间有转换动作时
2、流操作的对象需要进行编码表的指定时
InputStreamReader:字节到字符的桥梁
OutputStreamWriter:字符到字节的桥梁
它们有转换作用,而本身是字符流,所以在构造的时候,需要传入字节流对象进来。
InputStreamReader(InputStream):
完成初始化, 使用本系统默认编码表GBK。
InputStreamReader(InputStream,String CharSet):
完成初始化,指定编码表
InputStreamReader isr = new InputStreamReader(
new FileInputStream(“D:\\1.txt”),”utf-8”
);
OutputStreamWriter(OutputStream):
完成初始化,使用默认编码表GBK
OutputStreamWriter(OutputStream,String CharSet):
完成初始化,指定编码表
操作文件字符流是转换流的子类:
Reader
|--InputStreamReader
|--FileReader
Writer
|--OutputStreamWriter
|--FileWriter
转换流中的read方法已经融入了编码表。在底层调用字节流的read方法读取一个或多个字节进行临时存储,并去查指定编码表,如果编码表没有指定,则去查默认编码表,那么转换流的read方法就可以返回一个字符,比如中文。
转换流已经完成了编码转换的动作,对于直接操作文本文件的FileReader而言,就不用在重新定义了,只需继承该转换流,获取其方法,就可以直接操作文本文件中的字符数据了。
流操作的基本规律:
1、明确数据源和数据汇(数据目的)
其实是为了明确输入流还是输出流
2、明确操作的数据是否是纯文本数据
其实是为了明确是字符流还是字节流
数据源:键盘System.in,硬盘,File开头的流对象,内存(数组)
数据汇:控制台System.out,硬盘,File开头的流对象,内存(数组)
数据源:System.in
既然是源,使用的就是输入流,可用的体系有InputStream,Reader。因为从键盘输入的数据一定是纯文本数据,所以,可以使用专门操作纯文本字符数据的Reader。
发现System.in(类型为InputStream)对应的流是字节读取流,所以要将其进行转换,将字节转换为字符即可,所以要用到Reader体系中的InputStreamReader。接下来是否需要提高效率呢?如果需要就要加入字符流的缓冲区:BufferedReader。
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
数据汇:一个文件,硬盘
既然是数据汇,使用的是输出流,可用的体系有OutputStream,Writer。往文件中存储的都是文本数据,那么使用字符流较为方便,比如Wirter.
因为操作的是一个文件,所以使用Writer中的FileWriter。是否要提高效率呢?可以考虑使用BufferedWriter。
FileWriter fw = new FileWriter(“out.txt”);
BufferedWriter bfw = new BufferedWriter(fw);
一、将一个文本数据按照指定的编码表存入文件中。
import java.io.*;
public class Java_IO { public static void main(String[] args) throws Exception{ String charSet = "utf-8"; //缓冲区对象 = 转换流对象(字节输入流对象) BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( new FileOutputStream("E:\\output.txt"),charSet ) ); //向打开的文件输出流中按照utf-8格式写入数据 bw.write("abc"); bw.newLine(); bw.write(new Integer(123).toString()); bw.flush(); bw.close(); } } |
二、将一个文本文件的数据展示在控制台上
import java.io.*;
public class Java_IO { public static void main(String[] args) throws Exception{ BufferedReader br = new BufferedReader( new FileReader("E:\\input.txt") ); char[] cbuf = new char[1024]; int count = br.read(cbuf); while(-1 != count){ System.out.println( new String(cbuf,0,count)); count = br.read(cbuf); } br.close(); } } |
三、复制文件
import java.io.*;
public class Java_IO { public static void main(String[] args) throws Exception{ BufferedReader br = new BufferedReader( new FileReader("E:\\input.txt")); BufferedWriter bw = new BufferedWriter( new FileWriter("E:\\cpoy.txt")); char[] cbuf = new char[1024]; int count = br.read(cbuf); while(-1 != count){ bw.write(cbuf, 0, count); count = br.read(cbuf); } bw.flush(); br.close(); } } |
File类:
boolean exits():判断问价或文件夹是否存在。
boolean isFile():判断File对象中封装的是否是文件
boolean isDirectory():判断File对象中封装的是否是文件夹
boolean isHidden():判断文件或文件夹是否隐藏。
File[] listFiles():获取指定目录下的文件及文件夹对象
/* 以递归形式访问文件夹及文件夹下的目录或文件,并显示出来 */ import java.io.*;
public class JAVA_IO { public static void main(String[] args) throws Exception { File f = new File("E:\\"); showFileName(f); }
public static void showFileName(File f) { int i; System.out.println(f); File[] name = f.listFiles(); for (i = 0; i < name.length; i++) { if(name[i].isHidden()){ break; } if (name[i].isDirectory()) { showFileName(name[i]); } else{ System.out.println(name[i]); } } } } |
/* 以递归形式删除指定文件夹及文件夹下的目录或文件,并显示删除项 */ import java.io.*;
public class JAVA_IO { public static void main(String[] args) throws Exception { File f = new File("E:\\dir"); deletAll(f); } //删除带内容的目录 /* 递归操作,深度优先遍历,在函数递归返回时执行删除操作,文件可以直接删除, 目录也会变成空,可以直接删除 */ public static void deletAll(File dir){ //列出该目录下的所有内容 File[] list = dir.listFiles(); for(int i = 0;i < list.length; i++){ if(list[i].isFile()){ if(list[i].delete()){ System.out.println(list[i]); } } else{ deletAll(list[i]); } } if(dir.delete()){ System.out.println(dir); } } } |
IO其他流:
1、打印流:
PrintStream:是一个字节打印流,System.out对应的类型就是PrintSteam。
它的构造函数可以接收三种类型的数据:
1、字符串路径:PrintStream(String fileName)
2、File对象:PrintStream(File file)
3、OutputStream对象:PrintStream(OutputStream out)
PrintWriter:
是一个字符打印流。构造函数可以接收四种类型的数据:
1、字符串路径:PrintWriter(String fileName)
2、File对象:PrintWriter(File file)
对于1,2两种类型的数据还可以指定编码表,也就是字符集。
3、OutputStream:PrintWriter(OutputStream out)
PrintWriter(OutputStream out, boolean autoFlush)
4、Writer:PrintWriter(Writer out)
PrintWriter(Writer out, boolean autoFlush)
对于3,4两种类型的数据可以指定自动刷新。
//创建既能自动刷新,又能执行编码的输出流对象 PrintWriter pw = new PrintWriter( new OutputStreamWriter( new FileOutputStream("D:\\1.txt"),"utf-8"),true); |
//创建使用了缓冲区的既能自动刷新,又能执行编码的输出流对象 PrintWriter pw =new PrintWriter( new BufferedWriter( new OutputStreamWriter( new FileOutputStream("D:\\1.txt"), "utf-8")), true); pw.println("abc"); //写入成功 pw.println(123); //写入成功 //pw.write("qwert"); //写入失败 //pw.write("567"); //写入失败 pw.close(); |
import java.io.*;
public class Java_IO { public static void main(String[] args) throws Exception { //创建一个带有缓冲区的打印流对象 PrintStream ps = new PrintStream( new BufferedOutputStream( new FileOutputStream("E:\\1.txt"))); ps.println("abcqwert"); ps.println("1234"); //设置控制台输入为该打印流对象 System.setOut(ps); System.out.println("ok....."); ps.close(); } } |
对象的序列化:
ObjectInputStream ObjectOutputStream
可以使用这两个流对象直接操作已有的流对象,并将对象进行本地化存储。
存储后的对象可以直接进行网络传输。
ObjectInputStream的特有方法Object readObject()从流中读出一个对象
ObjectOuputStream的特有方法:void writeObject(Object):向流中写入一 个对象,该对象必须实现Serializable接口
Serializable接口:该接口就是一个没有方法的标记接口。 用于给类指定一个UID,该UID是一个long型值,它通过该类中可序列化数字成员的运算得出。只要这些成员没有变化,则每次运算的结果都一样。 该值用于判断被序列化的对象和类文件是否兼容。 如果被序列化的对象需要被不同的类版本所兼容,可以在类中自定义UID,定义方式:static final long serialVersionUID = 66L; 注意:静态的成员不会被序列化。 对于非静态且不想被序列化的成员而言,可以使用transient进行修饰。 |
操作基本数据类型的流对象:
DataInputStream:
DataInputStream(InputStream)
操作基本数据类型的方法:
int readInt():一次读取四个字节,并将其转换为int值。
boolean readBoolean():一次读取一个字节
short readShort()
long readLong()
................
String readUTF():按照utf-8修改版读取字符。只能读取由writeUTF() 写入的数据。
DataOutputStream:
DataOutputStream(OutputStream)
操作基本数据类型的方法:
writeInt(int):一次写入四个字节。
和write(int)不同,write(int)只将低8最低一个8位写入,剩余 三个8位丢弃。
wirteBoolean(boolean)
writeShort(short)
writeLong(long)
.................
writeUTF(String):按utf-8修改版将字符数据进行存储,只能通过readUTF
读取。
操作数组流的对象:
1、操作字节数组:
ByteArrayInputStream
ByteArrayOutputStream
toByteArray()
toString()
writeTo(OutputStream)
2、操作字符数组:
CharArrayReafer
CharArrayWriter
对于这些流源是内存,目的也是内存。而且这些流没使用系统资源,使用的是内存中的数组。所以,这些在使用的时候不需要close。
操作数组的读取流在构造时,必须要明确一个数据源,所以要传入一个相对应的数组。
对于操作数组的写入流,构造函数可以使用空参数,因为它内置了一个可变长度数组作为缓冲区。
类似的对象同理:
StringReader
StringWriter
编码转换:
在IO操作中涉及到编码转换的流只有转换流和打印流,而打印流只用输出。
在转换流中可以使用指定的编码表。在默认情况下使用本机默认的编码表:GBK,它是怎么来的呢?System.getProperty(“file.encoding”)
WYG
2014年7月21日 20:23:28