文章目录
- 数据源
- 流的分类
- IO节点流
- 四大抽象类
- IO读取文件的标准步骤
- 相关方法详解
- 实战1:字节数据的读取(FileInputStream)
- 实战1_3:字节数据的拷贝
- 实战2: 字符数据的读取FileReader
- 实战2_1:字符数据的写出FileWriter
- 实战2-2:字符数据的拷贝
- 实战总结
数据源
定义:提供原始数据的媒介(也就是存放数据的地方)。可以是数据库、内存、程序、文件以及网络连接和IO设备
注:一切以程序为中心。文件–>程序(输入流);程序–>文件(输出流)
下面是关于Java输入流和输出流的常用实现类,也是我接下介绍IO主要内容,可以在这里大致扫一眼,混个眼熟就行啦!因为这也不是一下子就能记住的,等你看完下面几个章节的内容相信就可以将下面的图记个差不多啦
流的分类
- 按流向分类:
输入流:数据源到程序(InputStream、Reader)读进来
输出流:程序到目的地(OutStream、Writer)写出去
- 按功能分类:(是否直接操作数据源)
节点流:可以直接从数据源或目的地中读写数据
处理流(包装流):不直接连接到数据源和目的地,是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。
注:节点流是处于io操作的第一站,所有操作必须通过他来进行。
- 按照数据分类
字节流:按照字节读取数据(InputStream、OutputStream)
字符流:按照字符读取数据(Reader、Writer)
- 注:操作字符:即我们可以看懂的纯文本,汉语、英语。由于要让计算机认识,所以字符流的底层还是基于字节流的操作(自动的在码表中进行转换)。
IO节点流
- 字节流:按照字节读取数据(音频、视频、word、excel)inputStream、outputStream
- 字符流:按照字符读取数据(处理的数据为文本,字符时)Reader、Writer
注:能使用字符流的地方一定可以使用字节流处理,但是可以使用字节流的地方不一定可以使用字符流来处理。
要知道的:
- 在计算机里面的文件是存储在硬盘上,Java虽然需要和文件打交道,但是它是不可以直接对硬盘进行操作,而是通过虚拟机和我们的操作系统进行交互(向操作系统进行申请),所以File代表的是Java和文件的一种联系.。
- java虚拟机无权调用垃圾回收机制,只能通知操作系统可以释放资源了
四大抽象类
即:InputStream、OutputStream、Reader、Writer(心细的你可能会发现他们是上图中介绍的节点流中的当源头为文件时的主要内容。)
这四个类属于抽象流类,不能在程序中直接实例化使用,可以使用其派生的类。
IO读取文件的标准步骤
- 创建源(选择要搬家的房子)
- 选择流(选择搬家公司)
- 具体操作(选择搬家方式)
- 释放资源(打发搬家公司走人)
文件、java程序和IO流之间的关系如下:(此过程灰常之重要,要理解记忆)
1. InputStream:字节输入流的父类,数据单位为字节
//从输入流读取数据的下一个字节。
abstract int read()
//从输入流读取一些字节数,并将它们存储到缓冲区 b 。(返回的读取的字节数)
int read(byte[] b)
//关闭此输入流并释放与流相关联的任何系统资源。
void close()
2. OutputStream:字节输出流的父类,数据单位为字节
注:在我们操作IO的时候内部会有一个类似于缓冲器,当这个缓冲器达到一定数值才会将里面的数据缓冲出去,如果没有达到指定数值,则缓冲器里面的数据则会驻留在内存中;此时为了避免数据能够缓冲出去,需要强制刷新flush或者关闭资源也会强制刷新。
//写入字符数组的一部分。
void write(char[] cbuf, int off, int len)
//写一个字符
void write(int c)
//写一个字符串的一部分。
void write(String str, int off, int len)
//刷新流。
void flush()
//关闭流,先刷新。
void close()
3. Reader:字符输入流的父类,数据单位为字符
//读一个字符
int read()
//将字符读入数组。
int read(char[] cbuf)
//关闭流并释放与之相关联的任何系统资源。
abstract void close()
4. Writer:字符输出流的父类,数据单位为字符
//写入一个字符数组。
void write(char[] cbuf)
//写入字符数组的一部分。
abstract void write(char[] cbuf, int off, int len)
//写一个字符
void write(int c)
//写一个字符串
void write(String str)
//写一个字符串的一部分。
void write(String str, int off, int len)
//刷新流。
abstract void flush()
//关闭流,先刷新。
abstract void close()
相关方法详解
你要知道的:在Java7中,InputStream被定义为一个抽象类,相应的,该类下的read()方法也是一个抽象方法,这也就意味着必须有一个类继承InputStream并且实现这个read方法。 可以看出下面在这三个方法中,只有参数列表为空的read方法定义为抽象方法,这也就意味着在直接继承自InputStream的所有子类中,必须重写这个方法.
//从输入流读取数据的下一个字节。
abstract int read()
//从输入流读取一些字节数,并将它们存储到缓冲区 b 。
int read(byte[] b)
//从输入流读取最多len字节的数据到一个字节数组。
int read(byte[] b, int off, int len)
1.常用方法 read()
/**
*方法详解:
* (1)从输入流读取数据的下一个字节。 值字节被返回作为int范围0至255 。
* (2)如果没有字节可用,因为已经到达流的末尾,则返回值-1 ;该方法阻塞直到输入数据可用,检测到流的结尾,或抛* * 出异常。
* (3)一个子类必须提供这个方法的一个实现。
* (4)返回的结果:数据的下一个字节,如果达到流的末尾, -1 。
*/
public abstract int read()throws IOException
通俗大白话版本:
注1:这个方法的返回值是int类型.
注2:这个方法每次从数据源中读取一个byte并返回
要知道的:一个byte表示8个二进制位,那么这8个二进制位就是一个0-255之间的十进制数字,实际上在Java中,byte就是一个0-255之间的整数,而将从文件中读取的二进制转化成十进制这一过程是由read()方法完成的。 也就是说,read()这个方法完成的事情就是从数据源中读取8个二进制位,并将这8个0或1转换成十进制的整数,然后将其返回。
2.常用方法 read(byte[] b)
/*
* (1)从输入流读取一些字节数,并将它们存储到缓冲区b 。 实际读取的字节数作为整数返回。(返回值为23则说明读取* 了23个字节)
* (2)该方法阻塞直到输入数据可用,检测到文件结束或抛出异常。
* (3)如果b的长度为零,则不会读取字节并返回0 ; 否则,尝试读取至少一个字节。 如果没有字节可用,因为流在文件 * 末尾,则返回值-1 ; 否则,读取至少一个字节并存储到b 。
*
* 第一个字节读取存储在元素b[0] ,下一个字节存入b[1]等等。 读取的字节数最多等于b的长度。 令k为实际读取的字* 节数; 这些字节将存储在元素b[0]至b[ k -1] ,使元素b[ k ]至b[b.length-1]不受影响。
* 该read(b)用于类方法InputStream具有相同的效果为:
* 方法1:read(b, 0, b.length)
* 方法2:read(b,0,k)
*/
public int read(byte[] b)
throws IOException
通俗大白话版本
注1:这个方法使用一个byte的数组作为一个缓冲区,每次从数据源中读取和缓冲区大小(二进制位)相同的数据并将其存在缓冲区中。读取的字节数最多等于数组的个数当然byte数组中存放的仍然是0-255的整数,(将二进制转换为十进制这个过程仍然是read方法实现的。 )
注2:虽然我们可以指定缓冲区的大小,但是read方法在读取数据的时候仍然是按照字节来读取的。在utf-8等变长编码中,一个复杂字符(比如汉字)所占字节往往大于1,并且长度往往是不固定的。(参照UTF-8编码规则)按照字节读取数据会将字符割裂,这就导致我们在使用read(byte[] b)方法读取文件时,虽然指定了缓冲区的大小,但是仍然会出现乱码。
下面的“标尺,小车”是为了方便记忆所给的描述,感觉啰嗦的话可以忽略
实战1:字节数据的读取(FileInputStream)
//标准版
/**
* 1.创建源
* 2.选择流(字节输入流:InputStream)
* 3.操作(读数据)
* 4.关闭资源
* @描述:按照每个字节的进行读取(标准版)
* 操作工具:标尺
*/
public class IoTest02 {
public static void main(String[] args) {
//1.创建源
File file = new File("E:/Java workspace/gui_gu/src2/com/sxt/IO/test.txt");
//2.选择流(文件字节输入流)
InputStream in = null;
try {
in = new FileInputStream(file);
//3.操作(读取数据)
int temp;//标尺:操作之前需要判断源文件是否有字节可读
try {
while((temp = in.read())!=-1){
System.out.println((char)temp);
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally{
//4.关闭资源
try {
if(in != null){
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
实战练习2:按照指定字节数组的形式进行数据的读取
/**
* 1.创建源
* 2.选择流(字节输入流:InputStream)
* 3.操作(读取数据)
* 4.关闭
* @描述:将数据按照字节数组的形式进行读取(升级版推荐使用)
* 操作工具:小车(byte[])、标尺
*/
public class IoTest03 {
public static void main(String[] args) {
//1.创建源
File file = new File("E:/Java workspace/gui_gu/src2/com/sxt/IO/test.txt");
//2.选择流(文件字节输入流)
InputStream in = null;
try {
in = new FileInputStream(file);
//3.操作(读取数据)
//3.1缓冲容器(以指定字节数的形式存放进去)(准备小车和标尺)
byte[] flush = new byte[3];//小车:将读取的数据存放在这里面
int len = -1;//标尺:用来判断是否还有数据可读
try {
//3.2开始读取数据并装进刚才准备好的小车中(flush字节数组)
while((len = in.read(flush)) != -1){
//字节数组-->字符串(解码)(将小车中的数据进行包装并显示)
String str = new String(flush,0,len);
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally{
if(in != null){
try {
//4.关闭
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileInputStream
定义:通过字节的方式读取文件,可以读取任意类型的文件。(图片、视频等);如果是全字符读取可以考虑FileReader
FileOutputStream
定义:通过字节的方式写出或追加数据到文件,适合所有类型的文件(图片、视频等);如果是全字符可以考虑FileWriter
实战练习3:通过字节的方式写出或追加数据到文件
/**
* 1、创建源
* 2、选择流(字节输出流:OutputStream)
* 3、操作(写出)
* 4、关闭
* @描述:以字节的方式写出数据到文件
* 操作工具:小车(byte[])、要写出的数据
*/
public class OutTest {
public static void main(String[] args) {
//1、创建源(如果源文件不存在则会自动创建)
File dest = new File("destination.txt");
OutputStream out = null;
try {
//2、选择流(文件字节输出流)
//out = new FileOutputStream(dest,true);追加写入
out = new FileOutputStream(dest);
//3.操作(写出)
//3.1准备要写出的数据,并放进一个字节数组中
String str = "Believes Yourself!";//准备数据
byte[] b = str.getBytes();//将数据放进小车中
try {
//3.2:开始写出输入(将装满数据的小车写出)
out.write(b);
//3.3为了防止数据驻留在内存,每次写出后可以flash一下哦
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally{
//关闭
if(out != null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
实战1_3:字节数据的拷贝
package com.sxt.IO;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class Copy {
/**
* 1、创建数据源头和目的源
* 2、选择流:字节输入流和输出流(InputStream、OutputStream)
* 3、操作:读取操作和写出操作
* 4、关闭:先打开的后关闭
* 操作工具:小车(byte[])、标尺
*@描述:通过文件输入流和文件输出流完成文件的复制操作
* @param args
*/
public static void main(String[] args) {
File src = new File("src.txt");
File des = new File("des.txt");
copy(src,des);
}
public static void copy(File src,File des){
//2.1、选择输入流
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(src);
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
try {
//2.2、选择输出流
out = new FileOutputStream(des);
//3.1、文件的读取操作(准备小车和标尺)
int tem;//标尺:判断是否还有可读的数据
byte[] flush = new byte[1024];//小车:用来存放读取的数据
try {
//3.2、文件的读取操作(将数据进行读取操作并放进小车中)
while((tem = in.read(flush))!=-1){
//3.3、文件的写出操作(将装满数据从小车进行写出操作)
out.write(flush,0,tem);
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally{
if(out != null){
try {
//4.1、关闭资源
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(in != null){
try {
//4.2、关闭资源
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
实战2: 字符数据的读取FileReader
package com.sxt.IO;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
/**
* 1.创建源
* 2.选择流(字符输入流:Reader)
* 3.操作
* 4.释放
* @描述:通过字符输入流来完成数据的读取
* 操作工具;标尺、小车(char[])
*/
public class ReaderTest {
public static void main(String[] args) {
//1.创建源
File file = new File("hello.txt");
//2.选择流
Reader reader = null;
try {
reader = new FileReader(file);
//3.读取数据(操作):当读取的数据不存在时返回-1
int len;//标尺
char[] flush = new char[1024];//小车
while((len = reader.read(flush))!=-1){
//字符数组-->字符串(打包整理)
String str = new String(flush,0,len);
System.out.println(str);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
//4.关闭资源
try {
if(reader != null){
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
实战练习6:通过使用字符输出流里来进行数据的写出
package com.sxt.IO;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
/**
* 1.准备源
* 2.选择流(字符输出流:Writer)
* 3.写出操作
* 4.关闭
* @描述:通过字符输出流完成数据的写出
* 操作工具:小车(char[])、要写出的数据
*/
public class WriterTest {
public static void main(String[] args) {
//1.准备源
File des = new File("des.txt");
//2.选择字符输出流
Writer writer = null;
try {
writer = new FileWriter(des);
} catch (IOException e) {
e.printStackTrace();
}
//3.读取操作
//3.1准备要写出的数据
String data = "我能行!加油!";
//3.2准备小车
char[] flush = new char[1024];
//3.3将数据放进小车中
flush = data.toCharArray();
try {
//3.3读取操作
writer.write(flush);
//3.4flush操作
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(writer != null){
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
实战2_1:字符数据的写出FileWriter
package com.sxt.IO;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
/**
* 1.创建源
* 2.选择流(字符输出流)
* 3.操作
* 4.释放
* @描述:按照每个字节的进行读取
* 和字节输出流的差别:选择流的不同、小车的类型不同
*/
public class ReaderTest {
public static void main(String[] args) {
//创建源
File file = new File("hello.txt");
//选择流
Reader reader = null;
try {
reader = new FileReader(file);
//读取数据(操作):当读取的数据不存在时返回-1
int len;
char[] flush = new char[1024];
while((len = reader.read(flush))!=-1){
//字符数组-->字符串
String str = new String(flush,0,len);
System.out.println(str);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
//关闭资源
try {
if(reader != null){
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
实战2-2:字符数据的拷贝
package com.sxt.IO;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
/**
* 1.准备源(数据源和目的源)
* 2.选择流(字符输入流和字符输出流:Reader、Writer)
* 3.操作
* 4.关闭(先打开的后关闭)
* @描述:通过字符输出流和字符输入流来完成文件的复制
* 操作工具:小车(char[])、标尺
*/
public class Copy_read_writer {
public static void main(String[] args) {
//1.准备源
File src = new File("src.txt");
File des = new File("des.txt");
//2.选择流
Reader reader = null;
Writer writer = null;
try {
reader = new FileReader(src);
writer = new FileWriter(des);
//3.操作
//3.1准备小车
char[] flush = new char[1024];
//3.2准备标尺
int len;
//3.3读取数据并装进小车
while((len = reader.read(flush))!= -1){
//3.4将装满数据的小车进行写出操作
writer.write(flush);
//3.4flush操作
writer.flush();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//4.关闭
if(writer != null){
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
实战总结
- 以上处理的流都有一个共同特点:即源头都是文件(FileInputStream、FileOutputStream)
- 源头处于硬盘上,而JVM是无法直接访问硬盘上的资源,必须借助操作系统;因此操作完成后要释放资源