Java基础视频教程第21天_IO流之其他流对象、字符编码

一、IO流——对象的序列化 

	java.lang.Object 
		-java.io.InputStream 
			-java.io.ObjectInputStream 
			public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants 

	java.lang.Object 
		-java.io.OutputStream 
			-java.io.ObjectOutputStream 
			public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants 

	java.io.Serializable 
	public interface Serializable 

	操作对象
		ObjectInputStream 
		ObjectOutputStream 
		被操作的对象需要实现 Serializable(标记接口)。

	构造方法:
		ObjectInputStream(InputStream in) :创建从指定 InputStream 读取的 ObjectInputStream。
		ObjectOutputStream(OutputStream out) :创建写入指定 OutputStream 的 ObjectOutputStream。
	特有方法:
		ObjectInputStream 中:  Object readObject() 从 ObjectInputStream 读取对象。 
		ObjectOutputStream 中: void writeObject(Object obj) 将指定的对象写入 ObjectOutputStream 。 
	=====对象的持久化、对象的序列化、对象的可串行性。

	=====没有方法的接口,通常称为标记接口。

	=====类中的静态成员不可以被序列化。(可以将堆内存中的数据序列化,却不可以将方法区中的数据序列化)

	=====如果不需要将非静态成员序列化,可以用 transient 修饰该成员。

	=====序列化的对象一般不存成 .txt文件,而是存成 .object ;例如:person.object 

	代码:
	import java.io.*;
	class ObjectStreamDemo 
	{
		public static void main(String[] args) throws Exception 
		{
			//writeObj();
			readObj();
		}

		public static void writeObj() throws IOException 
		{
			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));

			oos.writeObject(new Person("wangwu",18));

			oos.close();
		}

		public static void readObj() throws Exception 
		{
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));

			Person p = (Person)ois.readObject();

			System.out.println(p);
		}
	}

	import java.io.*;

	class Person implements Serializable
	{
		static final long serialVersionUID = 42L;
		String name;
		int age;
		Person(String name, int age)
		{
			this.name = name;
			this.age = age;
		}
		public String toString()
		{
			return name+":"+age;
		}
	}

二、IO流——管道流 

java.lang.Object 
-java.io.InputStream 
-java.io.PipedInputStream 

java.lang.Object 
-java.io.OutputStream 
-java.io.PipedOutputStream 

IO包中的其他类

管道流:
PipedInputStream 和 PipedOutputStream 
输入输出可以直接进行连接,通过结合线程使用。

	该类都分别有两个构造方法,一个是空参数的构造方法,另一个需要传入管道流对象;
	空参数的构造方法表示创建时尚未建立连接,传入对象的构造方法则在创建时连接到指定的管道流。

	注意:如果创建尚未连接的管道流,需要调用connect方法进行连接,以下两个任选一个即可:
	PipedInputStream 类中的方法: void connect(PipedOutputStream src) 使此管道输入流连接到管道输出流 src。 
	PipedOutputStream 类中的方法: void connect(PipedInputStream snk) 将此管道输出流连接到接收者。 

	代码:
	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秒后开始写入数据......");

				try{Thread.sleep(6000);}catch(Exception e){}

				out.write("piped aaaa bb".getBytes());

				out.close();
			}
			catch (IOException 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();
		}
	}

三、IO流—— RandomAccessFile 

java.lang.Object 
-java.io.RandomAccessFile 

public class RandomAccessFile extends Object implements DataOutput, DataInput, Closeable 

IO包中的其他类
RandomAccessFile: 
随机访问文件,自身具备读写的方法;
通过skipBytes(int x);seek(int x)来达到随机访问。

