一、数据输入输出流
数据输入输出流属于字节流,它最大的特点是能够读写基本数据类型。DataInputStream (数据输入流)里面有各种 read 方法,相应的 DataOutputStream(数据输出流)中也有相应的读取方法,需要注意的是“写入顺序和读取顺序需要一样”。下面让我们用代码来了解一下它们中的读写方法。
public class MyTest {
public static void main(String[] args) throws IOException {
//构造方法需要传入一个字节流对象
DataInputStream in = new DataInputStream(new FileInputStream("a.txt"));
// 按照顺序对数据进行读取
boolean b = in.readBoolean();
double v = in.readDouble();
char c = in.readChar();
int i = in.readInt();
String s = in.readUTF();
System.out.println(b);
System.out.println(v);
System.out.println(c);
System.out.println(i);
System.out.println(s);
}
private static void writeData() throws IOException {
DataOutputStream out = new DataOutputStream(new FileOutputStream("a.txt"));
out.writeBoolean(true); //写入boolean类型的数据
out.writeDouble(3.14); // 写入double类型的数据
out.writeChar('a'); //写入字符类型的数据
out.writeInt(2000); // 写入int型数据
out.writeUTF("字符串"); //写入String类型的数据
out.close();
}
}
二、内存操作流
这个流比较特殊的是不关联任何文件,不直接读写文件,而是直接在内存中对数据进行操作。
操作字节数组 | 操作字符数组 | 操作字符串 |
---|---|---|
ByteArrayOutputStream | CharArrayWrite | StringWriter |
ByteArrayInputStream | CharArrayReader | StringReader |
上表中的流可以对不同的数据进行操作,具体使用方法如下。
1、操作字节数组
ByteArrayOutputStream 类实现了一个输入流,其数据被写入了一个字节数组。缓冲区会随着数据的不断写入而自动增长。在写入数据之后我们可以直接使用 toByteArray()和 toString() 来获取数据。还有就是这个流不需要关闭。实例代码如下:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class Demo {
public static void main(String[] args) throws IOException {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
String s = "你的名字";
String ss = "我的名字";
byte[] bytes = s.getBytes();//把字符串转换为字节数组
byte[] bytes1 = ss.getBytes();
bao.write(bytes);
bao.write(bytes1);
//toByteArray获取写入的数据
byte[] bytes2 = bao.toByteArray();
System.out.println(new String(bytes2));
//toString获取写入的数据
System.out.println(bao.toString());
//通过ByteArrayInputStream获取数据,
ByteArrayInputStream byi = new ByteArrayInputStream(bytes);
byte[] bytes4 = new byte[100];
int read = byi.read();
System.out.println(new String(bytes4, 0, read));
}
}
上面最后一种方法是通过ByteArrayInputStream 进行获取,此类需要一个字节缓冲数组,构造方法如下:
ByteArrayInputStream(byte[] buf)
创建一个 ByteArrayInputStream,使用 buf 作为其缓冲区数组。
2、操作字符数组
CharArrayWriter 类可以对字符数组进行操作,下面通过代码来了解一下
import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
CharArrayWriter charArrayWriter = new CharArrayWriter();
charArrayWriter.write("字符串");
charArrayWriter.write(new char[]{'a', 'b', 'c'});
char[] chars = charArrayWriter.toCharArray();
System.out.println(String.valueOf(chars));
System.out.println(new String(chars));
// 通过CharArrayReader 获取数据
CharArrayReader charArrayReader = new CharArrayReader(chars);
int read = 0;
while ((read = charArrayReader.read()) != -1) {
System.out.print((char) read);
}
}
}
3、操作字符串
StringWriter 和 StringReader 可以对字符串进行操作,代码实现如下:
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
public class Demo2 {
public static void main(String[] args) throws IOException {
StringWriter stringWriter = new StringWriter();
stringWriter.write("所爱隔山海,");
stringWriter.write("山海皆可平。");
String s = stringWriter.toString();
System.out.println(s);
System.out.println("-------------------");
StringReader stringReader = new StringReader(s);
int read = 0;
while ((read = stringReader.read()) != -1) {
System.out.print((char) read);
}
}
}
三、打印流
打印流只关联目标文件,不能关联源文件(不能进行读取数据),打印流中的 print() 方法能写入任意类型的数据,在别的流需要自动刷新的时候需要调用 flush()方法,但是,在调用println、printf 或 format 方法中的一个方法的时候,会完成自动刷新。
1、字节打印流
PrintStream 类为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。它的构造方法如下:
- PrintStream(File file)
创建具有指定文件且不带自动行刷新的新打印流。 - PrintStream(File file, String csn)
创建具有指定文件名称和编码表且不带自动行刷新的新打印流。 - PrintStream(OutputStream out)
创建新的打印流。 - PrintStream(OutputStream out, boolean autoFlush)
创建新的打印流。
具体使用方法如下:
import java.io.IOException;
import java.io.PrintStream;
public class Demo3 {
public static void main(String[] args) throws IOException {
PrintStream printStream = new PrintStream("E:/f.txt", "Gbk");
printStream.print(13.14);
printStream.println(521);
printStream.println('a');
printStream.print("5月21号");
printStream.print(true);
printStream.close();
}
}
print()和 println()方法的区别就是后者在打印字符后会终止该行,也就是换行。
2、字符打印流
PrintWriter 在使用write方法写入数据后如果使用 flush 方法自动刷新,那么数据就不会被写入,如果开启了自动刷新,那么在调用的 println、printf或 format 的其中一个方法时才可能完成此操作(自动刷新的操作),字符打印流配合一个输入流也可以完成文本文档的复制,实例代码如下:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
public class MyTest{
public static void main(String[] args) throws FileNotFoundException {
PrintWriter printWriter = new PrintWriter(new FileOutputStream("E:/d.txt"),true);
printWriter.write("abc");
prinWriter.flush();
printWriter.println("情不知所起,一往而深;");
printWriter.println("爱不知所终,倏忽而逝。");
printWriter.close();
}
}
上面的代码,开启了自动刷新,所以在使用 println 方法的时候不用使用 flush 进行刷新。
PrintWriter 配合一个输入流就可以完成一个文本文档的复制,实例代码如下:
import java.io.*;
public class MyTest4 {
public static void main(String[] args) throws IOException {
BufferedReader bfr = new BufferedReader(new FileReader("E:/Test.txt"));
PrintWriter printWriter = new PrintWriter(new FileOutputStream("D:/test.txt"), true);
String line=null;
while ((line=bfr.readLine())!=null){
printWriter.println(line);
}
bfr.close();
printWriter.close();
}
}
四、随机访问流
RandomAccessFile类 不属于流,因为它的父类是 Object 类,但是它融合了 InputStream 和 OutputStream 的功能,既能读又能写,支持对文件的随机访问,同时可以对任意类型的数据进行操作。这个流在读写的时候顺序也要一致。它的构造方法如下:
RandomAccessFile(File file, String mode)
// 创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。
RandomAccessFile(String name, String mode)
//创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。
关于第二个参数用来设置该文件的访问模式,具体如下:
mode | 说明(了解) |
---|---|
r | 以只读方式打开 |
rw | 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件 |
rws | 打开以便读取和写入,对于 “rw”,还要求对文件的内容或元数据的每个更新都同步写入到基础存储设备 |
rwd | 打开以便读取和写入,对于 “rw”,还要求对文件内容的每个更新都同步写入到基础存储设备 |
实例代码实现如下:
import java.io.IOException;
import java.io.RandomAccessFile;
public class Demo4 {
public static void main(String[] args) throws IOException {
RandomAccessFile rw = new RandomAccessFile("E:/lxc.txt", "rw");
rw.writeUTF("九九");
rw.writeInt(99);
rw.writeDouble(99.9);
rw.writeBoolean(true);
RandomAccessFile r = new RandomAccessFile("E:/lxc.txt", "rw");
String s = r.readUTF();
System.out.println("指针位置:"+raf.getFilePointer());
int i = r.readInt();
double v = r.readDouble();
boolean b = r.readBoolean();
System.out.println(s);
System.out.println(i);
System.out.println(v);
System.out.println(b);
}
}
上面的代码中 getFilePointer方法是用来获取指针的位置,这个类中有一个 seek方法用来设置指针的位置。随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组,存在指向该隐含数组的光标或索引,称为文件指针,设置指针可以方便我们下载文件或者复制文件(可以接着上一次的进程继续下载或复制)
五、序列化流
通过序列化流(ObjectOutputStream)我们可以把一个对象保存到一个文件中去,通过反序列化流(ObjectInputStream)将对象读取到内存中去。它的构造方法需要传入一个输出流对象,构造方法如下:
ObjectOutputStream(OutputStream out)
序列化的前提是这个对象类需要实现一个序列化接口,而且我们还需要定义一个静态常量UID,因为在序列化过后系统会为这个类盖上一个“印章”,在反序列化是时候会对其进行验证,如果我们把这个序列化的类稍微的改动一下,那么系统就会识别不出来,所以我们加上一个静态常量UID的话,就可以表明是同一个对象。下面让我们来编写一个实现序列化接口的学生类,代码如下:
import java.io.Serializable;
public class Student implements Serializable {
//静态常量UID
private static final long serialVersionUID = 5455576216538966789L;
// public transient int age;
// transient 修饰的这个字段,不需要保存到,文件中去
public int age;
public String name;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
如果我们想让学生的某一个属性不保存到文本文档的时候,那么只需要用 transient 修饰即可。下面代码来进行学生对象的读写,代码如下:
import java.io.*;
public class MyTest {
public static void main(String[] args) throws IOException {
Student student = new Student("张三", 23);
ObjectOutputStream objOut = new ObjectOutputStream(new FileOutputStream("E:/student.txt"));
objOut.writeObject(student);
objOut.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("E:/student.txt"));
Object obj = inputStream.readObject();
Student student = (Student) obj;
System.out.println(student.name + "==" + student.age);
}
}
上面代码即是对一个学生对象的读写操作。
六、Properties
Properties 是一个双列集合,键和值默认的都是 String 类型,它里面有一些特殊的方法,如下所示:
- public Object setProperty(String key,String value): 调用 Hashtable 的方法 put
- public String getProperty(String key): 用指定的键在此属性列表中搜索属性
- public Set stringPropertyNames(): 返回此属性列表中的键集。
实例代码如下:
import java.io.IOException;
import java.util.Properties;
import java.util.Set;
public class Demo5 {
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
Object obj = properties.setProperty("李雪晨", "12");
Object obj2 = properties.setProperty("李雪", "18");
Set<String> strings = properties.stringPropertyNames();
for (String string : strings) {
String s = properties.getProperty(string);
System.out.println(s);
}
}
}
通过上面你的三个方法,我们对数据进行了存取。那么我们如何对,这个数据进行存储呢?下面让我们来介绍这个方法
- public void store(Writer writer, String comments)把Properties集合中的键值对数据写入到文件中, comments注释
- public void load(Reader reader): 读取键值对数据把数据存储到Properties中。