首页 > 代码库 > Thinking in java 琐碎知识点之 I/O流 、对象序列化
Thinking in java 琐碎知识点之 I/O流 、对象序列化
Java I/O流 、对象序列化
1、File类
此类的实例可能表示(也可能不表示)实际文件系统对象,如文件或目录。
File类可以新建、删除和重命名文件和目录,但是File不能访问文件本身的内容,这要使用IO流。
File对象的createNewFile()方法在磁盘上创建真实的文件
写出一个文件,输出流
字符流:专门用于读写文本文件的(记事本可以正常打开),字符流操作的最小数据单元是16位的字符 查询本机上的编码表(GBK)
问题?word excel是不是文本文件
常见的文本文件: .txt .java html xml sql
字节流:操作的最小单元是8位的字节,最小的存储单位1个字节
1个字节8个二进制位
任意文件
问题?能操作文件夹吗 (不能)
3、Java中的io继承体系
继承体系,类与类之间继承关系,子类中的共性提取成的父类
父类中定义的功能,是这个体系中的最共性的内容
学习一个继承体系的时候,找父类去看,建立子类对象
字符流:
输出流,写入文件的抽象基类(体系中的最高层的父类) Writer
输入流,读取文件的抽象基类 Reader
字节流:
输出流,写入文件的抽象基类 OutputStream
输入流,读取文件的抽象基类 InputStream
4. 字符流的输入流(读文件)使用
字符流读取文件
查阅API文档找到了Reader
read()方法读取单个字符,返回int值?
返回的int值,是读取到的字符的ASCII码值
read()方法,每执行一次,自动的向后读取一个字符
读取到文件末尾的时候,得到-1的值
找到Reader类的子类 FileReader
FileReader(String fileName) 传递字符串文件名
read(字符数组)
返回int值
数组中存储的就是文件中的字符
int返回值,读到末尾就是-1
int返回数组中,读取到的字符的有效个数
好处:可以提高读取的效率
注意问题:
出现异常:XXXX拒绝访问
A.操作,确认是不是操作的文件
B.登录windows的账户是不是管理员,不是管理员登录的,不能操作c盘下的文件
其他盘符是可以的
5.字符流的输出流(写文件)使用
字符流写文件
查阅API文档,找到了使用的子类,FileWriter
FileWriter(String fileName)
根据给定的文件名构造一个 FileWriter 对象。
void write(String str) 是FileWriter类的父类的方法
记住:Java中字符流写数据,不会直接写到目的文件中,写到内存中
想将数据写到目的文件中,刷新,刷到目的文本中
flush()刷新
记住:流对象中的功能,调用了Windows系统中的功能来完成
释放掉操作系统中的资源,简称:关闭流
close方法,关闭流之前,关闭的时候,先要刷新流中的数据
但是,如果写文件的数据量很大,写一句刷一句才好
看到了父类中的write方法
记住:IO操作,需要关闭资源,关闭资源的时候,开了几个流,就要关闭几个流
单独的进行关闭资源,单独写try catch,保证每一个流都会被关闭
6. 复制文本文件
读取源文件 FileRreader
写入到目的文件 FileWriter
两种文件复制方式,分别计算时间
读一个字符,写一个字符
第一个数组,写一个数组
利用缓冲区复制文件
第一行,写一行的操作
7. 字符流的缓冲区对象(也叫处理流、包装流)
利用数组提升了文件的读写速度,运行效率提升了
我们想到了效率问题,Java工程师(Oracle),也想到了提升效率
写好了字符流的缓冲区对象,目的提供流的写,读的效率
写入流的缓冲区对象BufferedWriter
BufferedWriter(Writer out)
参数Writer类型的参数,传递的参数是Writer类的子类对象
缓冲区,提供流的写的效率,哪个流的效率 FileWriter
void newLine() 写一个换行,具有跨平台
不用newLine()方法,也可以实现换行 \r\n
\r\n Windows下的换行符号
\n Linux下的换行符号
读取流的缓冲区对象BufferedReader
BufferedReader(Reader in)
参数Reader类型参数,传递的是Reader类的子类对象
缓冲区,提供流的读的效率,哪个流的效率FileRreader
读取一行的方法 readLine(),文件末尾返回null
不是末尾返回字符串 String
9. 处理流PrintStream流的用法
10、重定向标准输入输出流
Java的标准输入输出分别通过System.in和System.out来代表,默认情况下它们分别代表键盘和显示器
System类提供了重定向标准输入输出的方法
Scanner和IO流接(BufferedReader)受键盘输入的比较(BufferedReader输入都被当成String对象,BufferedReader不能读取基本类型输入项)
11、RandomAccessFile对象
12、对象序列化
在Java中如果需要将某个对象保存到磁盘或者通过网络传输,那么这个类应该实现Serializable标记接口或者Externalizable接口之一
序列化的步骤:
a、创建一个ObjectOutputstream处理流(必须建立在其它结点的基础上)对象
b、调用ObjectOutputstream对象的writeObject方法输出可序列化对象
从二进制流中恢复Java对象的反序列化步骤(按照实际写入的顺序读取):
a、创建一个ObjectInputStream处理流(必须建立在其它结点的基础上)对象
b、调用ObjectInputstream对象的readObject方法输出可序列化对象(该方法返回的是Object类型的Java对象)
(反序列化恢复Java对象时必须要提供java对象所属类的class文件)
注:如果一个可序列化对象有多个父类,则该父类要么是可序列化的,要么有无参的构造器,因为反序列化机制要恢复其关联的父类实例
而恢复这些父类实例有两种方式:使用序列化机制、使用父类无参的构造器
采用Java序列化机制时,只有当第一次调用writeObject输出某个对象时才会将该对象转换成字节序列写到ObjectOutputStream
在后面程序中如果该对象的属性发生了改变,即再次调用writeObject方法输出该对象时,改变后的属性不会被输出
如果父类没有实现Serializable接口,则其必须有默认的构造函数(即没有参数的构造函数)
但是若把父类标记为可以串行化,则在反串行化的时候,其默认构造函数不会被调用。
这是因为Java 对串行化的对象进行反串行化的时候,直接从流里获取其对象数据来生成一个对象实例,而不是通过其构造函数来完成。
13、自定义序列化
在属性前面加上transient关键字,可以指定Java序列化时无需理会该属性值,由于transient修饰的属性将被完全隔离在序列化机制外,
这会导致在反序列化恢复Java对象时无法取得该属性值。
实现自定义序列化要重写类的如下方法:
private void writeObject(java.io.ObjectOutputStream out) throws IOException;
private void readObject(java.io.ObjectInputStream in) throws IOException,ClassNotFoundException;
还有种更彻底的序列化机制可以在序列化某对象时替换该对象,此种情况下应为序列化类提供如下特殊方法:
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
Java的序列化机制保证在序列化某个对象之前,先调用对象的writeReplace()方法,如果该方法返回另一个Java对象,
则系统转为序列化另一个对象。
小结:系统在序列化某个对象之前,先会调用该对象的如下两个方法:
writeReplace和writeObject,系统先调用被序列化对象的writeReplace方法,如果返回另一个Java对象,则再次调用该java对象的writeReplace方法....
直到该方法不在返回一个对象为止,程序最后调用该对象的writeObject方法来保存该对象的状态
与writeReplace方法相对的有一个方法可以替代原来反序列化的对象
即:private Object readResolve() throws ObjectStreamException;
这个方法会紧接着readObject之后被立即调用,该方法的返回值会代替原来反序列化的对象,而原来readObject反序列化的对象会被立即丢弃
(此方法在序列化单例类、早期枚举类时很有用)
14、另一种序列化机制:Java类实现Externalizable接口
系统自动存储必要信息
Java内建支持,易于实现只需实现该接口即可
无须任何代码支持
15、Charset对象
Java提供了Charset来处理字节序列和字符序列之间的转换关系,该类包含了用于创建解码器和编码器的方法
例程:CharsetTransformTest.java
16、Buffer(缓冲)抽象基类
常用子类ByteBuffer,这些Buffer的子类不是通过构造器创建而是通过static xxxBuffer allocate(int capacity)来创建一个容量为capacity的xxxBuffer对象。Buffer中包含两个重要的方法flip(该方法将limit设置为position所在位置,position设置 为0,)和clear(将position设为0,limit设为 capacity)。flip为从Buffer中读取数据做好准备,而clear是为向Buffer中装入数据做好准备
例程:BufferTest.java
17、Channel(通道)
程序不能直接访问Channel中放入数据,Channel只能和Buffer进行交互,发送到Channel中的数据必须先放到Buffer对象中,程序再将Buffer的输入写入Channel。而从Channel中读取数据也必须先读到Buffer中,程序再从Buffer中取出这些数据。
例程:FileChannelTest.java
1、File类
此类的实例可能表示(也可能不表示)实际文件系统对象,如文件或目录。
File类可以新建、删除和重命名文件和目录,但是File不能访问文件本身的内容,这要使用IO流。
File对象的createNewFile()方法在磁盘上创建真实的文件
例程:FileTest.java import java.io.*; public class FileTest { public static void main(String[] args) throws IOException { File file=new File("test1");//此相对路径即表示相对JVM的路径 File file2=new File(".");//以当前路径来创建一个File对象 File file3=new File("F:\\workplace\\IO\\jaa.txt");//系统不一定就存在jaa.txt这个文件 System.out.println("file.getName()\t"+file.getName()); System.out.println("file2.getName()\t"+file2.getName()); System.out.println("file3.getName()\t"+file3.getName()); System.out.println("file.getParent()\t"+file.getParent()); System.out.println("file2.getParent()\t"+file2.getParent()); System.out.println("file3.getParent()\t"+file3.getParent()); System.out.println("file.getAbsolutePath()\t"+file.getAbsolutePath()); System.out.println("file.getAbsoluteFile()\t"+file.getAbsoluteFile()); System.out.println("file.getAbsoluteFile().getParent()\t"+file.getAbsoluteFile().getParent()); System.out.println("file2.getAbsolutePath()\t"+file2.getAbsolutePath()); System.out.println("file3.getAbsolutePath()\t"+file3.getAbsolutePath()); File file4=new File("F://FileTest//test1.doc"); System.out.println("file4.exists()\t"+file4.exists()); //在系统中创建一个文件,注意test1.doc前面的目录一定要是真实存在的,否则执行createNewFile方法会报错 file4.createNewFile(); System.out.println("file4.exists()\t"+file4.exists()); File file5=new File("F:\\zpc"); System.out.println("file5.mkdir()"+file5.mkdir());//在系统中创建一个File对象所对应的目录 File file6=new File("F:\\workplace"); String fileList[]=file6.list(); System.out.println("=======F:\\workplace目录下的所有文件和路径如下======="); for(String s:fileList){ System.out.println(s); } //File的静态方法listRoots列出所有的磁盘根路径 File[] roots=File.listRoots(); System.out.println("======系统所有根路径======="); for(File f:roots){ System.out.println(f); } //在F:\\zpc目录下创建一个临时文件,并指定当JVM退出时删除该文件 File temFile=File.createTempFile("zpca", ".txt",file5); temFile.deleteOnExit(); //通过挂起当前线程5秒,会看到临时文件被创建5秒后由于程序执行完毕,JVM退出,该文件又自动删除 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } } 文件过滤器: import java.io.*; public class FileNameFilterTest { public static void main(String[] args) { File file=new File("F:\\workplace\\collection\\src"); String fileList[]=file.list(new MyFilenameFilter()); for(String s:fileList){ System.out.println(s); } } } class MyFilenameFilter implements FilenameFilter{ public boolean accept(File dir, String name) { return name.endsWith(".java")||new File(name).isDirectory(); } }
2. io流分类
读取一个文件,输入流写出一个文件,输出流
字符流:专门用于读写文本文件的(记事本可以正常打开),字符流操作的最小数据单元是16位的字符 查询本机上的编码表(GBK)
问题?word excel是不是文本文件
常见的文本文件: .txt .java html xml sql
字节流:操作的最小单元是8位的字节,最小的存储单位1个字节
1个字节8个二进制位
任意文件
问题?能操作文件夹吗 (不能)
3、Java中的io继承体系
继承体系,类与类之间继承关系,子类中的共性提取成的父类
父类中定义的功能,是这个体系中的最共性的内容
学习一个继承体系的时候,找父类去看,建立子类对象
字符流:
输出流,写入文件的抽象基类(体系中的最高层的父类) Writer
输入流,读取文件的抽象基类 Reader
字节流:
输出流,写入文件的抽象基类 OutputStream
输入流,读取文件的抽象基类 InputStream
4. 字符流的输入流(读文件)使用
字符流读取文件
查阅API文档找到了Reader
read()方法读取单个字符,返回int值?
返回的int值,是读取到的字符的ASCII码值
read()方法,每执行一次,自动的向后读取一个字符
读取到文件末尾的时候,得到-1的值
找到Reader类的子类 FileReader
FileReader(String fileName) 传递字符串文件名
read(字符数组)
返回int值
数组中存储的就是文件中的字符
int返回值,读到末尾就是-1
int返回数组中,读取到的字符的有效个数
好处:可以提高读取的效率
注意问题:
出现异常:XXXX拒绝访问
A.操作,确认是不是操作的文件
B.登录windows的账户是不是管理员,不是管理员登录的,不能操作c盘下的文件
其他盘符是可以的
例程:字符流读取文件 import java.io.*; //Reader是字符流读的抽象基类(输入流,读取文件的抽象基类) public class FileReaderDemo { public static void main(String[] args) { FileReader fr1 = null; FileReader fr2 = null; try { fr1 = new FileReader("F:\\f.txt"); int len = 0;// read方法返回读取的单个字符的ASCII码(可以转换成字符输出) while ((len = fr1.read()) != -1) { System.out.print((char) len); } System.out.println("\n******将字符读入缓冲数组再输出*****"); // 定义字符数组 char[] buf = new char[512];// 将512*2字节字符读入缓冲数组 fr2 = new FileReader("F:\\Thinking.txt"); while ((len = fr2.read(buf)) != -1) {//返回值len表示读取的有效字符的长度 System.out.print(new String(buf,0,len));//将字符数组包装成String } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fr1 != null) { fr1.close(); } } catch (IOException e) { // TODO Auto-generated catch block throw new RuntimeException("文件关闭失败"); } try { if (fr2 != null) { fr2.close(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
例程:自己实现的一行一行读取的方法 import java.io.*; class MyReadLine { // 自己实现一行一行读取 private Reader r; public MyReadLine(Reader r) { this.r = r; } public String myReaderLine() throws IOException { // 定义一个字符缓冲区,读一个字符就存储到这里 StringBuilder sb = new StringBuilder(); int len = 0; while ((len = r.read()) != -1) { if (len == ‘\r‘) { continue; } if (len == ‘\n‘) { return sb.toString(); } else { sb.append((char) len);// 读到的是有效字符,存到缓冲区 } } //看看缓冲区是否还有内容,有可能内容不是以回车符结尾的 if (sb.length() != 0) { return sb.toString(); } else return null; } public void MyClose() { try { if (r != null) r.close(); } catch (IOException e) { e.printStackTrace(); } } } public class TestMyReadLine { public static void main(String[] args) throws IOException { MyReadLine my = null; try { my = new MyReadLine(new FileReader("F:\\Thinking.txt")); String line = null; while ((line = my.myReaderLine()) != null) { System.out.println(line); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (my != null) { my.MyClose(); } } catch (Exception e) { e.printStackTrace(); } } } }
5.字符流的输出流(写文件)使用
字符流写文件
查阅API文档,找到了使用的子类,FileWriter
FileWriter(String fileName)
根据给定的文件名构造一个 FileWriter 对象。
void write(String str) 是FileWriter类的父类的方法
记住:Java中字符流写数据,不会直接写到目的文件中,写到内存中
想将数据写到目的文件中,刷新,刷到目的文本中
flush()刷新
记住:流对象中的功能,调用了Windows系统中的功能来完成
释放掉操作系统中的资源,简称:关闭流
close方法,关闭流之前,关闭的时候,先要刷新流中的数据
但是,如果写文件的数据量很大,写一句刷一句才好
看到了父类中的write方法
记住:IO操作,需要关闭资源,关闭资源的时候,开了几个流,就要关闭几个流
单独的进行关闭资源,单独写try catch,保证每一个流都会被关闭
例程:Writer类的使用 import java.io.*; //Writer是字符流写的抽象基类(输出流,写入文件的抽象基类) public class FileWriterDemo { public static void main(String[] args) { FileWriter fw1 = null; FileWriter fw2 = null;// 关流的时候要分别处理异常 try { fw1 = new FileWriter("F:\\filewriter.txt"); fw1.write("作品z"); char[] ch={‘q‘,‘w‘,‘e‘,‘你‘}; fw2 = new FileWriter("E:\\fileWriter.txt"); fw2.write(ch,1,ch.length-1); //第二个参数表示开始写入字符处的偏移量(从第几个字符开始写),第三个参数表示写多少长度的数据 fw2.write(ch,0,ch.length-3); fw2.write(ch,0,ch.length); fw2.write(ch);//Writer能一次写入一个字符数组大小的内容;Reader能一次读出一个字符数组(相当于缓冲)大小的内容 fw1.flush();// 字符流必须手动刷新才能真的写入文件 fw2.flush(); } catch (IOException e) { throw new RuntimeException("文件写入失败"); } finally { try { if (fw1 != null) // 健壮性判断,流有可能为空(流对象没有建立成功) { fw1.close();// 调用close方法时会自动刷新,但是不要都等到这一步才刷 } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } //开了几个流关闭几个,单独关闭(否则第一个关的时候抛了异常,第二个就没有机会关了) try { if (fw2 != null) fw2.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
6. 复制文本文件
读取源文件 FileRreader
写入到目的文件 FileWriter
两种文件复制方式,分别计算时间
读一个字符,写一个字符
第一个数组,写一个数组
利用缓冲区复制文件
第一行,写一行的操作
例程:复制文本文件 import java.io.*; public class CopyText { // 读取一个数组,写一个数组 public static void copyText1() { FileReader fr = null; FileWriter fw = null; try { fr = new FileReader("F:\\Thinking.txt"); fw = new FileWriter("F:\\Thinking2.txt"); char[] buff = new char[1024]; int len = 0; while ((len = fr.read(buff)) != -1) { fw.write(buff, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fr != null) fr.close(); } catch (IOException e) { throw new RuntimeException("文件读取关闭失败"); } try { if (fw != null) fw.close(); } catch (IOException e) { throw new RuntimeException("文件写入关闭失败"); } } } // 读取一个字符,写一个字符 public static void copyText2() { FileReader fr = null; FileWriter fw = null; try { fw = new FileWriter("F:\\Thinking2.txt"); fr = new FileReader("F:\\Thinking.txt"); int len = 0; while ((len = fr.read()) != -1) { fw.write((char) len); } } catch (Exception e) { throw new RuntimeException("文件复制失败"); } finally { try { if (fr != null) fr.close(); } catch (IOException e) { throw new RuntimeException("文件读取关闭失败"); } try { if (fw != null){ fw.close(); } } catch (IOException e) { throw new RuntimeException("文件写入关闭失败"); } } } //读取一行写一行的方式,利用的缓冲区对象 public static void copyText3(){ FileReader fr=null; FileWriter fw=null; BufferedReader bfr=null; BufferedWriter bfw=null; try { fr=new FileReader("F:\\Thinking.txt"); fw=new FileWriter("F:\\Thinking3.txt"); bfr=new BufferedReader(fr); bfw=new BufferedWriter(fw); String sLine=null; while((sLine=bfr.readLine())!=null){ bfw.write(sLine); //bfw.write("\r\n"); bfw.newLine(); } } catch (IOException e) { throw new RuntimeException("文件复制失败"); }finally{ try { if(bfr!=null); bfr.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { if(bfw!=null) bfw.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void main(String[] args) { long start = System.currentTimeMillis(); copyText1(); //copyText2(); //copyText3(); long cost = System.currentTimeMillis() - start; System.out.println("耗时:" + cost+"ms"); } }
7. 字符流的缓冲区对象(也叫处理流、包装流)
利用数组提升了文件的读写速度,运行效率提升了
我们想到了效率问题,Java工程师(Oracle),也想到了提升效率
写好了字符流的缓冲区对象,目的提供流的写,读的效率
写入流的缓冲区对象BufferedWriter
BufferedWriter(Writer out)
参数Writer类型的参数,传递的参数是Writer类的子类对象
缓冲区,提供流的写的效率,哪个流的效率 FileWriter
void newLine() 写一个换行,具有跨平台
不用newLine()方法,也可以实现换行 \r\n
\r\n Windows下的换行符号
\n Linux下的换行符号
字符缓冲流示例 BufferedWriter将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。 import java.io.*; public class BufferedWriterDemo { public static void main(String[] args)throws IOException { //字符输出流对象 FileWriter fw=new FileWriter("F:\\f.txt"); //建立缓冲区对象,提高输出流的效率 BufferedWriter bfw=new BufferedWriter(fw); bfw.write("哈w哈"); bfw.write("呵w呵"); bfw.newLine();//写一行,BufferedReader可以读一行 bfw.write("嘻w嘻"); bfw.append("append");//追加写 bfw.flush(); bfw.close(); } }
读取流的缓冲区对象BufferedReader
BufferedReader(Reader in)
参数Reader类型参数,传递的是Reader类的子类对象
缓冲区,提供流的读的效率,哪个流的效率FileRreader
读取一行的方法 readLine(),文件末尾返回null
不是末尾返回字符串 String
字符缓冲流示例 BufferedReader 从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。 import java.io.*; public class BufferedReaderDemo { public static void main(String[] args)throws IOException { //字符输出流对象 FileReader fr=new FileReader("F:\\Thinking.txt"); //建立缓冲区对象,提高输出流的效率 BufferedReader bfw=new BufferedReader(fr); //读一行,返回字符串,末位返回null String line=null; while((line=bfw.readLine())!=null){ System.out.println(line); } bfw.close(); } }8、字节流
例程1: import java.io.*; public class InputStreamDemo { public static void main(String[] args)throws IOException { FileInputStream fis=new FileInputStream("F:\\Thinking.txt"); byte[] bytes=new byte[1024];//1kb int len=0; len=fis.read(bytes);//len表示有效个数 System.out.println(new String(bytes)); System.out.println(len); } } 例程2: import java.io.*; public class OutPutStreamDemo { public static void main(String[] args) throws IOException{ FileOutputStream fos=new FileOutputStream("F:\\f.txt"); //写字节数组 byte[] bytes={97,98,99}; fos.write(bytes,0,bytes.length);//第二个参数是下标,第三个是写入的个数 fos.write("\r\n".getBytes());//换行 //写字符数组,只能将字符串转成字节数组,用String类方法getBytes fos.write(97);//写入了a字符 fos.write("zpc".getBytes()); fos.close(); } } 例程3: import java.io.*; public class CopyFile { public static void main(String[] args) throws IOException { FileInputStream fis=new FileInputStream("E:\\KuGou\\屠洪刚-孔雀东南飞.mp3"); FileOutputStream fos=new FileOutputStream("I:\\孔雀东南飞.mp3"); int len=0; byte[] bytes=new byte[1024]; while((len=fis.read(bytes))!=-1){ fos.write(bytes,0,len); } fos.close(); fis.close(); } } 例程4: import java.io.*; /* * 字节流读取键盘的输入 * 用户一行一行的写,但是read方法每次只能读取一个字节,不方便 * 能否一次读取一个行呢?可以使用转换流 * 字节流转换成字符流后可以使用字符缓冲流按行读取 */ public class KeyInDemo1 { public static void main(String[] args) throws IOException { InputStream in = System.in;// 返回字节输入流对象 // int len=0; // len=in.read();//read方法每次只能读取一个字节 // System.out.println(len); // 使用转换流将字节输入流转成字符输入流 InputStreamReader isr = new InputStreamReader(in); //为了达到最高效率,可要考虑在 BufferedReader内包装 InputStreamReader BufferedReader bfr = new BufferedReader(isr); String line = null; while ((line = bfr.readLine()) != null) { // 使用了while循环,则要自定义结束循环的语句 if ("exit".equals(line)) { System.out.println("程序终止!"); break; } System.out.println(line); } } } 例程5: import java.io.*; public class KeyInDemo2 { public static void main(String[] args) throws IOException { InputStream in = System.in;// 返回字节输入流对象 //将字节输入流转换成字符输入流 InputStreamReader isr = new InputStreamReader(in); // 为了达到最高效率,可要考虑在 BufferedReader内包装 InputStreamReader BufferedReader bfr = new BufferedReader(isr); OutputStream out=new FileOutputStream("F:\\test.txt"); //将字节输出流转化为字符输出流 OutputStreamWriter osw=new OutputStreamWriter(out); BufferedWriter bfw=new BufferedWriter(osw); String line = null; while ((line = bfr.readLine()) != null) { // 使用了while循环,则要自定义结束循环的语句 if ("exit".equals(line)) { System.out.println("程序终止!"); break; } //一行一行的写 bfw.write(line); bfw.newLine(); bfw.flush(); } } }
9. 处理流PrintStream流的用法
import java.io.*; /* * PrintStream类的输出功能非常强大,通常需要输出文本内容时, * 应该将输出流包装成PrintStream后再输出 * 对应的由于BufferedReader具有readLine方法,可以方便地一次读取一行内容, * 所以经常把读取文本的输入流包装成BufferedReader,用以方便读取输入流的文本内容 */ public class PrintStreamTest { public static void main(String[] args) { PrintStream pst=null;//称作处理流或包装流 try { FileOutputStream fos=new FileOutputStream("F:\\Thinking.txt");//直接和物理文件打交道的流也称节点流 //以PrintStream来包装FileOutputStream输出流 pst=new PrintStream(fos); //使用printStream来实现输出 pst.println("普通字\r\n符串"); pst.println(new PrintStreamTest()); } catch (FileNotFoundException e) { e.printStackTrace(pst); } finally{ if(pst!=null){ pst.close();} } } }
10、重定向标准输入输出流
Java的标准输入输出分别通过System.in和System.out来代表,默认情况下它们分别代表键盘和显示器
System类提供了重定向标准输入输出的方法
例程:RedirectIn.java import java.io.*; import java.util.Scanner; public class RedirectIn { public static void main(String[] args) { FileInputStream fis = null; try { // 将标准输入重定向到fis输入流 fis = new FileInputStream("F:\\Thinking.txt"); System.setIn(fis); //使用流接受标准输入 // BufferedReader bf = new BufferedReader(new InputStreamReader( // System.in)); // String len = ""; // while ((len = bf.readLine()) != null) { // System.out.println(len); // } //使用Scanner Scanner sn=new Scanner(System.in); System.out.println("=====使用Scanner====="); while(sn.hasNext()){ System.out.println(sn.next()); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { if (fis != null) fis.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } 例程:RedirectOut.java import java.io.*; public class RedirectOut { public static void main(String[] args) { // TODO Auto-generated method stub PrintStream p=null; try { p=new PrintStream(new FileOutputStream("F:\\out.txt")); p.println("p.println()"); System.out.println("重定向输出流到F:\\out.txt"); System.setOut(p); System.out.println("此行不显示在屏幕上(不在控制台输出),直接写入了文件"); } catch (FileNotFoundException e) { e.printStackTrace(); }finally{ if(p!=null){ p.close(); } } } }
Scanner和IO流接(BufferedReader)受键盘输入的比较(BufferedReader输入都被当成String对象,BufferedReader不能读取基本类型输入项)
例程:InputTest.java public class InputTest { public static void main(String[] args) throws IOException { BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); // int len=br.read(); // System.out.println((char)len); // String len2=br.readLine(); // System.out.println(len2); // Scanner sn=new Scanner(System.in); // System.out.println(sn.nextLine()); // System.out.println(sn.nextInt()); String input = "1 fish 3 fish red fish blue fish"; Scanner s = new Scanner(input).useDelimiter("\\s*fish\\s*");//自定义分隔符为fish System.out.println(s.nextInt()); System.out.println(s.nextInt()); System.out.println(s.next()); System.out.println(s.next()); input="324 42314 fish zpc"; s=new Scanner(input); System.out.println(s.nextInt()); System.out.println(s.nextInt()); System.out.println(s.next()); // System.out.println(s.nextByte(0)); s.close(); } } //Scanner也可以指定读取某个文件 public class TestScanner { public static void main(String[] args){ Scanner sn=null; //sn.useDelimiter("\n");//如果增加这一行将只把回车符作为分隔符 try { sn=new Scanner(new File("F:\\Thinking.txt")); while(sn.hasNext()){ System.out.println(sn.nextLine()); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
11、RandomAccessFile对象
package Serialize; /* * 功能:读一个文件,复制自身追加到此文件,操作完成后文件应该变成双倍大小 * RandomAccessFile对象不能向文件的指定位置插入内容,这会覆盖原来的内容 * 如果要在指定位置插入内容,程序需要先把插入点后面的内容都入缓冲区, * 等把需要插入的数据写入文件后,在将缓冲区的内容追加到文件后面,详见InsertContent.java */ import java.io.*; public class RandomAccessFileTest { public static void main(String[] args) { RandomAccessFile raf = null; // 以只读方式打开一个RandomAccessFile对象 try { raf = new RandomAccessFile("F:\\掀起你的盖头来.mp3", "rw"); System.out.println("RandomAccessFile对象的文件指针初始位置:" + raf.getFilePointer()); System.out.println("raf.length():"+raf.length()); //读文件 byte[] b = new byte[1024]; int hasRead = 0; int readTotal=0; long FileTotalLen= raf.length(); while (((hasRead = raf.read(b)) != -1)&&readTotal<=FileTotalLen) { readTotal+=hasRead; //System.out.println(new String(b, 0, hasRead)); raf.seek(raf.length()); raf.write(b); raf.seek(readTotal); } System.out.println("raf.length():"+raf.length()); } catch (IOException e) { e.printStackTrace(); } finally { try { if (raf != null) { raf.close(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } //InsertContent.java import java.io.*; /* * 功能:向一个文件的指定位置插入内容,而不覆盖原有的内容 */ public class InsertContent { public static void main(String[] args) { insert("F:\\thinking.txt",200,"===========我是鸟鹏!=========="); } public static void insert(String fileName, long pos, String content) { InputStream is = null; OutputStream os = null; RandomAccessFile raf = null; // 创建一个临时文件来保存插入点后面的数据 try { File temp = File.createTempFile("tem", null); temp.deleteOnExit(); raf = new RandomAccessFile(fileName, "rw"); os=new FileOutputStream(temp); byte[] buf=new byte[1024]; int hasRead=0; raf.seek(pos); while((hasRead=raf.read(buf))!=-1){ os.write(buf,0,hasRead); } raf.seek(pos); raf.write(content.getBytes()); //下面的代码实现插入数据的功能 is=new FileInputStream(temp); while((hasRead=is.read(buf))!=-1){ raf.write(buf,0,hasRead); } } catch (IOException e) { e.printStackTrace(); }finally{ try { if(raf!=null) raf.close(); } catch (IOException e) { e.printStackTrace(); } } } }
12、对象序列化
在Java中如果需要将某个对象保存到磁盘或者通过网络传输,那么这个类应该实现Serializable标记接口或者Externalizable接口之一
序列化的步骤:
a、创建一个ObjectOutputstream处理流(必须建立在其它结点的基础上)对象
b、调用ObjectOutputstream对象的writeObject方法输出可序列化对象
从二进制流中恢复Java对象的反序列化步骤(按照实际写入的顺序读取):
a、创建一个ObjectInputStream处理流(必须建立在其它结点的基础上)对象
b、调用ObjectInputstream对象的readObject方法输出可序列化对象(该方法返回的是Object类型的Java对象)
(反序列化恢复Java对象时必须要提供java对象所属类的class文件)
注:如果一个可序列化对象有多个父类,则该父类要么是可序列化的,要么有无参的构造器,因为反序列化机制要恢复其关联的父类实例
而恢复这些父类实例有两种方式:使用序列化机制、使用父类无参的构造器
采用Java序列化机制时,只有当第一次调用writeObject输出某个对象时才会将该对象转换成字节序列写到ObjectOutputStream
在后面程序中如果该对象的属性发生了改变,即再次调用writeObject方法输出该对象时,改变后的属性不会被输出
如果父类没有实现Serializable接口,则其必须有默认的构造函数(即没有参数的构造函数)
但是若把父类标记为可以串行化,则在反串行化的时候,其默认构造函数不会被调用。
这是因为Java 对串行化的对象进行反串行化的时候,直接从流里获取其对象数据来生成一个对象实例,而不是通过其构造函数来完成。
例程: import java.io.Serializable; public class Person implements Serializable { private String name ; private int age; public Person(){ System.out.println("Person的无参构造器"); } public Person(String name ,int age){ System.out.println("Person的有参构造器"); this.age=age; this.name=name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } import java.io.Serializable; public class Teacher implements Serializable { private String name; private Person student;//Teacher类持有的Person类应该是可序列化的 public Teacher(String name,Person student){ System.out.println("Teacher的有参构造器"); this.student=student; this.name=name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Person getStudent() { return student; } public void setStudent(Person student) { this.student = student; } } import java.io.*; public class SerializeMutable { public static void main(String[] args) { ObjectOutputStream oos=null; ObjectInputStream ois=null; try { oos=new ObjectOutputStream(new FileOutputStream("F:\\objecttest.txt")); Person p=new Person("zpc", 24); oos.writeObject(p); p.setName("niao鹏"); //第二次写同样的对象时只是输出序列化编号 oos.writeObject(p); ois=new ObjectInputStream(new FileInputStream("F:\\objecttest.txt")); Person p1=(Person) ois.readObject(); //实际得到的是和p1同样的对象 Person p2=(Person) ois.readObject(); System.out.println("p1=p2? "+(p1==p2)); System.out.println("p2.getName():"+p2.getName()); System.out.println("==================================="); Person perstu=new Person("云翔", 15); Teacher t1=new Teacher("马老师",perstu); Teacher t2=new Teacher("周老师",perstu); oos.writeObject(perstu); oos.writeObject(t1); oos.writeObject(t2); Person pperstu1=(Person)ois.readObject(); Teacher tt1=(Teacher)ois.readObject(); Teacher tt2=(Teacher)ois.readObject(); System.out.println("tt1.getStudent()==p? "+(tt1.getStudent()==pperstu1)); System.out.println("tt2.getStudent()==p? "+(tt2.getStudent()==pperstu1)); System.out.println(tt2.getStudent().getName()); oos.writeObject(perstu); Person pperstu2=(Person)ois.readObject(); System.out.println("pperstu2==pperstu1? "+(pperstu2==pperstu1)); System.out.println(pperstu1.getName()); System.out.println(pperstu2.getName()); //下面的语句报错,再次证明了写入、读出对象要一致 // Person pperstu3=(Person)ois.readObject(); // System.out.println(pperstu3.getName()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }catch(ClassNotFoundException e){ e.printStackTrace(); } } }
13、自定义序列化
在属性前面加上transient关键字,可以指定Java序列化时无需理会该属性值,由于transient修饰的属性将被完全隔离在序列化机制外,
这会导致在反序列化恢复Java对象时无法取得该属性值。
实现自定义序列化要重写类的如下方法:
private void writeObject(java.io.ObjectOutputStream out) throws IOException;
private void readObject(java.io.ObjectInputStream in) throws IOException,ClassNotFoundException;
还有种更彻底的序列化机制可以在序列化某对象时替换该对象,此种情况下应为序列化类提供如下特殊方法:
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
Java的序列化机制保证在序列化某个对象之前,先调用对象的writeReplace()方法,如果该方法返回另一个Java对象,
则系统转为序列化另一个对象。
小结:系统在序列化某个对象之前,先会调用该对象的如下两个方法:
writeReplace和writeObject,系统先调用被序列化对象的writeReplace方法,如果返回另一个Java对象,则再次调用该java对象的writeReplace方法....
直到该方法不在返回一个对象为止,程序最后调用该对象的writeObject方法来保存该对象的状态
与writeReplace方法相对的有一个方法可以替代原来反序列化的对象
即:private Object readResolve() throws ObjectStreamException;
这个方法会紧接着readObject之后被立即调用,该方法的返回值会代替原来反序列化的对象,而原来readObject反序列化的对象会被立即丢弃
(此方法在序列化单例类、早期枚举类时很有用)
14、另一种序列化机制:Java类实现Externalizable接口
两种序列化机制的区别:
实现Serializable接口系统自动存储必要信息
Java内建支持,易于实现只需实现该接口即可
无须任何代码支持
性能略差
实现Externalizable接口
程序员决定需要存储哪些信息
仅仅提供两个空方法实现该接口必须为两个空方法提供实现
类中必须存在一个无参构造方法
性能略高
15、Charset对象
Java提供了Charset来处理字节序列和字符序列之间的转换关系,该类包含了用于创建解码器和编码器的方法
例程:CharsetTransformTest.java
import java.nio.*; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; public class CharsetTransformTest { public static void main(String[] args) throws CharacterCodingException { // 创建简体中文对应的Charset Charset cn = Charset.forName("GBK"); CharsetEncoder cnEncoder = cn.newEncoder(); CharsetDecoder cnDecoder = cn.newDecoder(); CharBuffer chbuf = CharBuffer.allocate(10); chbuf.put(‘周‘); chbuf.put(‘总‘); chbuf.put(‘裁‘); chbuf.flip(); // 将CharBuffer中的字符序列转换成字节序列 ByteBuffer bbuf = cnEncoder.encode(chbuf); System.out.println("bbuf.capacity():" + bbuf.capacity()); System.out.println("chbuf.capacity():" + chbuf.capacity()); // 循环访问每个字节 for (int i = 0; i < bbuf.capacity(); i++) { System.out.println(bbuf.get(i) + ""); } //将字节序列解码成字符序列 System.out.println("\n " + cnDecoder.decode(bbuf)); } }
16、Buffer(缓冲)抽象基类
常用子类ByteBuffer,这些Buffer的子类不是通过构造器创建而是通过static xxxBuffer allocate(int capacity)来创建一个容量为capacity的xxxBuffer对象。Buffer中包含两个重要的方法flip(该方法将limit设置为position所在位置,position设置 为0,)和clear(将position设为0,limit设为 capacity)。flip为从Buffer中读取数据做好准备,而clear是为向Buffer中装入数据做好准备
例程:BufferTest.java
import java.nio.*; public class BufferTest { public static void main(String[] args) { //创建Buffer CharBuffer cbuf=CharBuffer.allocate(8); System.out.println("cbuf.position():"+cbuf.position()); System.out.println("cbuf.limit():"+cbuf.limit()); System.out.println("cbuf.capacity():"+cbuf.capacity()); //放入元素 cbuf.put(‘a‘); cbuf.put(‘走‘); cbuf.put(‘好‘); System.out.println("加入三个元素后position:"+cbuf.position()); System.out.println("加入三个元素后limit():"+cbuf.limit()); cbuf.flip(); System.out.println("调用flip后position:"+cbuf.position()); System.out.println("调用flip后limit():"+cbuf.limit()); System.out.println("第一个元素(position=0):"+cbuf.get()); System.out.println("取出第一个元素后position:"+cbuf.position()); cbuf.clear(); System.out.println("调用clear后position:"+cbuf.position()); System.out.println("调用clear后limit():"+cbuf.limit()); System.out.println("调用clear后缓冲区没有被清除:"+cbuf.get(2)); System.out.println("执行绝对读取(不影响position)后position:"+cbuf.position()); } }
17、Channel(通道)
程序不能直接访问Channel中放入数据,Channel只能和Buffer进行交互,发送到Channel中的数据必须先放到Buffer对象中,程序再将Buffer的输入写入Channel。而从Channel中读取数据也必须先读到Buffer中,程序再从Buffer中取出这些数据。
例程:FileChannelTest.java
/* * 把F:\\out.txt中的内容通过Channel的方式写到F:\\niao.txt中 */ import java.nio.CharBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.io.*; public class FileChannelTest { public static void main(String[] args) { FileChannel inChannel=null; FileChannel outChannel=null; try{ File f=new File("F:\\niao.txt"); //创建FileInputStream,以该文件输入流创建FileChannel inChannel=new FileInputStream(f).getChannel(); //将inChannel里的数据映射成byteBuffer MappedByteBuffer buff=inChannel.map(FileChannel.MapMode.READ_ONLY,0, f.length()); //直接把buff中的内容输出到另一个channel buff.limit(); outChannel =new FileOutputStream("F:\\out.txt").getChannel(); //buff是输入型的不能put //buff.put("ABC".getBytes()); System.out.println("buff.get():"+buff.get()); outChannel.write(buff); buff.clear();//复原limit,position的位置 Charset c=Charset.forName("GBK"); //使用解码器将ByteBuffer转换成CharBuffer CharsetDecoder d=c.newDecoder(); CharBuffer chbuf=d.decode(buff); System.out.println("chbuf:\n"+chbuf); }catch (IOException e) { e.printStackTrace(); } } } /* * 将一个文本文件的内容复制到它自身 */ import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class RandomAccessFileTest { public static void main(String[] args) { FileChannel randomChannel = null; try { File f = new File("F:\\niao.txt"); RandomAccessFile raf = new RandomAccessFile(f, "rw"); randomChannel = raf.getChannel(); // 把Channel中的数据映射到buffer(map方法一次将所有文件内容映射到内存) ByteBuffer buff = randomChannel.map(FileChannel.MapMode.READ_WRITE, 0, f.length()); // 把channel的指针后移到最后 randomChannel.position(f.length()); // 将buffer中的数据输出 randomChannel.write(buff); } catch (IOException e) { e.printStackTrace(); } finally { try { if (randomChannel != null) { randomChannel.close(); } } catch (Exception e) { e.printStackTrace(); } } } }
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。