25. IO流.md

时间:2023-03-09 22:06:49
25. IO流.md


IO分类:

字节流&字符流的IO操作:

  • 字节流:输入输出和字符有关的操作
    • IO类:FileInputStream和FileOutPutStream;缓冲:BufferedInputStream和BufferedOutStream
  • 字符流:输入输出和字符无关的操作
    • IO类:FileReader和 FileWriter;缓冲:BufferedReader和BufferedWriter

1.FIle类

1.1目录分隔符

不同机器上目录分隔符是不一样的,所以用File类的成员变量separator来表示:

public static final String separator = "" + separatorChar;  

注意:windows上认两种目录分隔符,也就是正(/)反(\)都可以

1.2常用方法

  • 创建:

createNewFile() 在指定位置创建一个空文件,成功就返回true,如果已存在就不创建然后返回false

mkdir() 在指定位置创建目录,这只会创建最后一级目录,如果上级目录不存在就抛异常。

mkdirs() 在指定位置创建目录,这会创建路径中所有不存在的目录。

renameTo(File dest) 重命名文件或文件夹,也可以操作非空的文件夹,文件不同时相当于文件的剪切,剪切时候不能操作非空的文件夹。移动/重命名成功则返回true,失败则返回false。

  • 删除:

delete() 删除文件或一个空文件夹,如果是文件夹且不为空,则不能删除,成功返回true,失败返回false。

   deleteOnExit()    在虚拟机终止时,请求删除此抽象路径名表示的文件或目录,保证程序异常时创建的临时文件也可以被删除
  • 判断:

exists() 文件或文件夹是否存在。

   isFile()      是否是一个文件,如果不存在,则始终为false。
isDirectory() 是否是一个目录,如果不存在,则始终为false。
isHidden() 是否是一个隐藏的文件或是否是隐藏的目录。
isAbsolute() 测试此抽象路径名是否为绝对路径名。
  • 获取:

getName() 获取文件或文件夹的名称,不包含上级路径。

getPath() 返回绝对路径,可以是相对路径,但是目录要指定

getAbsolutePath() 获取文件的绝对路径,与文件是否存在没关系

length() 获取文件的大小(字节数),如果文件不存在则返回0L,如果是文件夹也返回0L。

getParent() 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回null。

lastModified() 获取最后一次被修改的时间。

文件夹相关:

staic File[] listRoots() 列出所有的根目录(Window中就是所有系统的盘符)

list() 返回目录下的文件或者目录名,包含隐藏文件。对于文件这样操作会返回null。

list(FilenameFilter filter) 返回指定当前目录中符合过滤条件的子文件或子目录。对于文件这样操作会返回null。

listFiles() 返回目录下的文件或者目录对象(File类实例),包含隐藏文件。对于文件这样操作会返回null。

listFiles(FilenameFilter filter) 返回指定当前目录中符合过滤条件的子文件或子目录。对于文件这样操作会返回null

2.FileInputStream类

2.1读取文件

使用FileInputStream类可以用read方法,这个方法的不同重载都可以实现读取文件:


package per.liyue.code.filedemo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
File file = new File("");
String separator = file.separator; ReadFile1(separator);
ReadFile2(separator);
} //直接读取
public static void ReadFile1(String separator) throws FileNotFoundException, IOException {
File fileOpen = new File("d:" + separator + separator + "a.txt");
if (fileOpen.exists()) {
FileInputStream in = new FileInputStream(fileOpen); //read方法
int concent = 0;
System.out.println("read方法while读取:");
while(-1 != concent){
concent = in.read();
System.out.print((char)concent);
}
in.close();
}
} //byte数组读取
public static void ReadFile2(String separator) throws FileNotFoundException, IOException {
File fileOpen = new File("d:" + separator + separator + "a.txt");
if (fileOpen.exists()) {
FileInputStream in = new FileInputStream(fileOpen); //byte方法-一般数组大小为1024倍数
byte[] b = new byte[2];
System.out.println("byte方法while读取:");
int length = 0;
while(-1 != (length = in.read(b))){
System.out.print(new String(b, 0, length));
}
in.close();
Arrays
}
} }

读取文件的注意事项

  • 使用byte的方法效率高!

  • 重要的事情说三遍:一定要关闭资源!一定要关闭资源!一定要关闭资源!

3.FileOutputStream类

FileOutputStream 的write方法可以写:

每次写之前先清空目标文件


public static void WriteNew(String separator) throws FileNotFoundException, IOException {
File fileWrite = new File("d:" + separator + separator + "a.txt");
FileOutputStream out = new FileOutputStream(fileWrite);
String concent = "1234567890abcdef";
out.write(concent.getBytes());
out.close();
}

每次追加到最后


public static void WriteEnd(String separator) throws FileNotFoundException, IOException {
File fileWrite = new File("d:" + separator + separator + "a.txt");
FileOutputStream out = new FileOutputStream(fileWrite, true);
String concent = "1234567890abcdef";
out.write(concent.getBytes());
out.close();
}

拷贝文件

拷贝文件需要边读边拷写:


public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
File file = new File("");
String separator = file.separator; //缓冲
byte[] b = new byte[1024];
//拷贝源
File fileSour = new File("D:" + separator + "a.bmp");
FileInputStream fileIn = new FileInputStream(fileSour);
//目标源
File fileTar = new File("D:" + separator + "b.bmp");
FileOutputStream FileOut = new FileOutputStream(fileTar, true); //开始拷贝
int concent = 0;
while(-1 != (concent = fileIn.read(b))){
FileOut.write(b, 0, concent);
//这样拷贝会增大文件,因为最后不满1024也按照1024拷贝了
//out.write(b);
}
fileIn.close();
FileOut.close();
}