构造方法摘要:
RandomAccessFile(File file, String mode) :创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。 
RandomAccessFile(String name, String mode) :创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。 
		其他特点:
		1、该类不算是IO体系中的子类,而是直接继承 Object 。

		2、它是IO包中的成员,具备读写功能。

		3、内部封装了一个数组,而且通过指针对数组中的元素进行操作:可以通过getFilePointer 获取指针的位置;也可以通过seek改变指针的位置。

		4、其实完成读写的原理就是内部封装了字节输入流和输出流。

		5、通过构造函数可以看出,该类只能操作文件,而且还需指定模式: 只读 r ; 读写 rw 。

		6、如果模式为读写rw,该对象的构造函数要操作的文件不存在时,会自动创建,如果存在则不会覆盖。

		7、如果模式为只读r,不会创建文件,会去读取一个已存在的文件;如果该文件不存在,则会出现异常。

		8、该类中的方法可以直接写入和读取基本数据类型。

	代码:
	import java.io.*;
	class RandomAccessFileDemo 
	{
		public static void main(String[] args) throws IOException 
		{
			writeFile_2();
			//readFile();
		}

		public static void readFile() throws IOException
		{
			RandomAccessFile raf = new RandomAccessFile("ran.txt","r");

			//调整数组中的指针
			//raf.seek(8*0);

			//跳过指定的字节数,只可以往下跳
			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);

			raf.close();
		}

		public static void writeFile() throws IOException
		{
			RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");

			raf.write("李四".getBytes());
			//raf.write(258);
			raf.writeInt(97);
			raf.write("王五".getBytes());
			raf.writeInt(99);

			raf.close();
		}

		public static void writeFile_2() throws IOException 
		{
			RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");

			raf.seek(8*3);
			raf.write("周七".getBytes());
			raf.writeInt(103);

			raf.close();
		}
	}

四、操作基本数据类型的流对象DataStream 

java.lang.Object 
-java.io.OutputStream 
-java.io.FilterOutputStream 
-java.io.DataOutputStream 
public class DataOutputStream extends FilterOutputStream implements DataOutput 

java.lang.Object 
-java.io.InputStream 
-java.io.FilterInputStream 
-java.io.DataInputStream 
public class DataInputStream extends FilterInputStream implements DataInput 

操作基本数据类型
DataInputStream 与 DataOutputStream 

