黑马程序员——IO学习笔记(三)

------- 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,或者输入些其他的中文字符。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值