------- android培训、java培训、期待与您交流! ----------
ObjectOutputBean
class ObjectStreamDemo{
public static void main(String[] args){
writeObj();
}
public static void writeObj()throws IOException{//将一个Person对象持久化到文件中。
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream());
oos.writeObject(new Person("lisi",39));//方法参数必须是可序列化的类对象。
oos.close();
}
public static void readObj()throws ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.txt"));
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();
}
}
class Person implements Serializable{//实现Serializable接口,表示该类是可序列化的
//private static final long serialVersionUID = 42L;//如果不让jvm自动算出UID,而是自己设置好UID值,那么该对象内成员即使改变,也会认为与改变前的持久化对象匹配。
String name;
transient int age;//用transient修饰的变量不能被序列化。
static String country = "cn";//静态成员不能被序列化。
Person(){
this.name = name;
this.age = age;
}
public String toString(){
return name+" : "+age;
}
}
标记接口:没有方法的接口。
常用标记接口:序列化接口 Serializable:实现该接口类中会有一个序列号标记serilVersionUID,如果该类对象被持久化了以后,类如果改变了,则其序列号相应的改变。所以序列号可以用来判断类与其持久化的对象是否匹配。
serilVersionUID的数值并不是随机的,而是根据类中的成员算出的,类中无论是成员变量还是成员方法,都具备一个数字标识,根据此标识算出的号作为此UID的数值。
*注意:静态是不能被序列化的,因此静态成员不会被ObjectOutputStream操作。(原因是对堆内存中的对象序列化,不能讲方法区内的部分序列化)
管道流:PipedInputStream和PipedOutputStream:将输入流和输出流相连接(实现方式其实是双线程同时进行,一个线程执行输入,另一个线程执行输出)
class PipedStreamDemo{
public stati void main(String[] args)throws IOException{
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputSteeam();
in.connect(out);
Read r = new Read();
Write w = new Write();
new Thread(r).start();
new Thread(w).start();
}
}
class Read implement 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秒后")
sleep(6000);
out.write("piped lai la".getBytes());
out.close();
}
catch(Exception e){
throw new RuntimeException("管道输出流失败");
}
}
}
RandomAccessFile:随机访问文件
该类不算是IO体系中的子类,而是直接继承自Object。
但是它是IO包中的成员,因为它具备读和写功能,内部封装了一个数组,而且通过指针对数组中的元素进行操作。
可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置。
其实完成读写的原理就是内部封装了字节输入流和输出流。
通过构造函数可以看出,该类只能操作文件。而且操作文件还有模式,(只读r、读写rw等。。)
如果模式为只读r,不会创建文件,会去读取一个已存在的文件,如果该文件不存在,则会出现异常。
如果模式为读写rw,操作文件不存在,会自动创建,如果存在则不会覆盖。
class RandomAccessFileDemo{
public static void main(String[] args)throws IOException{
readFile();
}
public static void writeFile()throws IOException{
RandomAccessFile raf = new RandomAccessFile("ran.txt", "rw");
raf.write("李四".getBytes());
raf.writeInt(97);//由于write方法只写最低8位2进制数,因此出现丢失数据情况。如果数字超过255,则使用writeInt();方法
raf.write("王五".getBytes());
raf.writeInt(99);
raf.close();
}
public static void readFile()throws IOException{
RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
//调整对象中指针。(可以设置前后任意位置)
raf.seek(8);
//跳过指定的字节数。(只能往后跳,不能往前跳)
raf.skipBytes(8);
byte[] buf = new byte[4];
raf.read(buf);
String s = new String(buf);
int age = raf.readInt();
System.out.println("name="+name);
System.out.println("age="+age);
raf.close();
}
//随机写
public static void writeFile_2(){
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
//偏移到第四个位置
raf.seek(8*3);
raf.write("周七".getBytes());
raf.writeInt(103);
raf.close();
}
}
DataInputStream和DataOutputSteam:
可以用于操作基本数据类型的数据的流对象
class DataStreamDemo{
public static void main(String[] args){
//writeData();
//writeUTFDemo();
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"utf-8")
osw.write("你好");
osw.close();
}
public static void writeData()throws IOException{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeInt(234);//写入4字节
dos.writeBoolean(true);//写入1字节
dos.writeDouble(9887.543);//写入8字节
dos.close();
}
public static void readData(){
DataInputStream dis = new DataInputStream(new FileInputStraem("data.txt"));
int num = dis.readInt();
boolean b = dis.readBoolean();
double d = dis.readDouble();
System.out.println("num="+num);
System.out.println("b=" + b);
System.out.println("d="+ d);
dis.close();
}
//写入修改版UTF-8数据
public static void writeUTFDemo() throws IOException{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfData.txt"));
dos.writeUTF("你好");//添加UTF-8修改版格式数据。由于其是修改版,所以目前如果用此方法写入的数据,只能用DataInputStream中的readUTF();方法读取
dos.close();
}
//读取修改版UTF-8数据
public static void readUTFDemo()throws IOException{
DataInputStream dis = new DataInputStream(new FileInputStream("utfData.txt"));
String s = dis.readUTF();
System.out.println(s);
dis.close();
}
}
字节数组操作:ByteArrayInputStream和ByteArrayOutputStream
ByteArrayInputStream:在构造的时候,需要接收数据源,而且数据源是一个字节数组。
ByteArrayOutputStream:在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组,这就是数据目的地。
因为这链各个流对象都操作了数组,并没有使用系统资源,所以不用进行close关闭。
class ByteArrayStream{
public static void main(String[] args)throws IOException{
//数据源
ByteArrayInputStream bais = new ByteArrayInputStream("ABCDEFD".getBytes());
//数据目的
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int by = 0;
while((by = bis.read())!= -1){
bos.write(by);//将数据写进bos内部封装的动态数组里
}
System.out.println(bos.size());
System.out.println(bos.toString());
bos.writeTo(new FileOutputStream("a.txt"));//将数组流的内容写入另一个字节输出流中。
}
}
字符编码问题:
class EncodeStream{
public static void main(String[] args){
writeText();
}
public stati void writeText(){
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"GBK");//默认也是GBK,4字节
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");//6字节
osw.write("你好");
osw.close();
}
public static void readText()throws IOException{//读取方法
InputStreamReader isr = new InputStreamReader(new FileinputStream("gbk.txt"),"GBK");
char[] buf = new char[10];
int len = isr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
is.close();
}
}
编码:字符串变成字节数组。
解码:字节数组变成字符串。
String --> byte[]; str.getBytes();
byte[] --> String;new String(byte[]);
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);
//以上情况,将中文编码为GBK后,错误的解码,导致乱码,如何解决?
//解决:1,对s1进行iso8859-1编码
byte[] b2 = s1.getBytes("iso8859-1");
//2,编码后的字符按照另一种编码表解码
String s2 = new String(b2,"GBK");
}
}
*如果将GBK编码字节使用utf-8解码出,将会出现不可恢复的问题。
*“联通”问题:
描述:新建记事本后,当只在里面输入“联通”二字后,关闭记事本再重新打开,出现乱码。
分析原因:
先分析UTF-8的编码方式:
1字节:第一个字节:0开头
2字节:第一个字节:110开头,第二个字节:10开头
3字节:第一个字节:1110开头,第二个字节:10开头,第三个字节:10开头
由于“联通”二字按照GBK编码后,二进制内容为11000001 10101010 11001101 10101000恰巧与UTF-8编码规律一致,因此造成记事本程序误判成UTF-8编码形式。
所以如果不让系统误判成utf-8格式,需要调整记事本的编码格式为GBK,或者输入些其他的中文字符。