代码:
	import java.io.*;

	class DataStreamDemo 
	{
		public static void main(String[] args) throws IOException 
		{
			//writeData();
			//readData();
			//writeUTFDemo();
			readUTFDemo();
		}

		public static void writeData() throws IOException 
		{
			DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));

			dos.writeInt(234);
			dos.writeBoolean(true);
			dos.writeDouble(9887.543);

			dos.close();
		}

		public static void readData() throws IOException 
		{
			DataInputStream dis = new DataInputStream(new FileInputStream("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();
		}

		public static void writeUTFDemo() throws IOException 
		{
			DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdata.txt"));

			dos.writeUTF("你好");
			
			dos.close();
		}

		public static void readUTFDemo() throws IOException 
		{
			DataInputStream dis = new DataInputStream(new FileInputStream("utfdata.txt"));

			String s = dis.readUTF();

			System.out.println(s);

			dis.close();
		}
	}

五、IO流 —— ByteArrayStream 

java.lang.Object 
-java.io.InputStream 
-java.io.ByteArrayInputStream 

java.lang.Object 
-java.io.OutputStream 
-java.io.ByteArrayOutputStream 

操作字节数组
ByteArrayInputStream 与 ByteArrayOutputStream 

ByteArrayInputStream: 在构造的时候,需要接受数据源,而且数据源是一个字节数组;
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。

因为这两个流对象操作的都是数组,并没有使用系统资源。所以不用进行close()关闭。

在操作流规律讲解时,源设备和目的设备的补充定义:
源设备:
键盘:System.in  硬盘:FileStream  内存:ArrayStream 
目的设备:
控制台:System.out  硬盘:FileStream  内存:ArrayStream

===这两个对象的出现可以用流的思想操作数组。对数组的操作只有设置值和获取值,反映到IO流中就是写和读。

ByteArrayOutputStream 类中还有一个特殊的方法: 
void writeTo(OutputStream out) :将此 byte 数组输出流的全部内容写入到指定的输出流参数中,这与使用 out.write(buf, 0, count) 调用该输出流的 write 方法效果一样。 

操作字符数组
CharArrayReader 与 CharArrayWriter 

操作字符串
StringReader 与 StringWriter 

操作字节数组代码:
	import java.io.*;

	class ByteArrayStream 
	{
		public static void main(String[] args) throws IOException 
		{
			//数据源
			ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFG".getBytes());

			//数据目的
			ByteArrayOutputStream bos = new ByteArrayOutputStream();

			int by = 0;
			
			while((by=bis.read())!=-1)
				bos.write(by);

			System.out.println(bos.size());
			System.out.println(bos.toString());

			bos.writeTo(new FileOutputStream("bos.txt"));
		}
	}

六、IO流——转换流的字符编码

字符流的出现是为了方便操作字符;
更重要的是加入了编码转换;
通过子类转换流来完成:
InputStreamReader 
OutputStreamWriter 
在两个对象进行构造的时候可以加入字符集。

编码表的由来:
计算机智能识别二进制数据,早期由来是电信号;
为了方便应用计算机,让它可以识别各个国家的文字;
就将各个国家的文字用数字来表示,并一一对应,形成一张表;
这就是编码表。

常见编码表:
ASCII:美国标准信息交换码;
用一个字节的7位可以表示。

ISO8859-1:拉丁码表,欧洲码表;
用一个字节的8位表示。

GB2312:中国的中文编码表。

GBK:中国的中文编码表升级,融合了更多的中文文字符号。

Unicode:国际标准码,融合了多种文字。

UTF-8:最多用三个字节来表示一个字符。

代码:
	import java.io.*;

	class EncodeStream
	{
		public static void main(String[] args) throws IOException
		{
			//writeText();
			readText();
		}

		public static void writeText() throws IOException
		{
			//OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"));

			OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");

			osw.write("你好");

			osw.close();
		}

		public static void readText() throws IOException
		{
			//InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"UTF-8");

			InputStreamReader isr = new InputStreamReader(new FileInputStream("UTF.TXT"),"GBK");

			char[] buf = new char[10];

			int len = isr.read(buf);

			String str = new String(buf,0,len);

			System.out.println(str);

			isr.close();
		}
	}

七、字符编码

编码:字符串变成字节数组。

解码:字节数组变成字符串。

String-->byte[] : str.getBytes();
 str.getBytes(String charsetName);

byte[]-->String : new String(byte[]);
 new String(byte[], String charsetName);

代码:
	import java.util.*;
	import java.io.*;

	class EncodeDemo  
	{
		public static void main(String[] args) throws IOException 
		{
			String s = "你好";

			//编码
			byte[] b1 = s.getBytes();

			System.out.println(Arrays.toString(b1));//[-60, -29, -70, -61]

			//解码
			String s1 = new String(b1,"GBK");
			System.out.println("s1="+s1);
			
			//编码
			byte[] b2 = s.getBytes("UTF-8");

			System.out.println(Arrays.toString(b2));//[-28, -67, -96, -27, -91, -67]

			//解码
			String s2 = new String(b2,"UTF-8");
			System.out.println("s2="+s2);


			//解码错误时,解决办法:再编一次然后再解码(此法不适用于GBK与UTF-8之间的转换)
			String s3 = new String(b1,"ISO8859-1");
			System.out.println("s3="+s3);//s3=????

			byte[] b3 = s3.getBytes("ISO8859-1");//再次编码
			System.out.println(Arrays.toString(b3));//[-60, -29, -70, -61]

			String s4 = new String(b3,"GBK");//再次解码
			System.out.println(s4);//你好
		}
	}

八、字符编码——联通 

当我们新建一个txt文件,并在文件中存入“联通”两个字,然后保存。
第二次再次打开时,显示的不是“联通”两个子,而是两个乱码。

这是因为:
我们操作系统默认的是使用GBK的编码,所以在保存“联通”两个字时,就使用GBK编码将“联通”保存四个字节;
但是这四个字节用二进制表示出的结果符合UTF-8读取的标准;
所以,我们再次打开这个文件是,使用的是UTF-8解码;
这样就导致了编码和解码所使用的编码表不一致,所以出现了乱码。

当使用默认编码表解码时,到底使用什么编码表是根据标识头来决定的,如果符合UTF-8的标识头则使用UTF-8解码;如果不符合则使用GBK解码。


UTF-8标识头信息表:


“联通”用GBK编码后的二进制表现形式如下:
11000001
10101010
11001101
10101000
第一个字节和第二个字节符合UTF-8读取两个字节的标识头,第三个字节和第四个字节也符合UTF-8读取两个字节的标识头。
那么系统在对该文件进行解码时,就会默认的使用UTF-8解码。
最终导致了编码和解码的不一致。

解决办法:
如果只是保存这两个字,那么则在保存时选择UTF-8保存,就可以解决问题。


验证代码:

	import java.util.*;

	class EncodeDemo2 
	{
		public static void main(String[] args) throws Exception 
		{
			String s = "联通";

			byte[] b1 = s.getBytes("GBK");

			System.out.println(Arrays.toString(b1));//[-63, -86, -51, -88]

			for (byte b : b1)
			{
				System.out.println(Integer.toBinaryString(b&255));
				/*
				11000001
				10101010
				11001101
				10101000
				*/
			}
		}
	}

九、练习

有五个学生,每个学生有3门课的成绩;
从键盘输入以上数据(包括姓名,三门课成绩);
输入的格式:如:zhangsan,30,40,60 计算出总成绩;
并把学生的信息和计算出的总分数按高低顺序存放在磁盘文件“stud.txt”中。

步骤:
1、描述学生对象;
2、定义一个可以操作学生对象的工具类;

思路:
1、通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象;
2、使用TreeSet集合对学生进行存储;
3、将集合中的信息写入到文件中。
代码:
	import java.io.*;
	import java.util.*;

	class Student implements Comparable<Student>
	{
		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 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;
		}

		public int hashCoede()
		{
			return name.hashCode()+sum*71;
		}

		public boolean equals(Object obj)
		{
			if(!(obj instanceof Student))
				throw new ClassCastException("类型不匹配");
			Student s = (Student)obj;
			
			return this.name.equals(s.name) && this.sum==s.sum;
		}

		public String toString()
		{
			return "studentt["+name+": "+ma+", "+cn+", "+en+"]";
		}
	}

	class StudentInfoTool
	{
		public static Set<Student> getStudents() throws IOException
		{
			return getStudents(null);
		}

		public static Set<Student> getStudents(Comparator<Student> cmp) throws IOException
		{
			BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

			String line = null;

			Set<Student> stus = null;

			if(cmp==null)
				stus = new TreeSet<Student>();
			else
				stus = new TreeSet<Student>(cmp);

			while((line=bufr.readLine())!=null)
			{
				if ("over".equals(line))
					break;
				String[] info = line.split(",");
				Student stu = new Student(info[0],Integer.parseInt(info[1]),Integer.parseInt(info[2]),Integer.parseInt(info[3]));

				stus.add(stu);
			}
			bufr.close();
			return stus;
		}

		public static void write2File(Set<Student> stus) throws IOException
		{
			BufferedWriter bufw = new BufferedWriter(new FileWriter("stuinfo.txt"));

			for(Student stu : stus)
			{
				bufw.write(stu.toString()+"\t");
				bufw.write(stu.getSum()+"");
				bufw.newLine();
				bufw.flush();
			}
			bufw.close();
		}
	}

	class StudentInfoTest
	{
		public static void main(String[] args) throws IOException 
		{
			Comparator<Student> cmp = Collections.reverseOrder();

			Set<Student> stus = StudentInfoTool.getStudents(cmp);

			StudentInfoTool.write2File(stus);
		}
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值