黑马程序员18.IO流3

来源:互联网 发布:floyd算法结果如何看 编辑:程序博客网 时间:2024/06/05 10:34

-------android培训java培训java学习型技术博客、期待与您交流! ---------- 

今天学习了毕老师java基础第21天的内容,在这里总结一下。

IO流

对象的序列化

ObjectInputStream与ObjectOutputStream
将堆内存中的对象存到硬盘上。这些对象通常会封装一些数据,这些数据也将随着对象存到硬盘上。既是程序结束了,对象还是存在。当想再次使用这些数据的时候,重新读取一次就可以了,把对象存储在硬盘上,就叫做对象的持久化存储,或者叫对象的序列化,可串行性。
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。
对象的序列号(UID)是通过对象的成员产生的:
import java.io.*;class ObjectStreamDemo{public static void main(String[] args) throws Exception{//writeObj();readObj();}public static void writeObj()throws IOException{ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));oos.writeObject(new Person("lisi",39));oos.close();}public static void readObj() throws Exception{ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));Person p = (Person)ois.readObject();System.out.println(p);ois.close();}}
import java.io.*;class Person implements Serializable{String name;int age;Person(String name, int age){this.name = name;this.age = age;}public String toString(){return name+":"+age;}}
像以上代码,如果先写入person对象,然后修改Person类,在name前加private,这样,读取的时候是会报异常,因为前后person产生的UID不同。


可以通过固定UID解决这个问题:
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
当在Person类中定义static成员变量coutry = cn的时候,如果修改为kn,是传不到obj.txt里面的。静态是不能被序列化的,静态是在方法区里面的,而只有堆内存中的数据才能被序列化。
如果对非静态的变量也不想序列化的时候,可以用关键字transient修饰。


管道流

PipedInputStream和PipedOutputStream
输入输出可以直接进行连接,通过结合线程使用。
import java.io.*;class Read implements Runnable{private PipedInputStream in;Read(PipedInputStream in){this.in = in;}public void run(){try{byte[] buf = new byte[1024];System.out.println("读取前。。没有数据阻塞");int len = in.read(buf);System.out.println("读到数据。。阻塞结束");String s = new String(buf,0,len);System.out.println(s);in.close();}catch (IOException e){throw new RuntimeException("管道读取流失败");}}}class Write implements Runnable{private PipedOutputStream out;Write(PipedOutputStream out){this.out = out;}public void run(){try{System.out.println("开始写入数据,等待6秒后。");Thread.sleep(6000);out.write("管道来了".getBytes());out.close();}catch (Exception e){throw new RuntimeException("管道输出流失败");}}}class PipedStreamDemo{public static void main(String[] args) throws IOException{PipedInputStream in = new PipedInputStream();PipedOutputStream out = new PipedOutputStream();in.connect(out);Read r = new Read(in);Write w = new Write(out);new Thread(r).start();new Thread(w).start();}}

RandomAccessFile

随机访问文件,自身具备读写的方法。
通过skipBytes(int x), seek(int x)来达到随机访问。

该类不是算是IO体系中的子类。
而是直接继承自Object。

但是它是IO包中成员。因为它具备读和写功能。
内部封装了一个数组,而且通过指针对数组的元素进行操作。
可以通过getFilePointer获取指针位置,
同时可以通过seek改变指针的位置。

其实完成读写的原理就是内部封装了字节输入流和输出流。

通过构造函数可以看出,该类只能操作文件。
而且操作文件还有模式:只读r,读写rw等。

如果模式为只读 r。不会创建文件。会去读取一个已存在的文件,如果该文件不存在,则会出现异常。
如果模式为rw。操作的文件不存在,会自动创建。如果存在则不会覆盖。

import java.io.*;class RandomAccessFileDemo {public static void main(String[] args) throws IOException{writeFile();}public static void writeFile() throws IOException{RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");raf.write("李四".getBytes());raf.write(97);raf.close();}}
运行结果是李四a.
如果是write(258),结果是乱码,因为,write()写的是32位的最低8位。
RandomAccessFile提供了writeInt()方法。
writeInt(97)的结果为:
中间空了3个空格。

取王五,用seek()方法:
import java.io.*;class RandomAccessFileDemo {public static void main(String[] args) throws IOException{//writeFile();readFile();}public static void readFile() throws IOException{RandomAccessFile raf = new RandomAccessFile("ran.txt","r");//调整对象中指针。raf.seek(8);byte[] buf = new byte[4];raf.read(buf);String name = new String(buf);int age = raf.readInt();System.out.println("name="+name);System.out.println("age="+age);raf.close();}public static void writeFile() throws IOException{RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");raf.write("李四".getBytes());raf.writeInt(97);raf.write("王五".getBytes());raf.writeInt(99);raf.close();}}
也可以用skipByte(),但是这方法不能像seek()方法那样,往前跳。


随机写(而且可以修改)
将周七写到,第4个位置上去:
public static void writeFile_2()throws IOException{RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");raf.seek(8*3);raf.write("周七".getBytes());raf.writeInt(103);raf.close();}


操作基本数据类型的流对象DataStream

DataInputStream和DataOutputStream:可以用于操作基本数据类型的数据的流对象。
import java.io.*;class DataStreamDemo {public static void main(String[] args) throws IOException{//readData();//writeUTFDemo();//OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"utf-8");//osw.write("你好");//osw.close();readUTFDemo();}public static void readUTFDemo()throws IOException {DataInputStream dis = new DataInputStream(new FileInputStream("utfdata.txt"));String s = dis.readUTF();System.out.println(s);dis.close();}public static void writeData()throws IOException{DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));dos.writeInt(234);dos.writeBoolean(true);dos.writeDouble(9887.543);dos.close();}public static void readData()throws IOException{DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));int num = dis.readInt();boolean b = dis.readBoolean();double d = dis.readDouble();System.out.println(num+" "+b+" "+d);dis.close();}public static void writeUTFDemo() throws IOException{DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdata.txt"));dos.writeUTF("你好");dos.close();}}


ByteArrayStream

public class ByteArrayInputStream
extends InputStream

ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。

关闭 ByteArrayInputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException

ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组。

public class ByteArrayOutputStream
extends OutputStream

此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray()toString() 获取数据。

关闭 ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException

ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。这就是数据目的地。

因为这两个流对象都操作的数组,并没有使用系统资源。所以,不用进行close关闭。

在流操作规律讲解时:源设备, 键盘 System.in,硬盘 FileStream,内存 ArrayStream。目的设备:控制台 System.out,硬盘FileStream,内存 ArrayStream。用流的读写思想来操作数组。

import java.io.*;class ByteArrayStream {public static void main(String[] args) {//数据源ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFG".getBytes());//数据目的ByteArrayOutputStream bos = new ByteArrayOutputStream();int by = 0;while((by=bis.read())!=-1){bos.write(by);}System.out.println(bos.size());System.out.println(bos.toString());}}
除了操作数组的,还有用来操作字符数组(CharArrayReader)和字符串的(StringReader)。


字符编码

字符流的出现为了方便操作字符。更重要是的加入了编码转换。通过子类转换流来完成(InputStreamReader,OutputStreamWriter)。在两个对象进行构造的时候可以加入字符 集。

编码表的由来
计算机只能识别二进制数据,早期由来是 电信号。为了方便应用计算机,让它可以识别各个 国家的文字。就将各个国家的文字用数字来表示,并一 一对应,形成一张表。这就是编码表。

常见的编码表
ASCII:美国标准信息交换码。用一个字节的7位可以表示。
ISO8859-1:拉丁码表。欧洲码表。用一个字节的8位表示。
GB2312:中国的中文编码表。
GBK:中国的中文编码表升级,融合了更多的中文文字符号。
Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,Java语言使用的就是unicode
UTF-8:最多用三个字节来表示一个字符。
import java.io.*;class EncodeStream {public static void main(String[] args) throws IOException{//writeText();readText();}public static void readText() throws IOException{InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"utf-8");char[] buf = new char[10];int len = isr.read(buf);String str= new String(buf,0,len);System.out.println(str);isr.close();}public static void writeText() throws IOException{OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"utf-8");osw.write("你好");osw.close();}}

原因:查表查的是utf-8的表,编码时用的是GBK:


编码解码

编码:字符串变成字节数组。
解码:字节数组变成字符串。
String-->byte[];  str.getBytes(charsetName);
byte[] -->String: new String(byte[],charsetName);

编码正确,解码错误,怎么解决:
import java.util.*;class EncodeDemo {public static void main(String[] args) throws Exception{String s = "你好";byte[] b1 = s.getBytes("GBK"); System.out.println(Arrays.toString(b1));String s1 = new String(b1,"iso8859-1");System.out.println("s1="+s1);}}

import java.util.*;class EncodeDemo {public static void main(String[] args) throws Exception{String s = "你好";byte[] b1 = s.getBytes("GBK"); System.out.println(Arrays.toString(b1));String s1 = new String(b1,"iso8859-1");//b1用iso8859-1进行解码,变成字符串s1System.out.println("s1="+s1);byte[] b2 =s1.getBytes("iso8859-1");//字符串s1用iso8859-1进行编码,变成数组b2System.out.println(Arrays.toString(b2));String s2 = new String(b2,"gbk");//b2用gbk进行编码,变成字符串s2System.out.println("s2="+s2);}}


但是,如果是用GBK进行解码,然后不小心用utf-8进行编码产生的错误是不能用将用utf-8编码后的数组进行gbk解码纠正的,因为这两个码表都识别中文,当用utf-8进行解码之后,返回的是未知字符,当对这些未知字符进行utf-8编码的时候,得到的不是之前的数值了,这样,再用gbk进行编码,得到的就不是最开始的数据了:
import java.util.*;class EncodeDemo {public static void main(String[] args) throws Exception{String s = "你好";byte[] b1 = s.getBytes("GBK"); System.out.println(Arrays.toString(b1));String s1 = new String(b1,"iso8859-1");//b1用iso8859-1进行解码,变成字符串s1System.out.println("s1="+s1);byte[] b2 =s1.getBytes("iso8859-1");//字符串s1用iso8859-1进行编码,变成数组b2System.out.println(Arrays.toString(b2));String s2 = new String(b2,"gbk");//b2用gbk进行编码,变成字符串s2System.out.println("s2="+s2);}}


UTF-8编码


class EncodeDemo2 {public static void main(String[] args) throws Exception{String s = "联通";byte[] by = s.getBytes("gbk");for(byte b : by){System.out.println(Integer.toBinaryString(b&255));//与上255,因为只要最后8位}}}
 
11010,11010,“联通”用gbk进行编码,但是产生的字节是utf格式的。当解码的时候,会用utf-8进行解码。只有联通两个字会产生这种现象.......
解决办法:前面加个汉字。


练习

有五个学生,每个学生有3门课的成绩,
从键盘输入以上数据(包括姓名,三门课成绩),
输入的格式:如:zhagnsan,30,40,60计算出总成绩,
并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。

1,描述学生对象。
2,定义一个可操作学生对象的工具类。

思想:
1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。
2,因为学生有很多,那么就需要存储,使用到集合。因为要对学生的总分排序。
所以可以使用TreeSet。
3,将集合的信息写入到一个文件中。
import java.io.*;import java.util.*;class Student implements Comparable<Student>//学生之间得能相互比较{private String name;private int ma,cn,en;//语数外private int sum;Student(String name,int ma,int cn,int en){this.name = name;this.ma = ma;this.cn = cn;this.en = en;sum = ma + cn + en;}public int compareTo(Student s)//复写接口Comparable中的compareTo方法{int num = new Integer(this.sum).compareTo(new Integer(s.sum));//比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。if(num==0){return this.name.compareTo(s.name);//看是否是同一个人}return num;}public String getName(){return name;}public int getSum(){return sum;}public int hashCode()//返回此set的哈希值{return name.hashCode()+sum*78;}public boolean equals(Object obj){if(!(obj instanceof Student)){throw new ClassCastException("类型不匹配");}Student s =(Student)obj;return this.name.equals(s.name) && this.sum.equals(s.sum);//复写Object的equals()方法}public String toString(){return "student{"+name+", "+ma+", "+cn+", "+en+"]";}}class StudentInfoTool//用于存储学生对象{public static Set<Student> getStudents() throws IOException//此方法返回一个数据类型为Student的Set{return getStudents(null);//不需要比较器,传一个空。}public static Set<Student> getStudents(Comparator<Student> cmp) throws IOException{BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));//键盘录入的标准写法String line = null;Set<Student> stus = null;if(cmp==null)stus = new TreeSet<Student>();//不带比较器的TreeSetelsestus = new TreeSet<Student>(cmp); while ((line=bufr.readLine())!=null){if("over".equals(line)){break;}String[] info = line.split(",");//用逗号分隔开输入的数据,存入info数组中Student stu = new Student(info[0],Integer.parseInt(info[1]),Integer.parseInt(info[2]),Integer.parseInt(info[3]));//创建学生对象,将info数组中的数据传进去stus.add(stu);//将学生对象存入set中}bufr.close();return stus;//返回set}public static void write2File(Set<Student> stus) throws IOException//将学生集合写入文件{BufferedWriter bufw = new BufferedWriter(new FileWriter("stuinfo.txt"));//建立文件for(Student stu : stus)//遍历学生集合{bufw.write(stu.toString()+"\t");bufw.write(stu.getSum()+"");bufw.newLine();bufw.flush();}bufw.close();}}class StudentInfoTest{public static void main(String[] args) throws IOException{//将成绩从高到底排列,本来自然排序是从第到高排的Comparator<Student> cmp = Collections.reverseOrder();Set<Student> stus = StudentInfoTool.getStudents(cmp);StudentInfoTool.write2File(stus);}}

 -------android培训java培训java学习型技术博客、期待与您交流! ---------- 




0 0
原创粉丝点击