JAVASE_21_#java高级IO_IO包中的其他类

1,对象的序列化
    1.1,什么叫序列化?什么叫反序列化?
序列化:将对象的状态信息转换为可以存储或传输的形式的过程。只能序列化堆内存中的数据,无法序列化其他
反序列化:它将流转换为对象
    1.2,对象序列化的目的?
1、以某种存储形式使自定义 对象持久化
2、将对象从一个地方传递到另一个地方。
3、使程序更具维护性。
     1.3,读取

ObjectOutputStream;将 Java 对象的基本数据类型和图形写入 OutputStream

---------只能将支持 java.io.Serializable 接口的对象写入流中

ObjectInputStream:

Serializable接口没有任何抽象方法,这种没有抽象发发的接口通常叫做标记接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import  java.io.*;
class  ObjectStreamDemo {
     public  static  void  main(String[] args)  throws  Exception{
         //writeObj();
         readObj();
     }
     //从文件把序列化的对象读出来
     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();
     }
     //将对象序列化,存入文件
     //通常同文件存成person.object
     public  static  void  writeObj() throws  IOException{
         ObjectOutputStream oos = 
             new  ObjectOutputStream( new  FileOutputStream( "obj.txt" ));
         oos.writeObject( new  Person( "lisi0" , 399 , "kr" ));
         oos.close();
     }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import  java.io.*;
class  Person  implements  Serializable{
     //给类定义一个固定标识,
     //如果未定义,接口会根据类成员自己算一个
     public  static  final  long  serialVersionUID = 42L;
     private  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;
     }
}
注意:1,对象的静态成员不能被序列化
2,被关键字 transient 修饰的不能序列化
3,序列化的类要实现接口Serializable
4,序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,
该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。



2,管道流
PipedInputStream
PipedOutputStream--------输入输出可以直接连接,通过结合线程使用
通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream。
不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程
代码演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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( "piped lai la" .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();
     }
}




3,RandomAccessFile------------直接父类为Object
----随机访问文件,自身具备读写的方法
----通过skipBytes(int  x),seek(int  x)来达到随机访问
该类不是算是IO体系中子类。 
而是直接继承自Object。
但是它是IO包中成员。因为它具备读和写功能。
内部封装了一个数组,而且通过指针对数组的元素进行操作。
可以通过getFilePointer获取指针位置, 同时可以通过seek改变指针的位置。 
完成读写的原理:------------就是内部封装了字节输入流和输出流。

RandomAccessFile(File file, String mode) ;
RandomAccessFile(String name, String mode)
通过构造函数可以看出,该类只能操作文件。
而且操作文件还有模式:只读r,,读写rw等。
模式----- r>>>不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。
模式-----rw>>>操作的文件不存在,会自动创建。如果存则不会覆盖

写代码:
1
2
3
4
5
6
RandomAccessFile raf =  new  RandomAccessFile( "ran.txt" , "rw" ); //读写模式
raf.write( "李四" .getBytes());
raf.writeInt( 97 ); //write只写数的低8位,当大于255时,数据丢失
raf.write( "王五" .getBytes());
raf.writeInt( 99 );
raf.close();
1
2
3
4
5
6
7
8
9
public  static  void  writeFile_2() throws  IOException
{
     RandomAccessFile raf =  new  RandomAccessFile( "ran.txt" , "rw" );
     raf.seek( 8 * 0 ); //能够实现分段写入
     raf.write( "周期" .getBytes());
     raf.writeInt( 103 );
 
     raf.close();
}
注:它可以实现数据的分段写入,可以用不同线程同时写入不同段!他们直接不会互相冲突--------下载软件的原理
第一个线程0----10
第二个线程11----23
第三个线程24-----45........
其他流多线程就会造成数据虽然写入,但是不是连续的,解码出现错误
读代码:
skipBytes(int  x);//只能往前跳
seek(int  x);调整对象中指针
seek(8*倍数);//数据分段,因此希望数据有规律
//遇到假如name太长的情况就统一让nam按16个字节存!!年龄按4个字节seek(20*倍数)
1
2
3
4
5
6
7
8
9
10
11
RandomAccessFile raf =  new  RandomAccessFile( "ran.txt" , "r" );
//调整对象中指针。
//raf.seek(8*1);
//跳过指定的字节数
raf.skipBytes( 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);

4.DataStream操作基本数据类型
DataInputStream
DataOutputStream
----------------------可以用于操作基本数据类型的数据的流对象。
构造函数-----
DataOutputStream(OutputStream out);
DataInputStream(InputStream in) ;

