Java基础——对象序列化+管道流+RandomAccessFile+操作基本数据类型的DataStream等
来源:互联网 发布:4g通信模块数据发送 编辑:程序博客网 时间:2024/04/30 04:07
1. IO流
对象的序列化
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
用ObjectOutputStream保存的对象只能用ObjectInputStream读取
void writeObject(Object obj):将指定的对象写入 ObjectOutputStream。
Object readObject():从 ObjectInputStream 读取对象。
请看如下示例:
class Person implements Serializable//标记接口,没有方法{ //UID是给类添加固定标识,为了序列化方便 //自动生成UID时,class文件的代码变动,无法使用原有对象,所以手动生成唯一UID publicstaticfinallongserialVersionUID = 42L; private String name; transientintage;//transient修饰的变量,不能被序列化 static String country = "cn";//静态是不能被序列化的,;因为他在方法区中(共享区) Person(String name,int age,String country) { this.name = name; this.age = age; this.country = country; } public String toString() { returnname+":"+age+":"+country; }} import java.io.*; class ObjectStreamDemo { publicstaticvoid main(String[] args) throws Exception { //writeObj(); readObj(); } publicstaticvoid readObj()throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream ("obj.txt")); Person p = (Person)ois.readObject(); System.out.println(p); ois.close(); } publicstaticvoid writeObj()throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt")); oos.writeObject(new Person("lisi0",399,"kr")); oos.close(); }}
管道流
1.概念:
PipedInputStream:管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从 PipedInputStream对象读取,并由其他线程将其写入到相应的 PipedOutputStream。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。
void connect(PipedOutputStream src):使此管道输入流连接到管道输出流 src。
void connect(PipedInputStream snk) : 将此管道输出流连接到接收者。
2. 特点
管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。
涉及多线程的IO流是管道流,涉及IO流的集合是properties
3. 怎么使用?
class Read implements Runnable{ private PipedInputStream in; Read(PipedInputStream in) { this.in = in; } publicvoid run() { try { byte[] buf = newbyte[1024]; System.out.println("读取前。。没有数据阻塞"); int len = in.read(buf);//read()是阻塞式的方法 System.out.println("读到数据。。阻塞结束"); String s= new String(buf,0,len); System.out.println(s); in.close(); } catch (IOException e) { thrownew RuntimeException("管道读取流失败"); } }} class Write implements Runnable{ private PipedOutputStream out; Write(PipedOutputStream out) { this.out = out; } publicvoid run() { try { System.out.println("开始写入数据,等待6秒后。"); Thread.sleep(6000); out.write("piped lai la".getBytes()); out.close(); } catch (Exception e) { thrownew RuntimeException("管道输出流失败"); } }} class PipedStreamDemo{ publicstaticvoid 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
1. 概念
随机访问文件,自身具备读写的方法。
通过skipBytes(int x),seek(int x)来达到随机访问。
int read(byte[] b):将最多 b.length个数据字节从此文件读入 byte 数组。
int readInt():从此文件读取一个有符号的 32位(4字节)整数。
void seek(long pos): 设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。调整对象中指针。前后都能跳。
skipBytes(int n):尝试跳过输入的 n个字节以丢弃跳过的字节。不能往回跳。
2. 特点
该类不算是IO体系中子类。
而是直接继承自Object。
但是它是IO包中成员。因为它具备读和写功能。
内部封装了一个数组,而且通过指针对数组的元素进行操作。
可以通过getFilePointer获取指针位置,
同时可以通过seek改变指针的位置。
其实完成读写的原理就是内部封装了字节输入流和输出流。(操作数据必然是流)
通过构造函数可以看出,该类只能操作文件。
而且操作文件还有模式:只读r,,读写rw等。
如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。
如果模式rw。操作的文件不存在,会自动创建。如果存在则不会覆盖。
3. 怎么使用?
class RandomAccessFileDemo { publicstaticvoid main(String[] args) throws IOException { //writeFile_2(); //readFile(); //System.out.println(Integer.toBinaryString(258)); } publicstaticvoid readFile()throws IOException { RandomAccessFile raf = new RandomAccessFile("ran.txt","r"); //调整对象中指针。 //raf.seek(8*1); //跳过指定的字节数 raf.skipBytes(8); byte[] buf = newbyte[4];//一个中文两个字节,刚好读取到两个中文 raf.read(buf); String name = new String(buf); int age = raf.readInt();//一次读取四个字节,32位 System.out.println("name="+name); System.out.println("age="+age); raf.close(); } publicstaticvoid writeFile_2()throws IOException { RandomAccessFile raf = new RandomAccessFile("ran.txt","rw"); raf.seek(8*0); raf.write("周期".getBytes()); raf.writeInt(103);//writer()方法只写int类的最低八位,其他会丢失。writeInt()写INT类型的4字节32位 raf.close(); } publicstaticvoid writeFile()throws IOException { RandomAccessFile raf = new RandomAccessFile("ran.txt","rw"); raf.write("李四".getBytes()); raf.writeInt(97); raf.write("王五".getBytes()); raf.writeInt(99); raf.close(); }}
4. 什么时候使用?
实现数据的分段写入,每一段自己拥有一个线程,如:下载软件原理,每个软件下载独立执行,分段下载,实现多线程下载
操作基本数据类型的流对象DataStream
1. 概念
可以用于操作基本数据类型的数据的流对象。
gbk:一个中文占2字节
utf-8:一个中文占3字节
utf-8修改版:一个中文占4字节
不同编码写的文件,需要用不同的编码读取,否则读取错误
double readDouble() :读取八个输入字节并返回一个 double值。
int readInt():读取四个输入字节并返回一个 int值。
void writeUTF(String s):将表示长度信息的两个字节写入输出流,后跟字符串 s中每个字符的 UTF-8 修改版表示形式。
EOFException - 如果此输入流在读取所有字节之前到达末尾。既没读完数据就到结尾了
2. 特点
基本数据类型写入和取出的顺序要保持一致
3. 怎么使用?
class test { publicstaticvoid main(String[] args) throws IOException { //基本数据类型的读写操作 write(); read(); //按照固定字符编码格式写入字符 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("c:/t2.txt"),"GBK"); osw.write("你是傻蛋"); osw.close(); BufferedReader osr = new BufferedReader(new InputStreamReader(new FileInputStream("c:/t2.txt"))); String line=null; while((line=osr.readLine())!=null) { sop(line); } //writeUTF()方法读写文件 writeUTF(); readUTF(); } publicstaticvoid readUTF()throws IOException { DataInputStream dif= new DataInputStream(new FileInputStream("c:/t3.txt")); sop(dif.readUTF());//只能读取writeUTF()方法写的文件 dif.close(); } publicstaticvoid writeUTF()throws IOException { DataOutputStream dof = new DataOutputStream(new FileOutputStream("c:/t3.txt")); dof.writeUTF("我很帅");//英文不涉及编码,用中文 dof.close(); } publicstaticvoid read()throws IOException { DataInputStream dif= new DataInputStream(new FileInputStream("c:/t1.txt")); sop(dif.readDouble()); sop(dif.readInt()); sop(dif.readBoolean()); sop(dif.readByte()); dif.close(); } publicstaticvoid write()throws IOException { DataOutputStream dof = new DataOutputStream(new FileOutputStream("c:/t1.txt")); dof.writeDouble(33.333333); dof.writeInt(35); dof.writeBoolean(true); dof.writeByte(88); dof.close(); } publicstaticvoid sop(Object obj) { System.out.println(obj); }}
4.什么时候使用?
当需要操作基本数据类型的时候
操作字节数组-ByteArrayStream
1. 概念
用于操作字节数组的流对象。
ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。
ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组。
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。这就是数据目的地。
在流操作规律讲解时:
源设备,
键盘 System.in,硬盘 FileStream,内存 ArrayStream。
目的设备:
控制台 System.out,硬盘FileStream,内存 ArrayStream。
2. 特点
没有调用底层资源,关闭 ByteArrayInputStream无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
因为这两个流对象都操作的数组,并没有使用系统资源。
所以,不用进行close关闭。
3. 怎么使用?
class test { publicstaticvoid main(String[] args) { //读取数据源,在内存中会难过 ByteArrayInputStream bas = new ByteArrayInputStream("asdfqwer".getBytes()); //数据目的,在内存中 ByteArrayOutputStream bos = new ByteArrayOutputStream(); int len=0; while((len=bas.read())!=-1) { bos.write(len); sop("写入的数据是:"+(char)len); } //默认数组(缓冲区)的大小 sop(bos.size()); //取出缓冲区数据 sop(bos.toString()); //bos.writeTo(new FileOutputStream("a.txt"));//只有此方法会抛出异常 } publicstaticvoid sop(Object obj) { System.out.println(obj); }}
4. 什么时候使用?
当要将硬盘文件读到内存中,放到可变长度的数组里存储起来时(用流的思想操作数组,读和取)
操作字符数组-CharArrayReader与CharArrayWrite
用法与操作字节数组相似
操作字符串数组-StringReader与 StringWriter
用法与操作字节数组相似
转换流的字符编码
1. 概念
编码表
计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个
国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表。
常见的编码表
ASCII:美国标准信息交换码。
• 用一个字节的7位可以表示。
ISO8859-1:拉丁码表。欧洲码表
• 用一个字节的8位表示。
GB2312:中国的中文编码表。两个字节的8位表示,且两个字节的高位都是1。兼容ASCII码。容纳6000-7000个字。
GBK:中国的中文编码表升级,融合了更多的中文文字符号。容纳20000多个字。
Unicode:国际标准码,融合了多种文字。
• 所有文字都用两个字节来表示,Java语言使用的就是unicode。
UTF-8:最多用三个字节来表示一个字符。(能用一个字节的就用一个字节存储,两个字节的就用两个字节。减少空间)每个字节开头都有标识头,容易区分UTF-8。
当两个编码表都能识别中文,但是同一文字在两张码表中对应的数字不同。所以涉及到了编码转换问题
不同编码进行写出和读取操作原理:
2. 特点
可以将字符以指定编码格式存储。
可以对文本数据指定编码格式来解读。
指定编码表的动作由构造函数完成。
3. 怎么使用?
class EncodeStream { publicstaticvoid main(String[] args) throws IOException { //writeText(); readText(); } publicstaticvoid readText()throws IOException { InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"gbk"); char[] buf = newchar[10]; int len = isr.read(buf); String str = new String(buf,0,len); System.out.println(str); isr.close(); } publicstaticvoid writeText()throws IOException { OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8"); osw.write("你好"); osw.close(); }}
字符编码
1. 概念
编码:字符串变成字节数组。
解码:字节数组变成字符串。
编码正确,解码错误,解决方法原理:
注意:
当用UTF-8解码—》重新编码—》解码,结果错误;因为GBK和UTF-8都识别中文。UTF-8有未知编码区域,当UTF-8解码时,查不到指定的中文时就在未知区域返回相似的字符?,重新编码时,返回相似字符的数字,此时数字已经变化了。
2. 特点
String-->byte[]:str.getBytes(charsetName);
byte[] -->String: new String(byte[],charsetName);
Array.toString(byte[]):此方法可以将字节数组转变为字符串,但是不能指定编码格式。
中文不能用ISO8859-1进行编码,出错
3. 怎么使用?
class EncodeDemo{ publicstaticvoid 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");//String s1 = new String(b1,"utf-8"); System.out.println("s1="+s1); //对s1进行iso8859-1编码。 byte[] b2 = s1.getBytes("iso8859-1");//byte[] b2 = s1.getBytes("utf-8"); System.out.println(Arrays.toString(b2)); //对b2进行gbk解码。 String s2 = new String(b2,"gbk"); System.out.println("s2="+s2); }}
4. 什么时候使用?
当需要指定格式对数据进行编码,解码时。
字符编码-联通(特殊文字)
UTF-8和GBK编码表的特点
Utf-8根据标识头来判断一次是读一个字节还是两个字节还是三个字节,如下图:
请看如下案例:
class test { publicstaticvoid main(String[] args) throws Exception { String s = "联通"; byte[] by = s.getBytes("gbk"); for(byte b : by) { //将十进制的String类型数据转换成二进制,而且只取后八位 System.out.println(Integer.toBinaryString(b&255)); } }}
产生乱码的原因:
"联通"用GBK编码表产生的二进制数,与UTF-8的二进制数一致,当在记事本写入"联通"两字保存后,重新打开,进行解码。此时因为编码产生的二进制数与utf-8的一致,所以默认用utf-8编码表进行解码。导致解码错误;
解决方法:
在"联通"两字前面加上其他的中文;
练习-将学生信息写到文件中
/*有五个学生,每个学生有3门课的成绩,从键盘输入以上数据(包括姓名,三门课成绩),输入的格式:如:zhagnsan,30,40,60计算出总成绩,并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。 1,描述学生对象。2,定义一个可操作学生对象的工具类。 思想:1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。2,因为学生有很多,那么就需要存储,使用到集合。因为要对学生的总分排序。 所以可以使用TreeSet。3,将集合的信息写入到一个文件中。 */publicclass test{ publicstaticvoid main(String args[]) throws IOException { //默认排序,升序 Set<Student> set = StudentTool.getStudent(); StudentTool.writeStudent(set); //定义比较器,降序排序,所以要逆转指定比(默认)较器的顺序 /*Comparator<Student> cmp = Collections.reverseOrder(); Set<Student> set1 = StudentTool.getStudent(cmp); StudentTool.writeStudent(set1);*/ }} //定义学生类。因为要排序,所以要实现comparable类,复写hashcode(),equals(),compareTo()方法class Student implements Comparable<Student>{ private String name; privateintch,ma,en; privateintsum; Student(String name,int ch,int ma,int en) { this.name = name; this.ch = ch; this.ma = ma; this.en = en; sum = ch+ma+en; } public String getname() { returnname; } publicint getsum() { returnsum; } publicint HashCode() { returnname.hashCode()+sum*39; } publicboolean equals(Object obj) { if(!(obj instanceof Student)) thrownew ClassCastException("类型不匹配"); Student s = (Student)obj; returnthis.name.equals(s.name)&&this.sum==s.sum; } publicint compareTo(Student s) { int num = new Integer(this.sum).compareTo(new Integer(s.sum));//按分数排序,分数相同按照姓名 if(num==0) returnthis.name.compareTo(s.name); return num; } public String toString() { return"Student["+name+", "+ch+", "+ma+", "+en+"]"; }}//定义工具类,操作学生对象class StudentTool{ //将数据保存到学生对象中,将学生对象保存到TreeSet集合中 //按照自定义比较器排序,降序 publicstatic Set<Student> getStudent(Comparator<Student> cmp) throws IOException { BufferedReader buf = new BufferedReader(new InputStreamReader(System.in)); String line = null; //因为要按照分数排序,而且要将学生对象存储,所以定义TreeSet集合 Set<Student> set = null; if(cmp==null) set = new TreeSet<Student>(); else set = new TreeSet<Student>(cmp); while((line = buf.readLine())!=null) { if("over".equals(line)) break; String s[] = line.split(","); //将数据保存到学生对象中 Student stu = new Student(s[0], Integer.parseInt(s[1]), Integer.parseInt(s[2]), Integer.parseInt(s[3])); set.add(stu); } buf.close(); return set; } //按照默认排序,升序 publicstatic Set<Student> getStudent() throws IOException { return getStudent(null); } //将集合中的数据写到文件中 publicstaticvoid writeStudent(Set<Student> s) throws IOException { BufferedWriter bw = new BufferedWriter(new FileWriter("c:/Student1.txt")); for(Student stu:s) { bw.write(stu.toString()+"\t"); bw.write(stu.getsum()+"");//因为write方法只识别INT类型数据的后八位,所以转换成字符串类型 bw.newLine(); bw.flush(); } bw.close(); }}<span style="font-family:Calibri;font-size:14px;"> </span>
- Java基础——对象序列化+管道流+RandomAccessFile+操作基本数据类型的DataStream等
- Java基础---Java---IO流-----对象的序列化、管道流、RandomAccessFile、数据类型的流对象DataStream、ByteArrayStream
- 黑马程序员——Java基础——对象序列化、管道流、RandomAccessFile类、操作基本数据类型的流对象、字符编码
- IO流__【对象的序列化】【管道流】【RandomAccessFile】【DataStream】【ByteArrayStream等】
- JAVA基础学习(二十二)--IO流四-对象序列化、管道流、RandomAccessFile、DataStream、ByteArrayStream、转换流的字符编码
- 黑马程序员_IO流四(对象的序列化,管道流,RandomAccessFile,DataStream,ByteArrayStream)
- 黑马程序员_IO流4_(对象的序列化,管道流,RandomAccessFile,DataStream,ByteArrayStream)
- 黑马程序员—异常+IO流File类+Properties+PrinWriter+合并流+切割文件+对象序列化+管道流+RandomAccessFile+操作基本数据类型的DataStre
- 操作基本数据类型的流对象DataStream
- Java操作基本数据类型的流对象DataStream
- JAVA基础 day23 IO流的其他类 (对象的序列化 管道流 RandomAccessFile类(随机读取文件) DateStream(操作基本数据类型的流) 操作数组的流 )字符编码问题
- Java基础 管道流 RandomAccessFile DataStream ByteArrayStream 字符编码
- IO流——基本数据类型的流对象DataStream
- 黑马程序员_IO流——操作基本数据类型的流对象DataStream
- IO流(操作基本数据类型的流对象DataStream)
- 黑马程序员:操作基本数据类型的流对象DataStream
- 黑马程序员:操作基本数据类型的流对象DataStream
- java-----IO流 操作字节数组的流对象,对象序列化,打印流,随机存储文件,合并流,管道流,操作基本数据类型的流对象
- openfire推送插件开发
- 同步对象的区别与联系
- OpenCV的颜色空间转换函数 cvtColor
- C代码读文件demo
- 无数据库的网站数据流操作原理
- Java基础——对象序列化+管道流+RandomAccessFile+操作基本数据类型的DataStream等
- Ubuntu 13.10中安装eclipse后菜单栏下拉失效
- HashMap的学习(一)
- 使用注解风格学习Hibernate和JPA的主键生成策略
- Android Bluetooth蓝牙开发\蓝牙协议\蓝牙通信例子_Android支持蓝牙4.0版本_BLE开发
- Java 里把 InputStream 转换成 String 的几种方法
- 100元划分
- 工作那些事(二十九)团队建设和人才招聘
- Android SDK4.0 离线安装方法