Java课笔记(第十五节)

本节内容:文件处理

本文大部分内容摘自:原文链接:https://blog.csdn.net/qq_44715943/article/details/116501936

一、什么是IO流

  • I : Input
  • O : Output

通过IO可以完成硬盘文件的读和写

二、IO流的分类


按照 流的方向 进行分类:
以内存作为参照物:

往内存中去:叫做输入(Input)。或者叫做读(Read)。
从内存中出来:叫做输出(Output)。或者叫做写(Write)。

通过这张图更好地理解。


按照 读取数据方式 不同进行分类:

 


1. 按照 字节 的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位。
这种流是万能的,什么类型的文件都可以读取。包括:文本文件,图片,声音文件,视频文件 等…

eg.
假设文件file1.txt,采用字节流的话是这样读的:
a中国bc张三fe
第一次读:一个字节,正好读到’a’
第二次读:一个字节,正好读到’中’字符的一半。
第三次读:一个字节,正好读到’中’字符的另外一半。

2. 按照 字符 的方式读取数据的,一次读取一个字符.
这种流是为了方便读取 普通文本文件 而存在的,这种流不能读取:图片、声音、视频等文件。只能读取 纯文本文件,连word文件都无法读取。

注意:
纯文本文件,不单单是.txt文件,还包括 .java、.ini、.py 。总之只要 能用记事本打开 的文件都是普通文本文件。

eg.
假设文件file1.txt,采用字符流的话是这样读的:
a中国bc张三fe
第一次读:'a’字符('a’字符在windows系统中占用1个字节。)
第二次读:'中’字符('中’字符在windows系统中占用2个字节。)

综上所述:流的分类:

输入流、输出流
字节流、字符流

 

三、IO流四大家族首领
字节流
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
字符流
java.io.Reader 字符输入流
java.io.Writer 字符输出流


注意:

四大家族的首领都是抽象类。(abstract class)
所有的流都实现了:
java.io.Closeable接口,都是可关闭的,都有 close() 方法。
流是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费(占用)很多资源。养成好习惯,用完流一定要关闭。
所有的 输出流 都实现了:
java.io.Flushable接口,都是可刷新的,都有 flush() 方法。
养成一个好习惯,输出流在最终输出之后,一定要记得flush()刷新一下。这个刷新表示将通道/管道当中剩余未输出的数据强行输出完(清空管道!)刷新的作用就是清空管道。
ps:如果没有flush()可能会导致丢失数据。

在java中只要“类名”以 Stream 结尾的都是字节流。以“ Reader/Writer ”结尾的都是字符流。


四、Java要掌握的流(16个)


文件专属:

  • java.io.FileInputStream(掌握)
  • java.io.FileOutputStream(掌握)
  • java.io.FileReader
  • java.io.FileWriter

转换流:(将字节流转换成字符流)

  • java.io.InputStreamReader
  • java.io.OutputStreamWriter

缓冲流专属:

  • java.io.BufferedReader
  • java.io.BufferedWriter
  • java.io.BufferedInputStream
  • java.io.BufferedOutputStream

数据流专属:

  • java.io.DataInputStream
  • java.io.DataOutputStream

标准输出流:

  • java.io.PrintWriter
  • java.io.PrintStream(掌握)

对象专属流:

  • java.io.ObjectInputStream(掌握)
  • java.io.ObjectOutputStream(掌握)

File文件类

  • java.io.File

Windows/Linux 小tips:
Windows:D:\Soft\QQ\Plugin
Linux:      D:/Soft/QQ/Plugin

注意: Windows各个文件之间分隔符为:” \ “;Linux各个文件之间分割符为:” / “
 

下面我将对上课所讲的内容进行详细描述:

1. 应用无法直接与硬件相交互,要通过操作系统。

2. 什么样的inputstream写就要用什么样的outputstream读。

java.io.FileReader

FileReader 文件字符输入流,只能读取普通文本。读取文本内容时,比较方便,快捷。

构造方法

FileReader(String fileName)    name为文件路径


FileReader(File file)    
方法

int read()    读取一个字符,返回值为该字符ASCII码;读到文件末尾返回-1


int read(char[] c)    读c数组长度的字节到c数组中,返回值为读到的字符个数;读到文件末尾返回-1


int read(char[] c, int off, int len)    从c素组off位置读len长度的字符到c数组中,返回值为读到的字符个数;读到文件末尾返回-1


long skip(long n)    跳过n个字符


void close()    关闭文件输入流
 

import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Scanner;
//FileNotFoundException是IOException的一个子类,
public class Main {

	public static void main(String[] args) throws IOException 
	{
		FileReader fileIn = new FileReader("d://1.txt");
		//假设我在文本中写 1hello
		
		Scanner in = new Scanner(fileIn);		
		int i = fileIn.read();//读取一个字符,此处为1
		
		String j = in.next();//读取一个字符串,此处为hello
		System.out.println(i);
		System.out.println(j);

	}

}

输出:

hello

49

注意:

