前言
简要介绍Java中的字节流和字符流对应的类的使用。
目录
一、Java流的介绍
1、流的简介:
流(Stream)是计算机数据传输中一个相对比较抽象的概念,可以借助现实生活的各种流动的现象来理解。简单来说,流所具有的特点包括,总体以串行的方式一点点的移动,通常具有方向性和连续性,向某个特定的方向汇集。现实世界中的流,比如水流的特点正好与计算机数据传输的方式相对应,传输的数据可以是文字、视频、音频等等,都是一点点的从一端传输到另一端。因此就用流来代替计算机中数据传输的现象。
2、流的分类
可以有两种分类方式:
(1)、按流的方向:
分为输入(读)和输出(写)流
(2)、按读取数据的不同方式:
分为字节流和字符流。字节流一次取一个字节,什么文件都可以读取。字符流一次读取一个字符,只能读取普通txt文本(word文档也不行)。
3、Java中的流
Java中的流以对象的方式存在,都放在java.io包下
Java 流的四大家族:
java.io.InputStream:字节输入流
java.io.OutputStream:字节输出流
java.io.Reader:字符输入流
java.io.Writer:字符输出流
以上这四大家族全是抽象类,都有对应的具体类。
它们都实现了java.io.Closable接口,因此都有close( )方法释放资源。
所有的输出流都实现了Flushable接口,都有flush()方法来刷新,以便能强制输出,清空输出流。
4、java.io包下的六大流
都是具体类,都能直接创建对象。要看它们继承的是哪个家族,就看结尾是哪个抽象类。如 FileInputStream就是继承的InputStream。InputStreamReader就是继承的Reader.
文件专属:
FileInputStream、FileOutputStream、FileReader、FileWriter
带有缓冲区的流:
BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
数据专属流:
DataInputStream、DataOutputStream
标准流:
PrintSream、PrintWriter
对象专属流:
ObjectInputStream、ObjectOutputStream
转换流(将字节流转换为字符流):
InputStreamReader、OutputStreamWriter
二、文件专属流
流创建和关闭的时候都有异常。
1、FileInputStream
文件字节输入流(FileInputStream)对象的创建要调用有参构造方法传入一个相对路径(在IDEA中,它的相对路径是从项目下开始)或者绝对路径。
import java.io.*;
import java.util.Arrays;
public class Test01 {
public static void main(String[] args) {
//创建文件字节输入流对象
FileInputStream fis = null;
try {
fis = new FileInputStream("streamtest.txt");
//有read()方法读取数据,读取到的是字符的编码。当不传入参数时,一次读取一个字节。
//可传入一个byte[]数组来接收读取的字节。
//该方法会报IO异常
int readInfo = fis.read();
System.out.println(readInfo);//104
//传入byte[]数组时,read()的返回值是读取的元素个数
//注意,文件指针此时已经移动了一个字节
byte b[] = new byte[15];
int readCount = fis.read(b);
System.out.println(readCount);//11
System.out.println(Arrays.toString(b));//[101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 0, 0, 0, 0]
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
//一定要记得关闭流
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
有read( )方法去读取数据,读取到的是字符的编码。当不传入参数时,一次读取一个字节。可传入一个byte[]数组来接收读取的字节。read( )方法会报IO异常,传入byte[]数组时,read()的返回值是读取的元素个数。注意,文件指针读取时会移动位置,也就是说下一次读取时,将会从文件指针指向的位置开始读。
FileInputStream还有int available( )和long skip( long n)方法。
available( )方法返回文件中剩余的没有读到的字节数量。
skip( )方法可用来跳过几个字节不读。
2、FileOutputStream
该类对象使用write( )方法来输出。注意,输出流每输出一次都要使用flush( )方法刷新一下,write( )和flush( )方法都有异常需要处理。
import java.io.*;
import java.util.Arrays;
public class Test02{
public static void main(String[] args) {
FileOutputStream fos = null;
//创建文件字节输出流
try {
fos = new FileOutputStream("streamtest.txt");
//write()方法中可传入一个整形数字,一个byte[]数组,还可以指定从数组哪个部分开始,和输出的元素个数
fos.write(100);//UTF-8编码下是d
//使用write()方法写入时,没有该文件会创建一个新的文件再写入。
// 如果文件中原本有内容会清空后再写入。如果想要追加写入就要使用FileOutputStream的另一个构造方法
//FileOutputStream(String path,boolean append),append为true代表追加写入
//在连续使用write()方法时由于文件指针在移动,因此是在上一次写入的位置之后追加写入
byte[] b = new byte[]{85,86,87,88,89,90};//UTF-8编码下是dUVWXYZ
fos.write(b);//
fos.write(b,2,2);//表示从下表为2的元素开始写入,一共写进去两个元素
//记得刷新
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(fos != null){
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
write()方法中可传入一个整形数字,一个byte[]数组,还可以指定从数组哪个部分开始,和输出的元素个数.
使用write()方法写入时,没有该文件会创建一个新的文件再写入。如果文件中原本有内容会清空后再写入。如果想要追加写入就要使用FileOutputStream的另一个构造方法:FileOutputStream(String path,boolean append),append为true代表追加写入
在连续使用write()方法时由于文件指针在移动,因此是在上一次写入的位置之后追加写入。
3、FileReader和FileWriter
文件字符输入(输出)与文件字节输入(输出)流的主要区别在于,它们除了可以传一个byte[ ]数组用来读(写)外,还可以传入一个char[ ] 数组来读(写)。
import java.io.*;
public class Test03 {
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try {
fw = new FileWriter("streamtest.txt");
//传入一个char数组写
char[] c1 = new char[]{'n','i',',','h','a','o'};
fw.write(c1);//ni,hao
//还可以直接传字符串写,也可以指定读(写)的开始下标和长度
fw.write(c1,1,2);//ni,haoi,
fw.write("hello");//ni,haoi,hello
fw.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(fw != null){
try {
fw.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
三、带有缓冲区的流、数据专属流和标准流
1、带有缓冲区的流
就是BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter这四种。顾名思义,就是该流会自己创建一个缓冲区来存放读取的元素,不用在传入一个数组了。
不过该构造方法需要传入对应的四大家族的对象,如BufferInputStream的构造方法需要传入一个InputStream类型的参数。一般把这种参数就是流的特殊流,叫做包装流或处理流,把作为参数传递的叫做节点流。
关闭时,只需要关闭包装流,关闭了包装流会自动关闭节点流。
import java.io.*;
public class Test04{
public static void main(String[] args) {
BufferedReader br = null;
BufferedWriter bw = null;
try {
//BufferedWriter需要传入一个Writer类型的参数
bw = new BufferedWriter(new FileWriter("streamtest.txt"));
bw.write("利用write( )方法写入\n");
bw.write("换一行写入\n");
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(bw != null){
try {
bw.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
try {
br = new BufferedReader(new FileReader("streamtest.txt"));
//readLine()方法读一行
String info = br.readLine();
System.out.println(info);//利用write( )方法写入
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
throw new RuntimeException(e);
} finally{
if(br != null){
try {
br.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
2、数据流
可连数据类型一起写入文件,但是用该流写进去数据的文件为特殊文件,记事本没法读取。只能用DataInputStream去读出来。数据流也是包装流,需要传入一个对应的InputStream类型的参数和OutputStream类型的参数。
import java.io.*;
public class Test05 {
public static void main(String[] args) {
DataInputStream dis = null;
DataOutputStream dos = null;
try {
dos = new DataOutputStream(new FileOutputStream("datatest.txt"));
//利用write+类型()的方式写入数据
dos.writeInt(1);
dos.writeBoolean(true);
dos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
throw new RuntimeException(e);
} finally{
if(dos != null){
try {
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
dis = new DataInputStream(new FileInputStream("datatest.txt"));
//利用read+类型()的方式读=数据
int i = dis.readInt();
System.out.println(i);//1
boolean j = dis.readBoolean();
System.out.println(j);//true
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if(dis != null){
try {
dis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
3、标准流
标准输出流不需要手动刷新和关闭。
mport java.io.*;
public class Test06 {
public static void main(String[] args) {
PrintStream ps = System.out;//向控制台输出
ps.println("haha");//haha
//改变输出方向
try {
ps = new PrintStream(new FileOutputStream("streamtest.txt"));
System.setOut(ps);
System.out.println("向该文件输出");//不再在控制台输出
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
四、对象专属流
对象专属流是用来将对象向文件中写和读取的。对象的存和取有个特殊的名称,叫序列化和反序列化。从内存向硬盘输出对象叫做序列化,从硬盘向内存读对象就是反序列化。
ObjectOutputStream和ObjectInputStream也是需要传入节点流,使用对应的writeObject( )和readObject( )去写和读对象,其中readObject( )方法返回一个Object类型的对象。但是要求改类要继承Serializable接口,该接口只是一个标志,没有任何方法。用于JVM看到该标志后生成一个序列化版本号,主要是针对属性。一旦该类有修改,序列化版本号就会不同。
import java.io.*;
public class Test07 {
public static void main(String[] args) {
ObjectInputStream ois = null;
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("objectstream.txt"));
oos.writeObject("object1");//存多个对象用集合
} catch (IOException e) {
e.printStackTrace();
}finally{
if(oos != null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
在属性类型前添加transient关键字可以让该属性没有序列化版本号,不过不参与序列化也就取不出来 。
可手动序列化版本号:
格式:
private static final long serialVersionUID = 编写的数字;
五、File类
File是文件和文件夹路径的抽象表示,继承自Object,它不是用来完成文件的读和写的,主要是与文件相关的一些操作。可以当做路径传入流的构造方法中。
它有几个常用方法:
public boolean exists( ):判断该文件是否存在
public boolean createNewFile( ):不存在文件时,创建该文件
public boolean mkdir( ):创建指定目录
public boolean mkdirs( ):创建指定路径上的一系列目录
public String getParent( ):获取上一级目录名,如果没有指定上一级目录名,返回null
public String getAbsoluteFile( ):获取绝对路径
publlic String getName( ):获取文件名
public long lastModified( ):该文件最后一次修改时间(获取的是从标准基准时间到当前系统时间的总毫秒数)
public File[ ] listFiles( ):获取当前目录下所有的子文件夹
import java.io.File;
import java.io.IOException;
public class Test08 {
public static void main(String[] args) {
File f1 = new File("streamtest.txt");
System.out.println(f1.exists());//true
//在文件不存在时创建文件,该方法会报异常
try {
System.out.println(f1.createNewFile());//false
} catch (IOException e) {
e.printStackTrace();
}
File f2 = new File("BlogTest03");
//如果不存在创建该文件夹
if(!f2.exists()){
f2.mkdir();
}
//获取上一级路径
System.out.println(f2.getParent());//
//获取该文件绝对路径
System.out.println(f2.getAbsoluteFile());//D:\Javatest\BlogTest03
//获取当前目录下所有的子文件
File[] fs2 = f2.listFiles();
System.out.println(fs2.length);//0,当前目录为空
}
}
如有错误,希望能批评指正,不胜感激。