今天简单的谈一谈Java的输入和输出吧,算是做一个总结概括
Java输入输出系统(input/output)简称Java I/O,Java的 I/O是通过Java.io包下的类和接口支持,其中最重要的是5个类,分别是File、Output Stream、Input Stream、Write、Reader及一个接口Serializable,且io操作必须捕获异常。
Java程序的所有读写操作都是通过流来实现的,根据组成流的不同,可以将其分为字节流和字符流,字节流是由字节组成的,主要用于处理二进制数据;字符流是由字符组成,主要用于处理文本的数据。
注意: Java中,一个字节有8b,一个字符由两个字节组成。
File类
java.io.File类不属于Java流系统,但是它对文件流进行辅助操作(箭头处表示常用方法)
**注意:**Windows中得使用“\”,当然也可以使用Linux中的“/”,因为“\”是”/“的转义符。
字节流
字节流是一般读取或写出8比特的字节数据,比如图片、音频之类的非文本数据。
input Stream一般有如下方法:
示例:
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
public class filedemo1 {
//io操作必须捕获异常
public static void main(String []args)throws IOException{
File file=new File("C:\\Users\\Desktop\\XX.txt");
InputStream input=new FileInputStream(file);
byte b[]=new byte[(int)file.length()];
int temp=0;
int len=0;
while((temp=input.read())!=-1)
{
b[len]=(byte)temp;
len++;
}
input.close();
System.out.println(new String(b));
}
}
outputStream一般有如下方法
示例:
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.FileOutputStream;
public class filedemo2 {
public static void main(String []args)throws IOException{
File file=new File("C:\\Users\\Desktop\\XX.txt");
OutputStream Output=new FileOutputStream(file);
String str="Hello World";
byte b[]=str.getBytes();
try{
Output=new FileOutputStream(file);
Output.write(b);
Output.close();
System.out.print("successful!");
}
catch(IOException e){
System.out.print("error");
e.printStackTrace();
}
}
}
字符流
使用字符输出流操作时,可以实现文件的追加功能,一般有两种方式:
1、使用Writer类的append()方法
2、是使用FileWriter的构造方法FileWriter(File file,boolean append),
即前者不能实现增加,同样的字符串只能够增加一次,后者可以实现增加,同样的字符串可以增加多次。
字节流是本文件本身直接操作的,不需要通过缓冲区;字符流是通过缓冲区操作的,即使用字节流时,就算没有执行close()方法,还是可以输出内容,但字符流若没有执行close()方法,则无法输出内容。
字符流若写操作完毕后,不将缓冲区内容刷新到文件,文件将为空,但可以使用flush()强制刷新到文件中。事实上执行close()方法会默认执行flush方法。
其他操作与字节流类似,故不赘述。
转换流
从继承上看它们都是属于字符流的,但它们主要是用来将已存在的字节流转换成字符流。转换时可以使用系统默认编码或指定编码。
基本流
为了减少程序开发人员,因频繁应用标准的输入输出设备,需要频繁地建立输入输出流对象的工作量,java系统预先定义好3个流对象,分别表示标准输出设备、标准输入设备和标准错误设备。他们分别是:
System.in :用于程序的输入; 对应外设为键盘
System.out:用于一般输出; 对应外设为屏幕
System.err:用于显示出错信息; 对应外设为屏幕
System 类的所有属性都是静态static的,调用时以类名System为前缀。上述3个流对象均为静态属性。
缓冲流
缓冲流包括BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter,分别是InputStream、OutputStream、Reader、Writer的子类,
看一个从键盘输入字节流,然后使用转换流转为字符流输出的示例
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
public class BufferDemo {
public static void main(String[]args){
String str="";
BufferedReader bin=null;
try{
bin=new BufferedReader(new InputStreamReader(System.in));
System.out.println("input :");
str=bin.readLine();
bin.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("the str:"+str);
}
}
内存操作流
ByteArrayInputStream是一个把字节数组作为源的输入流实现,它继承了InputStream类,且有close()、mark()等方法,但close()方法并没有实际操作,ByteArrayInputStream类的read方法重写了InputStream类的read方法,且没有抛出异常。
调用close()方法需要处理异常,可调用的close方法是没有任何操作的一个空方法,在关闭字节数组输入流后认可被调用,且不会产生任何IOException。
Scanner类
对象序列化
听老师说对象序列化是一个十分复杂的问题,今天就先简单的啃一啃这个硬骨头
对象序列化是将对象状态转换为可保持或传输的格式的过程,一般的格式是与平台无关的二进制流(字节序列),可以将这种二进制流持久的保存在磁盘上,也可传输到网络节点上,与对象序列化对应的是对象反序列化是指将这种二进制流读入,重新恢复到原来的对象。
对象序列化实际只适合较小的对象,即是轻量级的。
Java对象可以分为可序列化对象和不可序列化对象,若需要某个对象能够支持序列化机制,必须让它实现Serializable接口或者Externalizable接口,
1、Serializable接口定义如下
public interface Serializable{
}
惊奇的发现此接口是一个标记接口,实现该接口无须实现任何方法,仅仅表明该类对象是可序列化的
示例:定义一个可序列化对象
import java.io.Serializable;
public class Student implements Serializable{
private String name;
private int age;
private String school;
public student(){}
public student(String name ,int age,String school){
this.name=name;
this.age=age;
this.school=school;
}
public void tell(){
System.out.println("name:"+name+"age:"+age+"school:"+school);
}
}
若一个对象可以被序列化,则对象中的属性和类名可以被序列化,而方法(所有方法)和static和transient关键字修饰的属性则不能够被序列化
对象输出流ObjectOutputStream(序列化)
代码示例(将stu1和stu2的序列化对象保存到磁盘上)
import java.io.File;
import java.io.IOException;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class ObjectOut{
public static void main(String []args){
Student stu1=new Student("小名",12,"cuwt");
Student stu2=new Student("大名",13,"hfut");
File file=new File("objectout.txt");
FileOutputStream fout=null;
ObjectOutputStream oos=null;
try{
fout=new FileOutputStream(file);
oos=new ObjectOutputStream(fout);
oos.writeObject(stu1);
oos.writeObject(stu2);
oos.close();
}
catch (IOException e){
e.printStackTrace();
}
}
}
反序列化(ObjectInputputStream)就是将磁盘中的序列化对象输出给stu1和stu2,序列化和反序列化的读和写都是加上object的,这个要注意。
更多情况下使用集合保存多个对象,然后再对集合中的对象进行序列化和反序列化操作,因为需要知道文件中对象的先后顺序,否则转换数据类型时将会出错。
2、Externalizable接口
Externalizable接口接口声明的类对象可以选择需要序列化的内容,Externalizable接口定义了readExternal()和writeExternal()方法,实现该接口必须实现这两种方法,若没有在这两种方法内的属性,则表示不进行序列化操作,且必须提供无参构造方法,且readExternal必须按照writeExternal方法写入顺序来读取属性值。
虽然Externalizable接口可以选择需要的属性序列化,但其序列化过程明显要比Serializable接口实现困难的多,为此Java提供了transient关键字,若不需要对某个对象属性序列化,只需使用transient进行声明即可,它只能修饰属性成员变量,不能修饰方法和类。默认的序列化是深度序列化,静态成员不会被默认序列化。
3、若transient修饰,如何在不删除transient的情况下让其可以序列化?
在writeObject中调用ObjectOutputsream的defaultwriteObject()方法,该方法会执行默认的序列化机制。
为一个实现serializable接口的父类编写一个能够完全序列化的子类需要父类有一个无参构造方法,且子类需要先序列化自身,然后子类负责序列化父类的域。
小结
输入输出本来是不想看的,前一周草草的掠过一遍,现在再来复习一遍感觉还是挺好的,File、字节流、字符流、缓冲流、转换流、内存流、基本流、还有压轴的序列化。就在刚刚发生了一件很惊心动魄的事情,快要写完的时候突然按错了快捷键,导致博客编辑页面关闭了,后来战战兢兢的尝试重新点开,哎又恢复了,不得不佩服优快云的这种缓存机制之强大,不免让我联想到了一本神书张银奎写的《格蠹汇编》,书中的作者可惨多了,这本书中第一篇就讲述了作者也是写着写着博客突然刷新了页面,内容全没了,然后通过各种技术手段,重新从机器内存中的缓存中找到了丢失的资料等等,想想自己这本神书也只是看第一章而已,不免有些汗颜,如此神书确实得抽空看一下了。