Java IO流详解

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

2014721日 20:23:28

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值