4.缓冲流

4.1 BufferedInputStream

  • BufferedInputStream其实只是在内部维护了一个8KB的字节数组,快的原因和上面byte[]一样,每次从缓冲数组中读取,减少IO次数

  • BufferedInputStream的close实际上是调用父类的方法。

4.2 BufferedOutputStream

  • BufferedOutputStream的内部也是维护了一个字节数组。

  • BufferedOutputStream真正写入文件的时机:

    • 调用其close方法

    • 调用其flush方法

    • 当内部缓冲数组满了也会写入

5.字符流

字符流的操作和字节流类似

5.1BufferedReader

5.2 BufferedWriter



6.序列流

  • Serializable称作标志接口,对象的输入输出必须实现此接口,称作序列化。
  • 这个接口没有方法,输出中只需要实现此接口!
  • 对象的反序列化并不会创建对象
  • 反序列化读取的时候,使用serialVersionUID来判断是否是同一个序列化对象,如果反序列化的时候,类对象经过变更,不同于序列化时候,反序列化就会失败。对于需要的情况,可以使用默认的serialVersionUID(在类名上Ctrl+1`)来避免无法序列化的问题,但是要注意,反序列化必须对应的是同一个类
  • 如果序列化的类中包含了另一个没有序列化的类是不可以的
package per.liyue.code.serializabledemo;
import java.io.Serializable;
public class Employee implements Serializable{
//使用默认的serialVersionUID
private static final long serialVersionUID = 1L;
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private int age;
}
package per.liyue.code.serializabledemo;
import java.io.Serializable;
public class User implements Serializable{
//使用默认的serialVersionUID
private static final long serialVersionUID = 1L;
private String name;
//可以下序列化写文件以后,增加这个成员变量,用反序列化读取实验serialVersionUID的作用
//private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
} @Override
public String toString() {
// TODO Auto-generated method stub
return "用户的名字是:" + this.name;
}
}
package per.liyue.code.serializabledemo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class DemoPrint {
public static void main(String[] args) throws IOException, ClassNotFoundException{
//序列化对象
WriteData();
//反序列化对象
ReadData();
}
public static void ReadData() throws IOException, FileNotFoundException, ClassNotFoundException {
ObjectInputStream inStream = new ObjectInputStream(new FileInputStream(new File("D:\\a.txt")));
//这里直接强转了,并没有创建新的类对象,克隆的时候也是一样的没有创建类对象
User u = (User)inStream.readObject();
System.out.println(u);
//如果下面反序列化的是Employee类,那么必定会报错
// Employee u = (Employee)inStream.readObject();
// System.out.println(u);
inStream.close();
}
public static void WriteData() throws IOException, FileNotFoundException {
ObjectOutputStream outStream = new ObjectOutputStream(new FileOutputStream(new File("D:\\a.txt")));
User u = new User();
u.setName("张三");
outStream.writeObject(u);
outStream.close();
}
}

如果一个成员变量不想被序列化,可用关键字**transient **修饰:

//使用transient关键字修饰后不会被序列化
private transient String id;

7.配置文件:Properties类

这个类是集合体系下的类(uitl),基础map类。

7.1写文件

  • 写的时候要注意,api推荐使用 setProperty 方法而不是Properties类继承map的put方法,因为前者有类型检查,强制使用String类安全
  • 传入store方法的stream要注意,默认Java使用ISO 8859-1,不支持中文,所以要用字符流来写而不是字节流

7.2读文件

Properties类继承于map,所以用迭代器可遍历

7.3修改文件

修改Properties类对象后,要写入文件才能生效!

Demo:

package per.liyue.code.properities;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
public class ProperitiesMain {
public static void main(String[] args) throws IOException{
Properties p = new Properties(); WriteProperties(p); ReadProperties(p); UpdateProperties(p); }
public static void UpdateProperties(Properties p) throws IOException {
p.setProperty("张三", "000");
//必须要写进去才生效,但是会覆盖原来的
p.store(new FileWriter(new File("D:\\a.properties")), "这是一个属性文件例子");
}
public static void ReadProperties(Properties p) throws IOException, FileNotFoundException {
p.load(new FileReader(new File("D:\\a.properties")));
//注意,这里必须写明是Object类
Set<Entry<Object, Object>> entrys = p.entrySet();
for (Entry<Object, Object> entry : entrys) {
System.out.println("键:" + entry.getKey() + "值:" + entry.getValue());
}
}
public static void WriteProperties(Properties p) throws IOException {
p.setProperty("张三", "123");
p.setProperty("李四", "456");
p.setProperty("王五", "789");
p.store(new FileWriter(new File("D:\\a.properties")), "这是一个属性文件例子");
}
}

8.打印流

可以对System.out.println重新定位输出对象到文件

System.setOut(new PrintStream(new File("D:\\errorlog.txt")));
System.out.println("输出到文件");

异常的情况也是一样

try {
//...
} catch (Exception e) {
//追加模式,否则会每次覆盖原来的日志
PrintStream error = new PrintStream(new FileOutputStream("D:\\errorlog.txt"), true);
e.printStackTrace(error); }

9.转换流

InputStreamReader和OutputStreamReader是字符流FileReader和FileWriter的父类。用于字节流和字符流的转换。在网络编程中用的比较多。

  • InputStreamReader:字节流->字符流
  • OutputStreamWriter:字符流->字节流