Java IO 专题
一、编码问题
从联通.txt和联.txt显示不正常,进而找到编码问题。
0xff 就是16进制数,前面全是0,后8位全为1。
import java.io.UnsupportedEncodingException;
public class EncodeDemo {
public static void main(String[] args) throws UnsupportedEncodingException
{
String s = "慕课ABC";
//获得本地默认的编码
byte[] bytes1 = s.getBytes();
for(byte b : bytes1){
//把字节转换成int,以16进制的方式显示
System.out.print(Integer.toHexString(b & 0xff)+" ");
}
System.out.println();
//转换成gbk编码
byte[] bytes2 = s.getBytes("gbk");
//gbk编码中文占用2个字节,英文占用1个字节
for(byte b : bytes2){
System.out.print(Integer.toHexString(b & 0xff)+" ");
}
System.out.println();
//utf-8 中,中文占3个字节,英文占1个字节,一个字节8bit
byte[] bytes3 = s.getBytes("utf-8");
for(byte b : bytes3){
System.out.print(Integer.toHexString(b & 0xff)+" ");
}
//
System.out.println();
byte[] byte5 = new byte[]{(byte) 01110111,(byte) 11100101};
String str0 = new String(byte5,"utf-16be");
System.out.println(str0);
//ystem.out.println(Integer.toHexString(0xa1+0xa2+0xb4+0xff),"GBK");
//java是双字节编码 utf-16be
//utf-16be 中文占用两个字节,英文也是占用两个字节
byte[] bytes4 = s.getBytes("utf-16be");
for(byte b : bytes4){
System.out.print(Integer.toHexString(b&0xff)+" ");
}
System.out.println();
/*
* 当你的字节序列是某种编码时,这个时候想把字节序列变成
* 字符串,也需要用这种编码方式,否则会出现乱码
*/
String str1 = new String(bytes4);//用项目默认的编码
System.out.println(str1);
String str2 = new String(bytes4,"unicode");
System.out.println(str2);//使用java的双字节编码,utf-16be
System.out.println(s);
/*
* 文本文件 就是字节序列
* 可以是任意编码的字节序列
* 如果我们在中文机器上直接创建文本文件,那么该文本文件只认识ANSI编码,别的不认识,但是文本文件本身可以放任何编码
* 联通,联 这是一种巧合,他们正好符合了utf-8编码的规则。纯属巧合
* 直接粘贴过来的都认识
* 什么编码项目认识什么样的东西 如果复制内容过去,会自动做转换。
*/
}
}
import java.io.UnsupportedEncodingException;
public class EncodeDemo {
public static void main(String[] args) throws UnsupportedEncodingException
{
String s = "慕课ABC";
//获得本地默认的编码
byte[] bytes1 = s.getBytes();
for(byte b : bytes1){
//把字节转换成int,以16进制的方式显示
System.out.print(Integer.toHexString(b & 0xff)+" ");
}
System.out.println();
//转换成gbk编码
byte[] bytes2 = s.getBytes("gbk");
//gbk编码中文占用2个字节,英文占用1个字节
for(byte b : bytes2){
System.out.print(Integer.toHexString(b & 0xff)+" ");
}
System.out.println();
//utf-8 中,中文占3个字节,英文占1个字节,一个字节8bit
byte[] bytes3 = s.getBytes("utf-8");
for(byte b : bytes3){
System.out.print(Integer.toHexString(b & 0xff)+" ");
}
//
System.out.println();
byte[] byte5 = new byte[]{(byte) 01110111,(byte) 11100101};
String str0 = new String(byte5,"utf-16be");
System.out.println(str0);
//ystem.out.println(Integer.toHexString(0xa1+0xa2+0xb4+0xff),"GBK");
//java是双字节编码 utf-16be
//utf-16be 中文占用两个字节,英文也是占用两个字节
byte[] bytes4 = s.getBytes("utf-16be");
for(byte b : bytes4){
System.out.print(Integer.toHexString(b&0xff)+" ");
}
System.out.println();
/*
* 当你的字节序列是某种编码时,这个时候想把字节序列变成
* 字符串,也需要用这种编码方式,否则会出现乱码
*/
String str1 = new String(bytes4);//用项目默认的编码
System.out.println(str1);
String str2 = new String(bytes4,"unicode");
System.out.println(str2);//使用java的双字节编码,utf-16be
System.out.println(s);
/*
* 文本文件 就是字节序列
* 可以是任意编码的字节序列
* 如果我们在中文机器上直接创建文本文件,那么该文本文件只认识ANSI编码,别的不认识,但是文本文件本身可以放任何编码
* 联通,联 这是一种巧合,他们正好符合了utf-8编码的规则。纯属巧合
* 直接粘贴过来的都认识
* 什么编码项目认识什么样的东西 如果复制内容过去,会自动做转换。
*/
}
}
输出如下:
二、File类的使用
注意:java.io.File类用于表示文件(目录)
File类只用于表示文件(目录)的信息(名称,大小等),不能用于进行文件的访问。
package com.Imooc;
import java.io.File;
import java.io.IOException;
public class FileDemo {
public static void main(String[] args){
//了解构造函数的情况,查帮助 alt+/
// ctrl+ // 得到的是注释这行
File file = new File("E:\\kankan\\联通.txt");
System.out.println(file.exists());
if(!file.exists())
file.mkdir();//file.mkdirs() 创建多级目录
else
file.delete();
//是否是一个目录,如果是目录返回true,不是或不存在,返回false
System.out.println(file.isDirectory());
//是否是一个文件
System.out.println(file.isFile());
File file2 = new File("E:\\kankan\\1.txt");
if(!file2.exists())
try {
file2.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
else
file2.delete();
//常用的File对象的API
System.out.println(file);//直接打印file.toString()内容,打印出来的是文件String名
System.out.println(file.getName()+" 的绝对路径为::"+file.getAbsolutePath());
System.out.println(file.getName());
System.out.println(file2.getName());
System.out.println(file.getParent());
System.out.println(file2.getParent());
System.out.println(file2.getParentFile());
System.out.println(file.getParentFile().getAbsolutePath());
System.out.println(file.getParentFile().getName());
}
}
System.out.println();快捷键,syso+Alt+/ 。 下面的程序,用来展示list的用法
先写一个类,用来写测试函数:
下面方法中的递归,直接把函数再一次加进去,这样就达到了无限次的递归的原理,这样可以统计一个文件夹里共有多少个文件。
package com.Imooc;
import java.io.*;
//功能,列出File的一些常用操作,比如过滤,遍历目录等操作,包装成一个工具类
public class FileUtils {
/*
* 列出指定目录下(包括其子目录)的所有文件
*/
public static void listDirectory(File dir) throws IOException
{
if(!dir.exists()){
//写一半可以使用alt+/获取帮助
throw new IllegalArgumentException("目录"+dir+"不存在");
}
if(!dir.isDirectory()){
throw new IllegalArgumentException(dir+"不是目录");
}
/*
//foreach出名字 list()返回一个String数组
String[] filenames = dir.list();//list()方法用于列出当前目录下的子目录和文件,返回字符串数组,直接子的名称,不包含子目录下的内容
for(String filename : filenames){
System.out.println(dir+"\\"+filename);
}
*/
//需要增加判断。拿到文件名,再构造成一个File对象,递归遍历下面的内容。File提供了直接返回File对象的API
File[] files = dir.listFiles();//返回的直接子目录(文件)的抽象
if(files!=null && files.length>0){
for(File file:files){
if(file.isDirectory()){
//递归
listDirectory(file);
}else{
System.out.println(file);
}
}
}
/* for(File filename : files){//foreach语句
if(filename.isDirectory()){
//对下面的文件继续递归
listDirectory(filename);//方法嵌套
}
else
System.out.println(filename);
}*/
}
}
给一段加注释,①加//这样的注释,选中按“Ctrl+/”。②加/* */ 这样的注释,按“Shift+Ctrl+/”。 下面再写一个测试类:
package com.Imooc;
import java.io.*;
public class FileUtilTest1 {
public static void main(String[] args) throws IOException {
FileUtils.listDirectory(new File("E:\\java\\codes\\12"));
}
}
这样就把 “E:\java\codes\12”下的所有文件(包括子目录里的文件)全部列出三、RandomAccessFile
RandomeAccessFile java提供的对文件内容的访问,即可以读文件,也可以写文件。RandomeAccessFile支持随机访问文件,可以访问文件的任意位置。
1、java文件模型
在硬盘上的文件是byte byte byte存储的,是数据的集合
2、打开文件
两种模式“”“rw”(读写),“r”(只读)
RandomeAccessFile raf = new RandomeAccessFile(file,"rw")
因为是随机访问文件,所以会包含一个文件指针,打开文件时,指针在开头,pointer=0; 这个指针会移动。
3、写方法
raf.write(int) ---> 只写一个字节(后8位),同时指针指向下一个位置。
4、读方法:
int b = raf.read() ---> 读一个文件,从指针出开始
5、file.close();
文件读写完后以后一定要关闭(否则会有意想不到的错误)
下面写一个类,完成的功能就是打开文件,然后RandomeAccessFile进行读写操作。
里面的0x7fffffff是int最大的,01111111111111111111111111111111,首位0是符号位,表示是正数。很多方法都可以边学编写。不用把所有的API都看一遍。
package com.Imooc;
import java.io.*;
import java.util.Arrays;
public class RafDemo {
public static void main(String[] args) throws IOException{
File file1 = new File(".");
System.out.println(file1.getAbsolutePath());
File demo = new File("demo");
if(!demo.exists())
demo.mkdir();
File file = new File(demo,"raf.dat");
if(!file.exists())//不存在就创建
file.createNewFile();
RandomAccessFile raf = new RandomAccessFile(file,"rw");
//查看指针位置
System.out.println(raf.getFilePointer());
raf.write('A');//写一个字节,写入后八位,也有可能后八位代表
System.out.println(raf.getFilePointer());//输出1
raf.write('B');
System.out.println(raf.getFilePointer());//输出2
int i = 0x7fffffff;//一个字节是8位,一共四个字节
//用write方法每次只能写一个字节,如果要把i写进去就得写4次
raf.write(i>>>24);
raf.write(i>>>16);
raf.write(i>>>8);
raf.write(i);
System.out.println(raf.getFilePointer());//输出6
//可以直接写一个int进去
raf.writeInt(i);//又写了4个
System.out.println(raf.getFilePointer());//输出10
String s = "吕泽正";//6个字节
byte[] gbk = s.getBytes("gbk");
raf.write(gbk);
System.out.println("现在文件中内容长度为(按照字节数来说):"+raf.length());
//读文件,必须把指针移动到头部
raf.seek(0);///移动到10的时候,他能正确读到 吕泽正 这个字
//一次性读取,吧文件中的内容都读到字节数组中
byte[] buf = new byte[(int)raf.length()];
raf.read(buf);//方法read(byte[] buf) 读取到buf数组中去
System.out.println(raf.getFilePointer());//这就读取到16位了
System.out.println(Arrays.toString(buf));
String s1 = new String(buf);
System.out.println(s1+" ");
for(byte b : buf)
{
System.out.print(Integer.toHexString(b & 0xff)+" ");
}
raf.close();//记得关闭
}
}
IO流,Java做输入输出的基础,分为输入流/输出流。又分为字节流,字符流。可以以字节为单位,可以以字符为单位。
四、字节流
两个抽象父类
1、InputStream、OutputStream
InputStream抽象了应用程序读取数据的方式
OutputStream抽象了应用程序写出数据的方式。
OutputStream抽象了应用程序写出数据的方式。
2、EOF = End 读到-1就读到结尾了。
重要方法:
3、输入流:基本方法主要是读
int b = in.read();
读取一个字节,
无符号填充到int的低8位,高8位补零。读到-1 EOF结束。
in.read(byte[] buf) 读取数据填充到字节数组buf
int.read(byte[] buf,int start,int size)。读取数据到字节数组buf。从buf的start位置开始,存放size长度的数据
4、输出基本方法
out.write(int b);
写出一个byte到流
,b的低8位。
out.write(byte[] buf); 将buf字节数组都写入到流
out.write(byte[] buf,int start,int size);字节数组buff从start位置开始,写size长度的字节到流。
5、FileInputStream子类 继承勒InputStream ----> 具体实现了在文件上读取数据
package com.Imooc; import java.io.*; public class IOUtil { /* * 读取指定文件内容,按照16进制输出到控制台 * 并且每输出10byte字节,我们就换行 */ public static void printHex(String fileName) throws IOException{//调用的时候传入 //把文件作为字节流进行读操作 FileInputStream in = new FileInputStream(fileName); int b;//4个字节。 int i=1; while((b=in.read())!=-1){//如何读,如何读到结束,其实b的值每次都刷新 //in.read():读取一个字节,一个字节是8bit if(b<=0xf){ //单位数前面补0 System.out.print("0"); } //toHexString是进制转换 System.out.print(Integer.toHexString(b)+" ");//将整型b转换为16进制表示的字符串 if(i++%10==0){ System.out.println(); } } in.close(); } public static void printHexByByteArray(String fileName) throws IOException{ FileInputStream in = new FileInputStream(fileName); byte[] buf = new byte[1]; //从in这个文件中批量读取字节,放到buf这个字节数组中, //从第0个位置开始放,最多放buf.length个, //返回的是独到的字节的个数 /*int bytes = in.read(buf,0,buf.length);//一次性读完,说明字节数组足够大,这里返回的就是字节长度。所以下面是 i<bytes。 int j=1; for(int i=0;i<bytes;i++){ if(buf[i]<=0xf){ System.out.print(0); } System.out.print(Integer.toHexString(buf[i])+" "); if(j++%10==0) System.out.println(); }*/ int bytes = 0; int j=1; while((bytes = in.read(buf,0,buf.length))>=0){ for(int i = 0;i< buf.length;i++){ if(buf[i]<0xf){ System.out.print("0"); } System.out.print(Integer.toHexString(buf[i] & 0xff)+" "); //byte类型为8位,int类型为32位,为了避免数据转换错误,通过&0xff将高24位清零 if(j++%10 == 0){ System.out.println(); } } } in.close(); } //写一个文件复制的程序 利用读操作和写操作,字节批量读取,直接字节数组 public static void copyFile(File srcFile,File desFile) throws IOException{ if(!srcFile.exists()){//文件不存在不行 throw new IllegalArgumentException("文件:"+srcFile+"不存在!"); } if(!srcFile.isFile()){//不是文件不行 throw new IllegalArgumentException(srcFile+"不是文件"); } FileInputStream in = new FileInputStream(srcFile); FileOutputStream out = new FileOutputStream(desFile); byte[] buf = new byte[1024];//这里的大小很重要,大的话才是整体输入 int b; while ((b=in.read(buf,0,buf.length))>0){ out.write(buf, 0, b); out.flush();//最好加上 } //关闭文件 in.close(); out.close(); } /* * 使用带缓冲的字节流,进行文件的拷贝 */ public static void copyFileByBuffer(File srcFile,File destFile) throws IOException{ if(!srcFile.exists()){//文件不存在不行 throw new IllegalArgumentException("文件:"+srcFile+"不存在!"); } if(!srcFile.isFile()){//不是文件不行 throw new IllegalArgumentException(srcFile+"不是文件"); } BufferedInputStream bis = new BufferedInputStream( new FileInputStream(srcFile));//参数必须是字节流,不能使文件,就是要包装成字节流。 //FileInputStream是节点字节流,必须跟物理节点相连接。 BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(destFile)); int c; while((c = bis.read())!=-1){ bos.write(c); bos.flush();//刷新缓冲区,必须 } bis.close(); bos.close(); } /* * 单字节不带缓冲进行文件拷贝 */ public static void copyFileByByte(File srcFile,File destFile)throws IOException{ if(!srcFile.exists()){//文件不存在不行 throw new IllegalArgumentException("文件:"+srcFile+"不存在!"); } if(!srcFile.isFile()){ //不是文件不行 throw new IllegalArgumentException(srcFile+"不是文件"); } FileInputStream in = new FileInputStream(srcFile); FileOutputStream out = new FileOutputStream(destFile); int c; while((c=in.read())!=-1) { out.write(c); out.flush();//缓冲的必须要flush } in.close(); out.close(); } }
下面写一个测试类:
package com.Imooc; import java.io.*; public class IOUtilTest1 { public static void main(String[] args)throws IOException{ //IOUtil.printHex("E:\\workspace\\com.Imooc\\Telphone.java"); System.out.println(); System.out.println("==================================="); //IOUtil.printHexByByteArray("E:\\workspace\\com.Imooc\\Telphone.java"); long s =System.currentTimeMillis(); IOUtil.copyFile(new File("E:\\workspace\\com.Imooc\\Telphone.java"), new File("E:\\workspace\\com.Imooc\\CopyTelphone.java")); long start1 = System.currentTimeMillis(); IOUtil.copyFileByBuffer(new File("E:\\workspace\\com.Imooc\\Telphone.java"), new File("E:\\workspace\\com.Imooc\\BufferedCopyTelphone.java")); long end1_start2 = System.currentTimeMillis(); IOUtil.copyFileByByte(new File("E:\\workspace\\com.Imooc\\Telphone.java"), new File("E:\\workspace\\com.Imooc\\SingleCopyTelphone.java")); long end2 = System.currentTimeMillis(); System.out.println("-------------------------------------------"); System.out.println(start1-s); System.out.println(end1_start2-start1); System.out.println(end2-end1_start2);//测试时间 } }
输出结果:
这是注释掉的部分,必须 byte[] buf = new byte[1024],足够大,才行,因为是一次性读取
这是用下面方法的输出。
6、FileOutputStream 实现了向文件中写入byte数据的方法
package com.Imooc;
import java.io.*;
public class FileOutDemo1 {
public static void main(String[] args)throws IOException{
//如果该文件不存在,则直接创建,如果存在,删除后创建
FileOutputStream out = new FileOutputStream("demo/out.dat");
//new FileOutputStream("demo/out.dat",true);//如果这样的话,就是追加,不会删除后创建了
out.write('A');//写出A字符的低8位
out.write('B');//写出B的低八位
int a = 10;//write只能写8位,那么写一个整数需要写4次,每次8位
out.write(a>>>24);
out.write(a>>>16);
out.write(a>>>8);
out.write(a);
byte[] gbk="中国".getBytes("gbk");
out.write(gbk);
out.close();
IOUtil.printHex("demo/out.dat");//调用前面的,将写入的内容输出到控制台
}
}
输出:
41 A 42 B 00 00 00 0a 10 因为整型是4字节,32位。写四次 d6
7、DataOutputStream/DataInputStream
对流功能的扩展,可以更加方便读取int long,字符,等类型数据。主要操作类型数据。
DataOutputStream
writeInt()/writeDouble()/writeUTF()/
package com.Imooc;
import java.io.*;
public class DosDemo {
public static void main(String[] args)throws IOException{
String file = "demo/dos.dat";
DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));
//用fileOutputSream的方法,写几次
dos.writeInt(10);//执行了4次write()
dos.writeInt(-10);
dos.writeLong(10l);
dos.writeDouble(10.5);
//采用UTF-8编码写出
dos.writeUTF("中国");
//采用utf-16be编码写
dos.writeChars("中国");
dos.close();
IOUtil.printHex(file);
}
}
package com.Imooc;
import java.io.*;
public class DisDemo {
public static void main(String[] args)throws IOException{
String file = "demo/dos.dat";
IOUtil.printHex(file);
DataInputStream dis = new DataInputStream(new FileInputStream(file));
int i = dis.readInt();//读取了4次 long是8次
System.out.println(i);
i=dis.readInt();
System.out.println(i);
long l = dis.readLong();
System.out.println(l);
double d = dis.readDouble();
System.out.println(d);
String s = dis.readUTF();
System.out.println(s);
}
}
8、 BufferedInputStream&BufferedOutputStream
这两个流泪为IO提供了带缓冲区的操作,一般打开文件进行写入或读取操作时,都会加上缓冲,这种流模式提高了IO的性能。
从应用程序中输入放入文件,相当于将一缸水倒入另一缸水中:
FileOutputStream ---->write()方法 相当于一滴一滴的把水转移过去。
DataOutputStream ----->writeXxx()方法会方便一些。一下写4个字节、8个 相当于一瓢一瓢的转移
BufferedOutputStream---->write()方法更方便,相当于一瓢一瓢水先放入桶中(缓存区),再从桶倒入到另一个缸中。
五、字符流
1、编码问题要清楚
2、认识文本和文本文件
Java的文本(char)是16位无符号整数,是字符的unicode编码(双字节编码)文件是byte byte byte .....的数据序列
文本文件是文本(char)序列按照某种编码方法(utf-8,uft-16be,gbk)序列化为byte的存储结果
3、字符流(Reader Writer)------>操作的是文本 文本文件
字符的处理,一次处理一个字符
字符的底层仍然是基本的字节序列。
字符流的基本实现
InputStreamReader 完成byte流解析为char流,按照编码解析OutputStreamWriter 提供char流到byte流,按照编码存储
package com.Imooc;
import java.io.*;
public class IsrAndOswDemo {
public static void main(String[] args) throws IOException
{
FileInputStream in = new FileInputStream("E:\\workspace\\com.Imooc\\imoocutf-8");
InputStreamReader isr = new InputStreamReader(in,"utf-8");//这里默认是gbk,可以省略
FileOutputStream out = new FileOutputStream("E:\\workspace\\com.Imooc\\imoocutf-81.txt");
OutputStreamWriter osw = new OutputStreamWriter(out,"utf-8");
/*int c;
while((c=isr.read())!=-1){
System.out.print((char)c);
}*/
char[] buffer = new char[8];
int c;
//批量读取,放入buffer这个字符数组,从第0个位置开始防止,最多放buffer.length
while((c = isr.read(buffer,0,buffer.length))!=-1){
//System.out.print(c);
//System.out.println("-----");
String s = new String(buffer,0,c);
System.out.print(s);
osw.write(buffer,0,c);
osw.flush();
}
isr.close();
osw.close();
}
}
4、FileReader 和 FileWriter
可以不用上上面的InputStreamReader 这样的嵌套,直接可以写文件地址,直接对物理资源操作。
package com.Imooc;
import java.io.*;
public class FrAndFwDemo {
public static void main(String[] args) throws IOException{
// FileInputStream in = new FileInputStream("E:\\workspace\\com.Imooc\\imoocutf-8");
// InputStreamReader ins = new InputStreamReader(in,"utf-8");
// FileReader fr1 = new FileReader(new InputStreamReader(new FileInputStream("")));
//File in = new File("E:\\workspace\\com.Imooc\\imoocutf-8.txt");
FileReader fr = new FileReader("E:\\workspace\\com.Imooc\\imoocutf-8.txt");//没有放编码格式的例如“gbk”
FileWriter fw = new FileWriter("E:\\workspace\\com.Imooc\\imoocutf1-8.txt");//如果名字一样的话,这样会删掉原来的东西
//FileWriter fw = new FileWriter("E:\\workspace\\com.Imooc\\imoocutf-8.txt",true);//追加内容,带个true。
char[] buffer = new char[1];
int c;
while((c=fr.read(buffer, 0, buffer.length))!=-1){
//System.out.println("+++++++++++");
System.out.print(new String(buffer,0,c));
fw.write(buffer,0,c);
fw.flush();
}
fr.close();
fw.close();
}
}
5、字符流可以加过滤,具备更强大的功能。
BufferedReader ----> readLine 一次读一行
BufferedWriter/(结合)PrintWriter ----> writeLine 一次写一行
FileReader fr = new FileReader("E:\\workspace\\com.Imooc\\imoocutf-8.txt");
BufferedReader br = new BufferedReader(fr); 等同于下面
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(
"E:\\workspace\\com.Imooc\\imoocutf-8.txt")));
package com.Imooc;
import java.io.*;
public class BrAndBwOrPwDemo {
public static void main(String[] args)throws IOException{
//对文件进行读写操作
// FileReader fr = new FileReader("E:\\workspace\\com.Imooc\\imoocutf-8.txt");
// BufferedReader br = new BufferedReader(fr); 等同于下面
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(
"E:\\workspace\\com.Imooc\\imoocutf-8.txt")));
/*BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(
"E:\\workspace\\com.Imooc\\bwutf-8")));*/
FileWriter pw2 = new FileWriter("E:\\workspace\\com.Imooc\\bwut");
//PrintWriter pw = new PrintWriter("E:\\workspace\\com.Imooc\\bwutf-8");
PrintWriter pw1 = new PrintWriter(pw2,true);//自动刷新
String line;
while((line = br.readLine())!=null){
System.out.println(line);//一次读一行,并不能识别换行,所以写为println 而不是print
pw1.println(line);
/*bw.write(line);
//不认识换行,单独写出胡按行操作
bw.newLine();
bw.flush();*/
/*pw.println(line);//换行方便
pw.flush();*/
}
br.close();
pw2.close();
//bw.close();
//pw.close();
}
}
六、对象的序列化和反序列化
1、对象序列化:将Object转换成byte序列,反之叫对象的反序列化。
2、序列化流(ObjectOutputStream),是过滤流 ----writeObject
反序列化(ObjectInputStream) ------readObject
3、序列化接口(Serializable)
对象必须实现序列化接口,才能进行序列化,否则将出现异常。
这个接口,没有任何方法,只是一个标准。
private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException,ClassNotFoundException
package com.Imooc;
import java.io.Serializable;
import java.util.ArrayList;
public class Student implements Serializable {
private String studo;//String类实现了Serializable
private String stuname;
private transient int stuage;//该元素不会进行jvm默认的序列化,也可以自己完成这个元素的序列化
//ArrayList a = new ArrayList();
public Student(){}
public Student(String studo,String stuname,int stuage){
super();//直接通过成员变量,生成构造方法
this.studo = studo;
this.stuname = stuname;
this.stuage = stuage;
}
public String getStudo(){
return studo;
}
public void setStudo(String studo){
this.studo = studo;
}
public String getStuname(){
return stuname;
}
public void setStuname(String stuname){
this.stuname = stuname;
}
public int getStuage(){
return stuage;
}
public void setStuage(int stuage){
this.stuage = stuage;
}
public String toString(){
return "Student[stuno="+studo+",stuname="+stuname+",stuage="+stuage+"]";
}
private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException
{
s.defaultWriteObject();//把jvm能默认序列化的元素进行序列化操作
s.writeInt(stuage);//自己完成stuage的序列化
}
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException,ClassNotFoundException{
s.defaultReadObject();//把jvm能默认反序列化的元素进行反序列化操作
this.stuage = s.readInt();//自己完成stuage的反序列化操作
}//用这个又能看到了
}
package com.Imooc;
import java.io.*;
public class ObjectSeriaDemo1 {
public static void main(String[] args)throws IOException, ClassNotFoundException{
//对象序列化就是把对象进行一个保存
String file = "E:/"
+ "workspace/com.Imooc/obj.dat";//把这个对象存在路径下
//1、对象的序列化
/*ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(file));
Student stu = new Student("10001","张三",20);//吧student对象保存起来
oos.writeObject(stu);
oos.flush();
oos.close();*/
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Student stu =(Student)ois.readObject();//读出来的都是Ibjectr,所以强制类型转换
System.out.println(stu);
ois.close();//打印出来,说明对对象进行了存储
}
}
4、分析ArrayList源码中序列化和反序列化的问题。 里面有一个 private transient Object[] elementData;
因为ArrayList不一定放满元素,序列化可以提高利用率。只是对有限元素做序列化。transient修饰,提高性能。将来自己做序列化的优化问题。
5、序列化中 子类和父类 构造函数的调用问题
对子类对象进行反序列化操作时,如果其父类没有实现序列化接口,那么其父类的构造函数会被显示调用。
package com.Imooc;
import java.io.*;
public class ObjectSeriaDemo2 {
public static void main(String[] args) throws IOException, ClassNotFoundException{
/*ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\workspace\\com.Imooc\\imoocutf-8"));
Foo2 foo2 = new Foo2();//序列化完成了
oos.writeObject(foo2);
oos.flush();
oos.close();*/
//反序列化是否递归调用父类的构造函数
/*ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\workspace\\com.Imooc\\imoocutf-8"));
Foo2 foo2 = (Foo2)ois.readObject();
System.out.println(foo2);
ois.close();*/
/*ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\workspace\\com.Imooc\\imoocutf-8"));
Bar2 bar2 = new Bar2();//序列化完成了
oos.writeObject(bar2);
oos.flush();
oos.close();*/
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\workspace\\com.Imooc\\imoocutf-8"));
Bar bar2 = (Bar2)ois.readObject();
System.out.println(bar2);
ois.close();//这里表示父类构造函数被调用了 因为没有实现Implements调用了
/*
* 对子类对象进行反序列化操作时,
* 如果其父类没有实现序列化接口,
* 那么其父类的构造函数会被显示调用。实现的接口及其子类不会调用。
*/
}
}
class Foo implements Serializable{
//一个类实现了序列化接口,那么其子类都可以进行序列化
public Foo(){
System.out.println("foo...");
}
}
class Foo1 extends Foo{
public Foo1(){
System.out.println("foo1...");
}
}
class Foo2 extends Foo1{
public Foo2(){
System.out.println("foo2...");
}
}
class Bar{
public Bar(){
System.out.println("Bar");
}
}
class Bar1 extends Bar{// implements Serializable{
public Bar1(){
System.out.println("bar1...");
}
}
class Bar2 extends Bar1 implements Serializable{
public Bar2(){
System.out.println("bar2...");
}
}