黑马程序员-Java IO输入与输出-day21

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

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

操作对象:ObjectInputStream与ObjectOutputStream,被操作的对象需要实现Serializable(标记接口);

import java.io.*;
class Person implements Serializable
{
       //自定义UID值,保证UID值的唯一性
       public static final long serialVersionUID = 42L;
       String name;
       transient int age;//transient可以让age无法序列化,保证其值在堆内存中存在而不再文本文件中存在
       static String country="cn";
       Person(String name,int age,String country)
       {
              this.name = name;
              this.age = age;
              this.country = country;
       }
       public String toString()
       {
              return name+":"+age+"::"+country;
       }
}

import java.io.*;
class ObjectStreamDemo
{
       public static void main(String[] args) throws Exception
       {
              //writeObj();
              readObj();
       }
 
       public static void readObj()throws Exception
       {
              ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
 
              Person p = (Person)ois.readObject();
 
              System.out.println(p);
 
              ois.close();
       }
       public static void writeObj()throws IOException
       {
              ObjectOutputStream oos =
                     new ObjectOutputStream(new FileOutputStream("obj.txt"));
 
              oos.writeObject(newPerson("lisi",30,"cn"));
 
              oos.close();
       }
}
注意:在使用对象序列化的时候,必须是两者搭配使用,即用了ObjectInputStream就得用ObjectOutputStream;而其中,被静态所修饰的成员变量是无法被序列化的,另外transient也能让成员变量无法序列化

特别注意一点:静态时无法被序列化的

2、IO流——管道流

管道流分PipedInputStream和PipedOutputStream

输入输出可以直接进行连接,通过结合线程使用。

管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。

集合当中涉及到IO流的是Properties,IO当中涉及到多线程的就是管道流

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("读取前。。没有数据,阻塞");
                     intlen = in.read(buf);
                     System.out.println("读到数据。。阻塞结束");
                     Strings = 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秒后...");
                     Thread.sleep(6000);
                     out.write("pipedlai le".getBytes());//转换成字节流
                     out.close();
              }
              catch (Exception 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();
 
       }
}
小结:

管道流之所以不会造成线程死锁的原因是管道读取流中有一个read方法,只要没有读取到数据,该线程则会出现阻塞情况


3、IO流——RandomAccessFile

RadomAccessFile:

1.随机访问文件,自身具备读写的方法。

2.通过skipBytes(int x),seek(int x)来达到随机访问。

操作基本数据类型:DataInputStream与DataOutputStream

操作字节数组:ByteArrayInputStream与ByteArrayOutputStream

操作字符数组:CharArrayReader与CharArrayWrite

操作字符串:StringReader与StringWriter

RandomAccessFile该类不算是IO体系中的子类,而是直接集成自Object。但是他是IO包中成员,因为它具备读和写功能。内部封装了一个数组,而且通过指针对数组的元素进行操作。可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置。其实完成读写的原理就是内部封装了字节输入流和输出流。通过构造函数可以看出,该类只能操作文件。而且操作文件还有模式:只读r,读写rw等。如果模式为只读r,不会创建文件,然后去读取已存在的文件;如果该文件不存在,则会出现异常。如果模式为rw,操作的文件不存在,会自动创建,如果存在则也不会被覆盖。

public class RandomAccessFile extendsObjece implements DataOutput,DataInput,Closeable   此类的实例支持对随机访问文件的读取和写入

import java.io.*;
class RandomAccessFileDemo
{
       public static void main(String[] args) throws IOException
       {
              //writeFile();
              //readFile();
              writeFile_2();
       }
       public static void writeFile_2()throws IOException
       {
              RandomAccessFile raf = new RandomAccessFile("fan.txt","rw");
              raf.seek(8*1);
              raf.write("周七".getBytes());
              raf.writeInt(107);
 
              raf.close();
       }
       public static void readFile()throws IOException
       {
              RandomAccessFile raf = new RandomAccessFile("fan.txt","r");
      
              //调整对象中的指针
              //raf.seek(8*1);
 
              //跳过制定的字节数,缺点是不能往回跳
              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("fan.txt","rw");
              raf.write("李四".getBytes());
              raf.writeInt(97);//之所用writeInt是为了防止精度丢失
              raf.write("王五".getBytes());
              raf.writeInt(99);
 
              raf.close();
       }
}
小结:

1.由于write底层封装了只写出数据的最低八位,因此这样容易造成精度丢失,而为了防止出现这样的错误,于是就得将数据的最低八位都写出去,这样才能保证数据的原样性。

2.seek可以随机调整指针位置,而skipBytes只能向后调整指针位置。

 

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

操作基本数据类型:DataInputStream与DataOutputStream

操作字节数组:ByteArrayInputStream与ByteArrayOutputStream

操作字符数组:CharArrayReader与CharArrayWriter

操作字符串数组:StringReader与StringWriter

DataInputStream与DataOutputStream可以用于操作基本数据类型的数据的流对象。

