Java学习笔记——IO流与文件

Java学习笔记——IO流与文件

标签: IOStreamFile



一、文件(File类

1. File对象创建方法

  • Java文件类以抽象的方式代表文件名和目录路径名。该类主要用于文件和目录的创建、文件的查找和文件的删除等。
  • File类提供管理文件或目录的方法。File实例表示真实文件系统中的一个文件或者目录,可以通过以下四种构造方法创建一个File对象。

构造器

(1). File(File parent, String child):参数parent表示根路径,参数child表示子路径。例如:

    File file = new File(new File("src"), "test.txt");

(2). File(String pathname):参数pathname表示文件路径或者目录路径。例如:

    File file = new File("src/test.txt"); //等价于(1)中例子

(3). File(String parent, String child):参数parent表示根路径,参数child表示子路径。例如:

    File file = new File("src", "test.txt"); //等价于(2)中例子

(4). File(URI uri):参数uri表示路径。例如:

    File file = new File(new URI("src/test.txt")); //等价于(3)中例子

只处理一个文件,使用第二个或者第四个构造方法更方便;如处理一个公共目录的若干子目录或文件,那么使用第一个或者第三个更方便。

2. File类常用方法

假设现在在/com/hzj/目录下有个test.txt文件,内容如下:

    qwertyuiopasdfghjklzxcvbnm

(1). boolean exists():判断该文件或者目录是否存在。

(2). long length():返回该文件的长度。

(3). String getName():返回该文件名字。

(4). boolean createNewFile():创建指定文件。

(5). boolean delete():删除指定文件或者空目录。

(6). boolean mkdir():创建一个目录(不连续创建),即如果该目录的父目录不存在话,那么就会创建失败。

(7). boolean mkdirs():创建一个目录(连续创建),如果该目录的父目录不存在话,那么还会创建所有的父目录。

(8). String[] list():返回指定目录下所有文件名或者子目录名所组成的字符串数组。

(9). long lastModified():返回指定文件最后一次被修改的时间(从1970年1月1日凌晨12点到这个文件的修改时间
之间所经历的毫秒数)。

(10). String getPath():返回指定文件或者目录的路径。

(11). String getAbsolutePath():返回指定文件或者目录的绝对路径。

(12). boolean renameTo(File dest):修改该目录或文件名字为指定的值。

    File parent = new File("/com/hzj");
    File child = new File(parent, "test.txt");
    File file = new File("/com/hzj/test2.txt");
    File mkdir_test = new File("/com/hzj/aaa/bbb");

    System.out.println(child.exists()); // true
    System.out.println(child.length()); // 26
    System.out.println(child.getName()); // test.txt
    System.out.println(file.createNewFile()); // true(创建file文件)
    System.out.println(file.delete()); // true(删除file文件)
    System.out.println(mkdir_test.mkdir()); // false(没有aaa目录)
    System.out.println(mkdir_test.mkdirs()); // true(连续创建)

    System.out.println(parent.list()); // test.txt   aaa

    System.out.println(parent.renameTo(new File("/com/jzh"))); // true

二、IO流(Stream

  • 在Java中,把一组有序的数据序列称为流。程序的主要任务就是操纵数据。
  • 根据流中最小的数据单元,可以把流分为字节流和字符流。
  • 根据操作的方向,可以把流分为输入流和输出流两种。程序从输入流读取数据,向输出流写出数据。示例图如下:
输入流
输出流
文件,内存,键盘
Java程序
文件,内存,控制台

一定要搞清楚什么时候用输入流,什么时候用输出流。通俗的讲,就是把Java程序看为主体,如果要读文件,就是文件系统 --> Java程序,看箭头方法可知,从文件系统出,进入Java程序,即相对于Java程序就是input,如果要写文件,就是Java程序 --> 文件系统,看箭头方法可知,从Java程序出,进入文件系统,即相对于Java程序就是output

下面是各个流之间的层次关系(放大了看):

IO流
字符流
字节流
Reader
Writer
InputStream
OutputStream
BufferedReader
InputStreamReader
StringReader
PipedReader
CharArrayReader
FilterReader
BufferedWriter
OutputStreamWriter
StringWriter
PrinterWriter
PipedWriter
CharArrayWriter
FilterWriter
FileInputStream
FilterInputStream
ObjectInputStream
PipedInputStream
SequenceInputStream
StringBufferInputStream
ByteArrayInputStream
FileOutputStream
FilterOutputStream
ObjectOutputStream
PipedOutputStream
ByteArrayOutputStream
FileReader
PushbackReader
FileWriter
BufferedInputStream
DataInputStream
PushbackInputStream
BufferedOutputStream
DataOutputStream
PrintStream

1. 字节流InputStream && OutputStream

InputStreamOutputStream类处理的是字节流,也就是说,数据流中的最小单元为一个字节,它包括8个二进制位。

(1)输入流(InputStream

主要方法:

(1). int available():返回可以从输入流中读取的字节数目。

(2). abstract int read():从输入流读取一个8位的字节,把它转换为0-255之间的整数,并返回这一整数。如果遇到输入流的结尾,则返回-1

(3). int read(byte[] b):从输入流读取若干个字节,把它们保存到参数b指定的字节数组中。返回的整数表示读取的字节数。如果遇到输入流的结尾,则返回-1。使用byte[]数组可以降低物理读取次数。byte[]长度最大可以取到整个要读取的数据的长度。

(4). int read(byte[] b, int off, int len):从输入流读取若干个字节,把它们保存到参数b指定的字节数组中。返回的整数表示读取的字节数。参数off指定在字节数组中开始保存数据的起始下标(默认为0),参数len指定读取的字节数目。返回的整数表示实现读取的字节数。如果遇到输入流的结尾,则返回-1

(5). void close():关闭输入流,InputStream类本身的close()方法不执行任何操作。它的一些子类覆盖了close()方法,在close()方法中释放和流有关的系统资源。

(6). long skip(long n):从输入流中跳过参数n指定数目的字节。

(7). void mark(int readlimit):标记此输入流中的当前位置。

(8). boolean markSupported():返回此输入流是否支持标记(mark())和重置(reset())方法。

常用子类:

InputStream是个抽象类,常用的子类有:ByteArrayInputStreamDataInputStreamBufferedInputStreamPipedInputStreamFileInputStreamObjectInputStream

(2)输出流(OutputStream

主要方法:

(1). void close():关闭输出流。OutputStream类本身的close()方法不执行任何操作。它的一些子类覆盖了close()方法,在close()方法中释放和流有关的系统资源。

(2). void flush()OutputStream类本身的flush()方法不执行任何操作,它的一些带有缓冲区的子类(比如BufferedOutputStreamPrintStream类)覆盖了flush()方法。通过带缓冲区的输出流写数据时,数据先保存在缓冲区中,积累到一定程度才会真正写到输出流中。缓冲区通常用字节数组实现,实际上是指一块内存空间。flush:即使内容没有达到缓冲的标准大小,仍然输出到输出流中。只要使用到buffer的流,就一定要进行flush,否则最后很可能有数据没有完全达到要求而不能输出。

(3). void write(byte[] b):把参数b指定的字节数组中的所有字节写到输出流。

(4). void write(byte[] b, int off, int len):把参数b指定的字节数组中的所有字节写到输出流,参数off指定字节数组的起始下标,从这个位置开始输出由参数len指定数目的字节。

(5). abstract void write(int b):向输出流写入一个字节。在向文件或控制台写数据时,采用上面两个write方法可以减少进行物理读文件或键盘的次数,因此能提高I/O操作的效率。

常用子类:

OutputStream是个抽象类,常用的子类有:ByteArrayOutputStreamDataOutputStreamBufferedOutputStreamPipedOutputStreamFileOutputStreamObjectOutputStream

(3)各输入流、输出流用法详解
(1). ByteArrayInputStream && ByteArrayOutputStream

ByteArrayInputStream:把字节数组转换为输入流。ByteArrayInputStream类有两个默认的构造方法:

  • ByteArrayInputStream(byte[] b): 使用一个字节数组当中所有的数据做为数据源,程序可以像输入流方式一样读取字节,可以看做一个虚拟的文件,用文件的方式去读取它里面的数据。
  • ByteArrayInputStream(byte[] b,int offset,int length): 从数组当中的第offset(offset0开始)开始,一直取出length个这个字节做为数据源。

ByteArrayOutputStream:字节数组输出流在内存中创建一个字节数组缓冲区,所有发送到输出流的数据保存在该字节数组缓冲区中。

主要方法:

  • byte[] toByteArray():创建一个新分配的字节数组。数组的大小和当前输出流的大小,内容是当前输出流的拷贝。即把输出流转换为字节数组。
  • void writeTo(OutputStream out):将此字节数组输出流的全部内容写入到指定的输出流参数中。

示例(将字节数组转换为输入流,将输出流转换成字节数组):

    System.out.println("-----使用ByteArrayInputStream(byte[] b)构建对象------");
    byte[] b1 = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
    InputStream bis1 = new ByteArrayInputStream(b1);
    System.out.println(bis1.available()); // 8
    int n1;
    bis1.skip(2); // 跳过两个字符
    while ((n1 = bis1.read()) != -1) {
        System.out.print((char) n1 + " "); // c d e f g h
    }
    bis1.close();

    System.out.println();
    System.out.println("----使用ByteArrayInputStream(byte[] b,int offset,int length)构建对象-----");

    byte[] b2 = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
    ByteArrayInputStream bis2 = new ByteArrayInputStream(b2, 2, 3);
    System.out.println(bis2.available()); // 3
    int n2;
    while ((n2 = bis2.read()) != -1) {
        System.out.print((char) n2 + " "); // c d e
    }
    bis2.close();

    System.out.println();
    System.out.println("----利用read(byte[] b)读取数据-----");

    byte[] b3 = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
    byte[] b4 = new byte[3];
    ByteArrayInputStream bis3 = new ByteArrayInputStream(b3);
    System.out.println(bis3.available()); // 8
    int n3;
    while ((n3 = bis3.read(b4)) != -1) { // 利用read(byte[] b)方法时,返回值是当前读的字符个数
        System.out.print(n3 + " "); // 3 3 2
    }
    bis3.close();

    System.out.println();
    System.out.println("----ByteArrayOutputStream用法-----");
    byte[] b5 = new byte[10];
    ByteArrayOutputStream bos5 = new ByteArrayOutputStream();
    bos5.write('a');
    bos5.write('b');
    bos5.write('c');
    bos5.write('d');
    bos5.write('e');
    bos5.write('f');
    bos5.flush();
    b5 = bos5.toByteArray(); // 将输出流转换成字节数组
    for (byte b : b5) {
        System.out.print((char) b); // abcdef
    }
    
    ByteArrayOutputStream bos6 = new ByteArrayOutputStream();
    bos5.writeTo(bos6); // 将bos5字节数组输出流的全部内容写入到指定的输出流参数(bos6)中。
    b5 = bos6.toByteArray(); // 将输出流转换成字节数组
    for (byte b : b5) {
        System.out.print((char) b); // abcdef
    }
(2). FileInputStream && FileOutputStream

FileInputStream:从文件中读取数据。FileInputStream类常用的两个构造方法:

  • FileInputStream(File file):使用一个文件对象来创建一个输入流对象来读取文件。首先得使用 File()方法来创建一个文件对象。
  • FileInputStream(String name):使用字符串类型的文件名来创建一个输入流对象来读取文件。

FileOutputStream:向文件中写入数据。如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。FileOutputStream类的常用构造方法:

  • FileOutputStream(File file):使用一个文件对象来创建一个输出流来写文件。首先得使用 File()方法来创建一个文件对象。
  • FileOutputStream(String name):使用字符串类型的文件名来创建一个输出流对象来写文件。
  • FileOutputStream(File file, boolean append):使用一个文件对象来创建一个输出流来写文件,同时给定一个append参数(默认false),用来指定是追加还是覆盖。
  • FileOutputStream(String name, boolean append):使用字符串类型的文件名来创建一个输出流对象来写文件,同时给定一个append参数(默认false),用来指定是追加还是覆盖。

示例(向文件中写入文件,并读取出来):

    File file = new File("src/note/file_test.txt");
    OutputStream out = new FileOutputStream(file); // 写文件
    out.write('h');
    out.write('e');
    out.write('l');
    out.write('l');
    out.write('o');

    InputStream in = new FileInputStream(file); // 读文件
    System.out.println("可读取" + in.available() + "字节数"); // 可读取5字节数
    System.out.println(in.markSupported()); // false (不支持标记)
    int n;
    while ((n = in.read()) != -1) {
        System.out.print((char) n); // hello
    }

    in.close(); // 关闭资源
    out.close(); // 关闭资源
(3). DataInputStream && DataOutputStream

DataInputStream:用于读取基本类型数据,如int, float, long, doubleboolean等。构造方法为:

  • DataInputStream(InputStream in):需要传入一个InputStream对象,通常传入FileInputStream对象。

常用方法:

  • int skipBytes(int n):从输入流中跳过n个字节的数据。
  • boolean readBoolean():从输入流中读取1个字节,将它转换为boolean类型的数据。
  • byte readByte():从输入流中读取1个字节,将它转换为byte类型的数据。
  • char readChar():从输入流中读取2个字节,将它转换为char类型的数据。
  • double readDouble():从输入流中读取8个字节,将它转换为double类型的数据。
  • float readFloat():从输入流中读取4个字节,将它转换为float类型的数据。
  • int readInt():从输入流中读取4个字节,将它转换为int类型的数据。
  • long readLong():从输入流中读取8个字节,将它转换为long类型的数据。
  • short readShort():从输入流中读取2个字节,将它转换为short类型的数据。
  • String readUTF():从输入流中读取13个字节的串,将它转换为UTF-8字符编码的字符串。

DataOutputStream:用于写入基本数据类型。如int, float, long, doubleboolean等。构造方法为:

  • DataOutputStream(OutputStream out):需要传入一个OutputStream对象,通常传入FileOutputStream对象。

常用方法如下(这些方法都是将指定的基本数据类型以字节的方式写入到输出流):

  • void writeBoolean(boolean v)
  • void writeByte(int v)
  • void writeChar(int v)
  • void writeDouble(double v)
  • void writeFloat(float v)
  • void writeInt(int v)
  • void writeLong(long v)
  • void writeShort(int v)
  • void writeUTF(String str):写入的时候,首先会写入两个字节,代表字符串的长度,然后再以字节的方式把字符串写入。可查看writeUTF()源代码理解。

示例(向文件中写入指定类型的数据,并按照指定数据类型读取出来):

    // -----------写入DataOutputStream-----------
    OutputStream fos = new FileOutputStream("src/note/data_test.txt");
    DataOutputStream dos = new DataOutputStream(fos);
    dos.writeInt(1);
    dos.writeLong(100);
    dos.writeUTF("hello world!");
    dos.writeDouble(1.23);
    dos.writeBoolean(false);
    dos.writeByte('a');
    dos.writeChar('b');
    dos.writeFloat(1.1f);

    // -----------从DataOutputStream读出-----------
    InputStream fis = new FileInputStream("src/note/data_test.txt");
    DataInputStream dis = new DataInputStream(fis);
    System.out.println(dis.readInt());
    System.out.println(dis.readLong());
    System.out.println(dis.readUTF());
    System.out.println(dis.readDouble());
    System.out.println(dis.readBoolean());
    System.out.println(dis.readByte());
    System.out.println(dis.readChar());
    System.out.println(dis.readFloat());
    /* 结果:
            1
            100
            hello world!
            1.23
            false
            97
            b
            1.1
        */

如果需要跳过某些类型数据,只需利用skipBytes(n)方法跳过该类型所占字节数即可。其中String类型比较特殊,需要使用skipBytes(out.readShort()),因为writeUTF()会先写入一个short类型的数据,代表String的长度。示例:

    // -----------写入DataOutputStream-----------
    OutputStream fos = new FileOutputStream("src/note/data_test.txt");
    DataOutputStream dos = new DataOutputStream(fos);
    dos.writeInt(1);
    dos.writeLong(100);
    dos.writeUTF("hello world!");
    dos.writeDouble(1.23);

    // -----------从DataOutputStream读出-----------
    InputStream fis = new FileInputStream("src/note/data_test.txt");
    DataInputStream dis = new DataInputStream(fis);
//		System.out.println(dis.readInt());
    dis.skipBytes(4); // 跳过int类型,int占4个字节
    System.out.println(dis.readLong());
//		System.out.println(dis.readUTF());
    dis.skipBytes(dis.readShort()); // 跳过String类型的数据
    System.out.println(dis.readDouble());
    /* 结果:
            100
            1.23
        */
(4). ObjectInputStream && ObjectOutputStream

ObjectInputStream:对象的反序列化(对象需要实现Serializable序列化接口)。构造方法:

ObjectInputStream(InputStream in):需要传入一个InputStream对象,可以传入FileInputStream对象。

常用方法:

  • Object readObject():读取一个对象。

ObjectOutputStream:对象的序列化(对象需要实现Serializable序列化接口)。构造方法:

ObjectOutputStream(OutputStream out):需要传入一个OutputStream对象,可以传入FileInputStream对象。

常用方法:

  • void writeObject(Object obj):写入一个对象。

示例(对象的序列化与反序列化,利用文件流来实现数据持久化):

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

public class Test {
	public static void main(String[] args) throws IOException {
        // 借助FileOutputStream来实现对象的持久化存储,即存入文件

		// -----------写入ObjectOutputStream-----------
		OutputStream fos = new FileOutputStream("src/note/data_test.txt");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		oos.writeObject(new Student("张三"));
		oos.close();
		fos.close();

		// -----------从DataOutputStream读出-----------
		InputStream fis = new FileInputStream("src/note/data_test.txt");
		ObjectInputStream ois = new ObjectInputStream(fis);
		try {
			Student s = (Student) ois.readObject();
			System.out.println(s); // Student [name=张三]
			fis.close();
			ois.close();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

class Student implements Serializable { // Stduent类,实现Serializable接口
	private String name;

	public Student() {
	}

	public Student(String name) {
		super();
		this.name = name;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + "]";
	}

}
(5). PipedInputStream && PipedOutputStream

管道流,用于线程间的通信。一个线程的PipedInputStream对象从另外一个线程的PipedOutputStream对象读取输入。要使管道流有用,必须同时构造管道输入流和管道输出流。

PipedInputStream:管道输入流从一个管道输出流中读取数据。

PipedOutputStream:管道输出流给管道输入流传输数据。

示例(利用多线程实现管道传输Student对象):

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.Serializable;

/*
 * 开启两个线程,一个用于管道输出流用于写入Student对象,另一个用于管道输入流读取Student对象
 */
public class Test {
	public static void main(String[] args) throws IOException {
		PipedOutputStream pos = new PipedOutputStream();
		PipedInputStream pis = new PipedInputStream(pos);
		PipedOutputStreamThread thread1 = new PipedOutputStreamThread(pos);
		PipedInputStreamThread thread2 = new PipedInputStreamThread(pis);
		thread1.start();
		thread2.start();
	}
}

class PipedOutputStreamThread extends Thread { // 管道输出流线程
	PipedOutputStream pos;

	public PipedOutputStreamThread(PipedOutputStream pos) {
		this.pos = pos;
	}

	@Override
	public void run() {
		try {
            // 用ObjectOutputStream包装管道输出流,使之有序列化对象功能
			ObjectOutputStream oos = new ObjectOutputStream(pos); 
			oos.writeObject(new Student("李四"));
			oos.close();
			pos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

class PipedInputStreamThread extends Thread { // 管道输入线程
	PipedInputStream pis;

	public PipedInputStreamThread(PipedInputStream pis) {
		this.pis = pis;
	}

	@Override
	public void run() {
		try {
            // 用ObjectInputStream包装管道输入流,使之有反序列化对象功能
			ObjectInputStream ois = new ObjectInputStream(pis);
			Student s = (Student) ois.readObject();
			System.out.println(s); // Student [name=李四]
			pis.close();
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

class Student implements Serializable {
	private String name;

	public Student() {
	}

	public Student(String name) {
		super();
		this.name = name;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + "]";
	}

}
(6). BufferedInputStream && BufferedOutputStream

BufferedInputStream:字节缓冲输入流。BufferedInputStream类覆盖了被过滤的输入流的读数据行为,利用缓冲区来提高读数据的效率。BufferedInputStream类先把一批数据读入到缓冲区,接下来read()方法只需要从缓冲区内获取数据,就能减少物理性读取数据的次数。构造方法:

  • BufferedInputStream(InputStream in):参数in指定需要被过滤的输入流。
  • BufferedInputStream(InputStream in, int size):参数in指定需要被过滤的输入流。参数size指定缓冲区的大小,以字节为单位。

BufferedOutputStream:字节缓冲输出流。BufferedOutputStream类覆盖了被过滤的输出流的写数据行为,利用缓冲区来提高写数据的效率。BufferedOutputStream类先把一批数据写入到缓冲区,接下来只需要flush()将所有缓冲的输出字节写出到底层输出流中,就能减少物理性存数据的次数。构造方法:

  • public BufferedOutputStream(OutputStream out):采用的默认的缓冲区大小 ,来构造一个字节缓冲输出流对象。
  • public BufferedOutputStream(OutputStream out,int size):指定size缓冲区大小构造缓冲输出流对象。

主要方法:

(1). void flush():刷新此缓冲的输出流,让所有缓冲的输出字节被写出到底层输出流中。
(2). void close():关闭此输出流并释放与此流有关的所有系统资源。FilterOutputStreamclose方法先调用其flush方法,然后调用其基础输出流的close方法。

示例:

    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("test.txt"));
    bos.write("qwertyuiopasdfghjklzxcvbnm".getBytes());
    bos.flush();
    bos.close();

    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("test.txt"));
    int n;
    while ((n = bis.read()) != -1) {
        System.out.print((char) n); // qwertyuiopasdfghjklzxcvbnm
    }
    bis.close();

2. 字符流Reader && Writer

在许多应用场合,Java应用程序需要读写文本文件。在文本文件中存放了采用特定字符编码的字符,为了便于读于各种字符编码的字符,java.io包中提供了Reader/Writer类,它们分别表示字符输入流和字符输出流。

  • 在不同的操作系统有着不同的字符编码,windows默认是gbkgbk2312unixutf-8maceuc_cn
  • 在处理字符流时,最主要的问题是进行字符编码的转换。Java语言采用Unicode字符编码。对于每一个字符,Java虚拟机会为其分配两个字节的内存。而在文本文件中,字符有可能采用其他类型的编码,比如GBKUTF-8字符编码等。

在默认情况下,ReaderWriter会在本地平台的字符编码和Unicode字符编码之间进行编码转换。

编码转换1

如果要输入或输出采用特定类型编码的字符串,可以使用InputStreamReader类和OutputStreamWriter类。在它们的构造方法中可以指定输入流或输出流的字符编码。

编码转换2

(1)输入流(Reader
  • Reader类能够将输入流中采用其他编码类型的字符转换为Unicode字符,然后在内存中为这些Unicode字符分配内存。

主要方法:

  • abstract void close():关闭流并释放与其关联的所有系统资源。
  • void mark(int readAheadLimit):标记流中的当前位置。
  • boolean markSupported():判断此流是否支持mark()操作。
  • int read():读一个字符。
  • int read(char[] cbuf):将字符读入数组。
  • abstract int read(char[] cbuf, int off, int len):将字符读入数组的一部分。
  • int read(CharBuffer target):尝试将字符读入指定的字符缓冲区。
  • boolean ready():判断此流是否可以读取。
  • void reset():重置流。
  • long skip(long n):跳过指定长度字符。

常用子类:

Reader是个抽象类,常见子类有:

  • CharArrayReader:把字符数组转换为Reader,从字符数组中读取字符。
  • BufferedReader:过滤器,为其他Reader提供读缓冲区,此外,它的readLine()方法能够读入一行字符串。
  • StringReader:把字符串转换为Reader,从字符串中读取字符。
  • PipedReader:连接一个PipedWriter
  • PushBackReader:能把读到的字符压回到缓冲区中,通常用做编译器的扫描器,在程序中一般很少使用它。
  • InputStreamReader:过滤器,把InputStream转换为Reader,可以指定字符编码。
  • FileReader:从文件中读取字符。
(2)输出流(Writer
  • Writer类能够把内存中的Unicode字符转换为其他编码类型的字符,再写到输出流中。

主要方法:

  • Writer append(char c):将指定的字符追加到此writer
  • Writer append(CharSequence csq):将指定的字符序列追加到此writer
  • Writer append(CharSequence csq, int start, int end):将指定字符序列的子序列追加到此writer
  • abstract void close():关闭流,关闭前先flush()
  • abstract void flush():刷新流。
  • void write(char[] cbuf):向Writer中写一个字符数组。
  • abstract void write(char[] cbuf, int off, int len):向Writer中写一个字符数组的一部分。
  • void write(int c):向Writer中写一个字符。
  • void write(String str):向Writer中写一个字符串。
  • void write(String str, int off, int len):向Writer中写一个字符串的一部分。

常用子类:

Writer是个抽象类,常用的子类有:

  • BufferedWriter:带有缓冲的Writer
  • OutputStreamWriter:过滤器,把Writer转换为OutputStream,可以指定字符编码。
  • PrinterWriter:打印流。
  • StringWriter:将Writer转化为字符串。
  • PipedWriter:连接一个PipedReader
  • CharArrayWriter:将Writer写入字符数组。
  • FileWriter:将字符写入文件。
(3)各输入流、输出流用法详解
(1). InputStreamReader && OutputStreamWriter

InputStreamReader:字符输入流。InputStreamReader类把InputStream类型转换为Reader类型,即把字节输入流转换成字符输入流。构造方法:

  • InputStreamReader(InputStream in):按照本地平台的字符编码读取输入流中的字符。
  • InputStreamReader(InputStream in, String charsetName):按照指定的字符编码读取输入流中的字符。

示例(两种方式创建InputStreamReader对象,将FileInputStream字节流转换为InputStreamReader字符流):

    //--------------用InputStreamReader(InputStream in)构建对象--------------
    // 创建FileInputStream对象(字节输入流)
    InputStream fis = new FileInputStream("src/note/test.txt");
    // 创建InputStreamReader对象,把FileInputStream字节流转换为字符流
    Reader isr = new InputStreamReader(fis);
    int n;
    while ((n = isr.read()) != -1) {
        System.out.print((char) n); // hello,我是张三。
    }
    isr.close();
    fis.close();
    
    
    //---------------InputStreamReader(InputStream in, String charsetName)构件对象-------------
    // 创建FileInputStream对象(字节输入流)
    InputStream fis1 = new FileInputStream("src/note/test.txt");
    // 创建InputStreamReader对象,把FileInputStream字节流转换为字符流,并且制定字符编码为utf-8
    Reader isr1 = new InputStreamReader(fis1, "utf-8");
    int n1;
    while ((n1 = isr1.read()) != -1) {
        System.out.print((char) n1); // hello,我是张三。
    }
    isr1.close();
    fis1.close();

OutputStreamWriter:字符输出流。OutputStreamWriter类把OutputStream类型转换为Writer类型,即把字节输出流转换成字符输出流。构造方法:

  • OutputStreamWriter(OutputStream out):创建使用默认字符编码的OutputStreamWriter
  • OutputStreamWriter(OutputStream out, Charset cs):创建一个使用给定字符集的OutputStreamWriter
  • OutputStreamWriter(OutputStream out, CharsetEncoder enc):创建使用给定charset编码器的OutputStreamWriter
  • OutputStreamWriter(OutputStream out, String charsetName):创建一个使用指定charsetOutputStreamWriter

示例(用OutputStreamWriter向文件中写入字符、字符串):

    // 创建文件输出字节流
    OutputStream fos = new FileOutputStream("src/note/test.txt");
    // 将文件输出字节流转换为字符流
    OutputStreamWriter osw = new OutputStreamWriter(fos);
    // 向流中添加字符
    osw.append('胡');
    // 向流中写入字符串
    osw.write("hello world");
    osw.flush();
(2). FileReader && FileWriter

FileReaderInputStreamReader的一个子类,用于从文件中读取字符数据。该类只能按照本地平台的字符编码来读取数据,用户不能指定其他字符编码类型。构造方法:

  • FileReader(File file):参数file指定需要读取的文件。
  • FileReader(String name):参数name指定需要读取的文件的路径。

FileWriterOutputStreamWriter的一个子类,用于向文件中写入字符数据。该类只能按照本地平台的字符编码来写入数据,用户不能指定其他字符编码类型。构造方法:

  • FileWriter(File file):参数file指定需要写入的文件。
  • FileWriter(File file, boolean append):参数file指定需要写入的文件,参数append代表是追加还是覆盖。
  • FileWriter(FileDescriptor fd):构造与文件描述符关联的FileWriter对象。
  • FileWriter(String fileName):参数fileName指定需要写入的文件的路径。
  • FileWriter(String fileName, boolean append):指定需要写入的文件的路径,参数append代表是追加还是覆盖。

示例(用FileWriter向文件写入字符,并用FileReader读取出来):

    FileWriter fw = new FileWriter("src/note/test.txt");
    fw.write("hello world!");
    fw.flush();
    fw.close();
    FileReader fr = new FileReader("src/note/test.txt");
    int n;
    while((n = fr.read()) != -1) {
        System.out.print((char)n); // hello world!
    }
    fr.close();
(3). BufferedReader && BufferedWriter

带有缓存的字符输入输出流,常用于读写文件,效率高。BufferedWriter提供了newLine()方法,BufferedReader提供了readLine()方法。

BufferedReader:带有缓存的字符输出流。构造方法:

  • BufferedReader(Reader in):创建使用默认大小的输入缓冲区的缓冲字符输入流。需要传入一个Reader类型的参数,可以传入FileReader类型,可以理解为给FileReader添加新功能(读一行)。
  • BufferedReader(Reader in, int sz):创建使用指定大小的输入缓冲区的缓冲字符输入流。需要传入一个Reader类型的参数,可以传入FileReader类型,可以理解为给FileReader添加新功能(读一行)。

BufferedWriter:带有缓存的字符输入流。构造方法:

  • BufferedWriter(Writer out):创建使用默认大小的输入缓冲区的缓冲字符输出流。需要传入一个Writer类型的参数,可以传入FileWriter类型,可以理解为给FileWriter添加新功能(写一行)。
  • BufferedWriter(Writer out, int sz):创建使用指定大小的输入缓冲区的缓冲字符输出流。需要传入一个Writer类型的参数,可以传入FileWriter类型,可以理解为给FileWriter添加新功能(写一行)。

示例(利用BufferedReaderBufferedWriter实现文件的读写,写一行、读一行):

    FileWriter fw = new FileWriter("src/note/test.txt");
    BufferedWriter bw = new BufferedWriter(fw);
    bw.write("hello world!");
    bw.newLine(); // 另起一行
    bw.write("第二行");
    bw.flush();
    bw.close();
    fw.close();
    FileReader fr = new FileReader("src/note/test.txt");
    BufferedReader br = new BufferedReader(fr);
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
        // hello world!
        // 第二行
    }
    br.close();
    fr.close();

三、 实例

1. 本地文件传输(网络传输文件基础)

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

/**
 * @author HZJ
 * @date 2019年6月14日 下午2:42:15
 * @description 实现文件的复制功能,inPath代表输入的文件路径,outPath代表输出的文件路径。
 */
public class Test {
	private static String inPath = "src/note/in_test.jpg"; // 输入图片路径
	private static String outPath = "src/note/out_test.jpg"; // 输出图片路径

	public static void main(String[] args) throws Exception {

		// 创建输入文件对象
		File in_file = new File(inPath);
		if(!in_file.exists()) {
			throw new Exception("文件不存在!");
		}
		// 创建文件输入流(字节流)
		FileInputStream in = new FileInputStream(in_file);

		// 创建输出文件对象
		File out_file = new File(outPath);
		// 创建文件输出流(字节流)
		FileOutputStream out = new FileOutputStream(out_file);

		byte[] b = new byte[1024]; // 利用一个数组缓存,提高读取效率
		int n;
		while ((n = in.read(b)) != -1) { // n = -1 说明已经读到流的末尾了
			// 注意这里第三个参数不能是数组b的长度(1024),否则最后一次循环可能会多写数据到out流中,
			// 因为文件的可读字节不一定是b的长度(1024)的整数倍。
			out.write(b, 0, n);
		}
		out.flush();
		out.close();
		in.close();
	}
}

2. 网络传输文件

要先启动Server.java,再启动Client.java

Client.java

package note;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class Client {
	// 服务器ip地址
	private static String ip = "127.0.0.1";
	// 服务器端口
	private static int port = 10000;
	//本地文件,即需要发送的文件
	private static String client_file = "src/note/client.jpg";

	public static void main(String[] args) {
		try {
			System.out.println("正在连接服务器。。。");
			// 连接服务器
			Socket socket = new Socket(ip, port);
			System.out.println("与服务器连接成功!准备发送文件。。。");
			// 获取套接字输出流
			OutputStream out = socket.getOutputStream();
			// 获取本地文件输入流
			FileInputStream in = new FileInputStream(client_file);
			// 缓存数组
			byte[] b = new byte[1024];
			// 每次读出的字节长度
			int n;
			while ((n = in.read(b)) != -1) {
				// 将字节数组b中长度为n的子数组写入out
				out.write(b, 0, n);
			}
			out.flush();
			out.close();
			in.close();
			socket.close();
			System.out.println("文件发送成功!");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

Server.java

package note;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
	// 监听10000端口号
	private static int port = 10000;
	// 本地文件,即接收到的文件存放的地址
	private static String server_file = "src/note/server.jpg";

	public static void main(String[] args) {
		try {
			System.out.println("服务器启动中。。。");
			// 监听10000端口号,等待客户端连接
			ServerSocket serverSocket = new ServerSocket(port);
			System.out.println("服务器启动成功,监听" + port + "端口,等待客户端连接。。。");
			// 与客户端建立连接
			Socket server = serverSocket.accept();
			System.out.println("与客户端成功建立连接,接收文件中。。。");
			// 获取套接字输入流
			InputStream in = server.getInputStream();
			// 获取本地文件输出流
			FileOutputStream out = new FileOutputStream(server_file);
			// 缓存数组
			byte[] b = new byte[1024];
			// 每次读出的字节长度
			int n;
			while ((n = in.read(b)) != -1) {
				// 将字节数组b中长度为n的子数组写入out
				out.write(b, 0, n);
			}
			out.flush();
			out.close();
			in.close();
			server.close();
			serverSocket.close();
			System.out.println("服务器接收文件成功!文件路径:" + server_file);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值