- 熟悉文件的各种流类
- 了解字符的编码
- 掌握文件I/O操作的相关概念
- 了解对象的序列化
简单的引入
I:input 由外围输入到内存
O:output 由内存写出到外存。
I/O:是相对于内存来说的
在Java中所有的IO机制都是基于“流”方式进行输入和输出。这些流可以视为同一台计算机不同设备或者是不同计算机之间数据序列的流动。这些流序列的数据通常有两种格式:文本流和二进制流。文本流每一个字节存放一个ASCII码代表一个字符(而对于Unicode编码来说,每两个字节代表一个字符)。如整形数12345,其在内存中需要两个字节,其系统为整型分配四个字节,所以高两位的字节为0,而按文本流输出则占用五个字符,由于分别是12345这五个字符所对应的ASCII码
内存中的存储形式 | |||
00000000 | 00000000 | 00110000 | 00111001 |
二进制行形式:与内存中的存储形式完全相同 | |||
00000000 | 00000000 | 00110000 | 00111001 |
文本形式:分别对应各自的ASCII码 | |||
00000001 | 00000010 | 00000011 | 00000101 |
文本流与二进制流的优缺点
文本流:文本流形式和字节流一一对应,因而便于对于对字符进行逐个处理,也便于输出显示,但是占用较多的内存空间,且花费较多的转换时间(二进制和 编码之间的转换)。在Java中的Unicode编码,这是一种定长编码,每个字符两个字节,因此在存储ASCII码时会额外浪费一个字节的空间。
二进制流:可以节省外存空间和转化时间,但是一个字节不对应一个字符,不能直接输出字符形式。
总结:对于纯文本信息(比如字符串),以文本形式存储较佳;而对于数值信息,则用二进制形式较好。
I/O流的优势在于简单易用,缺点是效率低。Java的I/O提供了读写数据 的标准方法。Java语法中定义了许多类专门负责各种方式的输入输出,这些类被放在java.io包中。在java类库中,有关IO操作内容非常的庞大:有标准的输入输出、文件的操作、网路上的数据流、字符串流和对象流等。
1.文件操作类--File
总结:
对文件或文件夹进行操作(仅仅操作文件本身的属性不指定怎样从文件读取或者存储)
在java.io包中,File类是唯一一个与文件本身操作有关的操作类。它定义了一系列与平台无关的方法来操作文件,通过调用File类,能够完成创建、删除、重命名文件,判断文件的读写权限以及问价是否存在,设置
和查询问价创建的时间、权限等操作。File类除了对文件的操作外,还可以将目录当做文件来处理。
File(String directoryPath);//创建指定文件或者目录文件的File对象 File(String directoryPath,String filename);//创建由File对象和指定文件名的FIle对象 File(File dirObj,String filename);//创建指定文件目录和文件名册File对象 File F1=new File("G:\\");//创建 指向“G:\”的File对象 File F2=new File("G:\\","1.txt");//创建指向“G:\1.txt”的File对象 File F3=new File(F1,"1.txt");//创建指向“G:\1.txt”的File对象
注意:
路径分隔符。在Linux/Unix下用斜线(/)路径处理正确;在Windows/DOS下使用反斜线(\),,那么就需要在字符串内使用转义字符\(\\)。
常用方法如下
方法 | 功能描述 |
boolean canRead() | 测试应用程序是否能从指定的文件中读取 |
boolean canWirte() | 测试应用程序是否能够写当前文件 |
boolean delete() | 删除当前指定的文件 |
boolean equals() | 比较该对象指定的对象 |
boolean exists() | 测试当前的File是否存在 |
String getAbosolutePath() | 返回该对象表示的文件的绝对的路径名 |
String getCanonicalPath() | 返回当前File对象的路径名的规范格式 |
String getName() | 返回当前对象的文件名 |
String getParent() | 获取父路径名,没有就返回null |
String getPath) | 返回当前对象的路径名 |
boolean isAbosolute() | 测试当前对象是否是一个绝对路径名 |
boolean isDirectory() |
是否是一个路径 |
boolean isFile() | 是否是一个file |
long islastModified() | 返回最后修改的时间 |
long length() | 返回当前File的文件的长度 |
String list() | 返回路径文件列表 |
boolean mkdir() | 创建一个目录路径名由当前File对象指定 |
boolean mkdirs() | 创建一个目录路径名由当前File对象指定包括任一必须的父路径 |
boolean renameTo() | 将当前file对象指定的文件更改名为指定参数File指定的路径名 |
范例
package Learn; import java.io.File; public class FileDemo { public static void main(String[] args) { File f=new File("c:\\1.txt"); if(f.exists()){ f.delete(); } try{ f.createNewFile(); }catch(Exception e){ System.out.println(e.getMessage()); } System.out.println(f.getName());//取得文件名 System.out.println(f.getPath());//取得文件路径 System.out.println(f.getAbsolutePath());//取得绝对路径名 System.out.println(f.getParent());//父文件夹名 System.out.println(f.exists());//文件是否存在 System.out.println(f.canWrite());//文件可不可写 System.out.println(f.canRead());//文件可不可度 System.out.println(f.isDirectory());//文件是不是个目录 System.out.println(f.isFile());//文件是不是文件 System.out.println(f.isAbsolute());//文件是不是绝对路径 System.out.println(f.lastModified());//文件最后的修改时间 System.out.println(f.length());//文件的额长度 } }
在本范例中你会发现如下问题
问题一:在进行操作是出现了延迟,因为文件管理肯定是操作系统完成的,那么程序通过JVM与操作系统进行操作,多了一层操作,所以势必会产生延迟。
问题二:在windows中使用用“\”,而Linux使用“/”,想要使得Java程序具备可移植性,就必须考虑分隔符的问题,所以在File类中提供了一个常量:public static final String separator。
File file =new File("c:"+File.separator+"1.txt");
问题三: 如果文件目录不存在,要想创建文件之前首先要创建目录
创建一级目录:public boolean mkdir();
创建多级目录:public boolean mkdirs();
取得父路径: public File getParentFile();
public static void main(String[] args) throws IOException { File file=new File("d:"+File.separator+"mytest"+File.separator+"1.t.txt"); if(!file.getParentFile().exists()){ file.getParentFile().mkdirs();//创建父目录 } System.out.println(file.createNewFile());}
2.RandomAccessFile类
总结:可以跳转到文件的任意位置处读写数据(随机访问文件)
该类有一个位置指示器,指向当前的读写处的位置。该类的构造方法如下。
RandomAcessFile(File file,String mode)//创建随机存储文件流,文件属性有参数File对象指定。 RandomAcessFile(String name,String mode)//创建随机存储文件流,文件名由参数name指定
范例
package IO; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; public class RandomAcessFileDemo { public static void main(String[] args) throws IOException { // TODO 自动生成的方法存根 Emplyee e1=new Emplyee("zh",23); Emplyee e2=new Emplyee("Li",24); Emplyee e3=new Emplyee("wa",23); RandomAccessFile ra=new RandomAccessFile("G:\\employee.txt","rw"); ra.write(e1.name.getBytes()); ra.writeInt(e1.age); ra.write(e2.name.getBytes()); ra.writeInt(e2.age); ra.write(e3.name.getBytes()); ra.writeInt(e3.age); ra.close(); RandomAccessFile raf=new RandomAccessFile("G:\\employee.txt","r"); int len=8; raf.skipBytes(12);//跳过第一个员工的信息 System.out.println("第二个员工的基本信息为:"); String str=""; for(int i=0;i<len;i++) { str=str+(char)raf.readByte(); } System.out.println("name:"+str); System.out.println("age:"+raf.readInt()); System.out.println("第一个员工的信息:"); raf.seek(0);//将文件的额之争移动到文件开始的地方 str=""; for(int i=0;i<len;i++) { str=str+(char)raf.readByte(); } System.out.println("name:"+str); System.out.println("age:"+raf.readInt()); System.out.println("第三个员工的信息:"); raf.skipBytes(12);//跳过第二个员工的信息 str=""; for(int i=0;i<len;i++) { str=str+(char)raf.readByte(); } System.out.println("name:"+str); System.out.println("age:"+raf.readInt()); raf.seek(0); raf.close(); } } package IO; public class Emplyee { String name; int age; final static int LEN=8; public Emplyee(String name,int age){ if(name.length()>LEN) { name=name.substring(0, 8); } else { while(name.length()<LEN) { name=name+"\u0000"; } } this.name=name; this.age=age; } } 结果为: 第二个员工的基本信息为: name:Li age:24 第一个员工的信息: name:zh age:23 第三个员工的信息: name:wa age:23
上面的范例会出现错误: (感觉是编码的问题哪位看到的大神可以帮我解决一下吗?(已经解决!在输入年龄的时候应该用writeInt()而不是write()))
String.sunString(int beginIndex,int endIndex)方法中,取得部分字符创,字符串的长度是endIndex-beginIndex,所以位置是从beginIndex到endIndex-1
3.字节流和字符流
字节流
读:INputStream(抽象类)
写:OutputStream(抽象类)
字符流
读:Writer
写:Reader
Java的流操作包括字符流和字节流。字符流处理的对象的单元是Unicode字符,每个Unicode字符占两个字节,而字节流输入输出是以单个字节为单位的。这种流操作对双字节字符的操作带来了麻烦。字符流是由Java虚拟机将单个字节转换为两个字节的的Unicode字符,所以它对多国语言支持较好。
常用的字节流名称及对应的功能的简单介绍
InputStream:继承架构
类定义如下
intputStream主要方法
常用的构造方法如下
InputStream fo=new FileInputStream("d:\\test.txt");//构造犯法一:FileInputStream(String filepath),file是绝对路径 File f=new File("d:\\test.txt"); InputStream f1=new FileInputStream(f);//构造方法二:FileInputStream(File fileObj),fileObj是描述该文件的File对象
实例向文件中写入字符串并读出
package IO; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class inputStream { public static void main(String[] args) throws FileNotFoundException { // TODO 自动生成的方法存根 /*InputStream fo=new FileInputStream("d:\\test.txt");//构造犯法一:FileInputStream(String filepath),file是绝对路径 File f=new File("d:\\test.txt"); InputStream f1=new FileInputStream(f);//构造方法二:FileInputStream(File fileObj),fileObj是描述该文件的File对象 */ File f=new File("G:\\temp.txt"); OutputStream out=null; try { out=new FileOutputStream(f); }catch(FileNotFoundException e) { e.printStackTrace(); } //将字符串转为字节数组 byte b[]="Hello world!!!".getBytes(); try { out.write(b); }catch(IOException e) { e.printStackTrace(); } try { out.close(); }catch(IOException e) { e.printStackTrace(); } //以下为读操作 InputStream in=null; try { in=new FileInputStream(f); }catch(FileNotFoundException e) { e.printStackTrace(); } //开辟一个空间用于接收文件读进来的数据 byte b1[] =new byte[1024]; int i=0; try { i=in.read(b1); }catch(IOException e) { e.printStackTrace(); } try { in.close(); }catch (IOException e) { e.printStackTrace(); } System.out.println(new String(b1,0,i)); } }
实例来源和目的未知情况下实现输入流输出流
package IO; import java.io.*; public class IO { public static void dump(InputStream src,OutputStream dest) throws IOException { try(InputStream input=src;OutputStream output=dest){//尝试自动关闭串流OutputStream操作了java.io.Closeable接口,其父接口为java.lang.AutoCloseable接口 byte[] data =new byte[1024];//尝试每次从来源读取1024个字节 int length; while((length=input.read(data))!=-1) {//读取数据 output.write(data, 0, length);//写出数据 } } } public static void main(String[] args) throws IOException { IO.dump(new FileInputStream(args[0]),new FileOutputStream(args[1])); } }
实例http服务器读取网页,并且另存为文档
package IO; import java.io.*; import java.net.URL; public class IO { public static void dump(InputStream src,OutputStream dest) throws IOException { try(InputStream input=src;OutputStream output=dest){//尝试自动关闭串流OutputStream操作了java.io.Closeable接口,其父接口为java.lang.AutoCloseable接口 byte[] data =new byte[1024];//尝试每次从来源读取1024个字节 int length; while((length=input.read(data))!=-1) {//读取数据 output.write(data, 0, length);//写出数据 } } } public static void main(String[] args) throws IOException { /*IO.dump(new FileInputStream(args[0]),new FileOutputStream(args[1]));*/ URL url=new URL(args[0]); InputStream src=url.openStream(); OutputStream dest=new FileOutputStream(args[1]); IO.dump(src, dest); } }
接收客户端联机的例子
ServerSocket server=null; Socket client=null; while(true) { client=server.accept(); InputStream input=client.getInputStream(); OutputStream output=client.getOutputStream(); } ..... 接下来就是操作InputStream、OutputStream实例了。
servlet将文档输入至浏览器例子
response.setContentType("application/pdf"); InputStream in=this.getServletContext.getResourceAsStream("/WEB-INF/jdbc.pdf"); OutputStream out=response.getOutputStream(); byte[] data=new byte[1024]; int length; while((length=in.read(data))!=-1) { out.write(data,0,length);
OutputStrean继承架构
该类是一个抽象类,它定义了流式字节的输出模式,该类的所有方法返回一个void值,在出错的情况下回抛出一个IOException
补充:标准输入输出
System.in与System.out。它们分别是InputStream和PrintStream的实例。对于System.in而言在文本模式下通常是取得整行的收入,很少直接操作InputStream相关方法。使用java.util.Scanner打包System.in取得数据,并转换为你想取得的数据类型。可以使用System的SetIn()方法制定InputStream实例,重新指定标准输入来源。例子如下:
System.setIn(new FileInputStream(args[0])); try(Scanner scanner=new Scanner(System.in )){ while(scanner.hasNextLine()) { System.out.println(scanner.nextLine()); } }