下面是总结的思维导图,可以先有个结构层次概念
当涉及到数据交互的时候,Java就使用流来实现。数据源可以是数据库、磁盘文件、网络数据等。
打个比喻,这是我对数据和流的理解,数据库就好比一个大水池,Java程序是希望用水做点事情的器具,像洗车喷头,高压水枪这些,要获取水就要与水之间有管道,这个管道在计算机的世界里是各种传输介质,当然也包括传输协议。
现在回到Java,Java针对需要建立交互的数据源建立传输流,将数据流读取到JVM(内存)中,同时还可以对数据源进行一些在自己权限内的操作。
现在建立一个文件输入流
package com.my;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class IOTest_1 {
public static void main(String[] args) {
try{
// 打开文件,绝对路径方式
//File f = new File("D:\\CodeProjects\\JavaProjects\\LearningExamples\\src\\com\\my\\files/Test.txt");
//相对路径方式,以根目录为基点
File f = new File("src\\com\\my\\files/Test.txt");
// 创建基于文件的输入流
FileInputStream fis = new FileInputStream(f);
//创建字节数组,数组长度为文件中数据的长度
byte[] content = new byte[(int)f.length()];
//以字节流的形式读取文件所有内容
fis.read(content);
//使用增强型for打印文件内容
for(byte b: content){
//文件中的数据会以数字形式打印
System.out.println(b);
}
//每次使用完流,都应进行关闭
fis.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
由于是字节流操作,最终打印出来的都是相应的字节码即ASCII码。
以字节流的形式向文件写入数据,即文件输出流
package com.my;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class IOTest_2 {
public static void main(String[] args) {
try{
//相对路径方式,以根目录为基点
File f = new File("src\\com\\my\\files/Test.txt");
//ASCII表对应的数字,65位A,64为@
byte data[] = { 65, 64};
// 创建基于文件的输出流
FileOutputStream fos = new FileOutputStream(f);
//将数据写入到输出流
fos.write(data);
//关闭输出流
fos.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
FileInputStream和FileOutputStream都是以字节流的形式处理文件数据,它们分别是InputStream和OutputStream的子类,InputStream和OutputStream都是抽象类,只提供方法声明,不提供方法的具体实现,还有一些字节流操作继承自它们,如ObjectInputStream和ObjectOutputStream等。
Reader字符输入流
Writer字符输出流
专门用于字符的形式读取和写入数据
Reader和Writer和上面的InputStream和OutputStream,它们有一些子类负责相应的字符流操作。
以字符流方式读取文件内容
package com.my;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class IOTest_3 {
public static void main(String[] args) {
//相对路径方式,以根目录为基点
File f = new File("src\\com\\my\\files/Test.txt");
//将流定义在try()中,try、catch或finally结束时,会自动关闭
try(FileReader fr = new FileReader(f)){
char[] content = new char[(int)f.length()];
//以字符流的形式读取文件所有内容
fr.read(content);
for(char c: content){
System.out.println(c);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
把流定义在try()里,这种编写代码的方式叫做 try-with-resources, 这是从JDK7开始支持的技术
所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。
以字符流方式向文件中写入数据
package com.my;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class IOTest_4 {
public static void main(String[] args) {
//相对路径方式,以根目录为基点
File f = new File("src\\com\\my\\files/Test.txt");
//创建基于文件的Writer
try(FileWriter fw = new FileWriter(f)){
String data = "这是一个测试abcd";
//以字符流形式写入文件
char[] out = data.toCharArray();
fw.write(out);
}catch (IOException e){
e.printStackTrace();
}
}
}
字符流操作的基本单位是char,字节流操作的基本单位是byte,很多时候需要进行这种类型的数据转换。
字符流和字节流的很多功能相似,只是处理数据的方式不同,不过数据流DataOutputStream和DataInputStream是字节流独有的,它们基于多字节的处理方法,从而可以读取基本类型的数据,例如可以选择读取整型数据、字符串数据等。
注:要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException,因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。
package com.my;
import java.io.*;
public class IOTest_5 {
public static void main(String[] args) {
//相对路径方式
String path = "src\\com\\my\\files/Test.txt";
dataWrite(path);
dataRead(path);
}
public static void dataRead(String path){
File f = new File(path);
try(
//需要基于文件输入流字节流形式
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
){
//读取指定类型数据
String str= dis.readUTF();
int i = dis.readInt();
boolean b = dis.readBoolean();
System.out.format("字符串:%s,整型数据:%d,布尔数据:%b",str, i, b);
}catch (IOException e){
e.printStackTrace();
}
}
public static void dataWrite(String path){
File f = new File(path);
try(
FileOutputStream fos = new FileOutputStream(f);
DataOutputStream dos = new DataOutputStream(fos);
){
//写入指定类型数据
dos.writeUTF("奥德赛 and 123");
dos.writeBoolean(true);
dos.writeInt(23);
}catch (IOException e){
e.printStackTrace();
}
}
}
缓存处理流的功能在字节流和字符流中都有,其目的便是为了减少IO操作。
当我们读写一个文件诗,如果每次都访问源文件,效率会很低。
就好比你用水喷头给车冲洗,水源来自距离你五公里的大河,你每次用喷头都要等五公里外的水过来,一关闭喷头水又回到了那条大河。现在的缓存区就好比在你10米外加一个蓄水池,当你需要用水时,蓄水池先把远处水源中的水拿过来蓄满,当你的喷头将蓄水池的水用光后,蓄水池再去连接水源获取水。
当然Java的IO缓存还有更多操作,蓄水池只是一个简单比喻。
使用缓存流读取数据:
现在Test.txt的内容为:
hello world
you are a good man
thank you
package com.my;
import java.io.*;
public class IOTest_6 {
public static void main(String[] args) {
//相对路径方式
File f = new File("src\\com\\my\\files/Test.txt");
try (
// 缓存流必须建立在一个存在的流的基础上
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
)
{
while (true) {
// 一次读一行
String line = br.readLine();
if (null == line)
break;
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用缓存流写入数据:
package com.my;
import java.io.*;
public class IOTest_7 {
public static void main(String[] args) {
//相对路径方式
File f = new File("src\\com\\my\\files/Test.txt");
try (
FileWriter fw = new FileWriter(f);
PrintWriter pw = new PrintWriter(fw)
)
{
pw.println("as well as");
pw.println(123456);
pw.println(true);
//立即将数据写入到硬盘,而不是等到缓存满了才写出去
pw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
这里使用的是PrintWriter,这种写出可以支持多种类型,当然使用BufferedWriter也是可以的,都是Writer的子类,只是写入的数据类型会受到些限制,选择时需要结合具体情境。
这种流需要结合面向对象,先创建一个对象实例,将该对象序列化到相应的文件,然后在读取改文件进行反序列化重新转换为一个对象,前提是实现该对象的类需要实现Serializable接口。
一个Dog类:
package com.my;
import java.io.Serializable;
public class Dog implements Serializable {
public String name;
public int age;
public Dog(String name, int age){
this.name = name;
this.age = age;
}
public void run(){
System.out.println(name + " hp:" + age + "正在奔跑");
}
}
对象流读写操作:
package com.my;
import java.io.*;
public class IOTest_8 {
public static void main(String[] args){
Dog dog = new Dog("小黑",34);
File f = new File("src\\com\\my\\files/dog.txt");
try(
//创建对象输出流
FileOutputStream fos = new FileOutputStream(f);
ObjectOutputStream oos = new ObjectOutputStream(fos);
//创建对象输入流
FileInputStream fis = new FileInputStream(f);
ObjectInputStream ois = new ObjectInputStream(fis);
)
{
//将对象写入文件
oos.writeObject(dog);
//从文件中读取对象
Dog dog2 = (Dog) ois.readObject();
//查看获取到的对象信息
System.out.println(dog2.name);
System.out.println(dog2.age);
dog2.run();
}
catch (IOException e)
{
e.printStackTrace();
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
}
对象流在项目中应用很广,现在可以先了解下。