1. 该输入流需要抛出两个异常,一个是FileNotFoundException(找不到文件),一个是IOException(文件内部可能为空),但由于FileNotFoundException是IOException的一个子类,我们只需要throws IOException即可。

2. 注意到有两种读取方式

int i = fileIn.read();//读取一个字符,此处为1
String j = in.next();//读取一个字符串,此处为hello

这两种读取方式是不同的。

第一行使用FileReader类和文件路径“d://1.txt”创建了一个文件读取器对象,它将打开并读取文本文件。这个对象被传递给Scanner对象,以便从文本文件中读取数据。Scanner类允许通过next()或nextLine()等方法遍历文本文件,并将其转换为程序内部的数据类型,比如字符串类型、整数类型等。

第二行使用了FileReader类的read()方法直接从文件中读取单个字符并返回其ASCII值作为int类型。可以在循环结构中使用这个方法来逐个读取所有的字符。

在读取大量文本时,使用Scanner方法较为常见,因为它支持按诸如空格或特定字符(例如逗号)分割文本内容,并且提供了许多方便的方法来处理各种数据类型。而使用read()方法则非常适合读取较小的文本文件,或者需要精细控制读取进程的情况。

java.io.PrintWriter

public class Main {

	public static void main(String[] args) throws FileNotFoundException {
     //使用PrintWriter写入文件

		PrintWriter out = new PrintWriter("d://1.txt");
		out.print("Hello, ");
		out.println("world!");

	}

	}

这段代码的作用是在文本文件1中写入Hello,world!。、

但是值得注意的是,该程序执行完之后并没有将Hello,world!写入到文件中,这是因为将更改写入到磁盘之前,需要先关闭文件流或手动调用flush()方法。否则,可能会出现数据丢失或无法正常保存数据的情况。

加上一句即可

out.close();

或者用try-catch,

public class Main {

	public static void main(String[] args) throws FileNotFoundException {
		// 使用PrintWriter写入文件
		try (PrintWriter out = new PrintWriter("d://1.txt")) {
			out.print("Hello, ");
			out.println("world!");
		} catch (IOException e) {
			System.err.println(e);
		}

	}

}

这个代码段使用了Java 7或更高版本中的新特性,称为"try-with-resources"语句。它允许您在代码块结束时自动关闭资源,而无需显式地调用close()方法或清理资源。

在这个例子中,PrintWriter对象被包含在try-with-resources块中。当代码块执行完成时,PrintWriter将自动关闭和释放资源,以便其他程序可以访问该文件。由于printWriter被包含在try-with-resources块中,因此你不需要手动关闭该流,即便发生异常也会自动关闭该流。

值得注意的是,在try-with-resources块之外创建的对象,需要在使用完后进行显式的关闭操作。例如,如果您使用FileOutputStream来写入文件,则仍然需要在try-with-resources块之外调用close()方法来关闭文件输出流并释放资源。

java.io.FileOutputStream

这是一种字节输出流。

构造方法
FileOutputStream(String name)    name为文件路径
FileOutputStream(String name, boolean append)    name为文件路径,append为true表示在文件末尾追加;为false表示清空文件内容,重新写入
FileOutputStream(File file)    
FileOutputStream(File file, boolean append)    append为true表示在文件末尾追加;为false表示清空文件内容,重新写入
方法
void write(int b)    将指定字节写入文件中
void write(byte[] b)    将b.length个字节写入文件中
void write(byte[] b, int off, int len)    将b素组off位置开始,len长度的字节写入文件中
void flush()    刷新此输出流并强制写出所有缓冲的输出字节
void close()    关闭文件输出流
 

实现把中国写进文本文件中

public class Main {

	//实现把中国写进文本文件中
	public static void main(String[] args) throws IOException {
		FileOutputStream fileOut = new FileOutputStream("D:\\2.txt");
		String s = "中国";//字符串存入汉字,一个汉字占用两个字节
		byte[] bt = s.getBytes();//定义一个字节数组,获取字符串的字节,存入字节数组
		fileOut.write(bt);//直接把整个字节数组写入到文件就好了
		fileOut.close();//使用后记得关闭哦
		
		
	}

}

java.io.FileInputStream

这是一种字节输入流

构造方法
FileInputStream(String name)    name为文件路径
FileInputStream(File file)    
方法
int read()    读取一个字节,返回值为该字节ASCII码;读到文件末尾返回-1
int read(byte[] b)    读b数组长度的字节到b数组中,返回值为读到的字节个数;读到文件末尾返回-1
int read(byte[] b, int off, int len)    从b素组off位置读len长度的字节到b数组中,返回值为读到的字节个数;读到文件末尾返回-1
int available()    返回文件有效的字节数
long skip(long n)    跳过n个字节
void close()    关闭文件输入流
 

实现把中国从文件中读取并输出

public class Main {

	//实现把中国从文件中读取并输出
	public static void main(String[] args) throws IOException {
		FileInputStream fileIn = new FileInputStream("D:\\2.txt");
		Scanner sc = new Scanner(fileIn);
		String s = sc.next();
		System.out.println(s);
        fileIn.close();
		
		
	}

}

