Java IOStream

135 阅读17分钟

为什么需要IO流?

 在我们运行程序的时候,总是会出现程序运行结束,然后数据被清除的现象。
 这样我们就无法保存数据,以便下一次使用了。其次如果有大量数据我们都需要手动输入。
 如果有一个介质能保存数据,需要的时候能取出来的话。那么会非常方便,大大提高工作效率。

为什么是流?

  文件的主要操作就是程序和外存来回运东西的一个过程,这里我们可以知道如果文件的引入是为了更加方便地存取数据的话。
  那么就意味着最常见、最主要的两种操作就是存数据和取数据。
  将我们的数据作为主体的话,那么“流”,一定是一个非常重要的媒介。“流” , 
  扮演的角色就像的产品和客户之间的外卖员。
  如果没有外卖员的话,虽然在生活中即使没有外卖员也能自己取,但是这里只是强调“流”作用,总结一句话就是“搬运工”。

何为 IO

  IInput 的缩写,O是Output 的缩写 。
  前面我们知道文件是以流的形式来操作的,那么我们有一点有点拗口,或者有点不好理解的概念就是
  什么是数据的输入,什么是文件的输出。我们可能觉得是文件主体,不是文件保存数据吗,
  应该是向文件输入数据对吧,将文件的数据输出到程序中对吧,但是事实恰好相反!
  这是为什么呢 ? 因为我们是以程序为主体,保存的数据的一样还是能够交给程序处理。所以我们一定要把
  程序当作我们主体。正确的理解方式是将数据输入到程序中,将数据输出到文件当中。
  这个操作发生在内存和外存之间。

image.png

通过这个图我们能够清楚形象的看到java程序与文件之间的关系 , 注意这个流的定义 。 、
关键词 “路径” , 一语点出了流的本质就是媒介的作用,
并且清晰的指出了数据在文件与程序之间流动的关系。

常见的文件操作

 new File(String pathname)         // 根据路径创建一个File对象
 new File(File parent , String child)      // 根据父类文件 + 子路径目录
 new File(String parent , String child)    // 根据父目录 + 子路径构建
 
 createNewFile   创建新文件
 
 
这里我们解读一下这个 new File 其实就是 在内存里面生成指针 , 然后指向磁盘里面的实际物理空间
其实这个还是存在问题的, 因为Java是在虚拟机上跑,应该拿不到实际物理地址。但是暂时这么理解,正确性还需要经过验证!!!
createNewFile 方法 才是真正创建一个文件的方法。new File 只是创建一个对象!

import org.junit.Test;

import org.junit.Test;

import java.io.File;

import java.io.IOException;

@SuppressWarnings("all")  
public class FileInformation {  
  
private String filepath = "D:\\news1.txt";  
private File file = new File(filepath);  
  
/**  
* 创建文件  
*/  
@Test  
public void create() {  
  
try {  
if(file.createNewFile())  
System.out.println("文件创建成功");  
} catch (IOException e) {  
throw new RuntimeException(e);  
}  
}  
  
/**  
* 获取绝对路径  
*/  
@Test  
public void getAbsoPath(){  
  
System.out.println("文件绝对路径 = " + file.getAbsolutePath());  
  
}  
  
/**  
* 获取上级目录  
*/  
@Test  
public void getParent(){  
  
System.out.println("文件上级路径 = " + file.getParent());  
  
}  
  
/**  
* 文件的长度 , 按字节大小算 . 如果字符编码是 UTF-8 那么英文字符 1 字节 , 中文字符是 3 字节 .  
*/  
@Test  
public void length(){  
  
  
  
System.out.println("文件长度 = " + file.length());  
}  
  
/**  
* exists 判断文件是否存在  
*/  
@Test  
public void exists(){  
if( file.exists() ) {  
System.out.println("文件存在");  
}else{  
System.out.println("文件不存在");  
}  
  
}  
  
/**  
* 文件是不是一个目录 , 目录使用特殊的文件  
*/  
@Test  
public void isDirectory(){  
if( file.isDirectory() ){  
System.out.println(file.getName() + "是一个文件目录");  
}else{  
System.out.println("该文件不是一个目录");  
}  
}  
  
/**  
* 该指向的对象是不是一个文件  
*/  
@Test  
public void isFile(){  
if(file.isFile()){  
System.out.println("指向的对象是文件");  
}  
else{  
System.out.println("该指向的对象不是一个文件");  
}  
}  


}

