Java程序设计 Java Programming Spring, 2013 Java I/O 1. 2. 3. 4. 5. 6. 7. 8. java.io File Class RandomAccessFile Class InputStream & OutputStream Reader & Writer FileInputStream & FileOutputStream FileReader & FileWriter BufferedStream 2 /84 I/O 输入(Input)/输出(Output)系统,简称为I/O系统, 对于输入/输出问题,Java将之抽象化为流 (Stream)对象来解决,对不同的输入/输出问题, 提供了相应的流对象解决的方案。 流式输入输出的特点是数据的获取和发送沿数据 序列的顺序进行,即每一个数据都必须等待排在 它前面的数据,等前面的数据读入或送出之后才 能被读写。 3 /84 java.io 包 java.io包中定义与输入、输出流相关的类和接口, 构成了Java语言的I/O框架。 java.io包中定义的各种各样的输入输出流类,它 们都是Object类的直接或间接子类,每一个流类 代表一种特定的输入或输出流。 import java.io.*; 4 /84 流的类结构 • java.io包的类层次结构: – 以四个顶层抽象类为基础,衍生出系列具体的类 来完成各种输入/输出。 1. InputStream,OutputStream:用于字节的读/写。 2. 3. Reader,Writer:用于文本(字符)的读/写。 实际使用的是它们的子类的对象。 Object InputStream Reader OutputStream File Writer RandomAccessFile 5/84 File handling 1. File类 2. RandomAccessFile类 File类 Fundamental class: File Represents either a file(文件) or a dir(文件夹) location in a file system. Through its pathname(路径名) A pathname Unix: /usr/java/bin/javac Windows: c:\java\bin\javac 7 /84 Java文件路径的表示: 1. 2. Java约定是用UNIX和URL风格的斜线(/)来作路 径分隔符; 如果用Windows/DOS所使用的反斜线( \ )的约定, 则需要在字符串内使用它的转义序列( \\ )。 例: c:\\java\\bin\\javac (Windows/DOS) c:/java/bin/javac (UNIX和URL风格) 8 /84 Example—separator(分隔符) //显示分隔符 import java.io.*; public class Prova { public static void main(String[] arg) { System.out.println(File.separator+" - "+ File.separatorChar+" - "+ File.pathSeparator+" - "+ File.pathSeparatorChar); } } • Output (in Windows) \-\-;-; • Output (in Unix): /-/-:-: File类 java.io包中的File类提供了获得文件基本信息及文 件操作的方法。 通过File类,可以建立与磁盘文件的联系;可以用 来获取或设置文件或目录的属性,但不支持从文件 里读取数据或者往文件里写数据。 构造方法 1. 2. 3. File(String filename); File(String directoryPath, String filename); File(File f, String filename); 10 /84 Example: 创建文件和文件夹 //创建文件夹 File file1 = new File("e:/java"); file1.mkdir(); //在目录中创建文件 File file2 = new File(file1,"test.txt"); file2.createNewFile(); File类的常用方法 操作文件属性的方法 public boolean canRead();判断文件是否是可读的。 public boolean canWrite();判断文件是否可被写入。 public boolean exists(); 判断文件是否存在。 public boolean isFile(); 判断文件是否是一个正常文 件,而不是目录。 public boolean isDirectroy();判断文件是否是一个目 录。 12 /84 File类的常用方法 获取文件的名称、路径 public String getName():获取文件的名字。 public String getPath():得到文件的路径名。 public String getAbsolutePath():得到文件的绝对 路径名。 public String getParent():得到文件的上一级目录 名。 13 /84 File类的常用方法 获取文件处理信息 public long length(): 获取文件的长度(单位是字节)。 public long lastModified():获取文件最后修改时间。 public boolean delete():删除文件。 14 /84 File类的常用方法 目录(directory)操作 创建一个名为File的目录: public boolean mkdir() 列出目录中的文件 public String[] list() public File[] listFiles() 在目录中创建文件 public boolean createNewFile() 15 /84 Example: //创建文件夹 File file1 = new File(“e:/java”); //创建File对象 file1.mkdir(); //通过File对象创建文件夹 //在目录中创建文件 File file2 = new File(file1,"test.txt"); file2.createNewFile(); //通过File对象创建文件 //显示文件夹中所有文件的文件名 String[ ] files = file1.list(); for(int i = 0; i<files.length; i++) System.out.print(files[i]); //显示文件夹中所有文件的对象 File[ ] files = file1.listFiles(); for(int i = 0; i<files.length; i++) System.out.println(files[i].getName()+"\t"+ files[i].length()); 17/84 Example 1 to test the methods of ‘File’ import java.io.File; public class TryFile { public static void main(String[] args) { // Create an object that is a directory File myDir = new File(“C:/jdk1.5.0/src/java/io”); System.out.println(myDir + (myDir.isDirectory() ? “ is” : “ is not”) + “ a directory.”); // Create an object that is a file File myFile = new File(myDir, “File.java”); System.out.println(myFile + (myFile.exists() ? “ does” : “ does not”) + “ exist”); System.out.println(myFile + (myFile.isFile() ? “ is” : “ is not”) + “ a file.”); System.out.println(myFile + (myFile.isHidden() ? “ is” : “ is not”) + “ hidden”); System.out.println(“You can” + (myFile.canRead() ? “ “ : “not “) + “read “ + myFile); System.out.println(“You can” + (myFile.canWrite() ? “ “ : “not “) + “write “ + myFile); } } 18/84 文件的随机访问类 (RandomAccessFile) RandomAccessFile类可以对文件进行随机读写操 作。 用来访问保存数据记录的文件; 数据记录是指对应于数据源中一行信息的一组完 整的相关信息。 例如: 时间 地点 课程 2013/04/02 1302教室 Java程序设计 班级 数媒2011 19 /84 文件的随机访问类 (RandomAccessFile) 定义了对各种数据类型进行读写的方法,如: byte, char, double, float, int, short, long等。 可以用getFilePointer()方法获得当前的文件读取 指针,可以用seek(long pos)方法访问记录,并 进行读写。 getFilePointer():返回文件指针在文件中的当前偏移 量。 seek(long pos): 设置从文件开头到文件指针的偏移 量为pos,在偏移量pos的位置发生下一个读取或写入 操作。 文件的随机访问类(RandomAccessFile) 创建一个RandomAccessFile对象 1. RandomAccessFile(File file, String mode) 2. RandomAccessFile(String name, String mode); • 构造函数可能产生FileNotFoundException及IOException异常 • mode 1. r:只读; 2. rw:可读可写 File file=new File(“d:\\lx\\a.txt”); RandomAccessFile rf=new RandomAccessFile( file, ”rw”) ; RandomAccessFile rfile=new RandomAccessFile(“d:\\lx\\a.txt”, ”rw”); 21/84 文件的随机访问类(RandomAccessFile) 1. 2. 3. 读写数据的常用方法 读、写基本数据类型: readInt()、writeInt(int n)等; 读取文件中的一行: readLine(); 读、写UTF字符串: readUTF()、writeUTF(String str); UTF-8:8-bit Unicode Transformation Format,一种针 对Unicode的可变长度字符编码 。 22/84 文件的随机访问类 (RandomAccessFile) 文件随机读写流的读取指针控制 long getFilePointer(); 得到当前的文件读取指针。 void seek(long pos); 把指针从开始移动到pos位置。 long length(); 得到文件的长度(有多少个字节) 。 void setLength(long newLength); 重新设置文件的长度。 What is the stream (流) ? 流是数据的有序序列。 流可分为输入流和输出流(按流动方向) : 输入流指从某个数据来源输入Java应用程序的数据序列, InputStream和Reader处理输入 输出流指Java应用程序向某个数据目的地输出的数据序 列, OutputStream和Writer处理输出 24 /84 输入流、输出流 输入流、输出流分别如下图所示。 输入流 数据源 程序 输入流示意图 输出流 程序 数据宿 输出流示意图 25 /84 System类和标准输入输出流 System类代表系统,系统级的很多属性和控制方 法都放置在该类的内部。System类在java.lang 包中。 You do not instantiate the System class to use it. All of System's variables and methods are static variables and static cmethods. To use a class variable, you use it directly from the name of the class using Java's dot (.) notation. 26 /84 标准流-- System Class //获得运行程序的电脑系统的用户名 class UserNameTest { public static void main(String[] args) { String name; name = System.getProperty("user.name"); System.out.println(“Your name is “ + name); } } • Notice that the program never instantiates a System object. It just references the getProperty method and the out variable directly from the class. 27 /84 标准流 System类的3个静态变量就是系统的三个标准流: 1. System.in:表示系统的标准输入流对象,通常的 环境下标准输入流指向键盘输入; System.out:表示系统的标准输出流对象,通常 环境下指向屏幕输出; System.err:表示系统的标准错误输出流对象,通 常环境下也指向屏幕输出。 2. 3. 上述三个标准流由系统负责打开关闭,使用时不 需要显式地打开或关闭。 System.out和System.err都用于输出,通常情况下: Syetem.out用于程序输出一般信息,而System.err用于输出错误信 息和其他需要引起用户立即注意的信息。 在系统中,System.out具有缓冲机制,而System.err是没有缓冲的, 28 /84 这是两者之间的一个重要区别。 The Standard I/O Streams Standard Input Stream System.in -- The System class provides a stream for reading text -- the standard input stream. Use System.in.read to read the input entered by user. Standard Output and Error Streams System.out -- The standard output stream is typically used for command output. System.err -- The standard error stream is typically used to display any errors that occur when a program is running. The print, println, and write Methods The print and println methods write their String argument to the screen. System.out.println("Duke is not a penguin!"); The write method is used to write bytes to the stream. 29 /84 //DataTypePrintTest.java, println方法的重载 public class DataTypePrintTest { public static void main(String[] args) { String stringData = "Java Mania"; char[ ] charArrayData = { 'a', 'b', 'c' }; int integerData = 4; long longData = Long.MIN_VALUE; float floatData = Float.MAX_VALUE; double doubleData = Math.PI; boolean booleanData = true; System.out.println(stringData); System.out.println(charArrayData); System.out.println(integerData); System.out.println(longData); System.out.println(floatData); System.out.println(doubleData); System.out.println(booleanData); System.out.println(); } } 30 //标准输入、输出 import java.io.*; class TranslateByte{ public static void main(String[] args) throws IOException{ byte from=(byte)args[0].charAt(0); byte to=(byte)args[1].charAt(0); int b; while((b=System.in.read())!=-1) //从键盘读入一个byte System.out.write(b==from ? to : b);//输出到屏幕 } } javac TranslateByte.java java TranslateByte b B 输入: abracadabra! 输出: Args[0] Args[1] aBracadaBra! 31 32 /84 标准输入输出示例: import java.io.*; public class StandardIO1{ public static void main(String []args) { int ch; System.out.println("请输入一行字符"); try{ //read in a byte from keybord while((ch=System.in.read())!='\r') System.out.write(ch); //写入标准输出流 }catch(IOException e){ System.out.println(e.toString()); } } } System.out.write('\n'); 33 /84 34 流的分类 字节流和字符流 InputStream和OutputStream处理8位字节流数据 Reader和Writer处理16位的字符流数据 节点流和处理流(按流的处理位置 ) 节点流:可以从或向一个特定的地方(节点)读写数据。 如FileReader 。 处理流:使用节点流作为输入或输出。 处理流不直接与 数据源或目标相连,而是与另外的流进行配合,对数据 进行某种处理,例如: BufferedReader, InputStreamReader等。 35 /84 字符流和字节流 java.io包中类和接口从功能上主要分为字符流类 型和字节流类型 1. 2. 字符(character)流是指数据序列的基本构成单位是16 位的Unicode字符数据,如:各类基于字符编码的纯文 本文件。 字节(byte)流是指数据序列的基本构成单位是8位的字 节数据,如:各类基于二进制数据的文件、图形图像 文件。 36 /84 文本文件(Text Files,纯字符文件) vs. 二进制文件(Binary Files) Text file: 纯文本文件是指的只包含纯文字和字符的文件,这些文 字是没有格式的。 Example: ASCII (binary)文件: 00110001, 00110010, 00110111 记事本文件(.txt文件,默认ANSI编码 )。 Binary file: 非字符文件,如:图片、声音、word文档。 文件含有特殊的格式及计算机代码 。 37 /84 字节流(Byte Streams) 字节流可分为输入字节流和输出字节流 抽象类 InputStream 用于表示所有输入字节流 抽象类 OutputStream 用于表示所有输出字节流 38 /84 Byte Stream family System.in System.out System.err 39 /84 InputStream和OuputStream两个抽象类的子类 40 InputStream类 该抽象类作为所有输入字节流类的基类,声明用于读取字节 流数据的通用方法。 public abstract int read() throws IOException; public int read(byte[] buf, int offset, int count) throws IOException; public int read(byte[] buf) throws IOException; public long skip(long count) throws IOException; public int available() throws IOException; public void close() throws IOException; Every method here can throw an IOException; If a programmer uses any of these methods, he/she must catch IOException (or Exception); 41 /84 How to do I/O import java.io.*; 1. 2. 所有的流类对象在使用前必须打开(即:创建), 在使用后必须关闭。 Open the stream:创建流对象 Use the stream (read, write, or both) 3. 通过流对象调用方法 Close the stream: 关闭流,释放I/O资源 42 /84 InputStream类 创建InputStream类对象时,便自动打开了对象所表示的 输入流; InputStream所有与输入相关的方法声明抛出异常类 IOException InputStream类的对象在完成数据输入后,除标准输入流 类对象System.in外,必须调用close方法关闭输入流,通 常可将该方法的调用放入finally语句块中 43 import java.io.*; Example: public class StandardIO1{ public static void main(String []args) throws IOException { int b; InputStream stdin1= System.in; //声明输入字节流并指向标准输入流 try { b = stdin1.read( ); //使用流,从键盘读入一个字节 System.out.println((char)b); System.out.println(b); stdin1.close(); //关闭流 }catch(IOException ie) { Output: System.out.println(); } a } } a 97 /***** HelloWorld.java *****/ import java.io.*; public class HelloWorld { public static void main( String[ ] args ) { byte[] ba = new byte[10]; InputStream stdin = System.in; System.out.println("Please input a string: "); try{ stdin.read(ba); //从键盘读入多个字节 }catch(IOException ie){ System.out.println(); }finally { } } } String s = new String(ba); System.out.println("The string read in is "+ s); 45/84 OutputStream类 当创建OnputStream类对象时,便自动打开了对 象所表示的输出流。 OutputStream所有与输出相关的方法声明抛出 异常类IOException OutputStream类的对象在完成数据输出后,除 标准输出流对象System.out外,必须调用close 方法关闭输出流 46 /84 OutputStream类 该抽象类作为所有输出字节流类的基类,声明用于输出字 节流数据的通用方法: void write(int b) throws IOException public void write(byte[] buf, int offset, int count) throws IOException void write(byte[] b) throws IOException void flush() throws IOException void close() throws IOException 47 /84 OutputStream类 public abstract void write(int b) throws IOException 将指定的字节写入此输出流。 write 的常规协定是:向输出流写入一个字节。要写入的字 节是参数 b 的八个低位。b 的 24 个高位将被忽略。 OutputStream 的子类必须提供此方法的实现。 OutputStream stdout = System.out; stdout.write(104); // ASCII ‘h’ ,一个字节 stdout.flush(); 字符流(Character Streams) 字符流可分为输入字符流和输出字符流 抽象类 Reader 用于表示所有输入字符流 抽象类 Writer 用于表示所有输出字符流 49 /84 Character Stream Family 50 /84 Reader和Writer的主要子类如下: 51 /84 Reader类 • 该抽象类作为所有输入字符流类的基类,声明用于读 取输入字符文本数据的通用方法: public int read(); public abstract int read(char[] buf, int offset,int count); public int read(char[] buf); public long skip(long count); public int available(); public abstract void close(); 52 /84 Writer类 该抽象类作为所有输出字符流类的基类,声明用于输出字符 文本数据的通用方法: public void write(int ch); public abstract void write(char[] buf, int offset, int count); public void write(char[] buf); public void write(String str, int offset, int count); public void write(String str); public Writer append(char c) public Writer append(CharSequence csq) public Writer append(CharSequence csq, int start, int end) public abstract void flush(); public abstract void close(); 53 /84 文件的读写 FileOutputStream/FileInputStream 是抽象类 InputStream/OutputStream的子类. 它们生成与文件链接的字节流。 为打开文件,只需创建 FileOutputStream/FileInputStream类的一个对 象,在构造函数中以参数形式指定文件的名称。 54 /84 文件的读写 FileInputStream类和FileOutputStream类的构 造函数是创建一个输入输出的对象,通过引用该 对象的读写方法,来完成对文件的输入输出操作。 在构造函数中,需要指定与所创建的输入输出对 象相连接的文件。 要构造一个FileInputStream对象,所连接的文件 必须存在而且是可读的; 构造一个FileOutputStream对象,如果输出文件 已经存在且可写,该文件内容会被新的输出所覆 盖。 55 /84 FileInputStream类 从InputStream中派生出来,其所有方法都从InputStream类继承 而来。基本操作步骤: 1>建立文件的输入流对象 2>从输入流中读取字节 3>关闭流 常用构造方法 public FileInputStream(String name) throws FileNotFoundException public FileInputStream(File file) throws FileNotFoundException 均会抛出FileNotFoundException异常 构造方法参数指定的文件称作输入流的源 FileInputStream fin = new FileInputStream(“c:\\java\\test.java”); 56 /84 FileInputStream 文件字节流的读取 read方法给程序提供从输入流中读取数据的基本方法。 read方法按顺序读取流,直到流的末尾或流被关闭(close() 方法)。 read方法的格式: public int read() throws IOException 从输入流中顺序读取单个字节的数据,并将所读单个 字节以int返回。如果已到达文件末尾,则返回 -1 。 public int read(byte b[]) throws IOException public int read(byte b[], int off, int len) throws IOException 把多个字节读到一个字节数组中,返回实际所读的字节 数。 57 /84 Example with FileInputStream /* class example FileIn.java */ import java.io.*; public class FileIn{ public static void main(String[] args){ try { FileInputStream fin = new FileInputStream(“c:\\java\\test.java”); int input; while ((input = fin.read()) != -1)//读取字节直到末尾 System.out.print((char)input); fin.close(); }catch(IOException ie){ System.out.println(e); } } } 58/84 FileOutputStream类 从OutputStream中派生出来,其所有方法都从OutputStream 类继承来的。 基本操作步骤: 1>建立文件的输出流对象 2>向输出流中写字节 3>关闭流 常用构造方法 public FileOutputStream(String name) public FileOutputStream(File file) 如果输出流要写入数据的文件已经存在,该文件中的数据 内容就会被刷新;如果要写入数据的文件不存在,该文件 就会被建立。 FileOutputStream fout = new FileOutputStream(“c:\\java\\test.java”); 59 /84 FileOutputStream文件输出字节流 write方法把字节发送给输出流,顺序地写文件,直 到流的末尾或流被关闭(close()方法) write方法的格式: public void write(byte b[]) throws IOException public void write(byte b[], int off, int len) throws IOException 60 /84 /*例:编写程序,接收用户从键盘输入的数据,回车后保存到 文件test.txt中。若用户输入符号#,则退出程序。*/ import java.io.*; public class WriteFile{ public static void main(String args[]) { byte buffer[]=new byte[128]; System.out.println("请输入数据,回车后保存到文件test.txt"); System.out.println("输入 # 则退出!"); try{ FileOutputStream f=new FileOutputStream("test.txt"); while(true){ int n=System.in.read(buffer);//从键盘读多个字节存入buffer if(buffer[0]=='#' ) break; f.write(buffer, 0, n); f.write(‘\n'); } f.close(); }catch(IOException e){ System.out.println(e.toString()); } } } 61/84 上例程序运行结果如图所示: 62/84 例:使用FileInputStream类与FileOutputStream类复制文件。 /* class example FileIn_Out.java */ /* assumes each char is a single byte */ import java.io.*; public class FileIn_Out{ public static void main(String[] args) { try { FileInputStream fin=new FileInputStream(“c:\\java\\file1.java”); FileOutputStream fout=new FileOutputStream(“c:\\java\\file2.java”); byte b[]= new byte[512]; while((fin.read(b, 0, 512))!=-1) fout.write(b); fin.close(); fout.close(); }catch(IOException ie) { System.out.println(ie); }catch(Exception e) { System.out.println(e); } } 63/84 } //File and File Stream -- FileStream.java import java.io.*; class filestream { public static void main(String args[]) { try{ File inFile=new File(“file1.txt"); File outFile=new File(“file2.txt"); FileInputStream fis=new FileInputStream(inFile); FileOutputStream fos=new FileOutputStream(outFile); int c; while((c=fis.read())!=-1) fos.write(c); fis.close(); fos.close(); }catch(FileNotFoundException e) { System.out.println("FileStreamsTest: "+e); }catch(IOException e) { System.err.println("FileStreamsTest: "+e); } } } 7.3 File I/O 64/84 FileReader类 从InputStreamReader中派生出来,其所有方法都从 InputStreamReader类和Reader类继承而来。 基本操作步骤: 1>建立文件的输出流对象 2>向输出流中写字符 3>关闭字符流 常用构造方法 public FileReader(String name) throws FileNotFoundException public FileReader (File file) throws FileNotFoundException 均会抛出FileNotFoundException异常 构造方法参数指定的文件称作输入流的源. 65 /84 FileReader类:文件字符流的读取 read方法给程序提供从输入流中读取数据的基本 方法。 read方法的格式: public int read() throws IOException 从源中顺序读取一个字符,以int返回。 public int read(char ch[]) throws IOException public int read(char ch[], int off, int len) throws IOException 把多个字符读到一个字符数组中,返回实际所读 的字符数。 66 /84 //FileIn.java import java.io.*; class FileIn { public static void main(String args[]) throws IOException{ int i; FileReader fr; try { fr = new FileReader("D:\\workspace\\test\\FileIn.java"); while((i = fr.read())!= -1) System.out.print((char) i); fr.close(); }catch(FileNotFoundException e) { System.out.println("File Not Found"); }catch(ArrayIndexOutOfBoundsException e) { System.out.println("Usage: ShowFile File"); } } } 67/84 //FileReaderSample.java import java.io.*; public class FileReaderSample { public static void main(String args[ ]) throws IOException{ char data[ ]=new char[100]; //建立可容纳100个字符的数组 // 建立对象fr FileReader fr=new FileReader("D:\\workspace\\copy.txt"); // 将数据读入字符数组data内 int num=fr.read(data); // 输出在控制台 System.out.println("Characters read= "+num); for(int i=0; i<data.length; i++){ System.out.println("data["+i+"]="+data[i]); } fr.close(); } } 68/84 FileWriter类 从OutputStreamReader中派生出来,其所有方法 都从OutputStreamReader类和Writer类继承而来。 基本操作步骤: 1>建立文件的输出流对象 2>向输出流中写字符 3>关闭字符流 常用构造方法 public FileWriter(String name) public FileWriter(File file) 均会抛出FileNotFoundException异常 69 /84 FileWriter类:文件字符流的输出 write方法给程序提供把字符数据写入到输出流的基 本方法。 write方法的格式 public void write(char b[ ]) throws IOException public int write(char b[ ], int offset, int len) throws IOException public int write(String str) throws IOException public int write(String str, int offset, int len) throws IOException 70 /84 BufferedStream(缓冲流) 处理流 节点流 71 /84 BufferedStream缓冲流 • BufferedReader和BufferedWriter类被用来从基 于字符的输入和输出流中读取和写入文本。 • BufferdReader类缓存字符以更高效的读取字符串,数 组和文本行。 • BufferedWriter类缓存字符以更高效的写入字符串,数 组和文本行 Add BufferedStream to improve performance 72 /84 BufferedStream缓冲流 • 增加缓冲区流,减少访问硬盘的次数,提高效率。 处理 文件 文件流 file1.txt 输入流 输入缓 冲区 缓冲区流 程序 输出 file2.txt 缓冲 输出流 区 73 缓冲流--BufferedReader类 • Reader类的直接子类 构造方法 public BufferedReader(Reader in, int sz); 任何Reader流都可 以用来包装成缓冲流 //sz为缓冲区的大小 public BufferedReader(Reader in); BufferedReader对象的创建 先创建一个Reader子类的对象,然后使用这个对象来创建 缓冲流对象。 FileReader inOne = new FileReader("Student.txt"); BufferedReader inTwo = new BufferedReader(inOne); 74 /84 缓冲流--BufferedReader类 缓冲流的读取 从缓冲 区读取 public int read() throws IOException 读取一个字符 public int readLine() throws IOException 读取一行文本 读取完毕后,最好调用close()方法关闭流。 75 /92 BufferedWriter类 Writer类的直接子类 构造方法 public BufferedWriter(Writer in,int sz) public BufferedWriter(Reader in) BufferedWriter对象的创建 先创建一个Writer子类的对象,然后使用这个对象来创 建缓冲流对象。 FileWriter outOne = new FileWriter("Student.txt"); BufferedWriter outTwo = new BufferedWriter(outOne); 76 /84 BufferedWriter类 缓冲流的写入 public void write(String str) throws IOException 向缓冲区写入一个字符串。 public int writeLine(String str,int off,int len) throws IOException 向缓冲区写入一个字符串的一部分。 输出完毕后,最好调用close()方法关闭流。 77 /84 /**ReadWriteFile.java--读出1.txt中的内容,写入2.txt中 */ import java.io.*; public class ReadWriteFile{ public static void main(String[] args){ try{ File read = new File(“c:\\1.txt”); File write = new File(“c:\\2.txt”); BufferedReader br = new BufferedReader(new FileReader(read)); BufferedWriter bw = new BufferedWriter(new FileWriter(write)); String temp = null; temp = br.readLine(); //读一行文本 while(temp != null){ bw.write(temp + “\r\n”); //写文件, “\r\n”-回车只适用Windows系统 temp = br.readLine(); //继续读文件 } bw.close(); br.close(); }catch(FileNotFoundException e){ //文件未找到异常 System.out.println (e); }catch(IOException e){ \r\n是windows下的换行 System.out.println (e); } } } \n是UNIX下的 78/84 //BufferedStream.java import java.io.*; public class BufferedStream { public static void main(String[] args){ try{ BufferedWriter out=new BufferedWriter(new FileWriter("D:\\test.txt")); out.write("Hello,World"); out.close(); BufferedReader in=new BufferedReader(new FileReader("D:\\test.txt")); String s; s = in.readLine(); System.out.println(s); in.close(); }catch(Exception e){ System.out.println("error"); } } } 79/84 对象串行化(Serialization) 对象(object)的寿命通常随着生成该对象的程序的终止而 终止。某些时候,需要将对象的状态保存下来,将来需 要的时候可以恢复。 对象的这种能记录自己的状态以便将来再生的能力,叫 做对象的 持续性(persistence)。 对象通过写出描述自己状态的数值来记录自己的过程, 叫做对象的串行化(Serialization)。 串行化的主要任务是写出对象实例变量的数值。如果实 例变量是另一对象的一引用,则引用的对象也要串行化 。这个过程是递归的。 对象串行化机制就是将程序中对象的状态转化为一个字 节流,存储在文件中,作为这个对象的一个副本,之后 再从文件中把对象读取出来重新建立。 80 /84 Serializable接口 在java.io包中,接口Serializable用来作为实现对象 串行化的工具,只有实现了Serializable接口的类的 对象才可以被串行化。 Serializable接口中不含有任何的方法声明,是个空 接口。其定义如下: public interface Serializable{ } 实现Serializable接口,不需要编写任何的实现代 码。这个接口只是一个特殊的标记,用来表示一 个类可以被串行化。 如果一个类可以串行化,它的所有子类都可以串 行化。 81 /84 Java标记接口 标识接口是没有任何方法和属性的接口. 它仅仅表明实现它的类属于一个特定的类型,供其 他代码来测试允许做一些事情. 使用标记接口的唯一目的是使得可以用 instanceof进行类型查询,例如: if(obj instanceof Serializable) {………} 对象串行化(Serialization) 不参与串行化的数据可以用关键字transient(瞬 时的)来修饰。 比如:通常出于安全性的考虑,某些不宜公开的 数据(如用户的密码)用transient来修饰,能够 使其不被串行化。 用static修饰的静态成员变量与类对象无关,串行 化过程也与之无关。 有些对象,如Thread、FileInputStream、 FileOutputStream等对象,其对象状态也是瞬时 的,也不能进行串行化。 83 /84 //Example: Serializable接口 import java.io.Serializable; public class Student implements Serializable { private static final long serialVersionUID = 1663183895424656802L; int id; String name; int age; String department; public Student(){ } } public Student(int id, String name, int age, String department){ this.id = id; this.name = name; this.age = age; this.department = department; } 84 /84 读写对象流 ObjectInputStream和 ObjectOutputStream java.io包中,提供了ObjectInputStream (对象输入 流)和ObjectOutputStream(对象输出流),可读、写 对象。 对象串行化(serialization) 通过ObjectOutputStream将 对象状态保存下来(将对象保存到文件中,或者通过网络 传送到其他地方); 反串行化(deserialization) 再通过ObjectInputStream将 对象状态恢复。 85 /84 serialVersionUID 实现Serializable接口,需要定义域:serialVersionUID。 serialVersionUID是为了反序列化的时候JVM比对类的版本。有两种 生成方式: 1. 2. 一个是默认的1L,比如: private static final long serialVersionUID = 1L; 一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字 段,比如: private static final long serialVersionUID = xxxxL; 在反序列化的时候,即:将流转换为类的实例的时候,JVM会把传 来的字节流中的serialVersionUID与本地相应实体(类)的 serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列 化,否则就会出现序列化版本不一致的异常。 当一个类实现了Serializable接口,如果没有定义serialVersionUID, Eclipse会提供这个提示功能告诉你去定义 。在Eclipse中点击类中 warning的图标一下,Eclipse就会自动给定两种生成的方式。如果不 想定义它,在Eclipse的设置中也可以把它关掉的,或者全部默认为 1L. 写对象流 ObjectOutputStream中用writeObject()方法可以 直接将对象保存到输出流中。 writeObject( )用于对象的串行化。 写出了重构一个类对象所需要的信息:对象的类、类的 标记和非transient的对象成员,如果对象包含其他对象 的引用,则这些对象也会被串行化。 串行化能保存的元素 只能保存对象的非静态成员变量,不能保存任何的成员 方法和静态的成员变量,而且串行化保存的只是变量的 值,对于变量的任何修饰符,都不能保存。 87 /84 读对象流 在ObjectInputStream中用readObject()方法可以 直接读取一个对象, readObject( )方法从字节流中反串行化对象,也 就是将对象恢复过来。 每次调用readObject( )方法都返回流中下一个对象; 对象的字节流并不包含类的字节码,只是包含类名及其 签名,当readObject( )读取对象时,Java虚拟机需要装 载指定的类(恢复的对象的类),如果找不到这个类,则 此方法会抛出ClassNotFoundException异常。 88 /84 反串行化--恢复对象状态 import java.io.*; public class TestSeria { public static void main(String[] args){ Student stu=new Student(981036,"Liu Ming",18, "CSD"); try{ FileOutputStream fo=new FileOutputStream("data.ser"); ObjectOutputStream so=new ObjectOutputStream(fo); so.writeObject(stu); so.close(); }catch(IOException e){ System.out.println(e); } } } 89 /84 import java.io.*; public class test { public static void main(String[] args) throws IOException,ClassNotFoundException { //串行化 Student s1 = new Student(1,"Mar", 20, "Computer_Science"); Student s3 = new Student(2,"Cathy", 21, "Software_Engineering"); FileOutputStream fos = new FileOutputStream("D:\\serialization\\Student.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(s1); oos.writeObject(s3); //反串行化 FileInputStream fis = new FileInputStream("D:\\serialization\\Student.txt"); ObjectInputStream ois = new ObjectInputStream(fis); Student s2, s4; s2 = (Student) ois.readObject(); //必须添加强制类型转换 s4 = (Student) ois.readObject(); System.out.println("Student Information:"); System.out.println(s2.id+“ ”+s2.name+" “+s2.age+" “+s2.department); System.out.print(s4.id+" “+s4.name+" “+s4.age+" “+s4.department); oos.close(); ois.close(); } } 90/84 定制串行化 默认的串行化机制,对象串行化首先写入类数据 和类字段的信息,然后按照名称的上升排列顺序 写入其数值。 如果想自己明确地控制这些数值的写入顺序和写 入种类,必须定义自己的读取数据流的方式。就 是在类的定义中重写writeObject()和 readObject()方法。 例如:在一个可以串行化的类中有一个不可串行化的对 象,但又想保存该对象的状态信息该如何是好? 例:ObjectSerialization.java 91 /84 import java.io.*; //Student类实现Serializable接口 public class Student implements Serializable { int id; String name; int age; String department; public Student(int id, String name, int age, String department){ this.id = id; this.name = name; this.age = age; this.department = department; } //一般可不重写,定制串行化时需要重写writeObject()方法 private void writeObject(ObjectOutputStream out) throws IOException { out.writeInt(id); out.writeInt(age); out.writeUTF(name); out.writeUTF(department); } //一般可不重写,定制串行化时需要重写readObject()方法 private void readObject(ObjectInputStream in) throws IOException { id=in.readInt(); age=in.readInt(); name=in.readUTF(); department=in.readUTF(); 92 } } import java.io.*; public class ObjectSerialization{ public static void main(String[] args) throws IOException,ClassNotFoundException{ Student st=new Student(20010901,"黎明",20,"计算机系"); //创建可串行化的st对象 FileOutputStream fout=new FileOutputStream("data.txt"); ObjectOutputStream sout=new ObjectOutputStream(fout); try{ //输出流将对象状态存入文件"data.txt” sout.writeObject(st); sout.close(); }catch(IOException e){} st=null; // st对象与ObjectInputStream联系起来 FileInputStream fin=new FileInputStream("data.txt"); ObjectInputStream sin=new ObjectInputStream(fin); try{ //输入流将对象状态从文件"data.ser"读出 st=(Student) sin.readObject(); sin.close(); }catch(IOException e){} } } System.out.println("学生信息:"); //输出验证 System.out.print("学号: " + st.id +"; 姓名: " + st.name+"; 年龄: " + st.age); System.out.println("; 院系: " + st.department); 93 定制串行化:(参考) 调用重写的writeObject()和readObject()方法 方法writeObject()处理对象的序列化。 如果在Student 类里面重写该方法,它将会被 ObjectOutputStream调用而不是默认的序列化进程。 如果你是第一次看见它,你会很惊奇尽管它们被外部类调 用但事实上这是两个private的方法。并且它们既不存在于 java.lang.Object,也没有在Serializable中声明。那么 ObjectOutputStream如何使用它们的呢?因为 ObjectOutputStream使用了反射来寻找是否声明了这两个 方法。因为ObjectOutputStream使用getPrivateMethod, 所以这些方法不得不被声明为priate以至于供 ObjectOutputStream来使用。 94 /84