Java学习笔记——IO流与文件
标签: IO、Stream、File
文章目录
一、文件(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程序就是input,如果要写文件,就是Java程序 --> 文件系统,看箭头方法可知,从Java程序出,进入文件系统,即相对于Java程序就是output。
下面是各个流之间的层次关系(放大了看):
1. 字节流InputStream && OutputStream
InputStream和OutputStream类处理的是字节流,也就是说,数据流中的最小单元为一个字节,它包括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是个抽象类,常用的子类有:ByteArrayInputStream,DataInputStream,BufferedInputStream,PipedInputStream,FileInputStream,ObjectInputStream。
(2)输出流(OutputStream)
主要方法:
(1). void close():关闭输出流。OutputStream类本身的close()方法不执行任何操作。它的一些子类覆盖了close()方法,在close()方法中释放和流有关的系统资源。
(2). void flush():OutputStream类本身的flush()方法不执行任何操作,它的一些带有缓冲区的子类(比如BufferedOutputStream和PrintStream类)覆盖了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是个抽象类,常用的子类有:ByteArrayOutputStream,DataOutputStream,BufferedOutputStream,PipedOutputStream,FileOutputStream,ObjectOutputStream。
(3)各输入流、输出流用法详解
(1). ByteArrayInputStream && ByteArrayOutputStream
ByteArrayInputStream:把字节数组转换为输入流。ByteArrayInputStream类有两个默认的构造方法:
ByteArrayInputStream(byte[] b): 使用一个字节数组当中所有的数据做为数据源,程序可以像输入流方式一样读取字节,可以看做一个虚拟的文件,用文件的方式去读取它里面的数据。ByteArrayInputStream(byte[] b,int offset,int length): 从数组当中的第offset(offset从0开始)开始,一直取出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, double和boolean等。构造方法为:
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():从输入流中读取1到3个字节的串,将它转换为UTF-8字符编码的字符串。
DataOutputStream:用于写入基本数据类型。如int, float, long, double和boolean等。构造方法为:
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():关闭此输出流并释放与此流有关的所有系统资源。FilterOutputStream的 close方法先调用其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默认是gbk或gbk2312,unix是utf-8,mac是euc_cn。- 在处理字符流时,最主要的问题是进行字符编码的转换。
Java语言采用Unicode字符编码。对于每一个字符,Java虚拟机会为其分配两个字节的内存。而在文本文件中,字符有可能采用其他类型的编码,比如GBK和UTF-8字符编码等。
在默认情况下,Reader和Writer会在本地平台的字符编码和Unicode字符编码之间进行编码转换。

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

(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):将指定字符序列的子序列追加到此writerabstract 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):创建一个使用指定charset的OutputStreamWriter。
示例(用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
FileReader:InputStreamReader的一个子类,用于从文件中读取字符数据。该类只能按照本地平台的字符编码来读取数据,用户不能指定其他字符编码类型。构造方法:
FileReader(File file):参数file指定需要读取的文件。FileReader(String name):参数name指定需要读取的文件的路径。
FileWriter:OutputStreamWriter的一个子类,用于向文件中写入字符数据。该类只能按照本地平台的字符编码来写入数据,用户不能指定其他字符编码类型。构造方法:
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添加新功能(写一行)。
示例(利用BufferedReader和BufferedWriter实现文件的读写,写一行、读一行):
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();
}
}
}
本文深入探讨Java中文件操作及IO流的概念,包括File类的使用,字节流与字符流的区别,以及常见流的详细用法。通过具体实例展示如何进行文件的读写与传输。

1470

被折叠的 条评论
为什么被折叠?



