您的位置:首页 > 编程语言 > Java开发

JAVA基础 day23 IO流的其他类 (对象的序列化 管道流 RandomAccessFile类(随机读取文件) DateStream(操作基本数据类型的流) 操作数组的流 )字符编码问题

2016-10-25 23:01 1276 查看
IO流其他类

对象的序列化

序列化:把Java对象转换为字节序列的过程。

反序列化:把字节序列恢复为Java对象的过程

用途:把对象的字节序列永久的保存到硬盘上,通常存在文件中。

在网络上传输对象的字节序列。

编程:进行将对象存取到文件的操作

当类实现Serializable接口,序列化运行时,使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,这时如果改动可序列化类,重新编译的类生成的UID就不同了,这时将文件里的对象打印时(反序列化),就会出现InvalidClassException异常。

如果想可序列化类改动以后,仍然可以反序列化,序列化类可以通过声明名为 “serialVersionUID” 的字段显式声明其自己的 serialVersionUID。

例如:

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

import java.io.*;
class ObjectStreamDemo
{
public static void main(String[]args)throws Exception
{
//writeObj();
readObj();
}
public static void writeObj()throws IOException//将对象写入文件的方法
{
FileOutputStream fos=new FileOutputStream("person.txt");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(new PersonTest("lisi",18));
fos.close();
oos.close();
}
public static void readObj()throws Exception//从文件中读取对象的方法,因为文件中可能并不是对象,所以会抛出ClassNotFoundException异常,这里为了方便,直接抛出Exception异常
{
FileInputStream fis=new FileInputStream("person.txt");
ObjectInputStream ois=new ObjectInputStream(fis);
PersonTest p=(PersonTest)ois.readObject();
System.out.println(p.getName()+":"+p.getAge());
}
}