上面是我们对文件基本知识以及获取文件基本信息的介绍

IO流原理及其流的分类

原理

 1I/O 是input/Output 的缩写 , I/O 技术是非常实用的技术 , 用于处理数据传输。如读/写文件,网络通讯等。
2、Java程序中,对于数据的输入/输出操作以“流”的方式进行。
3、Java.io 包下提供了各种“流”类和接口 , 用以获取不同类型的数据 , 并通过方法输入或输出数据
4、输入input: 读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中
5、输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。

流的分类

1、 按照操作数据单元不同分为:字节流(8 bit) 二进制文件 ,字符流(按字符)文本文件 二进制文件主要是音频文件 2、按数据流的流向不同分为:输入流、输出流 3、按流的角色的不同分为:节点流,处理流/包装流

抽象基类 字节流 字符流

输入流 InputStream Reader

输出流 OutputStream Writer

   1) Java 的IO流共涉及40多个类 , 实际上非常规则 ,都是从如上4个抽象基类派生出来的
   2) 由这四个类派生出来的子类名称都是以其父类名作为子类的后缀     
   

image.png

图中的是基类(父类)

FileInputStream

  文件输入流
    ```
    @Test  

    public void InputStream(){  

    FileInputStream fileInputStream = null;  
    int length = 20;  
    int data = 0;  

    byte [] by = new byte[length];  


    try {  
    fileInputStream = new FileInputStream(filepath);  

    // fileInputStream.read(by); 如果读取完了 , 返回 -1 .  

    // 注意读取中文的时候, 因为我们字符编码是UTF-8 , 英文是1字节 , 中文是3字节 .  
    // 所以读取中文的时候很可能出现乱码。  


    while((data = fileInputStream.read()) != -1)  
    System.out.print((char)data);  


    } catch (IOException e) {  
    throw new RuntimeException(e);  
    } finally {  
    try {  
    fileInputStream.close();  
    } catch (IOException e) {  
    throw new RuntimeException(e);  
    }  
    }  
    } 
        }
        
   
   /* 如果你想每次都是追加数据, 而不是覆写文件所有数据的话 , 你可以这么做.
      fileInputStream = new FileInputStream(filepath , true);
   */

奇怪的现象

   while((data = fileInputStream.read()) != -1)  
    System.out.print((char)data);
    // 这样读取中文是乱码
    
  /--------------------------------------------------------------------------------------/

   int RealityLength = fileInputStream.read(by); // 如果读取完了 , 返回 -1 .  
   System.out.print(new String(by, 0 , RealityLength));
       // 这样读取中文是原样显示 
       
       不是很清楚这的原因          
      

1)OutputStream

@Test  
public void output(){  

FileOutputStream fileOutputStream = null ;  
String name = "肖宇文";  

try {  
fileOutputStream = new FileOutputStream(filepath);  

fileOutputStream.write(name.getBytes());       // 一定要注意这个方法哦, 很方便。
//fileOutputStream.write('1');  
// 注意写入 数字 1 , 那么就是对应 ASCII码 1 , 字符 1 , 那么肯定就是对应 ‘1’ 呗。  

} catch (IOException e) {  
throw new RuntimeException(e);  
} finally {  
try {  
fileOutputStream.close();  
} catch (IOException e) {  
throw new RuntimeException(e);  
    }  
  }  
}

注意关闭文件
刚刚我们发现其实 FileInputStream 读入字符串是通过用byte 数组装 , 然后 创建String 对象来输出数据的字符串形式。FileOutputStream ,如果要写入一个字符串那么就是通过"String".getbyte 方法, 写入文件。

文件拷贝

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

@SuppressWarnings("all")  
public class Main {  

/*  
实现将图片"考拉"的文件拷贝到另外一个文件 "others"  
*/  

public static void main(String[] args) {  
new Main().copy();  
}  


public void copy(){  
String filepath = "d:\\kaola.jpg";  
String othersPath = "d:\\kaola2.jpg";  

int realLength = 0;  
String InputString = "";  
FileInputStream fileInputStream = null;  
FileOutputStream fileOutputStream = null;  
byte [] bytes = new byte[1024];  

File file = new File(filepath);  

try {  
fileInputStream = new FileInputStream(filepath);  

fileOutputStream = new FileOutputStream(othersPath);  

while((realLength = fileInputStream.read(bytes)) != -1)  
fileOutputStream.write(bytes);  


} catch (IOException e) {  
throw new RuntimeException(e);  
} finally {  
try {  
fileInputStream.close();  
fileOutputStream.close();  
} catch (IOException e) {  
throw new RuntimeException(e);  
    }  
 }  
    }  
}
关闭文件流

FileReader

这是一个Reader 的子类 主要使用的方法就是 read() , 单个字符读取返回int 形式 ,读取完则 返回 -1. 还有它的重载 read(buf , 0 , realLength) , 将内容从[0 , realLength) 读取到buf 里面 读取完则返回 -1.

import java.io.FileReader;  
import java.io.IOException;  

@SuppressWarnings("all")  
public class FileReader_ {  

public static void main(String[] args) {  

String filePath = "d:\\hello.txt";  


FileReader reader = null;  
int length = 20;  
int data = 0 ;  
char [] buf = new char[length];  

try {  
reader = new FileReader(filePath);  
/* while( ( data = reader.read()) != -1 ) // 单个字符读取  
System.out.print((char)data);  
*/  

// 装进数组里面处理  
int realLength = 0;  
while( (realLength = reader.read(buf)) != -1)  
System.out.print(new String(buf , 0 , realLength));  

} catch (IOException e) {  
throw new RuntimeException(e);  
}finally {  
try {  
reader.close();  
} catch (IOException e) {  
throw new RuntimeException(e);  
   }  
  }  
 }  

} 注意关闭文件流

FileReader

import java.io.FileReader;  
import java.io.IOException;  

@SuppressWarnings("all")  
public class FileReader_ {  

public static void main(String[] args) {  

String filePath = "d:\\hello.txt";  


FileReader reader = null;  
int length = 20;  
int data = 0 ;  
char [] buf = new char[length];  

try {  
reader = new FileReader(filePath);  
/* while( ( data = reader.read()) != -1 ) // 单个字符读取  
System.out.print((char)data);  
*/  

// 装进数组里面处理 , 批量处理 .  
int realLength = 0;  
while( (realLength = reader.read(buf)) != -1)  
System.out.print(new String(buf , 0 , realLength));  

} catch (IOException e) {  
throw new RuntimeException(e);  
}finally {  
try {  
reader.close();  
} catch (IOException e) {  
throw new RuntimeException(e);  
      }  
    }  
  }  
} 

FileReader 作为Reader 的重要子类 , 我其实就是两个方法 或者说一个方法 . 那就是 read() 但是注意这是按字符处理 , 适合处理文本文件 .所以你可以注意到 , 我用的是char 数组装的.还是需要 new String() 去输出它的字符串形式 , 因为你不是每次读取数据都能读满整个数组 , 所以需要限制一下每次输出的长度.如果是返回当个字符的的话 , 那么返回的是一个int 类型的数据. 所以这意味着你可能得强制转化一下.

Filewriter

import java.io.FileWriter;  
import java.io.IOException;  

public class FileWriter__ {  

public static void main(String[] args) {  

String filePath = "d:\\hello.txt";  

FileWriter fileWriter = null ;  
int length = 20;  
String str = "肖宇文";  

try {  
fileWriter = new FileWriter(filePath);  

fileWriter.write('X');  
fileWriter.write(str , 0 , str.length());  
fileWriter.write(str.toCharArray() , 0 , str.length());  
fileWriter.write(0); // 按照ASCII码的 形式写入的 , 不是字符 '0'.  


} catch (IOException e) {  
throw new RuntimeException(e);  
} finally {  
try {  
fileWriter.flush();  
} catch (IOException e) {  
throw new RuntimeException(e);  
}  
}  
}  
}

注意一定要关流!!! 不关流 , 无法保存数据. 养成习惯 , 创建一个流 , 那么就需要现在 Fianlly 里面关流.

封装流/处理流

在讲这个BufferedReader 之前 , 我先讲一个非常重要的设计模式 , 也是BufferedReader 的实现核心思想.

封装流顾名思义就是封装了流嘛 , 封装了什么流呢? Reader子类的流 , 它的作用就是中间商. 集成了所有Reader子类.

image.png

这个图非常形象 , 它的本质就是将 FileReader 、 CharArrayReader 、StringReader………… 等等 封装进 BufferedReader . 说的直白就是 Reader 作为抽象父类 , 里面写的抽象方法read(read()方法只是举个例子,可能还有很多别的方法) ,然后给Reader的子类去重写,就可以实现统一了。这为包装流奠定了重要的基础。

image.png

我们可以看到这个里面有一个Reader in , 这个会用在构造器里面,去引用封装的节点流. 其实就是多态的运用.父类没有的方法,引用编译类型是父类的 , 引用的对象的运行类型是子类的. 父类的引用是调不了子类的方法,但是父类作为抽象类将子类那些实现功能的方法都做成了抽象方法. 这样将父类的节点流封装进处理流就可以正常使用对应方法了. 这样就实现了封装流(处理流)。 本质上就是多态的动态绑定!!! 这样的几句话可能基础很好也不能随随便便的看懂 , 可以多找对应的视频 , 看看源码 有助于更加深刻的理解.

好了 , 开始讲封装流BufferedReader!!!

使用就比较简单了,下面看代码即可。

代码

import java.io.BufferedReader;  
import java.io.FileReader;  
import java.io.IOException;  


@SuppressWarnings("all")  
public class BufferedReader_ {  
public static void main(String[] args) {  
String filePath = "d:\\hello.txt";  

BufferedReader bufferedReader = null ;  
FileReader fileReader ;  

int MaxLength = 1024;  

try {  
String line = "";  

fileReader = new FileReader(filePath);  

bufferedReader = new BufferedReader(fileReader);  
// fileReader 是我们的节点流  
// bufferedReader 是我们的封装流  

// 第一种方法  
while((line = bufferedReader.readLine()) != null )  
System.out.println(line);  
// 第二种方法  
// char [] chArr = new char[MaxLength];  
// int len = bufferedReader.read(chArr);  
// System.out.println(new String(chArr, 0 , len));  


} catch (IOException e) {  
throw new RuntimeException(e);  
} finally {  
try {  
bufferedReader.close();  
} catch (IOException e) {  
throw new RuntimeException(e);  
    }  
   }  
 }  
}

BufferedWriter

简单的方法的调用 , 代码如下

import java.io.BufferedWriter;  
import java.io.FileWriter;  
import java.io.IOException;  

@SuppressWarnings("all")  
public class BufferedWriter_ {  
public static void main(String[] args) {  

String filePath = "d:\\hello.txt";  
String writeContext = "xyw_66";  

FileWriter fileWriter = null;  
BufferedWriter bufferedWriter = null;  


try {  
fileWriter = new FileWriter(filePath , true);  
bufferedWriter = new BufferedWriter(fileWriter );  

bufferedWriter.write(writeContext);  
bufferedWriter.write(writeContext + writeContext);  
bufferedWriter.write('6');  
bufferedWriter.write('6');  


} catch (IOException e) {  
throw new RuntimeException(e);  
} finally {  
try {  
bufferedWriter.close();  
} catch (IOException e) {  
throw new RuntimeException(e);  
}  
}  
}  
}

记住关流只需要关封装流即可 , 底层会直接关掉节点流 .

利用封装流进行文件拷贝

      @Test  
public void output(){  

FileOutputStream fileOutputStream = null ;  
String name = "肖宇文";  

try {  
fileOutputStream = new FileOutputStream(filepath);  

fileOutputStream.write(name.getBytes());  
//fileOutputStream.write('1');  
// 注意写入 数字 1 , 那么就是对应 ASCII码 1 , 字符 1 , 那么肯定就是对应 ‘1’ 呗。  

} catch (IOException e) {  
throw new RuntimeException(e);  
} finally {  
try {  
fileOutputStream.close();  
} catch (IOException e) {  
throw new RuntimeException(e);  
}  
}  
} 

字节流实现文件拷贝

代码如下:

import java.io.*;  

public class BufferedOutStream_ {  
public static void main(String[] args) {  

String source = "d:\\kaola.jpg";  
String destination = "d:\\kaola2.jpg";  

BufferedInputStream bufferedInputStream = null ;  

BufferedOutputStream bufferedOutputStream = null;  

try {  

int realLength = 0;  
byte [] by = new byte[1024];  
bufferedInputStream = new BufferedInputStream(new FileInputStream(source));  
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(destination));  


while((realLength = bufferedInputStream.read(by)) != -1)  
bufferedOutputStream.write(by ,0 , realLength);  

} catch (IOException e) {  
throw new RuntimeException(e);  
} finally {  
try {  
bufferedInputStream.close();  
bufferedOutputStream.close();  
} catch (IOException e) {  
throw new RuntimeException(e);  
}  
}  
}  
}
   
   

ObjectInputStream

为什么需要对象处理流?

    我们很多时候需要保存的数据不是一个简单100, 而是一个对象的所有数据.并且很多时候我们保存的数据,
    看起来是一个数值,比如存入100 . 但是明显你不知道是存入的字符串还是int类型的数值。
    这时候为了满足我们不仅要保存数值还要保存数据类型的需求。所以我们需要对象处理流!

如何将保存数据,并且保存数据的数据类型呢 ?

      将数据的数值和数据类型都保存到文件的操作叫 “序列化” 。
      恢复数据时,恢复数据的值和数据类型这种行为就称为“反序列化”。 
      想要序列化和反序列化,只需要让存入的对象和其对象对应的属性实现Serializable、
      Externalizable 其中一个接口即可。  
      推荐实现Serializable 接口, 因为这个就是一个标记接口,没有任何方法。     

这是一个处理流还是节点流呢 ?

image.png 我们通过这个ObjectInputStream 构造器我们可以发现这是一个处理流. 那么它就是一个集成了InputStream子类的封装流

存储数据代码演示

代码有点麻烦 ,下面我放的代码需要仔细看.看不懂的,可以进入视频传送门。0629_韩顺平Java_ObjectOutputStream_哔哩哔哩_bilibili

package OutputStream_;  

import java.io.*;  

public class ObjectOutputStream_ {  
public static void main(String[] args) {  
String filePath = "d:\\hello.txt";  

ObjectOutputStream oos = null;  

try {  
oos = new ObjectOutputStream(new FileOutputStream(filePath));  

// 写入一个对象  
oos.writeObject(new Dog("小黄 ", 3 , "白"));  

} catch (IOException e) {  
throw new RuntimeException(e);  
} finally {  
try {  
oos.close();  
} catch (IOException e) {  
throw new RuntimeException(e);  
}  
}  
}  
}
  
  
  
  
  /————————————————————————————————————————————————————————————————————————————————————/
  
 package OutputStream_;  

import java.io.Serializable;  

@SuppressWarnings("all")  
public class Dog implements Serializable {  
private String name ;  
private int age;  
private String color;  

private Master master ;  

public Dog(String name, int age, String color) {  
this.name = name;  
this.age = age;  
this.color = color;  
this.master = new Master();  
}  

public String getName() {  
return name;  
}  

public void setName(String name) {  
this.name = name;  
}  

public int getAge() {  
return age;  
}  

public void setAge(int age) {  
this.age = age;  
}  

public String getColor() {  
return color;  
}  

public void setColor(String color) {  
this.color = color;  
}  

public Master getMaster() {  
return master;  
}  

public void setMaster(Master master) {  
this.master = master;  
}  

@Override  
public String toString() {  
return "Dog{" +  
"name='" + name + '\'' +  
", age=" + age +  
", color='" + color + '\'' +  
", master=" + master +  
'}';  
}  
}
 
 
 /——————————————————————————————————————————————————————————————————————————————————/
 
 package InputStream_;  

import OutputStream_.Dog;  

import java.io.FileInputStream;  
import java.io.IOException;  
import java.io.ObjectInputStream;  
import java.util.ArrayList;  

public class ObjectInputStream_ {  
public static void main(String[] args) throws Exception {  
String filePath = "d:\\hello.txt";  

ObjectInputStream ois = null;  
ArrayList<Dog> dogList = new ArrayList<>();  

try {  
ois = new ObjectInputStream(new FileInputStream(filePath));  

Dog dog = (Dog)( ois.readObject());  

dogList.add(dog);  

for(Dog dogs : dogList)  
System.out.println(dogs);  


} catch (Exception e) {  
throw new Exception(e);  
} finally {  
try {  
ois.close();  
} catch (IOException e) {  
throw new RuntimeException(e);  
}  
}  

}  
}

/—————————————————————————————————————————————————————————————————————————————————————————/

package OutputStream_;  

import java.io.Serializable;  

public class Master implements Serializable {  

}

注意看包名, 有的类不是同一个包下面的。

对象处理流的注意事项和细节说明

1)读写顺序要一致 2)要求序列化和反序列化对象, 需要实现Serializable 3)序列化的类中建议添加SerialVersionUID , 为了提升版本兼容性 这个解释一下就是你在一个将一个类序列化了之后, 你再在这个类里面加入新的属性 编译器不会视作是两个不同的类 4)序列化对象时, 默认将所有的属性都序列化 ,但是static 或 transient 修饰的成员除外 5)序列化对象时 , 要求里面的类型也需要实现序列化接口. 意思就是 假如这个类里面由String , int , char …… 组成. 那么这些属性都需要能序列化, 更加直白一点就是需要实现Serializable 6)序列化具备可继承性,也就是当某个类继承了已经实现可序列化接口的类,那么这个这个类就作为了子类。 这时这个子类也可以序列化

标准输入输出流

  @SuppressWarnings("all")  
    public class Main {  

    public static void main(String[] args) {  
    // public static final InputStream in = null;  

    System.out.println("System.in 的编译类型是 InputStream");  
    System.out.println("运行类型是" + System.in.getClass());  

    System.out.println();  
    // public static final PrintStream out = null;  
    System.out.println("System.out 的 编译类型是 PrintStream");  
    System.out.println("运行类型是 " + System.out.getClass());  

    }  
    }
    
    
    输出内容:
    System.in 的编译类型是 InputStream
    运行类型是class java.io.BufferedInputStream

    System.out 的 编译类型是 PrintStream
    运行类型是 class java.io.PrintStream
    

我们所熟悉的System.out 是我们的标准输出流 , 将内容输出到控制台上 我们所熟悉的System.in 是我们的标准输入流 , 读取我们输入在控制台输入的内容

    Scanner scanner = new Scanner(System.in);  
    String str = scanner.next();  
    System.out.println(str);
    读取数据 , 输出数据。
    

转换流

我们为什么需要转换流 ?

我们知道对于Java编译器 , 和存储数据的文件可能编码是不一致的。以IDEA为例,其标准编码是UTF-8 , 但是文本文件的字符编码可能是Gbk。读取数据的时候,就需要按照文件的存储数据的文件编码读取数据。不然就会出现乱码。所以我们去读取文件数据的时候,需要转化流按照文件对应编码去读取。存入数据的时候也是,我们需要按照文件本身的文件编码存入,所以需要转换流去完成。

我们什么是转换流 ?

常见的转换流是 InputStreamReader 、 OutputStreamWriter。

将字节流转化成字符流输入 , 一般这种情况都是需要显示中文的情况。因为显示乱码 , 所以需要转换一下文件编码。 其次输入的情况,一般这种情况可能也是需要输入中文 , 所以需要重新设置文件编码。

类图(只展示两种,其他类似)

image.png

image.png

其实看起来非常绕 , 或者我没讲清楚.但是用起来是非常方便 ,简单 。

 import java.io.*;
 
@SuppressWarnings("all")  
public class InputStreamReader__ {  
public static void main(String[] args) throws IOException {  
String filePath = "d:\\hello.txt";  
String fileCode = "gbk";  

int arrSize = 1024;  

int realLength = 0 ;  
char [] arr = new char[arrSize];  

FileInputStream fileInputStream = new FileInputStream(filePath);  
InputStreamReader isr = new InputStreamReader(fileInputStream, fileCode);  

BufferedReader buf = new BufferedReader(isr);  
while( ( realLength = buf.read(arr)) != -1)  
System.out.println(new String(arr , 0 , realLength));  
}  
}

这段代码主要做的事情就是把 FileInputStream 包装进 InputStreamReader , 然后再将InputStreamReader,封装进 BufferedReader. 此前我已经将文件编码设置成gdk . 然后我的IDEA 编码是 UTF-8 。我现在使用的gdk 码的方式去读取文件。

public class BufferedReader_ {  
public static void main(String[] args) {  
String filePath = "d:\\hello.txt";  

BufferedReader bufferedReader = null ;  
FileReader fileReader ;  

int MaxLength = 1024;  

try {  
String line = "";  

fileReader = new FileReader(filePath);  

bufferedReader = new BufferedReader(fileReader);  
    
while((line = bufferedReader.readLine()) != null )  
System.out.println(line);  

} catch (IOException e) {  
throw new RuntimeException(e);  
} finally {  
try {  
bufferedReader.close();  
} catch (IOException e) {  
throw new RuntimeException(e);  
    }  
  }  
 }  
}

如果你拿上面这段代码去读取同一个文本文件, 会出现乱码 , 已亲测 。

import java.io.*;  

public class OutputStreamWriter__ {  
public static void main(String[] args) throws IOException {  
String filePath ="d:\\hello.txt";  
String fileCode = "gbk";  


FileOutputStream fileOutputStream = new FileOutputStream(filePath , true);  

OutputStreamWriter owr = new OutputStreamWriter(fileOutputStream, fileCode);  

BufferedWriter bwf = new BufferedWriter(owr);  

bwf.write("是啥子");  

bwf.close();  
}  
}

读取文件 , 为什么需要BufferedWriter 呢 , 为了方便统一使用而已 。因为我们知道BufferedWriter 集成了所有的Reader 的子类 , 所以使用这个我想应该是为了方便操作.

printStream

就是一个打印类 , 直接贴代码即可。

    import java.io.IOException;  
    import java.io.PrintStream;  
    public class PrintStream__ {  
    public static void main(String[] args) throws IOException {  
    String filePath = "d:\\hello.txt";  

    PrintStream print = System.out;  

    print.println("我是小猪");  
    print.close();  

    // 将数据输出到文件当中去
    System.setOut(new PrintStream(filePath));  

    print.write("我是小猪".getBytes());  
    }  
    }

这是类其实可能用的不是那么多, 就是常用的接收、打印方法.

Properties

Properties 主要是对配置文件进行操作 , 没讲什么知识点主要是方法如何使用。

不使用propertise 操作配置文件

import java.io.*;  

public class Properties1_ {  
public static void main(String[] args) throws IOException {  
String filePath = "D:\\TestObject\\IOStream\\src\\mysql.properties";  
int maxSize = 1024;  
int realLength ;  

byte [] by = new byte[maxSize];  

BufferedInputStream bos = null ;  

bos = new BufferedInputStream(new FileInputStream(filePath));  

while((realLength = bos.read(by)) != -1)  
System.out.println(new String(by, 0, realLength));  

}  
}

使用propertise 操作配置文件

    import java.io.FileReader;  
    import java.io.IOException;  
    import java.util.Properties;  

    @SuppressWarnings("all")  
    public class Properties2_ {  
    public static void main(String[] args) throws IOException {  
    String filePath = "D:\\TestObject\\IOStream\\src\\mysql.properties";  

    Properties properties = new Properties();  

    // 加载指定配置文件  load 导入 , 加载的意思.
    properties.load(new FileReader(filePath));  

    // 把k-v 实现到控制台  

    properties.list(System.out);  

    // 根据key 获得对应的 vuale  

    String ip = properties.getProperty("ip");  
    System.out.println("用户的ip是 " + ip);  
    System.out.println("用户名是 " + properties.getProperty("user"));  

    }  
    }
    

/----------------------------------------------------------------------------------/

import java.io.FileWriter;  
import java.io.IOException;  
import java.util.Properties;  

public class properties3_ {  
public static void main(String[] args) throws IOException {  
String filePath = "D:\\TestObject\\IOStream\\src\\mysql.properties";  

Properties properties = new Properties();  

properties.setProperty("charset" , "UTF-8");  
properties.setProperty("user" , "汤姆");  
// IDEA 存储中文的时候存储的是汤姆的unicode 码 !  

// 将k - v 存储到配置文件中  
properties.store(new FileWriter(filePath) , "hello, world");  
// 这个hello , world 是一个注释 , 一般我们直接写null  

// 希望修改配置文件中的变量 , 就可以直接使用 store 方法 , 直接覆盖即可

System.out.println("保存成功!");  

}  
}

目前还没发现在不覆写的情况下 ,通过key , 修改其对应vuale. 目前还是太菜, 3天学完IO还是有点慢了。连着看课写博客代码,现在已经是2023/7/13/23:17 , 休息了。