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)
:将指定字符序列的子序列追加到此write
rabstract 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();
}
}
}