java----IO流

来源:互联网 发布:淘宝哪里申请商标认证 编辑:程序博客网 时间:2024/06/06 12:32
IO流
一、对象的序列化存储
ObjectInputStreamObjectOutputStream:对象的序列化(持久化)存储。
void writeBoolean(boolean val)           写入一个 boolean 值。 void writeByte(int val)           写入一个 8 位字节。 void writeBytes(String str)           以字节序列形式写入一个 String。 void writeChar(int val)           写入一个 16 位的 char 值。 void writeChars(String str)           以 char 序列形式写入一个 String。 protected  void writeClassDescriptor(ObjectStreamClass desc)           将指定的类描述符写入 ObjectOutputStream。 void writeDouble(double val)           写入一个 64 位的 double 值。 void writeFields()           将已缓冲的字段写入流中。 void writeFloat(float val)           写入一个 32 位的 float 值。 void writeInt(int val)           写入一个 32 位的 int 值。 void writeLong(long val)           写入一个 64 位的 long 值。
具体查阅Api文档。
write(int val);writeInt(int val);的区别:前者写入的是4个8位的低8位。后者写的是4个8位
writeObject();:写的是对象。
void writeObject(Object obj)           将指定的对象写入 ObjectOutputStream。 
:写入对象
import java.io.*;class Person{String name;int age;static String country = "cn";Person(String name,int age,String country){this.name = name;this.age = age;this.country = country;}public String toString(){return name+":"+age+":"+country;}}class Demo{public static void main(String []args) throws Exception{writeObj(new Person("lisi",20,"en"));}public static void writeObj(Person p) throws IOException{ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Info.txt"));oos.writeObject(p);oos.close();}}

分析:运行时报了异常,查看ApiNotSerializableException没有序列化异常。当实例需要具有序列化接口时,抛出此异常。序列化运行时或实例的类会抛出此异常。参数应该为类的名称。也就是说要实现Serializable接口。查阅Api,发现Serializable没有方法。这种接口称为标记接口。
修改
import java.io.*;class Person implements Serializable{String name;int age;static String country = "cn";Person(String name,int age,String country){this.name = name;this.age = age;this.country = country;}public String toString(){return name+":"+age+":"+country;}}class Demo{public static void main(String []args) throws Exception{writeObj(new Person("lisi",20,"en"));}public static void writeObj(Person p) throws IOException{ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Info.txt"));oos.writeObject(p);oos.close();}}

写入成功,但写入的其实是二进制文档,记事本解码会出现乱码。
:读取对象
import java.io.*;class Person implements Serializable{String name;int age;static String country = "cn";Person(String name,int age,String country){this.name = name;this.age = age;this.country = country;}public String toString(){return name+":"+age+":"+country;}}class Demo{public static void main(String []args) throws Exception{//writeObj(new Person("lisi",20,"en"));readObj();}public static void writeObj(Person p) throws IOException{ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Info.txt"));oos.writeObject(p);oos.close();}public static void readObj() throws Exception{ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Info.txt"));Person p = (Person)ois.readObject();System.out.println(p.toString());}}

读取成功,但是发现country值没有修改成功。countrystatic修饰存在方法区内,而ObjectOutputStream写入的是堆内存中的数据。两个区域处不同位置,所以static修饰的变量不能被序列化。若要使非静态成员也不序列化,可以在变量的类型前加transizent
import java.io.*;class Person implements Serializable{String name;transient int age;static String country = "cn";Person(String name,int age,String country){this.name = name;this.age = age;this.country = country;}public String toString(){return name+":"+age+":"+country;}}

特殊之处
import java.io.*;class Person implements Serializable{String name;int age;static String country = "cn";Person(String name,int age,String country){this.name = name;this.age = age;this.country = country;}public String toString(){return name+":"+age+":"+country;}}
当把name私有以后。再运行。

因为Person类的UID变化了,类改变了,UID也会跟着改变。在读取的时候会比较“.txt”上的UIDPreson类上的UID,两个不同时就会抛出异常。解决办法是写入一个固定UID
加上static final long serialVersionUID = 42L;
import java.io.*;class Person implements Serializable{private String name;int age;static final long serialVersionUID = 42L;static String country = "cn";Person(String name,int age,String country){this.name = name;this.age = age;this.country = country;}public String toString(){return name+":"+age+":"+country;}}
二、其他流对象
PipedInputStreamPipedOutputStream:管道流,输入输出可以直接进行连接,通过结合线程使用。
import java.io.*;class Demo{public static void main(String []args) throws Exception{PipedOutputStream pos = new PipedOutputStream();PipedInputStream pis = new PipedInputStream();pos.connect(pis);Write w = new Write(pos);Read r = new Read(pis);new Thread(w).start();new Thread(r).start();}}class Write implements Runnable{private PipedOutputStream pos;Write(PipedOutputStream pos){this.pos = pos;}public void run(){try{System.out.println("正在写入,请稍后...");Thread.sleep(3000);pos.write("我来了".getBytes());System.out.println("写入完毕");pos.close();}catch(Exception e){System.out.println("写入失败");}}}class Read implements Runnable{private PipedInputStream pis;Read(PipedInputStream pis){this.pis = pis;}public void run(){try{System.out.println("写入前...没有数据");byte[] by = new byte[1024];int len = pis.read(by);System.out.println(new String(by,0,len));pis.close();}catch(IOException e){System.out.println("读取失败");}}}

RandomAccessFile:随机访问文件,自身具备读写的方法,通过skipBytes(int n)seek(long pos)来达到随机访问。该类不是IO体系中的子类,而是直接继承自Object,但它是IO包中的成员,因为它具备了读和写功能。内部封装了一个数组,而且通过指针对数组进行操作。可以通过getFilePointer()获取指针位置,同时可以通过seek改变指针的位置。
其实完成读写的原理就是内部封装了字节输入流和输出流。通过构造函数可以看出,该类只能操作文件,而且操作文件还有模式:只读“r”,读写“rw”等。
"r" 以只读方式打开。调用结果对象的任何write方法都将导致抛出IOException。  
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
"rwd"   打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
而且该对象的构造函数要操作的文件不存在,会自动创建,如果存在,则不会覆盖。如果模式为只读r。不会创建文件。会去读取已存在的文件,如果该文件不存在,则会出现异常。如果模式为rw,操作的文件不存在,会自动创建,如果存在则不会覆盖。
import java.io.*;class Demo{public static void main(String []args) throws Exception{//writeFile();readFile();}public static void writeFile() throws IOException{RandomAccessFile wraf = new RandomAccessFile("ran.txt","rw");wraf.write("李四".getBytes());wraf.writeInt(97);wraf.close();}public static void readFile() throws IOException{RandomAccessFile rraf = new RandomAccessFile("ran.txt","r");byte[] b = new byte[4];rraf.read(b);String name = new String(b);int age = rraf.readInt();System.out.println(name+":"+age);rraf.close();}}


利用seek()随机存储。
import java.io.*;class Demo{public static void main(String []args) throws Exception{//writeFile();readFile();}public static void writeFile() throws IOException{RandomAccessFile wraf = new RandomAccessFile("ran.txt","rw");wraf.write("李四".getBytes());wraf.writeInt(97);wraf.seek(8);//调整对象指针wraf.write("王五".getBytes());wraf.writeInt(98);wraf.close();}public static void readFile() throws IOException{RandomAccessFile rraf = new RandomAccessFile("ran.txt","r");byte[] b = new byte[4];rraf.seek(8);//调整对象指针rraf.read(b);String name = new String(b);int age = rraf.readInt();System.out.println(name+":"+age);rraf.close();}}


多线程复制文件练习:
import java.io.*;//模板设计模式abstract class RunCode<T>{//只要复写runCode方法就可以获取到运行时间。public abstract void runCode(T t1,T t2) throws Exception;  public final void startRun(T t1,T t2) throws Exception{  long start = System.currentTimeMillis();  runCode(t1,t2);  long end = System.currentTimeMillis();  System.out.println(end-start+"ms");  }  }class Demo extends RunCode<File>{public static void main(String []args) throws Exception{Demo d = new Demo();//设置文件路径String fileURL = "C:\\Users\\Administrator\\Desktop\\kkkk\\Test.mp4";//复制后的文件名String copyfile = "copy_Test.mp4";//关联文件,封装成对象File file = new File(fileURL);File tofile = new File(file.getParent(),copyfile);d.startRun(file,tofile);}public void runCode(File file,File tofile) throws Exception{int pos = 1024*1024*20;int num = (int)file.length()/pos+1;for(int x=0;x<num;x++){new Thread(new CopyThread(file,tofile,x*pos,pos)).start();}}}//复制文件线程class CopyThread implements Runnable{private File file;private File tofile;private long pos;private int length;CopyThread(File file,File tofile,long pos,int length){this.file = file;this.tofile = tofile;this.pos = pos;this.length = length;}public void run(){synchronized(this){try{RandomAccessFile rraf = new RandomAccessFile(file,"r");RandomAccessFile wraf = new RandomAccessFile(tofile,"rw");byte[] by = new byte[length];rraf.seek(pos);int len = rraf.read(by);wraf.seek(pos);wraf.write(by,0,len);rraf.close();wraf.close();}catch(Exception e){throw new RuntimeException("复制失败");}System.out.println(Thread.currentThread().getName()+"复制完毕~!"+Thread.activeCount()+"条线程在运行");}}}
skipBytes(int n)seek(long pos)的区别:两者虽然都是跳过指定字节数,但是skipBytes只能向前跳。seek前后都可以跳。
应用:数据的分段写,多线程下载。断点下载,断点上传。
DataInputStreamDataOutputStream:基本数据类型流对象。用于操作基本数据类型最方便
import java.io.*;class Demo{public static void main(String []args) throws Exception{//writeFile();readFile();}public static void writeFile() throws IOException{DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));dos.writeInt(99);dos.writeByte(98);dos.writeDouble(3.14);dos.close();}public static void readFile() throws IOException{DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));int i = dis.readInt();byte b = dis.readByte();double d = dis.readDouble();System.out.println(i+".."+b+".."+d);dis.close();}}

因为是以字节形式存进去的,所以看不懂。

特殊之处
writeUTF()
void writeUTF(String str)           以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流。 
import java.io.*;class Demo{public static void main(String []args) throws Exception{//writeFile();readFile();}public static void writeFile() throws IOException{DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));dos.writeUTF("你好");}public static void readFile() throws IOException{DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));//byte[] b = new byte[1024];//int len = dis.read();//System.out.println(new String(b,0,len)+len);System.out.println(new String(dis.readUTF()));dis.close();}}

:用writeUTF()写的数据只能用readUTF()读取,其他读不了。
ByteArrayInputStreamByteArrayOutputStreamByteArrayInputStream在构造时需要接收数据源,而且数据源是一个字节数组。ByteArrayOutputStream在构造时,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组,这就是数据的目的。因为两个流对象都操作的是数组,并没有使用系统资源,所以不用进行close关闭。
import java.io.*;class Demo{public static void main(String []args) throws Exception{ByteArrayInputStream bais = new ByteArrayInputStream("ABCDEFG".getBytes());ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] b = new byte[1024];int len = 0;while((len = bais.read(b))!=-1){baos.write(b,0,len);baos.writeTo(System.out);}}}

源设备
键盘:System.in,硬盘:FileStream,内存:ArrayStream
目的设备
控制台:System.out,硬盘:FileStream,内存:ArrayStream
三、字符编码
字符流的出现方便操作字符,更重要的是加入了编码转换,通过子类转换流完成:
InputStreamReaderOutputStreamWriter。在两个对象进行构造的时候可以加入字符集。
编码由来:计算机只能识别二进制数据,早期由来是电信号,为了方便计算机可以识别各个国家的文字,就将各个国家的文字用数字来表示,并一一对应,形成了一张表,这就是编码表
ASCII:美国标准信息交换码。用一个字节的7位表示。
IOS8859-1:拉丁码表,欧洲码表。用一个字节8位表示。
GBK2312:中国中文码表。
GBK:中国中文码表的升级,融合了更多中文文字符号。
Unicode:国际标准码,融合了多种文字。所有文字都用2个字节表示。Java语言使用的就是Unicode
UTF-8:最多用3个字节来表示一个字符。
import java.io.*;class Demo{public static void main(String []args) throws Exception{//writeFile();readFile();}public static void writeFile() throws IOException{BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("Test.txt"), "GBK"));bw.write("你好");bw.close();}public static void readFile() throws IOException{BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("Test.txt"),"UTF-8"));System.out.println(br.readLine());br.close();}}

解析:图示

因为“你好”是用GBK编的码,GBK中用2个字节表示一个字。而UTF-8是用3个字节表示一个字。UTF-8在解码时,将数据33个进行解码,而“你好GBK编码后才占4个字节。所以UTF-8无法解码。编码用什么解码就用什么解。
编码:字符串变成字节数组。String——>byte[]:getBytes(String charsetName);。
解码:字节数组变成字符串。byte[]——>String:String(byte[] bytes);和String(byte[] bytes, String charsetName);。
class Demo{public static void main(String []args) throws Exception{String s = "你好";byte[] b = s.getBytes();String s1 = new String(b,"GBK");System.out.println(s1);}}

该示例证明了getBytes()默认使用GBK编码。
import java.util.*;class Demo{public static void main(String []args) throws Exception{String s = "你好";//对s进行GBK编码byte[] b = s.getBytes("GBK");//打印编码后的字节System.out.println(Arrays.toString(b));//用ISO8859-1解码String s1 = new String(b,"ISO8859-1");//打印看是否解码正确System.out.println(s1);//解码不正确,用ISO8859编码回到GBK的源码byte[] b1 = s1.getBytes("ISO8859-1");//打印对照GBK源码是否改变System.out.println(Arrays.toString(b));//用GBK解码String s2 = new String(b,"GBK");//打印看是否解码正确System.out.println(s2);}}

解析:图示

你好”用的是GBK编码,当用ISO8859-1解码时因为ISO8859-1是用1个字节8位表示一个字符,所以就得到了其4个的字符,和原字符不一致。若要解决这类情况,就要把这4个字符还原为字节表现形式,那么就是要对该字符再次进行ISO8859-1编码。然后再由GBK重新解码。如上图所示。
TomCat服务器使用的正是ISO8859-1编码。
在记事本中输入“联通”,然后保存。
再次打开时:

出现了乱码。疑问这是为神马呢?‘
看看txt文档保存的编码

居然是UTF-8的编码形式,正常的来说应该是ANSI。查阅相关信息了解到,UTF-8是按照标头来判断的。

看看联通的二进制表现形式:
import java.util.*;class Demo{public static void main(String []args) throws Exception{String s = "联通";byte[] by = s.getBytes();for(byte b:by){System.out.println(Integer.toBinaryString(b&255));}}}

110 10正好符合UTF-8的标准。所以保存为了UTF-8,也就出现了乱码情况。
解决办法:只要第一个字不符合UTF-8规则就行。如“你联通”。
练习:有五个学生,每个学生有3门课的成绩。从键盘输入以上数据(包括姓名,三门课成绩),输入的格式,如:zhansan,30,40,60,计算出总成绩,并把学生的信息和计算出的总分按高低顺序存放在磁盘文件“stud.txt”中。
import java.io.*;import java.util.*;//学生类class Student implements Comparable<Student>{private String name;private int cn,ma,en,sum;Student(String name,int cn,int ma,int en){this.name = name;this.cn = cn;this.ma = ma;this.en = en;sum = cn+ma+en;}//获取总分public int getSum(){return sum;}//获取名字public String getName(){return name;}//第一排序是学生成绩,第二排序是学生姓名public int compareTo(Student s){int num = new Integer(sum).compareTo(new Integer(s.sum));if(num==0)return name.compareTo(s.name);return num;}//判断对象是否重复public boolean equals(Object obj){if(!(obj instanceof Student))throw new ClassCastException("类型不匹配");Student s = (Student)obj;return name.equals(s.name)&&sum==s.sum;}//方便存入HashSet中public int hashCode(){return name.hashCode()+sum*11;}//打印public String toString(){return name+"::"+sum;}}//比较器class myCom implements Comparator<Student>{//第一排序是学生成绩,第二排序是学生姓名public int compare(Student s1,Student s2){int num = new Integer(s1.getSum()).compareTo(new Integer(s2.getSum()));if(num==0)return s1.getName().compareTo(s2.getName());return num;}}//学生工具类class StudentInfoTool{public static TreeSet<Student> getInfo() throws IOException{return getInfo(null);}//建立学生对象并存入集合中public static TreeSet<Student> getInfo(Comparator<Student> com) throws IOException{BufferedReader br = new BufferedReader(new InputStreamReader(System.in));TreeSet<Student> ts = null;if(com == null){ts = new TreeSet<Student>();}elsets = new TreeSet<Student>(com);String line = null;while((line = br.readLine())!=null){if("over".equals(line))break;else{String[] str = line.split(",");String name = str[0];int ch = Integer.parseInt(str[1]);int ma = Integer.parseInt(str[2]);int en = Integer.parseInt(str[3]);ts.add(new Student(name,ch,ma,en));}}br.close();return ts;}//将学生信息输出到文件public static void writeFile(TreeSet<Student> ts) throws IOException{BufferedWriter bw = new BufferedWriter(new FileWriter("stud.txt"));for(Student s : ts){bw.write(s.toString());bw.newLine();bw.flush();}bw.close();}}class Demo{public static void main(String []args) throws Exception{//翻转比较器TreeSet<Student> ts = StudentInfoTool.getInfo(Collections.reverseOrder(new myCom()));StudentInfoTool.writeFile(ts);}}