黑马程序员_java基础10--IO(2)和字符编码

来源:互联网 发布:ubuntu ntfs支持 编辑:程序博客网 时间:2024/06/01 12:38

------- android培训、java培训、期待与您交流! ----------
day21-IO流和字符编码-------------------------------------------------------------
@@对象的序列化!
把对象存在硬盘上,叫做对象的持久化、序列化、可串行性。
@@ObjectOutputStream 
将 Java 对象的基本数据类型和图形写入 OutputStream
ObjectOutputStream(OutputStream out) 特点:专门操作对象
          创建写入指定 OutputStream 的 ObjectOutputStream。
void writeObject(Object obj) 
          将指定的对象写入 ObjectOutputStream。 


@@ObjectInputStream(InputStream in) 
          创建从指定 InputStream 读取的 ObjectInputStream。
 Object readObject() 
          从 ObjectInputStream 读取对象。 
ObjectOutputStream  ObjectInputStream成对使用!
public static void writeObj()throws IOException
{
ObjectOutputStream oos = 
new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(new Person("lisi0",399,"kr"));//将对象存在硬盘上了
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);
ois.close();
}
//记住要序列化的对象类Person,要实现Serializable接口,
这个接口是标记接口,没有需要实现的函数。
但是这个接口会给实现它的类一个序列号UID,
如果这个类中有内容改变了,那么接口给它生成的序列号就改变了!


如果想在这个类改变了以后,序列号不变,可以给它定义一个序列号。
public static final long serialVersionUID = 42L;


静态是不能被序列化的,静态在方法区里,序列化主要是针对堆内存中的对象!
不想序列化非静态成员,在其前面加上 transient,这样这个非静态成员就会只存在于堆内存中,不会存到硬盘中!
@@管道流PipedInputStream&&PipedOutputStream
输入输出可以直接进行连接,通过结合线程使用。
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秒后。");
Thread.sleep(6000);
out.write("piped lai la".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();
}
}
@@RandomAccessFile 随机读写访问!!!重点掌握!!!!!!!!!!!
可以用来进行多线程下载,每个线程负责一段数据的下载!
该类主要可以同时进行基本数据的读和写!


该类不是算是IO体系中子类。
而是直接继承自Object。


但是它是IO包中成员。因为它具备读和写功能。
内部封装了一个数组,而且通过指针对数组的元素进行操作。
可以通过getFilePointer获取指针位置,
同时可以通过seek改变指针的位置。




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


通过构造函数可以看出,该类只能操作文件。
而且操作文件还有模式:只读r,,读写rw等。


如果模式为 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。
如果模式为 rw。并且操作的文件不存在,则会自动创建文件。如果文件存在则不会覆盖文件,可以修改文件内容。


RandomAccessFile(File file, String mode) 
          创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。 
RandomAccessFile(String name, String mode) 
          创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。 
含意
 
"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。  
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。  
"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。  
"rwd"   打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。  


 void seek(long pos) //设置指针的偏移量,单位是字节byte
          设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。 
 long getFilePointer() 
          返回此文件中的当前偏移量。 
 int skipBytes(int n) //返回跳过的字节数,只能往前跳,不能往回跳
          尝试跳过输入的 n 个字节以丢弃跳过的字节。 


public static void writeFile()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
sop(raf.getFilePointer());//0
raf.seek(8*1);//设置指针到 第8个字节
sop(raf.getFilePointer());//8
raf.write("李四".getBytes());//写在记事本上是 李四
raf.writeInt(98);//写在记事本上是 b
sop(raf.getFilePointer());//16

raf.seek(8*1);//设置指针到 第8个字节
byte[] buf = new byte[4];
raf.read(buf);
sop(new String(buf));//李四
int age = raf.readInt();
sop(age);//98


raf.close();
}
@@DataInputStream && DataOutputStream操作基本数据类型的流对象


public static void writeData()throws IOException
{
DataOutputStream dos = 
new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeInt(55);
dos.writeBoolean(false);
dos.writeDouble(4545.666);
dos.close();
}
public static void readData()throws IOException
{
DataInputStream dis = 
new DataInputStream(new FileInputStream("data.txt"));
sop(dis.readInt());
sop(dis.readBoolean());
sop(dis.readDouble());
dis.close();
}
public static void writeUTFDemo()throws IOException
{
DataOutputStream dos = 
new DataOutputStream(new FileOutputStream("utfdata.txt"));
dos.writeUTF("你好");//修改版的utf-8 八个字节
dos.close();
}
public static void readUTFDemo()throws IOException
{
DataInputStream dis = 
new DataInputStream(new FileInputStream("utf.txt"));
String s = dis.readUTF();
System.out.println(s);
dis.close();
}
@@ByteArrayStream 操作字节数组的流对象!
public class ByteArrayInputStream
extends InputStreamByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。 
关闭 ByteArrayInputStream 无效。此类中的方法在关闭此流后仍可被调用,
而不会产生任何 IOException。 


