一、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); } }