输出:

中国

补充:学习对象流前言(选看,只需要知道用到Object输出流的类一定要implements Serializable)


1、java.io.NotSerializableException: Student对象不支持序列化!!!!

2、参与序列化和反序列化的对象,必须实现 Serializable 接口。

3、注意:通过源代码发现,Serializable接口只是一个 标志接口:

public interface Serializable {
}


这个接口当中什么代码都没有。

3.1 Serializable接口起什么作用呢?
起到 标识 的作用,标志的作用,java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇。
Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类自动生成一个序列化版本号。
4、序列化版本号有什么用呢?
区分两个类是否相同。

5、java语言中是采用什么机制来区分类的?
第一:首先通过 类名 进行比对,如果类名不一样,肯定不是同一个类。
第二:如果类名一样,再怎么进行类的区别?靠 序列化版本号 进行区分。
eg.
小明编写了一个类:com.baidu.java.bean.Student implements Serializable
小红编写了一个类:com.baidu.java.bean.Student implements Serializable
不同的人编写了同一个类,但“这两个类确实不是同一个类”。这个时候序列化版本就起上作用了。
对于java虚拟机来说,java虚拟机是可以区分开这两个类的,因为这两个类都实现了Serializable接口,都有默认的序列化版本号,他们的序列化版本号不一样。所以区分开了。(这是自动生成序列化版本号的好处)

6、这种自动生成序列化版本号有什么缺陷?
Java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号。

这种自动生成的序列化版本号缺点是:一旦代码确定之后,不能进行后续的修改,因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候java虚拟机会认为这是一个全新的类。(这样就不好了!)

7、最终结论:
凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。
这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类。

java.io.ObjectOutputStream

ObjectOutputStream:序列化对象

 

构造方法
ObjectOutputStream(OutputStream out)    out为OutputStream对象

方法


方法
参考API

public class ObjectOutputStreamTest01 {
    public static void main(String[] args) throws Exception{
        // 创建java对象
        Student s = new Student(1111, "zhangsan");
        // 序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("students"));

        // 序列化对象
        oos.writeObject(s);
        // 刷新
        oos.flush();
        // 关闭
        oos.close();
    }
}

实现往文件中写入学生对象,可能在文件是一串乱码,因为这是给机器看的。

public class Main {

	//实现往文件中写入学生对象,可能在文件是一串乱码,因为这是给机器看的。
	public static void main(String[] args) throws IOException {
		FileOutputStream fileOut = new FileOutputStream("D://2.txt");//在用Object输出流之前要先用文件输出流哦!
		ObjectOutputStream out = new ObjectOutputStream(fileOut);//用到Object输出流的类一定要implements Serializable
		
		Stu stu1 = new Stu(18,"xh",60);
		out.writeObject(stu1); //往文件中写入一个学生对象

        out.close();//后用先关闭
        fileOut.close();
		
		
	}

}
class Stu implements Serializable //用到Object输出流的类一定要implements Serializable
{
	int age;
	String name;
	int score;
	public Stu(int age, String name, int score) {
		this.age = age;
		this.name = name;
		this.score = score;
	}
	

}

java.io.ObjectInputStream

ObjectInputStream:反序列化对象

构造方法
ObjectInputStream(InputStream in)    in为InputStream对象
方法

参考API

public class ObjectInputStreamTest01 {
    public static void main(String[] args) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("students"));
        // 开始反序列化,读
        Object obj = ois.readObject();
        // 反序列化回来是一个学生对象,所以会调用学生对象的toString方法。
        System.out.println(obj);
        ois.close();
    }
}

实现输出文件中的学生对象信息 

public class Main {

	//实现输出文件中的学生对象信息
	public static void main(String[] args) throws IOException, ClassNotFoundException{
		
//				FileOutputStream fileOut = new FileOutputStream("D:\\2.txt");//在用Object输出流之前要先用文件输出流哦!
//				ObjectOutputStream out = new ObjectOutputStream(fileOut);//用到Object输出流的类一定要implements Serializable
//				
//				Stu stu1 = new Stu(18,"xh",60);
//				out.writeObject(stu1); //往文件中写入一个学生对象
//				
//  			    out.close();​
		FileInputStream fileIn = new FileInputStream("D:\\2.txt");//2.txt包含之前Object输出流写的一个stu1对象信息
		ObjectInputStream in = new ObjectInputStream(fileIn);
		
		Stu stu =(Stu)in.readObject();//读取对象,要强制类型转换
		//然后你就可以对该对象随意操作了
		stu.print();//打印一下该学生信息
		
		in.close();
		fileIn.close();
		
		
		
	}

}
class Stu implements Serializable //用到Object输出流的类一定要implements Serializable
{
	int age;
	String name;
	int score;
	public Stu(int age, String name, int score) {
		this.age = age;
		this.name = name;
		this.score = score;
	}
	void print()
	{
		System.out.println(this.age + " " + this.name + " " + this.score);
	}
	

}

输出:

18 xh 60

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值