具体的流的介绍:
-1- 字节流;
(1) 字节输入流:InputStream
java.io.InputStream抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。
- public int read(): 通过输入流的对象调用该方法,从输入流中读取一个字节。返回读取的字节值。虽然读取了一个字节,但是会自动提升为int类型。如果已经到达流末尾,没有数据可读,则返回-1。 (在应用字节流数组的情况下,常常用在for循环语句中作为终止条件)
- public int read(byte[] b): 通过输入流的对象调用该方法,并传入实参byte数组。从输入流中读取一些字节数,并将它们存储到字节数组 b中 。每次最多读取b.length个字节。返回实际读取的字节个数。如果已经到达流末尾,没有数据可读,则返回-1。
- public int read(byte[] b,int off,int len):从输入流中读取一些字节数,并将它们存储到字节数组 b中,从b[off]开始存储,每次最多读取len个字节 。返回实际读取的字节个数。如果已经到达流末尾,没有数据可读,则返回-1。
- public void close() :关闭此输入流并释放与此流相关联的任何系统资源。
说明:close()方法,当完成流的操作时,必须调用此方法,释放系统资源。因此,可以使用try-catch-finally结构
(2) 字节输出流:OutputStream
java.io.OutputStream抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。
- public void write(int b) :将指定的字节输出流。虽然参数为int类型四个字节,但是只会保留一个字节的信息写出。
- public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
- public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流末尾。
- public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
- public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
说明:close()方法,当完成流的操作时,必须调用此方法,释放系统资源。
public class FileStreamTest {
@Test
public void test(){
//1、创建File的实例对象
//2、创建相应的字节流
//3、读入和写出数据
//4、关闭资源
FileInputStream fis = null;
FileOutputStream fos = null;
try {
File srcFile = new File("Edney.jpg");//创建File对象
File destFile = new File("Edney_copy.jpg");
fis = new FileInputStream(srcFile);//创建字节流
fos = new FileOutputStream(destFile);
byte[] buffer = new byte[1024];//因为是字节流,所以创建byte[]数组。如果是创建字符流,则此处创建char数组
int len;
while ((len = fis.read(buffer))!=-1){
fos.write(buffer,0,len);
}//输入流读取结束返回-1为循环终止条件
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
System.out.println("Successfully copy!");
try {
fis.close();
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
字符流:利用char数组存储和输出流,适用于文本文件,不适用于非文本文件如jpg文件等。
字节流:利用byte数组的存入和输出流,通常可以完成非文本文件的操作。如果涉及文本文件的复制操作,也可以使用字节流。但读取中文字符时可能会出错。
因为中文字符正常UTF-8占用3个字节,若读取时出现拆开中文字节的情况,可能出现乱码。
gbk中,中文字符只占用2字节。
文本文件:.txt .java .c .cpp .py等
非文本文件:.doc .xls .jpg .mp3 .mp4 .avi 等
-2- 缓冲流的使用
(1)主要的缓冲流:
BufferedInputStream
BufferedOutputStream
BufferedReader
BufferedWriter
处理非文本文件的字节流;
BufferedInputStream read(byte[] buffer)
BufferedOutputStream write(byte[] buffer,0,len)
处理文本文件的字符流:
BufferedReader read(byte[] buffer)
BufferedWriter write(byte[] buffer,0,len)
前一篇博客中已总结过,字符流对应文本文件,字节流对应非文本文件,
(2)flush方法
为内置缓冲区的原因,如果FileWriter不关闭输出流,无法写出字符到文件中。但是关闭流的对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush() 方法了。
- flush() :刷新缓冲区,流对象可以继续使用。
- close():先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
注意:即便是flush()方法写出了数据,操作的最后还是要调用close方法,释放系统资源。
//关于缓冲流BufferedInputStream and BufferedOutputStream的使用
@Test
public void test1(){
File srcFile = new File("Edney.jpg");//创建File类的对象
File destFile = new File("Edney_copy.jpg");//字节流可以处理非文本文件的复制操作
FileInputStream fis = null;//创建输入流的对象
FileOutputStream fos = null;//创建输出流的对象
BufferedInputStream bis = null;//创建缓冲输入流的对象
BufferedOutputStream bos = null;//创建缓冲输出流的对象
try {
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//分别对各对象进行初始化。为了防止编译时异常,此处进行try-catch捕获异常。
byte[] buffer = new byte[5];//创建byte[]数组
int len;
while ((len = bis.read(buffer))!=-1){
bos.write(buffer,0,len);//写出缓冲输入流的对象锁读取的byte字节,长度为读取对象字节的长度
}
System.out.println("successfully copy!");
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
try {
if (bos!=null)
bos.close();//分别关闭bos与bis的资源,防止资源的泄露。
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
if (bis!=null)
bis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
-3- 转换流的使用:
(1)字符编码与字符解码:
字符编码:字符、字符串、字符数组--->字节、字节数组 由可见可懂的转化为不可懂的
字符解码:字节、字节数组--->字符、字符串 由不可懂的转化为可见可懂的
(2)转换流的作用:
实现字节与字符之间的转换。
public void test2(){
//将使用gbk格式的文件转换为使用utf-8字符集的文件存储
InputStreamReader isr = null;
OutputStreamWriter osw = null;
try {
File file = new File("WithYou.text");
File file1 = new File("WithYou_to_utf8");//转换后的文件
FileInputStream fis = null;
isr = null;
fis = new FileInputStream(file);//初始化输入流
isr = new InputStreamReader(fis,"GBK");//指定编码字符集为GBK
FileOutputStream fos = null;
osw = null;
fos = new FileOutputStream(file1);//初始化输出流
osw = new OutputStreamWriter(fos,"utf8");//指定输出流转化为utf8的字符集
char[] cBuffer = new char[1024];//因为讨论的是文件格式所以使用字符流,创建char数组。
int len;
while ((len=isr.read(cBuffer))!=-1){
osw.write(cBuffer,0,len);
}
System.out.println("finish");
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
try {
if (osw!=null)
osw.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
try {
if (isr!=null)
isr.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
数据流;
DataOutputStream:将内存中的基本数据类型和String类型的变量写出到具体的文件中
DataInputStream:将文件中保存的数据还原为基本数据类型和引用数据类型
-4- 对象流:
(1)常用API:
对象输入流:ObjectInputStream
对象输出流:ObjectOutputStream
(2)作用:
可以读写基本数据类型的变量、引用数据类型变量
序列化:使用ObjectOutputStream 将内存中的java对象保存在文件中。
反序列化;使用ObjectInputStream 将文件中的数据额或网络传输过来的数据还原为内存中的java对象
@Test
public void test() throws IOException {
//序列化
File file = new File("object.txt");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
//根据构造器,应当传入输出流OutputStream
// 因为file文件不是流对象,只是一个标识文件名称和路径的对象,此时需要对输出流进行创建,此处选择了匿名的方式创建。
oos.writeUTF("How pity that I didn't try to detain you that time.");
oos.flush();
oos.writeObject("Though I know it would not change anything.");//写object对象
oos.flush();
oos.close();
}
@Test
public void test1()throws IOException{
//反序列化
File file = new File("object.txt");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
String str = ois.readUTF();
System.out.println(str);
String str2 = null;
try {
str2 = (String)ois.readObject();//对写入的object对象进行读取,因为进行强转,所以需要try-catch捕获异常
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
System.out.println(str2);
ois.close();
}
}
自定义类实现序列化:
1、自定义类实现接口Serializable
2、要求自定义类声明一个全局常量 static final long serialVersionUID
若不声明该全局常量,系统会自动生成。但改变自定义类的时候,因为序列号会改变,可能会导致异常。
要求定义的类的各个属性也必须是可序列化的
@Test
public void test2() throws IOException{
//序列化
File file = new File("object1.dat");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
Person p1 = new Person("Qum",18);//自定义类实现接口,Serializable
oos.writeObject(p1);
oos.flush();
Person p2 = new Person("Dit",1024,new Account(1314));
oos.writeObject(p2);
oos.flush();
oos.close();
}
@Test
public void test3() throws IOException{
//反序列化
File file = new File("object1.dat");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Person person = null;
try {
person = (Person) ois.readObject();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
Person person1 = null;
try {
person1 = (Person)ois.readObject();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
System.out.println(person);
System.out.println(person1);
ois.close();
}
关于class Person与class Account
public class Person implements Serializable {
//自动生成序列常量
private String name;
private int age;
Account account;
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 Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age, Account account) {
this.name = name;
this.age = age;
this.account = account;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", account=" + account +
'}';
}
}
class Account implements Serializable{
static final long serialVersionUID = 32574967298758L;
private int balance;
public Account(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
@Override
public String toString() {
return "Account{" +
"balance=" + balance +
'}';
}
public void setBalance(int balance) {
this.balance = balance;
}
}
加上transient关键字则属性不可序列化。
静态变了不可序列化。
-5- 其他流(仅做了解)
1、标准输入输出流
System.in:标准输入流,默认为键盘输入
System.out:标准输出流,默认为控制台(显示器)输出
通过调用如下方法,修改输入流和输出流的位置
setIn(InputStream is)
setOut(PrintStream ps)
2、打印流