import java.io.*;
class DataStreamDemo
{
       public static void main(String[] args) throws IOException
       {
              //writeData();
              //readData();
              //writeUTFDemo();
              //writeGBKDemo();
              readUTFDemo();
       }
       public static void readUTFDemo()throws IOException
       {
              DataInputStream dis = new DataInputStream(new FileInputStream("utfdata.txt"));
              String s = dis.readUTF();
              System.out.println(s);
 
              dis.close();
       }
       public static void writeGBKDemo()throws IOException
       {
              OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");
              osw.write("你好");
              osw.close();
       }
       public static void writeUTFDemo()throws IOException
       {
              DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdata.txt"));
              dos.writeUTF("你好");
              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 writeData()throws IOException
       {
              DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
 
              dos.writeInt(234);
              dos.writeBoolean(true);
              dos.writeDouble(9879.0);
 
              dos.close();
       }
}
小结:

1.切记一点,在读取数据的过程中,读取的顺序不能错,否则就会读取错误

2.凡是操作基本数据类型的就用DataStream

 

5、用于操作字节数组的流对象ByteArrayStream

ByteArrayInputStream:在构造的时候,需要接收数据源,而且数据源是一个字节数组。

ByteArrayOutputStream:在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。

public class ByteArrayInputStream extendsInputStream     ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪read方法要提供的下一个字节。关闭ByteArrayInputStreamReader无效。此类中的方法在关闭此流后仍可被调用而不会产生任何IOException。

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

在流操作规律讲解时:

源设备:

       键盘:System.in;   硬盘:FileStream;      内存:ArrayStream

目的设备:

       控制台:System.out;    硬盘:FileStream;      内存:ArrayStream

用流的读写思想 来操作数据

import java.io.*;
class ByteArrayStream
{
       public static void main(String[] args)
       {
              //数据源
              ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEF".getBytes());
 
              //数据目的
              ByteArrayOutputStream bos = new ByteArrayOutputStream();
              int ch =0;
              while((ch=bis.read())!=-1)
              {
                     bos.write(ch);
              }
              System.out.println(bos.size());
              System.out.println(bos.toString());
       }
}

6、转换流的字符编码

字符编码

1.字符流的出现为了方便操作字符。

2.更重要的是加入了编码符集。

3.通过子类转换来完成。InputStreamReader和OutputStreamWriter

4.在两个对象进行构造的时候可以加入字符集。

 

编码表的由来:

1.计算机只能识别二进制数据,早起由来是电信号;

2.为了方便应用计算机,让它可以识别各个国家的文字;

3.就将各个国家的文字用数字来表示,并一一对应,形成一张表,这就是编码表。

常见的编码表

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

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

GBK2312:中国的中文编码表。

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

Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,Java语言使用的就是unicode

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

import java.io.*;
class EncodeStream
{
       public static void main(String[] args) throws IOException
       {
              //writeText();
              readText();
       }
       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);
 
              isr.close();
       }
       public static void writeText()throws IOException
       {
              OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"GBK");
              osw.write("你好");
              osw.close();
       }
}


7、字符编码

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

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

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

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

编码解码的时候要是遇到解错码,且都是中文,一定得注意不能使用UTF-8去解码,因为都识别中文

import java.util.*;
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);
 
 
              //解码
              byte[] b2 =s1.getBytes("iso8859-1");
              System.out.println(Arrays.toString(b2));
              String s2 = new String(b2,"GBK");
              System.out.println("s2="+s2);
       }
}
注意:由于UTF-8和GBK均识别中文,因此在对字符串进行编码解码的过程中,千万要注意这点,否则就会得到乱码;Tomcat服务器使用的就是ISO8859-1的编码表,当数据传递给服务器时,需要通过编码解码才能得到最终数据。


8、字符编码—联通

由于联通的二进制编码表正好也符合UTF-8的编码表,因此在记事本解码联通两个字的时候,就会解析出错

联通二进制最后八位:

11000001

10101010

11001101

10101000

刚好符合UTF-8中的110    10的解码方式,因此就会导致解码出错。

class EncodeDemo2
{
       public static void main(String[] args) throws Exception
       {
              String s = "联通";
              byte[] by = s.getBytes();
              for(byte b : by)
              {
                     System.out.println(Integer.toBinaryString(b&255));
              }
       }
}

练习:有五个学生,每个学生有3门课的成绩,从键盘输入以上数据(包括姓名,三门课成绩),输入的格式

如:zhangsan,30,40,60计算出总成绩,并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。

 

1.描述学生对象;

2.定义一个可操作学生对象的工具类。

 

思路:

1.通过获取键盘录入一行数据,并将改行中的信息取出封装成学生对象。

2.因为学生有很多,那么就需要存储,使用到集合,因为要对学生的总分进行排序。所以可以使用TreeSet。

3.将集合的信息写入到一个文件中。

import java.io.*;
import java.util.*;
//定义一个学生类,并实现Comparable接口
class Student implementsComparable<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并不是传递进来的,因此这里是前者的累加和
              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;
       }
//定义一个hashCode方法,防止获取的值存到其他集合中
       public int hashCode()
       {
              return name.hashCode()+sum*78;
       }
       public boolean equals(Object obj)
       {
              if(!(obj instanceof Student))
                     thrownew ClassCastException("类型不匹配");
              //强制类型转换
              Student s = (Student)obj;
              return this.name.equals(s.name)&& this.sum==s.sum;
       }
 
       public String toString()
       {
              return "Student["+name+","+ma+", "+cn+", "+en+"]";
       }
 
}


//定义一个学生工具类
class StudentInfoTool
{
//定义一个不含比较器的方法
       public static Set<Student> getStudents()throws IOException
       {
              return getStudents(null);
       }
//定义一个含Comparator接口的方法
       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(",");
                     Studentstu = 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循环遍历集合
              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.writeTOFile(stus);
       }
}
注意:

reverse()只是反转的,而reverseOrder()则是反转比较器的,不能记错!



---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------
详细请查看:http://edu.csdn.net
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值