writeUTF(String str) ;//以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流
readUTF();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DataInputStream dis =  new  DataInputStream( new  FileInputStream( "data.txt" ));
DataOutputStream dos =  new  DataOutputStream( new  FileOutputStream( "data.txt" ));
 
 
//平时要想写入带编码的数据
OutputStreamWriter osw =  new  OutputStreamWriter( new  FileOutputStream( "gbk.txt" ), "gbk" );
osw.write( "你好" ); //GBK-----写入后是4个字节的大小
OutputStreamWriter osw =  new  OutputStreamWriter( new  FileOutputStream( "gbk.txt" ), "utf-8" );
osw.write( "你好" ); //"utf--8"---写入后是6个字节的大小
//修改版的UTF-8
DataOutputStream dos =  new  DataOutputStream( new  FileOutputStream( "utfdate.txt" ));
dos.writeUTF( "你好" ); //utf-8-------修改版写入后是8个字节的大小
DataInputStream dis =  new  DataInputStream( new  FileInputStream( "utf.txt" ));
String s = dis.readUTF();
System.out.println(s);
注:一个汉字gbk是2个字节,utf-8是3个字节

5.ByteArrayStream
用于操作字节数组的流对象。

ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数
这就是数据目的地。
因为这两个流对象都操作的数组,并没有使用系统资源。
所以,不用进行close关闭。
在流操作规律讲解时:
源设备,
 键盘 System.in,硬盘 FileStream,内存 ArrayStream
目的设备:
 控制台 System.out,硬盘FileStream,内存 ArrayStream
1
2
3
4
5
6
7
8
//数据源。
ByteArrayInputStream bis =  new  ByteArrayInputStream( "ABCDEFD" .getBytes());
//数据目的
ByteArrayOutputStream bos =  new  ByteArrayOutputStream();
int  by =  0 ;
while ((by=bis.read())!=- 1 ){
     bos.write(by);
}
方法writeTo(Outputstream out);
1
bos.writeTo( new  FileOutputStream( "a.txt" ));

CharStream类
StringStream类

6字符编码
字符流的出现时为了方便操作字符数据,方便的原因是内部加入了编码表。
InputStreamReader
OutputStreamReader-----两者构造时可以加入字符集编码表
其实还有2个类可以涉及到编码表,PrintWriter(File file, String csn);//csn字符集,编码-----但是只能打印时

编码表的由来:计算机只识别二进制数据,早期是电信号
为了方便应用计算机,让它可以识别各个国家的文字
就将各个国家的文字用数字来表示,并一一对应,形成一张表-----编码表
1
2
3
4
5
6
7
ASCII:美国标准信息交换码,用一个字节的 7 位表示
ISO8859-1 :拉丁码表,欧美码表,用一个字节的 8 表示
GB2312:中国的中文编码表, 2个字节
GBK:中国的中文编码表升级,永和了更多的中文文字符号
Unicode:国际标准码,融合了多种文字
           所有的文字都用2个字节表示,java语言使用的就是Unicode
UTF-8最多用 3 个字节表示一个字符
编码:字符串变成字节数组。
解码:字节数组变成字符串。
String-->byte[]; str.getBytes(charsetName);
byte[] -->String: new String(byte[],charsetName);
第一种情况: 编码是错误的无解
1
2
String s =  "你好" ;
byte [] b1 = s.getBytes( "ISO8859-1" );//ISO不识别中文
第二种情况: 编码对,解码错!
1
2
3
4
5
6
7
8
9
10
11
//编码。
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);
//解码错误的情况下,反着编码回去解码
byte [] b2 = s1.getBytes( "ISO8859-1" ); //对s1进行iso8859-1编码。
String s2 =  new  String(b2, "GBK" );
System.out.println( "s2=" +s2);
第三种情况:gbk<-->utf-8时,因为2者都识别中文,导致反编码时出错。
1
2
3
4
5
6
7
8
9
10
11
//编码。
String s =  "你好" ;
byte [] b1 = s.getBytes( "GBK" );
System.out.println(Arrays.toString(b1));
//解码,失败
String s1 =  new  String(b1, "utf-8" );
System.out.println( "s1=" +s1);
//解码错误的情况下,反着编码回去解码
byte [] b2 = s1.getBytes( "utf-8" ); //对s1进行iso8859-1编码。
String s2 =  new  String(b2, "GBK" );
System.out.println( "s2=" +s2);
第四种情况:联通<---->▉▉的情况
1
2
3
4
5
6
7
8
9
10
11
String s =  "联通" ;
byte [] by = s.getBytes( "gbk" );
for ( byte  b : by){
     System.out.println(Integer.toBinaryString(b& 255 ));
}
//打印结果:
11000100
11100011
10111010
11000011
//utf-8有标志头0,110,10

乱码的由来,
1是因为在编码和解码的过程用的是不同的编码表而造成的。
2也有的是在文件数据的输入输出时,造成字节的获取不足。
例如,当我们用以byte[]  buf=new byte[5];长度为5的字节数组
来作为缓冲存取以段数据“你们好啊”并打印,存取到“好”这个字我们才存到啦半个。
也会出现乱码。
3说个特例,“联通”这个词的,二进制表示时,即符合GBK码表也符合UTF-8码表。所以也会出现乱码。 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值