import java.io.*;
class PersonTest implements Serializable//创建的类,此类对象如果想序列化,必须实现Serializable接口。
{
String name;
int age;
PersonTest(String name,int age)
{
this.name=name;
this.age=age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}


管道流:

PipedOutputStream管道输出流 和 PipedInputStream管道输入流

管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。

import java.io.*;
class Read implements Runnable//这个线程用来读取
{
private PipedInputStream in;//私有一个管道输入流,传进来,进行读取操作
Read(PipedInputStream in)
{
this.in=in;
}
public void run()
{
try{
byte[]b=new byte[1024];
int len=0;
len=in.read(b);
System.out.println(new String(b,0,len));
in.close();
}
catch(IOException e)
{
System.out.println("管道输入流出现错误");
}
}
}
class Write implements Runnable//这个线程用来写入
{
private PipedOutputStream out;//用管道输出流进行写入操作
Write(PipedOutputStream out)
{
this.out=out;
}
public void run()
{
try{
out.write("piped come".getBytes());
out.close();
}
catch(IOException e)
{
System.out.println("管道输出流出现错误");
}
}
}
class PipedStreamDemo
{
public static void main(String[]args)throws IOException
{
PipedInputStream in=new PipedInputStream();
PipedOutputStream out=new PipedOutputStream();
Read r=new Read(in);
Write w=new Write(out);
in.connect(out);//把管道流相关联
new Thread(r).start();//开启线程。
new Thread(w).start();
}
}


RandomAccessFile:

该类不能算是IO体系中的子类,该类直接继承Object类

但是它是IO包中的成员,具备读写功能。其内部封装了一个数组,通过指针对数组元素进行操作。可以通过getFilePointer 获取指针位置,并且可以用seek方法设置指针位置。

该类构造方法接收一个文件和模式。

只有四种模式:

“r” 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。

“rw” 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。

“rws” 打开以便读取和写入,对于 “rw”,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。

“rwd” 打开以便读取和写入,对于 “rw”,还要求对文件内容的每个更新都同步写入到底层存储设备。

如果模式为r,会去读取文件,如果文件不存在,则报异常。

如果模式为rw,去读取文件,如果文件不存在,则创建文件。

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

seek方法可以指定指针位置,用途可以开启多线程,分别从不同位置读取文件,多线程下载可以通过这个实现。

编程:RandomAccessFile方法的使用

import java.io.*;
class RandomDemo
{
public static void main(String[]args)throws IOException
{
//write();
//read();
read_2();
}
public static void read_2()throws IOException//指针读取操作
{
RandomAccessFile raf=new RandomAccessFile("random.txt","r");
byte[]b=new byte[4];
raf.seek(8);//设置指针位置,即从第几个字节开始读,当指针指的位置已经有数据,这时写入会覆盖原来的数据,即完成修改操作
raf.read(b);
String name=new String(b);
int age=raf.readInt();
System.out.println(name+":"+age);
raf.close();
}
public static void read()throws IOException//读取操作
{
RandomAccessFile raf=new RandomAccessFile("random.txt","r");
byte[]b=new byte[4];
raf.read(b);
String name=new String(b);
int age=raf.readInt();
System.out.println(name+":"+age);
raf.close();
}
public static void write()throws IOException//写入操作
{
RandomAccessFile raf=new RandomAccessFile("random.txt","rw");
byte []b=new byte[4];
raf.write("张三".getBytes());
raf.writeInt(56);//因为write方法是写入字节,当写入整数时,只读取最后八个字节,会造成数值损失,所以要用writeInt方法写入int数值
raf.write("李四".getBytes());
raf.writeInt(34);
raf.close();
}
}


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

DateInputStream和DateOutputStream

这个类可以很方便的操作基本数据类型。

import java.io.*;
class DataStreamDemo
{
public static void main(String[]args)throws IOException
{
//DataOutputDemo();
//DataInputDemo();
DataUtf();
}
public static void DataUtf()throws IOException//writeUTF()和readUTF()以UTf-8修改版编码将一个字符串写入或读取,他是一种特殊的编码格式,只能采用相应的方法进行写入和读取。
{
DataOutputStream dos=new DataOutputStream(new FileOutputStream("utf.txt"));
DataInputStream dis=new DataInputStream(new FileInputStream("utf.txt"));

dos.writeUTF("你好");
String s=dis.readUTF();
System.out.println(s);
dos.close();
dis.close();
}
public static void DataInputDemo()throws IOException//读取方法
{
DataInputStream dis=new DataInputStream(new FileInputStream("data.txt"));
boolean b=dis.readBoolean();
int i=dis.readInt();
double d=dis.readDouble();
System.out.println("boolean="+b);
System.out.println("int="+i);
System.out.println("double="+d);

dis.close();
}
public static void DataOutputDemo()throws IOException//写入方法
{
DataOutputStream dos=new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeBoolean(true);
dos.writeInt(125);
dos.writeDouble(1645.26841);
dos.close();
}
}


操作数组的流对象

操作字节数组

ByteArrayInputStream和ByteArrayOutputStream

因为此类并未操作系统底层资源,所有不需要close操作。

操作字符数组

CharArrayReader和CharArrayWriter

操作字符串

StringReader和StringWriter

编程:操作字节数组流的方法例子

import java.io.*;
class ByteArrayStreamDemo
{
public static void main(String[]args)
{
read();
}
public static void read()
{
ByteArrayInputStream bais=new ByteArrayInputStream("abcdefg".getBytes());//接收一个字节数组
ByteArrayOutputStream baos=new ByteArrayOutputStream();//内部有一个长度可变的字节数组
int len=0;
while((len=bais.read())!=-1)
{
baos.write(len);
}
System.out.println(baos.size());
System.out.println(baos.toString());
}
}


转换流的字符编码

import java.io.*;
class EncodeDemo
{
public static void main(String[]args)throws IOException
{
//writeText();
readText();
}
public static void readText()throws IOException
{
InputStreamReader isr=new InputStreamReader(new FileInputStream("gbk.txt"));//java默认是gbk编码,所以读取会出现乱码
char[]c=new char[10];
int len=0;
len=isr.read(c);
String s=new String(c,0,len);
System.out.println(s);
isr.close();
}
public static void writeText()throws IOException
{
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("gbk.txt"),"utf-8");//将字符以utf-8编码写入
osw.write("你好");
osw.close();
}
}