ByteArrayInputStream(byte[] buf) 
          创建一个 ByteArrayInputStream,使用 buf 作为其缓冲区数组。 
ByteArrayInputStream(byte[] buf, int offset, int length) 
          创建 ByteArrayInputStream,使用 buf 作为其缓冲区数组。 
从构造函数看,没有调用底层资源(没有与硬盘中的文件关联)




public class ByteArrayOutputStream
extends OutputStream此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。 
关闭 ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。


ByteArrayOutputStream() 
          创建一个新的 byte 数组输出流。 
ByteArrayOutputStream(int size) 
          创建一个新的 byte 数组输出流,它具有指定大小的缓冲区容量(以字节为单位)。 


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


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


在流操作规律讲解时:
*************************************************************************
源设备:  键盘 System.in,硬盘 FileStream,内存 ArrayStream。
目的设备:控制台 System.out,硬盘FileStream,内存 ArrayStream。
用流的读写思想来操作数据。


@@同理CharArrayReader&&CharArrayWriter==StringRader&&StringWriter
还有ByteArrayStream,这三种流对象都是操作的内存中的数据!


@@转换流的字符编码
一 ASCII:美国的标准信息交换码:用一个字节的七位可以表示。
一 ISO8859-1:拉丁码表。欧洲码表,用一个字节的八位表示。
     最高位为1。


二 GBK2312:中国的中文编码表(六七千)
二 GBK:中国的中文编码表升级版(两万多),融合了更多的中文文字符号。
两个字节的高位都是1。(中文码表兼容ASCII)
二 Unicode:国际标准码,融合了多种文字。
所有文字都用两个字节来表示,Java语言使用的就是Unicode
三 UTF-8:最多用三个字节来表示一个字符。给每个字节都加了一个标识头信息
io中的DataInput接口中看 UTF-8的修改版
    一位 0xxxxxxx
    二位 110xxxxx 10xxxxxxxx
    三位 1110xxxx 10xxxxxxxx 10xxxxxxxx


编码:字符串变成字节数组。
解码:字节数组变成字符串。
charsetName可以是 gbk或者utf-8、ISO8859-1(tomcat服务器默认的)
String -->byte[]:  str.getBytes(charsetName);
byte[] -->String:  new String(byte[],charsetName);


String s = "你好";


byte[] b1 = s.getBytes("GBK");
System.out.println(Arrays.toString(b1));//[-60, -29, -70, -61]
String s1 = new String(b1,"ISO9958-1");//解码使用的码表错误,产生乱码 s1
System.out.println("s1="+s1);//????


怎么解决 解码码表错误的 问题?
也就是想将乱码s1代表的内容 给解析出来!
那么就先将s1 再进行一次ISO9958-1错误码表的编码,将乱码变成[-60, -29, -70, -61]
再按照正确的解码码表进行解码就行了!


byte[] b2 = s1.getBytes("ISO9958-1");//安照错误码表进行编码成[-60, -29, -70, -61]
System.out.println(Arrays.toString(b2));
String s2 = new String(b2,"gbk");//按照正确的码表进行解码!
System.out.println("s2="+s2);// 你好


gbk和utf-8之间编码错误就不能这样解决==》因为gbk和utf-8都识别中文


联通  问题?
联通的GBK编码后的字节数组
11000001
10101010
11001101
10101000
记事本误以为是按照utf-8编码的,所以按照utf-8码表进行解码。这样就解码成了乱码。
解决办法是,在联通的前面加个数据(这个数据必须不是utf-8能解码的!)
@@IO练习


/*
有五个学生,每个学生有3门课的成绩,
从键盘输入以上数据(包括姓名,三门课成绩),
输入的格式:如:zhagnsan,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 String getName()
{
return name;
}
public int getSum()
{
return sum;
}
public int hashCode()
{
return name.hashCode()+sum*33;
}
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 boolean equals(Object obj)
{
if(!(obj instanceof Student))
throw new RuntimeException("类型不匹配");
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);
}
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("student.txt"));
for(Student s : stus)
{
bufw.write(s.toString()+"\t");
bufw.write(s.getSum()+"");
bufw.newLine();
bufw.flush();
}
bufw.close();
}
}
class Test 
{
public static void main(String[] args) throws IOException 
{
Comparator<Student> comp = Collections.reverseOrder();
Set<Student> stus = StudentInfoTool.getStudents(comp);

StudentInfoTool.write2File(stus);
}


public static void sop(Object o)
{
System.out.println(o);
}
}

------- android培训、java培训、期待与您交流! ----------

原创粉丝点击