黑马程序员——自学总结(五)Java IO技术之File类
2015-08-01 23:57
761 查看
<a href="http://www.itheima.com"
target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流!
一、File类概述
File类用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作,File对象可以作为参数传递给流的构造函数。File对象构造方法:File(文件名),File(路径,文件名),File(文件对象即路径,文件名),构造File对象时文件可以不存在,后两种构造方法可用变量来改变路径, 打印File对象只打印构造时传递的参数。
File类中字段seperator表示默认文件名称分隔符,为了跨平台识别,可以将程序代码中的“\\”换成File.seperator。
File类常见方法:
1、创建
boolean createNewFile() 在指定位置创建文件,如果该文件已经存在,则不创建,返回false,和文件输出流不一样,文件输出流对象一建立就会创建文件,而且文件已经存在会覆盖。mkdir()创建目录,只能创建一级目录 mkdirs()可以创建多级目录。
2、删除
boolean delete() 删除失败时返回false,boolean deleteOnExit() 在程序退出时删除文件,如果程序运行时抛出异常,则必须在finally块内删除文件,但可能某个流正在操作文件,使用该方法系统提示删不掉,只能用deleteOnExit()删除。
3、判断
canExecute()返回是否能执行,compareTo(File) 比较文件路径,exists()返回文件是否存在。
isDirectory(),isFile() 判断文件对象是否是文件或者目录,判断时必须先判断该文件对象封装的内容是否存在。
isHidden()返回是否隐藏,可以避免访问系统文件,isAbsolute() 判断构建时文件路径是否为绝对路径。
4、获取信息
getName()返回文件名
getPath()返回封装即构造的路径
getParent()返回绝对路径中的文件父目录,否则若获取的是相对路径则返回空,如果相对目录中有上一层目录,那么返回上一层目录。
getAbsolutePath()返回绝对路径
getAbsoluteFile()返回文件对象,该文件对象用绝对路径构造。
lastModefied()返回时间,可判断文件是否被修改过。
length()返回文件大小
renameTo()类似剪切
static list[ ] listRoots()返回list[ ],列出系统可用的根目录
list()返回String数组,包含文件路径下的文件名字符串,包含隐藏文件,调用list()方法的file对象必须是封装了一个目录,该目录还必须存在,否则遍历时会发生空指针异常。String[] names=file.list();file目录若不存在,则数组names为空,遍历会抛出异常,file目录存在,但没内容,数组names长度为0。
list[] list(FilenameFilter) 按照文件名过滤器返回路径下的文件名字符串。接口FilenameFilter有方法accept(File,String) 。类似的方法有listFiles(),listFiles(FileFilter)
需求:列出指定目录下文件或者文件夹,包含子目录中的内容,也就是列出指定目录下所有内容。
因为目录中还有目录,只要使用同一个列出目录功能的函数完成就行。在列出过程中出现的还是目录的话,还可以再次调用本功能,也就是函数自身调用自身,这种表现形式或编程手法称为递归。
代码如下:
输出结果如下:
递归举例:求二进制数:
void toBin(int num){
if(num>0){
toBin(num);
打印num%2;
}
}
求1到n的和:
int getSum(int n){
if(n=1)
return 1;
return n+getSum(n-1);
}
递归层数注意不能太多,否则会内存溢出,抛出异常。递归要注意:(1)限定条件;(2)注意递归的次数,尽量避免内存溢出。
需求:带层次的打印目录下文件名
代码如下:
运行结果如下:
需求:删除一个带内容的目录。删除原理,在windows中,删除目录从里面往外删除的,既然是从里往外删除,就需要用到递归。
代码如下:
java删除的文件不保存在回收站里。有些系统隐藏文件Java不能访问,删除时会返回空文件数组,接下来在高级for循环里会抛出空指针异常,所以遍历时应避开隐藏文件,可加判断语句if(!file.isHidden()),防止抛出空指针异常。
练习:创建Java文件列表,将一个指定目录下的java文件的绝对路径,存储到一个文本文件中,建立一个java文件列表文件。
思路:(1)对指定的目录进行递归;(2)获取递归过程中所有的java文件的路径;(3)将这些路径存储到集合中;(4)将集合中的数据写入到一个文件中。
代码如下:
二、Properties
Properties对象是Hashtable子类,具备了Map集合的特点,而且它里面存储的键值对都是字符串,不需要泛型,是集合中和IO技术相结合的集合容器,该对象的特点是可以用于键值对形式的配置文件。因为配置文件中数据都是键值对形式,那么在加载数据时,需要数据有固定格式,通常为键=值。Properties对象常用方法:设置和获取元素
setProperty(String,String),getProperty(String key),Set<String> stringPropertyNames()返回键集合。
演示:如何将流中的数据存储到集合中,例如想要将info.txt中键值对数据存储到集合中进行操作
步骤: 1、用一个流和info.txt关联;2、读取一行数据,将该行数据用“=”进行切割;3、=左边作为键,右边作为值存到Properties集合中即可。
Properties对象提供了更简易的方法,将流中数据加载进集合的方法:Properties.load(读取流),文件中凡是加#表示注释信息,不会被load加载,显示配置信息方法:Properties.list(PrintStream),store(OutputStream,字符串形式的注释信息)将内存结果存入流当中,文件自动添加时间。
练习:用于记录应用程序运行的次数,如果使用次数已到,那么给出注册提示。很容易想到计数器,可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行了自增,可是随着该应用程序的退出,该计数器也在内存中消失,下一次再启动该程序又重新开始从0计数,这样不是我们想要的。程序即使结束,该计数器的值依旧存在,下次程序启动会先加载该计数器的值,并+1后重新存储起来,所以要建立一个配置文件,用于记录该软件的使用次数。该配置文件使用键值对的形式。这样便于阅读数据并操作数据。键值对数据是Map集合,数据是以文件形式存储,使用io技术,那么综合集合和IO就使用Properties,配置文件可以实现应用程序数据的共享。
代码如下:
运行结果如下:
我在调试这个程序时,发现了一个问题,如果在prop.load(fis)之前将fos关联到配置文件,那么执行prop.load(fis)会将文件内容清空,而且prop内部也没有获得数据。如果在prop.load(fis)之后将fos关联到配置文件,则不会出现这种问题。
程序先创建File,如果不存在,则创建。如果直接建立读取流,那么第一次运行会抛出异常。
三、IO包中的其他流
打印流PrintWriter与PrintStream可以对数据进行格式化打印输出,PrintStream构造函数可以接受的参数类型有:file对象,字符串路径,字节输出流OutputStream,PrintWriter造函数可以接受的参数类型有:file对象,字符串路径,字节输出流OutputStream,字符输出流Writer,PrintWrite在创建时候指定true,输出时将自动刷新缓冲区。
序列流SequenceInputStream,可以通过构造函数SequenceInputStream(Enumeration<? extends InputStream>)对多个输入流进行合并,可以通过Vector.elements()获得Enumeration对象,SequenceInputStream
没有对应的OutputStream。
需求:切割文件并恢复,恢复文件的过程:用ArrayList存储InputStream(不用Vector,因为ArrayList效率较高),创建一个Enumeration(因为序列流构造函数需要Enumeration对象),覆盖Enumeration对象中的迭代器操作方法,将Enumeration对象中的迭代器操作方法方法基于ArrayList中迭代器操作来完成。
代码如下:
ObjectInputStream与ObjectOutputStream直接操作对象的流,可以将对象持久化(序列化),被操作的对象需要实现Serializable接口,该接口没有抽象方法,是一种标记接口。假设定义了一个Person类,有如下代码:
Person p=new Person(......);
ObjectInputStream ois=new
ObjectInputStream(new FileOutputStream("obj.txt"));
ObjectOutputStream oos=new
ObjectOutputStream (new FileInputStream("obj.txt"));
oos.writeObject(p);ois.readObject(p);
以上代码分别写入和读取一个Person对象,前提是Person必须实现Serializable接口。凡是实现了Serializable接口的类在内存中都有一个唯一的序列号,序列号由代表各个成员的数字标签生成,writeObject(p)和readObject(p)都是根据类Person类的序列号来确定要读取的内容,如果Person类的某个成员改变,比如由public变成private,那么该类的序列号就会改变,readObject()按照新的序列号去读取原有的内容,将出现错误,提高了安全性。如果想避免因修改类,造成再次读取原有对象失败,可以自己手动修改类的序列号,确保其始终唯一,这样新类仍然可以访问曾经被序列化的旧类。
静态数据不能被序列化,也就是说如果从文件中读取一对象,该对象中的静态成员将始终保持第一次写入的值,如果某非静态成员不想序列化用关键字transient ,保证该成员在堆内存中存在,而不存在于文件中,如果读取返回的是默认初始化值,比如int型返回0。一般编程时真正存储Object用文件名:类名.object。
ObjectOutputStream对象的write(int)读取数据的低8位,write(Int)读取数据的全部32位。
管道流PipedInputStream和PipedOutputStream,输入流和输出流可以直接进行连接,通过结合多线程使用,可以在构造函数中就直接和对方流对象连接,也可以用connect(对方流对象)方法连接。
操作基本数据类型的流DataInputStream与DataOutputStream,可以用于操作基本数据类型的数据的流对象。构造方法中要传递流对象。
DataOutputStream对象的writeUTF()写入的数据只能用对应的DataInputStream对象的.readUTF()方法读取,到结尾没读完UTF规则指定的字节,则抛出EOF异常。
ByteArrayInputStream和ByteArrayOutputStream操作字节数组的流对象,该类流对象内部没有调用底层资源,关闭无效,不会产生任何IO异常。ByteArrayOutputStream的缓冲区自动增长,可以通过toByteArray(),toString()获得内部字节数组,数据目的封装在内部,即字节数组。 ByteArrayInputStream在构造的时候需要接收数据源,数据源是一个字节数组,输出的流在构造时不用定义数据目的,因为该对象中已经内部封装了一个可变长度的字节数组,这就是数据目的地。因为这两个流对象都操作的是字节数组,并没有使用系统资源,所以不用进行close()关闭,流对象可继续使用。
ByteArrayOutputStream对象的size()方法返回缓冲区大小。在流操作时,数据源设备包含键盘,硬盘,内存,目的设备包括控制台,硬盘,内存。内存就是数组流,用流的读写思想来操作数组就用到了ByteArrayInputStream和ByteArrayOutputStream。
ByteArrayOutputStream对象的writeTo(OutputStream),可以将内部字节数组输出到其他字节输出流中。
CharArray
a85a
Reader和CharArrayWriter 操作字符数组,,StringReader和StringWriter操作字符串数组。
四、RandomAccessFile
RandomAccessFile随机访问文件,自身具备读写的方法,通过skipBytes(int x),seek(int x)调整文件指针来达到随机访问,skipBytes(int x)跳过指定的字节数,只能往文件尾移动,seek(int x)可以前后调整,按指定位置书写,且覆盖原有数据。getFilePointer()读取文件指针。该类不是IO体系中子类,而是直接继承自Object,但是它是IO包中成员,因为它具备读和写功能。RandomAccessFile类内部封装了字节输入流、字节输出流和一个数组,通过指针对数组中的元素进行操作来达到随即访问的效果,
RandomAccessFile类只能操作文件,在构造函数中可以指定操作文件的模式:只读r,读写rw等。如果模式为r,不会创建文件,会去读取一个已存在的文件,如果该文件不存在,则会出现异常。如果模式为rw,要操作的文件不存在则自动创建。readXXX()读某个基本数据类型,readLine()读取一行数据,writeXXX()写入某个基本数据类型 。
该类可以实现分段读写,通过调整文件指针,使多个线程负责每一段,互相不冲突,这就是下载软件的工作原理。
五、字符编码
字符流出现后,为了方便操作字符,加入了编码转换,通过子类转换流来完成:InputStreamReader和OutputStreamWriter,这两类对象进行构造的时候可以加入字符集,这样就将字节数据按照指定的编码转换成文本数据。
编码表的由来:由于计算机只能识别二进制数据,为了方便应用计算机,让它可以识别各个国家的文字,就将各个国家的文字用数字来表示,并一一对应,形成一张表,这就是编码表。常见的编码表有ASCII,美国标准信息交换码,用一个字节的7位表示表中字符,ISO8859-1,拉丁码表(欧洲码表),用一个字节的8位表示表中字符,GB2312,中国的中文编码表,GBK:中国的中文编码表升级版,融合了更多的中文文字符号,Unicode,国际标准码,融合了多种文字,所有文字都用两个字节来表示,java使用的就是Unicode,UTF-8编码最多用三个字节来表示一个字符。
编码:将字符串变成字节数组getBytes(),该方法可以指定字符编码集
解码:将字节数组变成字符串new String(byte[ ]),或者Arrays.toString(byte[ ])
如果A用户按GBK方式编码成字节数组发给B用户,而B用户却错用ISO8859-1方式解码,显然得不到正确数据,这时可将错误转换得到的字符串再按照ISO8859-1方式编码成字节数组,重新将新字节数组按GBK方式解码即可。但是用这种方式有个特例:UTF-8和GBK都能识别中文,如果A用户使用GBK,B用户用UTF—8解码,因为UTF—8也能识别中文字符,遇到不能识别的中文字符将转换成?进行解码,改变了原来的字节数组中数据,不能还原数据,ISO8859-1不能识别中文,可以通过编码解码还原数据。
target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流!
一、File类概述
File类用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作,File对象可以作为参数传递给流的构造函数。File对象构造方法:File(文件名),File(路径,文件名),File(文件对象即路径,文件名),构造File对象时文件可以不存在,后两种构造方法可用变量来改变路径, 打印File对象只打印构造时传递的参数。
File类中字段seperator表示默认文件名称分隔符,为了跨平台识别,可以将程序代码中的“\\”换成File.seperator。
File类常见方法:
1、创建
boolean createNewFile() 在指定位置创建文件,如果该文件已经存在,则不创建,返回false,和文件输出流不一样,文件输出流对象一建立就会创建文件,而且文件已经存在会覆盖。mkdir()创建目录,只能创建一级目录 mkdirs()可以创建多级目录。
2、删除
boolean delete() 删除失败时返回false,boolean deleteOnExit() 在程序退出时删除文件,如果程序运行时抛出异常,则必须在finally块内删除文件,但可能某个流正在操作文件,使用该方法系统提示删不掉,只能用deleteOnExit()删除。
3、判断
canExecute()返回是否能执行,compareTo(File) 比较文件路径,exists()返回文件是否存在。
isDirectory(),isFile() 判断文件对象是否是文件或者目录,判断时必须先判断该文件对象封装的内容是否存在。
isHidden()返回是否隐藏,可以避免访问系统文件,isAbsolute() 判断构建时文件路径是否为绝对路径。
4、获取信息
getName()返回文件名
getPath()返回封装即构造的路径
getParent()返回绝对路径中的文件父目录,否则若获取的是相对路径则返回空,如果相对目录中有上一层目录,那么返回上一层目录。
getAbsolutePath()返回绝对路径
getAbsoluteFile()返回文件对象,该文件对象用绝对路径构造。
lastModefied()返回时间,可判断文件是否被修改过。
length()返回文件大小
renameTo()类似剪切
static list[ ] listRoots()返回list[ ],列出系统可用的根目录
list()返回String数组,包含文件路径下的文件名字符串,包含隐藏文件,调用list()方法的file对象必须是封装了一个目录,该目录还必须存在,否则遍历时会发生空指针异常。String[] names=file.list();file目录若不存在,则数组names为空,遍历会抛出异常,file目录存在,但没内容,数组names长度为0。
list[] list(FilenameFilter) 按照文件名过滤器返回路径下的文件名字符串。接口FilenameFilter有方法accept(File,String) 。类似的方法有listFiles(),listFiles(FileFilter)
需求:列出指定目录下文件或者文件夹,包含子目录中的内容,也就是列出指定目录下所有内容。
因为目录中还有目录,只要使用同一个列出目录功能的函数完成就行。在列出过程中出现的还是目录的话,还可以再次调用本功能,也就是函数自身调用自身,这种表现形式或编程手法称为递归。
代码如下:
import java.io.*; class getFiles{ static void show(File file){ File[] files=file.listFiles(); for(File f:files){ if(f.isDirectory()){ System.out.println(f); show(f); } else System.out.println(f); } } public static void main(String[] args){ show(new File("F:\\第二阶段:Java基础\\Java高级视频_IO输入与输出")); } }
输出结果如下:
递归举例:求二进制数:
void toBin(int num){
if(num>0){
toBin(num);
打印num%2;
}
}
求1到n的和:
int getSum(int n){
if(n=1)
return 1;
return n+getSum(n-1);
}
递归层数注意不能太多,否则会内存溢出,抛出异常。递归要注意:(1)限定条件;(2)注意递归的次数,尽量避免内存溢出。
需求:带层次的打印目录下文件名
代码如下:
import java.io.*; class getFiles{ static void show(File file,int level){ level++; File[] files=file.listFiles(); for(File f:files){ if(f.isDirectory()){ for(int i=0;i<=level;i++) System.out.print("—");//按层次打印前导符 System.out.println(f.getName()); show(f,level); } else{ for(int i=0;i<=level;i++) System.out.print("—");//按层次打印前导符 System.out.println(f.getName()); } } } public static void main(String[] args){ show(new File("F:\\第二阶段:Java基础\\Java高级视频_IO输入与输出"),0); } }
运行结果如下:
需求:删除一个带内容的目录。删除原理,在windows中,删除目录从里面往外删除的,既然是从里往外删除,就需要用到递归。
代码如下:
import java.io.*; class my 4000 delete{ static void mydelete(File file){ File[] files=file.listFiles(); for(File f:files){ if(f.isDirectory()) mydelete(f); else f.delete(); } file.delete(); } public static void main(String[] args){ mydelete(new File("F:\\第二阶段:Java基础\\Java高级视频_IO输入与输出(备份)")); } }
java删除的文件不保存在回收站里。有些系统隐藏文件Java不能访问,删除时会返回空文件数组,接下来在高级for循环里会抛出空指针异常,所以遍历时应避开隐藏文件,可加判断语句if(!file.isHidden()),防止抛出空指针异常。
练习:创建Java文件列表,将一个指定目录下的java文件的绝对路径,存储到一个文本文件中,建立一个java文件列表文件。
思路:(1)对指定的目录进行递归;(2)获取递归过程中所有的java文件的路径;(3)将这些路径存储到集合中;(4)将集合中的数据写入到一个文件中。
代码如下:
import java.io.*; import java.util.*; class getJavaList{ static List<String> con=new ArrayList<String>(); static void getJavaList(File file,File to,FileFilter ff){ search(file,ff); Writer w=null; try{ w=new FileWriter(to.getAbsolutePath()); for(int i=0;i<con.size();i++){ w.write(con.get(i)); w.write("\r\n"); } }catch(IOException e){ throw new RuntimeException(e); }finally{ if(w!=null) try{ w.close(); }catch(IOException e){ throw new RuntimeException(e); } } } static void search(File file,FileFilter ff){ File[] files=file.listFiles(ff); for(File f:files){ if(f.isDirectory()) search(f,ff); else con.add(f.getAbsolutePath()); } } public static void main(String[] args){ getJavaList(new File("C:\\Documents and Settings\\TEMP"), new File("D:\\javalist.txt"), new FileFilter(){ public boolean accept(File file){ return file.getAbsolutePath().matches(".+\\.java"); } } ); } }
二、Properties
Properties对象是Hashtable子类,具备了Map集合的特点,而且它里面存储的键值对都是字符串,不需要泛型,是集合中和IO技术相结合的集合容器,该对象的特点是可以用于键值对形式的配置文件。因为配置文件中数据都是键值对形式,那么在加载数据时,需要数据有固定格式,通常为键=值。Properties对象常用方法:设置和获取元素
setProperty(String,String),getProperty(String key),Set<String> stringPropertyNames()返回键集合。
演示:如何将流中的数据存储到集合中,例如想要将info.txt中键值对数据存储到集合中进行操作
步骤: 1、用一个流和info.txt关联;2、读取一行数据,将该行数据用“=”进行切割;3、=左边作为键,右边作为值存到Properties集合中即可。
Properties对象提供了更简易的方法,将流中数据加载进集合的方法:Properties.load(读取流),文件中凡是加#表示注释信息,不会被load加载,显示配置信息方法:Properties.list(PrintStream),store(OutputStream,字符串形式的注释信息)将内存结果存入流当中,文件自动添加时间。
练习:用于记录应用程序运行的次数,如果使用次数已到,那么给出注册提示。很容易想到计数器,可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行了自增,可是随着该应用程序的退出,该计数器也在内存中消失,下一次再启动该程序又重新开始从0计数,这样不是我们想要的。程序即使结束,该计数器的值依旧存在,下次程序启动会先加载该计数器的值,并+1后重新存储起来,所以要建立一个配置文件,用于记录该软件的使用次数。该配置文件使用键值对的形式。这样便于阅读数据并操作数据。键值对数据是Map集合,数据是以文件形式存储,使用io技术,那么综合集合和IO就使用Properties,配置文件可以实现应用程序数据的共享。
代码如下:
import java.io.*; import java.util.*; class MySoft{ public static void main(String[] args){ Properties prop=new Properties(); File file=new File("peizhi.txt"); try{ if(!file.exists()) file.createNewFile(); }catch(IOException e){ throw new RuntimeException(e); } FileInputStream fis=null; FileOutputStream fos=null; try{ fis=new FileInputStream(file); prop.load(fis); int count=0; String value=prop.getProperty("time"); if(value!=null){ count=Integer.parseInt(value); System.out.println("第"+(++count)+"次免费使用"); if(count>=5){ System.out.println("已达到免费使用次数"); return; } }else{ count++; System.out.println("第"+count+"次免费使用"); } prop.setProperty("time",count+""); fos=new FileOutputStream(file); prop.store(fos,""); }catch(IOException e){ throw new RuntimeException(e); }finally{ if(fis!=null) try{ fis.close(); }catch(IOException e){ throw new RuntimeException(e); }finally{ if(fis!=null) try{ fis.close(); }catch(IOException e){ throw new RuntimeException(e); } } } } }
运行结果如下:
我在调试这个程序时,发现了一个问题,如果在prop.load(fis)之前将fos关联到配置文件,那么执行prop.load(fis)会将文件内容清空,而且prop内部也没有获得数据。如果在prop.load(fis)之后将fos关联到配置文件,则不会出现这种问题。
程序先创建File,如果不存在,则创建。如果直接建立读取流,那么第一次运行会抛出异常。
三、IO包中的其他流
打印流PrintWriter与PrintStream可以对数据进行格式化打印输出,PrintStream构造函数可以接受的参数类型有:file对象,字符串路径,字节输出流OutputStream,PrintWriter造函数可以接受的参数类型有:file对象,字符串路径,字节输出流OutputStream,字符输出流Writer,PrintWrite在创建时候指定true,输出时将自动刷新缓冲区。
序列流SequenceInputStream,可以通过构造函数SequenceInputStream(Enumeration<? extends InputStream>)对多个输入流进行合并,可以通过Vector.elements()获得Enumeration对象,SequenceInputStream
没有对应的OutputStream。
需求:切割文件并恢复,恢复文件的过程:用ArrayList存储InputStream(不用Vector,因为ArrayList效率较高),创建一个Enumeration(因为序列流构造函数需要Enumeration对象),覆盖Enumeration对象中的迭代器操作方法,将Enumeration对象中的迭代器操作方法方法基于ArrayList中迭代器操作来完成。
代码如下:
import java.io.*; import java.util.*; class mysplit{ static List<File> mysplit(File file,List<File> li){//对file进行切割,切割后的文件存储在li中 int i=0; FileInputStream in=null; try{ in=new FileInputStream(file); FileOutputStream out=null; byte[] buff=new byte[1024*20];//每个切割文件大小不超过20KB File f=null; int len=0; while((len=in.read(buff))!=-1){ f=new File((++i)+".part");//每次切割都要创建一个新文件 out=new FileOutputStream(f); out.write(buff,0,len); li.add(f);//将本次切割后得到的文件存入li中 } }catch(IOException e){ throw new RuntimeException(e); }finally{ if(in!=null) try{ in.close(); }catch(IOException e){ throw new RuntimeException(e); } } return li; } } class mymerge{ static void mymerge(List<File> li){ File result=new File("merge.jpg"); ArrayList<FileInputStream> al=new ArrayList<FileInputStream>(); //将li中存储的切割文件对象一一取出,并分别用输入流关联 for(int i=0;i<li.size();i++){ try{ al.add(new FileInputStream(li.get(i))); }catch(IOException e){ throw new RuntimeException(e); } } Iterator<FileInputStream> it=al.iterator(); SequenceInputStream si=null; FileOutputStream fos=null; try{ //定义一个序列流,用ArrayList替代Enumeration做参数,效率更高,内部调用ArrayList中迭代器的操作完成 si=new SequenceInputStream(new Enumeration<FileInputStream>(){ public boolean hasMoreElements(){ return it.hasNext(); } public FileInputStream nextElement(){ return it.next(); } }); fos=new FileOutputStream(result); byte[] buff=new byte[1024]; while(si.read(buff)!=-1){ fos.write(buff); } }catch(IOException e){ throw new RuntimeException(e); }finally{ if(si!=null) try{ si.close(); }catch(IOException e){ throw new RuntimeException(e); }finally{ if(fos!=null) try{ si.close(); }catch(IOException e){ throw new RuntimeException(e); } } } } public static void main(String[] args){ List<File> li=new ArrayList<File>(); mysplit.mysplit(new File("test.jpg"),li); mymerge(li); } }
ObjectInputStream与ObjectOutputStream直接操作对象的流,可以将对象持久化(序列化),被操作的对象需要实现Serializable接口,该接口没有抽象方法,是一种标记接口。假设定义了一个Person类,有如下代码:
Person p=new Person(......);
ObjectInputStream ois=new
ObjectInputStream(new FileOutputStream("obj.txt"));
ObjectOutputStream oos=new
ObjectOutputStream (new FileInputStream("obj.txt"));
oos.writeObject(p);ois.readObject(p);
以上代码分别写入和读取一个Person对象,前提是Person必须实现Serializable接口。凡是实现了Serializable接口的类在内存中都有一个唯一的序列号,序列号由代表各个成员的数字标签生成,writeObject(p)和readObject(p)都是根据类Person类的序列号来确定要读取的内容,如果Person类的某个成员改变,比如由public变成private,那么该类的序列号就会改变,readObject()按照新的序列号去读取原有的内容,将出现错误,提高了安全性。如果想避免因修改类,造成再次读取原有对象失败,可以自己手动修改类的序列号,确保其始终唯一,这样新类仍然可以访问曾经被序列化的旧类。
静态数据不能被序列化,也就是说如果从文件中读取一对象,该对象中的静态成员将始终保持第一次写入的值,如果某非静态成员不想序列化用关键字transient ,保证该成员在堆内存中存在,而不存在于文件中,如果读取返回的是默认初始化值,比如int型返回0。一般编程时真正存储Object用文件名:类名.object。
ObjectOutputStream对象的write(int)读取数据的低8位,write(Int)读取数据的全部32位。
管道流PipedInputStream和PipedOutputStream,输入流和输出流可以直接进行连接,通过结合多线程使用,可以在构造函数中就直接和对方流对象连接,也可以用connect(对方流对象)方法连接。
操作基本数据类型的流DataInputStream与DataOutputStream,可以用于操作基本数据类型的数据的流对象。构造方法中要传递流对象。
DataOutputStream对象的writeUTF()写入的数据只能用对应的DataInputStream对象的.readUTF()方法读取,到结尾没读完UTF规则指定的字节,则抛出EOF异常。
ByteArrayInputStream和ByteArrayOutputStream操作字节数组的流对象,该类流对象内部没有调用底层资源,关闭无效,不会产生任何IO异常。ByteArrayOutputStream的缓冲区自动增长,可以通过toByteArray(),toString()获得内部字节数组,数据目的封装在内部,即字节数组。 ByteArrayInputStream在构造的时候需要接收数据源,数据源是一个字节数组,输出的流在构造时不用定义数据目的,因为该对象中已经内部封装了一个可变长度的字节数组,这就是数据目的地。因为这两个流对象都操作的是字节数组,并没有使用系统资源,所以不用进行close()关闭,流对象可继续使用。
ByteArrayOutputStream对象的size()方法返回缓冲区大小。在流操作时,数据源设备包含键盘,硬盘,内存,目的设备包括控制台,硬盘,内存。内存就是数组流,用流的读写思想来操作数组就用到了ByteArrayInputStream和ByteArrayOutputStream。
ByteArrayOutputStream对象的writeTo(OutputStream),可以将内部字节数组输出到其他字节输出流中。
CharArray
a85a
Reader和CharArrayWriter 操作字符数组,,StringReader和StringWriter操作字符串数组。
四、RandomAccessFile
RandomAccessFile随机访问文件,自身具备读写的方法,通过skipBytes(int x),seek(int x)调整文件指针来达到随机访问,skipBytes(int x)跳过指定的字节数,只能往文件尾移动,seek(int x)可以前后调整,按指定位置书写,且覆盖原有数据。getFilePointer()读取文件指针。该类不是IO体系中子类,而是直接继承自Object,但是它是IO包中成员,因为它具备读和写功能。RandomAccessFile类内部封装了字节输入流、字节输出流和一个数组,通过指针对数组中的元素进行操作来达到随即访问的效果,
RandomAccessFile类只能操作文件,在构造函数中可以指定操作文件的模式:只读r,读写rw等。如果模式为r,不会创建文件,会去读取一个已存在的文件,如果该文件不存在,则会出现异常。如果模式为rw,要操作的文件不存在则自动创建。readXXX()读某个基本数据类型,readLine()读取一行数据,writeXXX()写入某个基本数据类型 。
该类可以实现分段读写,通过调整文件指针,使多个线程负责每一段,互相不冲突,这就是下载软件的工作原理。
五、字符编码
字符流出现后,为了方便操作字符,加入了编码转换,通过子类转换流来完成:InputStreamReader和OutputStreamWriter,这两类对象进行构造的时候可以加入字符集,这样就将字节数据按照指定的编码转换成文本数据。
编码表的由来:由于计算机只能识别二进制数据,为了方便应用计算机,让它可以识别各个国家的文字,就将各个国家的文字用数字来表示,并一一对应,形成一张表,这就是编码表。常见的编码表有ASCII,美国标准信息交换码,用一个字节的7位表示表中字符,ISO8859-1,拉丁码表(欧洲码表),用一个字节的8位表示表中字符,GB2312,中国的中文编码表,GBK:中国的中文编码表升级版,融合了更多的中文文字符号,Unicode,国际标准码,融合了多种文字,所有文字都用两个字节来表示,java使用的就是Unicode,UTF-8编码最多用三个字节来表示一个字符。
编码:将字符串变成字节数组getBytes(),该方法可以指定字符编码集
解码:将字节数组变成字符串new String(byte[ ]),或者Arrays.toString(byte[ ])
如果A用户按GBK方式编码成字节数组发给B用户,而B用户却错用ISO8859-1方式解码,显然得不到正确数据,这时可将错误转换得到的字符串再按照ISO8859-1方式编码成字节数组,重新将新字节数组按GBK方式解码即可。但是用这种方式有个特例:UTF-8和GBK都能识别中文,如果A用户使用GBK,B用户用UTF—8解码,因为UTF—8也能识别中文字符,遇到不能识别的中文字符将转换成?进行解码,改变了原来的字节数组中数据,不能还原数据,ISO8859-1不能识别中文,可以通过编码解码还原数据。
相关文章推荐
- 【剑指Offer面试题】 九度OJ1524:复杂链表的复制
- 【黑马程序员】函数和方法
- 黑马程序员----java基础HashSet和TreeSet总结
- 黑马程序员------OC Foundation框架
- 黑马程序员_Java基础:IO流总结
- 面试题36:数组中的逆序对
- nefu987孙大神的面试
- JS函数式编程【译】2.3 函数式程序员的工具集
- 剑指Offer面试题18(Java版):树的子结构
- 剑指Offer面试题17(Java版):合并两个排序的链表
- 黑马程序员------OC block(代码块)和protocol(协议)
- ios面试题分享
- 黑马程序员——java基础_面向对象
- 黑马程序员——数组
- 黑马程序员——流程控制
- 黑马程序员——基础语法
- 黑马程序员——OC中的封装
- 黑马程序员——初入Java
- 黑马程序员 第十篇 控制逻辑(foundation, completed)
- C语言面试题