字符编码问题:

编码:将字符串按特定字符集编码成字节数组。

解码:将字符数组按特定字符集解码成字符串。

编码时用的什么字符集,解码时就要用相同的字符集,否则就会出现乱码。‘

编程:当解码时指定错误字符集,怎么得到原来的数据。(将其再进行编码操作,然后用正确的字符集解码)

import java.util.*;
class CharEncodeDemo
{
public static void main(String[]args)throws Exception
{
String s="你好";
byte[]b1=s.getBytes("gbk");
System.out.println(Arrays.toString(b1));//程序采用gbk字符集对字符串进行编码

String s1=new String(b1,"ISO8859-1");//程序不小心采用ISO8859-1字符集进行了解码,出现了乱码问题。
System.out.println(s1);

byte[]b2=s1.getBytes("ISO8859-1");//解决方法,先将得到的字符串用ISO8859-1字符集进行编码,因为每个字节数字还是一样的
System.out.println(Arrays.toString(b2));
String s2=new String(b2);//然后用gbk字符集进行解码,就得到了正确字符串
System.out.println(s2);

}
}
结果截图:
![这里写图片描述](http://img.blog.csdn.net/20161025163616511)

//注意:当解码时用utf-8出错时,用编码再解码的方法并不能解决乱码问题。


字符编码里的特殊字符。“联通”

“新建一个文本文档,输入”联通”,保存退出,再次打开就会出现乱码情况。”

UTF-8为了节约空间,在每个字节前面加上了标识符,来确定每次读出的字节。



编写程序查看联通两个字的二进制组成

class TestDemo
{
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));//&是只取后八位
}
}
}

这里看到联通两个字的二进制形式和utf-8的形式非常像。
记事本打开时是用了gbk编码方式,当写入联通时,会将其当成utf-8编码来查,就出现了这种特殊情况。


IO流练习程序:

五个学生,每个学生有三门成绩,从键盘输入以上数据,格式为 姓名,成绩,成绩,成绩

计算出总成绩,并把学生信息和总成绩高低顺序存放在stu.txt文件中。

思路:

1.定义学生类,学生类内部有比较方法,按照总成绩高低进行排序

2.定义一个集合,将学生对象存入

3.将集合内容输入在文件中

import java.io.*;
import java.util.*;
class Student implements Comparable<Student>//创建学生类,实现Comparable接口,以达到类本身具有排序功能
{
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 hashCode()
{
return name.hashCode()+10*sum;
}
public boolean equals(Object obj)//判断是否是学生类型
{
if(!(obj instanceof Student))
throw new ClassCastException("类型不匹配");
Student s=(Student)obj;
return (s.getName()).equals(this.name)&&s.getSum()==this.sum;
}
public int compareTo(Student s)//实现排序方法
{
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;
}
}
class StudentTools//操作学生的工具类
{
public static Set<Student> getStudents()throws IOException//将键盘输入的值创建为学生对象,并且存在集合中
{
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
TreeSet<Student> ts=new TreeSet<Student>();
String[]str=new String[4];
String line=null;
while (!(line=br.readLine()).equals("over"))
{
int sum=0;
str=line.split(",");
int n1=Integer.parseInt(str[1]);
int n2=Integer.parseInt(str[2]);
int n3=Integer.parseInt(str[3]);
ts.add(new Student(str[0],n1,n2,n3));
}
br.close();
return ts;
}
public static void writeFile(Set<Student> stus)throws IOException//将集合中的数据写入文件中
{
BufferedWriter bw=new BufferedWriter(new FileWriter("stu.txt"));
for (Student stu:stus)
{
bw.write(stu.getName());
bw.write("::");
bw.write(stu.getSum()+" ");
bw.newLine();
}
bw.close();
}
}
class IODemo//主函数
{
public static void main(String[]args)throws IOException
{
StudentTools st=new StudentTools();
Set<Student> s=st.getStudents();
st.writeFile(s);
}
}


IO流学习到此告一段落,IO流太多的流对象,要多作练习,熟能生巧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java
相关文章推荐