黑马程序员——自学总结(四)Java IO技术之流对象
2015-07-25 20:11
543 查看
<a href="http://www.itheima.com"
target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流!
一、System类
该类位于java.lang包中,用于描述系统的一些信息,不能被实例化,没有构造函数,静态成员有err(标准错误流),in(标准输入,默认是键盘),out(标准输出,默认是控制台),System类中方法和属性都是静态的。
System.getProperties()获取系统属性信息,返回Properties类对象,Properties是Hashtable子类,可以用Map中的方法取出该集合中的元素。该集合中存储的键和值都是字符串,没有泛型定义。还可以用Properties对象的方法Set<String>
stringPropertyNames()返回键的集合。
在系统中自定义一些特有信息,可以通过System类中的setProperty(String,String),但是不能永久改变。获取单个信息用getProperty(key),找不到时value返回null。可以在虚拟机启动时,动态的加载一些属性信息,命令格式:java -D 键=值
二、Runtime类
Runtime也在java.lang包中,不能被构造,方法都是非静态的,除了静态方法static Runtime getRuntime()可以返回Runtime对象,该方法中采用了饿汉式单例模式。
调用Runtime对象的Process exec(String command)方法可以开启一个可执行线程,返回一个Process对象,如果命令名非绝对路径,系统会在Path变量里设置的默认路径里找。Process对象的destroy()方法可以杀掉子进程,只能杀通过exec()方法启动的进程,command还可以带命令参数,比如往记事本写入文件数据。
三、Date类
在java.util包中Date类用于描述时间信息,构造一个Date对象打印出来,感觉格式不习惯,可以导入java.text.*;,该包内有一 SimpleDataFormat类,可以格式化date对象。
Date d=new Date();
SimpleDataFormat sdf=new SimpleDataFormat("yyyy年MM月dd日");
sdf.format(d);
将模式封装到SimpleDataFormat对象中,调用formar()方法,让模式格式化指定的data对象,返回String。
用"yyyy”格式化date对象,返回String,得到年份,再用Integer.parseInt()转换,可得到date对象的年份数值。
四、Calendar类
该类是抽象类,可以通过静态方法Calendar.getInstance()实例化,可以在实例化时传递时区对象返回指定的日历对象。Calendar类中封装了多个静态字段,可以返回日历对象的年份,月份,星期,日期等值。get(Calendar.YEAR)获得年份,get(Calendar.MONTH)获得月份,get(Calender.DAY_OF_MONTH)获得某月的几号,Calendar.MONTH值从0到11。显示时,可以建立String数组,存储"一月"到“十二月”,利用查表法输出正确汉字格式的月份值。set(int
年份值,int 月份值,int 日期值)可以更改日历对象字段属性,注意月份从0开始,add(Calendar.YEAR,4)增加4年。
练习:(1)、获取任意年的二月有多少天。思路:根据指定的年设置一个时间c.set(year,2,1)得到某一年的3月1日,c.add(Calendar.DAY_OF_MONTH,-1)得到某一年2月的最后一天,c.get(Calendar.DAY_OF_MONTH)得到某一年2月有多少天。
代码如下:
(2)、获取昨天的现在这个时刻
代码如下:
注意:星期从星期日开始算起,星期日返回0,星期六返回0。
五、Math类
Math类静态成员E,PI,分别表示自然对数的底数和圆周率,abs(double d)返回绝对值,ceil(double d)返回大于或等于参数的最小整数,返回值double型,floor(double d)返回小于等于参数的最大整数,返回值double型,long
round(double d)返回四舍五入后的整数值,pow(double a, double b)求幂,random()返回[0,1)之间的随机double值,可new Random()创建随机数对象,随机数对象有nextInt(),nextDouble()等返回随即整数,浮点数等。
练习:给定一个小数,保留该小数的后两位。
六、IO流
IO流用来处理设备之间的数据传输,Java对数据的操作是通过流的方式,Java用于操作流的对象都在IO包中,流按操作数据分为两种:字节流和字符流,流按流向分为输入流和输出流。
字节流的抽象基类:InputStream,OutputStream,字符流的抽象基类:Reader,Writer,由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。如InputStream的子类FileInputStream,Reader的子类FileReader。Write类包含各种重载的write方法。
需求:在硬盘上,创建一个文件并写入一些文字数据。
使用FileWriter类(后缀名是父类名,前缀名是该流对象的功能)FileWriter类构造函数中需传递文件信息,该对象一被初始化就必须要明确被操作的文件,声明捕捉异常,该文件会被创建到指定的目录下,如果该目录下已有同名文件,写操作后将被覆盖。
调用writer()方法将字符串写入到流中,再flush()清空缓冲区,数据送至目的地。close()关闭流资源,但是关闭之前会刷新一次内部缓冲区中的数据,再调用write()将写不进去,抛出异常,而调用flush()后可以继续写数据,close()和flush()都需声明抛出IO异常。
一般应采用如下格式创建流对象:
try{
}catch(IO异常)
finally(关闭流) 确保即使出现异常,也能关闭流。如果流对象在try块中声明,那么在finally中将无法访问,所以try块外边建立流对象,在try内进行流对象初始化。关闭流对象同样需要声明捕捉异常,为了防止出现调用空的流对象的close()方法,在关闭流对象之前,需判断是否为空。初始化FileWriter对象时,可能会抛出FileNotFoundException,它是IOException的子类。
如果不覆盖原有文件内容,只是对已有文件的数据续写,用write(数据,true);注意:在Windows中,回车符由两个字符表示,\r\n,Linux\n可以换行。
对文本文件读取使用Reader抽象类,使用构造方法创建一个文件读取流对象,和指定名称的文件相关联,要保证文件是已经存在的,如果不存在,会发生FileNotFoundException异常。
Reader类中多种重载的read()方法,方法内部一次读一个字符,将读到的字符作为int返回,而且会自动往下读,读到末尾返回-1,编程时用while(reader.read(char[]))循环判断,read(char[])返回读到的字符个数,到末尾返回-1,按照字符数组的长度读取相应的字符存入字符数组,继续读字符会在字符数组中循环存储,已全部取完到文件末尾,返回-1,通常将字符数组长度定义为1024的整数倍。
练习:读取一个.java文件,并打印在控制台上。
将C盘一个文本文件复制到D盘,复制原理其实就是将C盘下的文件数据存储到D盘的一个文件中,步骤:1、在D盘创建一个文件,用于存储C盘文件中的数据;2、定义一个读取流和C盘文件关联;3、通过不断的读写完成数据的存储;4、关闭资源。
Reader读取文本文件有两种方式:(1)单个字符读取;(2)字符数组读取。
字符流中的缓冲区的出现提高了对数据的读写效率,对应类为BufferedWriter,BufferedReader,缓冲区要结合流才可以使用,在流的基础上对流的功能进行了增强。缓冲区的出现是为了提高流的操作效率,所以在创建缓冲区之前,必须要先有流对象,缓冲区的原理其实就是在内部封装了一个数组。只要用到缓冲区,就要记得刷新flush(),否则数据写不出去,关闭缓冲区就是在关闭缓冲区中的流对象,不用再重复关闭。BufferedWriter中的newLine()方法跨平台,在不同的操作系统中可以本地化输出换行符。BufferedReader类为了提高效率,加入了缓冲技术,构造时将字符读取流对象作为参数传递给缓冲对象的构造器,该缓冲区提供了一个依次读一行的方法readLine()方法,方便对文本数据的获取,到文件末尾返回null。
练习:通过缓冲区复制.java文件
readLine()方法返回的时候只返回回车符之前的数据,并不返回回车符。
下面构建MyBufferedeReader类,自定义readLine()方法和close方法。
为了确保最后一行数据也能读到,在myreadLine()方法内while循环结束后,判断如果因为读到文件尾且sb中有数据,则将最后一行数据返回。
装饰设计模式:当想要对已有的对象进行功能增强时,可以定义一个类,将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该类就称为装饰类。装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
装饰和继承的区别:继承扩展性不好,非常臃肿。装饰模式比继承模式要灵活,避免了继承体系臃肿,而且降低了类与类之间的关系。装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能,所以装饰类和被装饰类通常都是属于一个体系中的。装饰模式实际上由继承结构变成了组合结构。
自定义装饰类,继承自Reader,覆盖Reader中的抽象方法:read(char[],int off ,int len)和close()。
修改上例中的MyBufferedReader类:
BufferedReader有一子类LineNumberReader,该类有getLineNumber()方法获得当前行号,setLineNumber(int lineNumber)方法设置行号,readLine()方法读取一行数据。 感觉这个程序有点小问题,如果是汉字字符,把汉字字符的两个字节拆成两次读取,可能会出现错误字符。
练习:模拟一个带行号的缓冲区对象。
代码如下:
程序运行结果如下:
字节流主要有两个基类InputStream和OutputStream,想要操作图片数据,这时就要用到字节流。其实字符流内部也是按照字节流进行操作,只不过将读到的字节数据临时存储,比如汉字2个字节读两次,然后一起刷新读出。而字节流不设置缓冲区,直接输入或输出,不用刷新。字节流有两种读取方式,一个一个地读,或存储到字节数组地读(该方法内部通过available()方法得到剩余文件大小,以确定还将往数组中存储多少数据),可在读取开始调用available()方法获取文件大小,建立刚刚好大小的数组读。java虚拟机启动默认占用内存是64M,可以启动时调整大小,因为若文件太大的话建立刚刚好大小的数组则不合适,会造成内存溢出。一般定义数组大小为1024整数倍最合适。
复制一个图片。思路:1、用字节读取流对象和图片关联;2、用字节数组存储获取到的图片数据3、通过循环读写完成数据存储,4、关闭资源。注意不要拿字符流处理字节文件,因为若读到未知编码数据,可能会改变数据。
字节流的缓冲区BufferedInputStream和BufferedOutStream,通过字节流的缓冲区完成复制MA3文件,记录复制时间。可调用System.currentTimeMills()方法获得系统当前时间。
说明一点:MP3数据都是01代码,有可能出现第一个字节全是1,这时如果read()方法将得到的字节数据转换为int型返回,将返回-1,那么读取就将结束。而实际上为了防止出现这种情况,Java在设计read()方法时,对得到的字节数据是将其高位全部补0提升至int型返回,write()方法写入数据时只写int变量的强转,只写入数据低8位。
需求:通过键盘录入数据,当录入一行数据后,就将该行数据进行打印,如果录入的数据是over,那么就停止录入,在命令窗口按ctrl+c,实际上就是加结束标记,read此时返回-1。
System.out 对应的是标准输出设备—控制台,System.in对应的是标准输入设备—键盘,程序中br.read()方法是阻塞方法。为了提高读写效率,可以采用BufferedReader类的readLine()方法,但Systenm.in是InputStream,考虑使用InputStreamReader转换流,将InputStream转换为Reader对象。步骤:(1)获取键盘录入对象;(2)使用转换流将字节流对象转成字符流对象;(3)为了提高效率,将字符串进行缓冲区技术高效操作,使用装饰类包装。对应的,OutputStreamWriter类可将字节输出流转换成字符输出流。
上面代码中数据源是键盘录入,数据目的是控制台,现在想把键盘录入的数据存储到文件中。
需求:想要将一个文件的数据打印在控制台上。
使用流操作最纠结的就是流对象很多,不知道该用哪个。可以通过两个明确来完成。(1)明确源和目的,数据源用输入流,数据目的用输出流;(2)操作的数据是否是纯文本,是纯文本使用字符流,不是使用字节流;(3)当体系明确后再明确要使用哪个具体的流对象,通过设备来进行区分,数据源设备有内存,硬盘,键盘等,数据目的设备有内存,硬盘,控制台等。
需求:将一个文本文件中数据存储到另一个文件中,复制文件。
分析:被复制文件是数据源,使用输入流,操作纯文本文件,使用Reader,体系明确了,接下来明确要使用该体系中的哪个对象,明确数据源设备是文件(硬盘),体系中可以操作文件的对象是FileReader。
复制文件是数据目的,使用输出流,操作纯文本,使用Writer,数据目的设备是硬盘,Writer可以操作文件的类是FileWriter。
需要提高效率,加入Reader体系中的缓冲区BufferedReader。
练习:将一个图片文件中数据存储到另一个文件中,复制文件,要按照以上格式自己完成三个明确。
分析:被复制文件是数据源,使用输入流,不是操作纯文本文件,使用InputStream,体系明确了,接下来明确要使用该体系中的哪个对象,明确数据源设备是文件(硬盘),体系中可以操作文件的对象是FileInputStream。
复制文件是数据目的,使用输出流,不是操作纯文本,使用OutputStream,数据目的设备是硬盘,OutputStream类中可以操作文件的类是FileOutputStream。
需要提高效率,加入InputStream体系中的缓冲区BufferedInputStream。
需求:将键盘录入的数据保存到一个文件中
这个需求中源和目的都存在,分别分析:
键盘录入是数据源,且操作纯文本,使用Reader,文件(硬盘)是数据目的,需要提高效率,使用BufferedWriter(new FileWriter())。这里键盘System.in对应的是字节流,为了操作键盘的文本数据方便,转成字符流,用到了Reader体系中的转换流InputStreamReader。需要提高效率,用BufferedReader包装。
扩展一下,想要把录入的数据按照指定的编码表存入到文件中,比如指定编码表UTF-8,因为FileWriter使用的是默认编码表GBK,所以只有转换流OutputStreamWriter才能完成,该转换流对象要接收一个FileOutputStream对象,OutputStreamWriter对象写入数据时可以指定编码表。转换流使用场合:字符流和字节流需要转换,涉及到字符编码转换时。
练习:将一个文本数据打印在控制台上,按照以上格式完成三个明确。
改变标准输入输出设备System.setIn(),System.setOut(PrintStream)。可以通过重新设定标准输出设备,将异常信息记录到指定文件中。网上有建立日志信息的工具,比如log4j。
Properties可以和流对象结合,Properties对象的list(PrintStram)方法可以输出系统信息,通过指定PrintStream得到系统信息。
target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流!
一、System类
该类位于java.lang包中,用于描述系统的一些信息,不能被实例化,没有构造函数,静态成员有err(标准错误流),in(标准输入,默认是键盘),out(标准输出,默认是控制台),System类中方法和属性都是静态的。
System.getProperties()获取系统属性信息,返回Properties类对象,Properties是Hashtable子类,可以用Map中的方法取出该集合中的元素。该集合中存储的键和值都是字符串,没有泛型定义。还可以用Properties对象的方法Set<String>
stringPropertyNames()返回键的集合。
在系统中自定义一些特有信息,可以通过System类中的setProperty(String,String),但是不能永久改变。获取单个信息用getProperty(key),找不到时value返回null。可以在虚拟机启动时,动态的加载一些属性信息,命令格式:java -D 键=值
二、Runtime类
Runtime也在java.lang包中,不能被构造,方法都是非静态的,除了静态方法static Runtime getRuntime()可以返回Runtime对象,该方法中采用了饿汉式单例模式。
调用Runtime对象的Process exec(String command)方法可以开启一个可执行线程,返回一个Process对象,如果命令名非绝对路径,系统会在Path变量里设置的默认路径里找。Process对象的destroy()方法可以杀掉子进程,只能杀通过exec()方法启动的进程,command还可以带命令参数,比如往记事本写入文件数据。
三、Date类
在java.util包中Date类用于描述时间信息,构造一个Date对象打印出来,感觉格式不习惯,可以导入java.text.*;,该包内有一 SimpleDataFormat类,可以格式化date对象。
Date d=new Date();
SimpleDataFormat sdf=new SimpleDataFormat("yyyy年MM月dd日");
sdf.format(d);
将模式封装到SimpleDataFormat对象中,调用formar()方法,让模式格式化指定的data对象,返回String。
用"yyyy”格式化date对象,返回String,得到年份,再用Integer.parseInt()转换,可得到date对象的年份数值。
四、Calendar类
该类是抽象类,可以通过静态方法Calendar.getInstance()实例化,可以在实例化时传递时区对象返回指定的日历对象。Calendar类中封装了多个静态字段,可以返回日历对象的年份,月份,星期,日期等值。get(Calendar.YEAR)获得年份,get(Calendar.MONTH)获得月份,get(Calender.DAY_OF_MONTH)获得某月的几号,Calendar.MONTH值从0到11。显示时,可以建立String数组,存储"一月"到“十二月”,利用查表法输出正确汉字格式的月份值。set(int
年份值,int 月份值,int 日期值)可以更改日历对象字段属性,注意月份从0开始,add(Calendar.YEAR,4)增加4年。
练习:(1)、获取任意年的二月有多少天。思路:根据指定的年设置一个时间c.set(year,2,1)得到某一年的3月1日,c.add(Calendar.DAY_OF_MONTH,-1)得到某一年2月的最后一天,c.get(Calendar.DAY_OF_MONTH)得到某一年2月有多少天。
代码如下:
import java.util.*; class getday{ public static int getday(int year){ Calendar c=Calendar.getInstance(); c.set(year,2,1); c.add(Calendar.DAY_OF_MONTH,-1); return c.get(Calendar.DAY_OF_MONTH); } public static void main(String[] args){ for(int year=2000;year<2016;year++) System.out.println(getday(year)); } }
(2)、获取昨天的现在这个时刻
代码如下:
import java.util.*; class gettime{ public static Calendar gettime(){ Calendar c=Calendar.getInstance(); c.add(Calendar.DAY_OF_MONTH,-1); return c; } public static void main(String[] args){ System.out.println(gettime()); } }
注意:星期从星期日开始算起,星期日返回0,星期六返回0。
五、Math类
Math类静态成员E,PI,分别表示自然对数的底数和圆周率,abs(double d)返回绝对值,ceil(double d)返回大于或等于参数的最小整数,返回值double型,floor(double d)返回小于等于参数的最大整数,返回值double型,long
round(double d)返回四舍五入后的整数值,pow(double a, double b)求幂,random()返回[0,1)之间的随机double值,可new Random()创建随机数对象,随机数对象有nextInt(),nextDouble()等返回随即整数,浮点数等。
练习:给定一个小数,保留该小数的后两位。
class myround{ public static double myround(double d){ return Math.round(d*100)/100.0; } public static void main(String[] args){ System.out.println(myround(3.141592653)); } }
六、IO流
IO流用来处理设备之间的数据传输,Java对数据的操作是通过流的方式,Java用于操作流的对象都在IO包中,流按操作数据分为两种:字节流和字符流,流按流向分为输入流和输出流。
字节流的抽象基类:InputStream,OutputStream,字符流的抽象基类:Reader,Writer,由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。如InputStream的子类FileInputStream,Reader的子类FileReader。Write类包含各种重载的write方法。
需求:在硬盘上,创建一个文件并写入一些文字数据。
使用FileWriter类(后缀名是父类名,前缀名是该流对象的功能)FileWriter类构造函数中需传递文件信息,该对象一被初始化就必须要明确被操作的文件,声明捕捉异常,该文件会被创建到指定的目录下,如果该目录下已有同名文件,写操作后将被覆盖。
import java.io.*; class IOdemo{ public static void main(String[] args){ FileWriter fw=null; try{ fw=new FileWriter("iodemo.txt"); fw.write("hello!"); fw.flush(); }catch(IOException e){ throw new RuntimeException(e); }finally{ try{ if(fw!=null) fw.close(); }catch(IOException e){ throw new RuntimeException(e); } } } }
调用writer()方法将字符串写入到流中,再flush()清空缓冲区,数据送至目的地。close()关闭流资源,但是关闭之前会刷新一次内部缓冲区中的数据,再调用write()将写不进去,抛出异常,而调用flush()后可以继续写数据,close()和flush()都需声明抛出IO异常。
一般应采用如下格式创建流对象:
try{
}catch(IO异常)
finally(关闭流) 确保即使出现异常,也能关闭流。如果流对象在try块中声明,那么在finally中将无法访问,所以try块外边建立流对象,在try内进行流对象初始化。关闭流对象同样需要声明捕捉异常,为了防止出现调用空的流对象的close()方法,在关闭流对象之前,需判断是否为空。初始化FileWriter对象时,可能会抛出FileNotFoundException,它是IOException的子类。
如果不覆盖原有文件内容,只是对已有文件的数据续写,用write(数据,true);注意:在Windows中,回车符由两个字符表示,\r\n,Linux\n可以换行。
对文本文件读取使用Reader抽象类,使用构造方法创建一个文件读取流对象,和指定名称的文件相关联,要保证文件是已经存在的,如果不存在,会发生FileNotFoundException异常。
Reader类中多种重载的read()方法,方法内部一次读一个字符,将读到的字符作为int返回,而且会自动往下读,读到末尾返回-1,编程时用while(reader.read(char[]))循环判断,read(char[])返回读到的字符个数,到末尾返回-1,按照字符数组的长度读取相应的字符存入字符数组,继续读字符会在字符数组中循环存储,已全部取完到文件末尾,返回-1,通常将字符数组长度定义为1024的整数倍。
练习:读取一个.java文件,并打印在控制台上。
import java.io.*; class IOdemo2{ public static void main(String[] args){ Reader reader=null; try{ reader=new FileReader("IOdemo2.java"); char[] buff=new char[1024]; int len=0; while((len=reader.read(buff))!=-1) System.out.print(new String(buff,0,len)); }catch(IOException e){ throw new RuntimeException(e); }finally{ if(reader!=null) try{ reader.close(); }catch(IOException e){ throw new RuntimeException(e); } } } }
将C盘一个文本文件复制到D盘,复制原理其实就是将C盘下的文件数据存储到D盘的一个文件中,步骤:1、在D盘创建一个文件,用于存储C盘文件中的数据;2、定义一个读取流和C盘文件关联;3、通过不断的读写完成数据的存储;4、关闭资源。
import java.io.*; class IOdemo3{ public static void main(String[] args){ Reader reader=null; Writer writer=null; try{ reader=new FileReader("C:\\IOdemo3.java"); writer=new FileWriter("D:\\IOdemo3_copy.java"); char[] buff=new char[1024]; int len=0; while((len=reader.read(buff))!=-1){ writer.write(new String(buff,0,len)); writer.flush(); } }catch(IOException e){ throw new RuntimeException(e); }finally{ if(reader!=null) try{ reader.close(); }catch(IOException e){ throw new RuntimeException(e); }finally{ if(writer!=null) try{ writer.close(); }catch(IOException e){ throw new RuntimeException(e); } } } } }
Reader读取文本文件有两种方式:(1)单个字符读取;(2)字符数组读取。
字符流中的缓冲区的出现提高了对数据的读写效率,对应类为BufferedWriter,BufferedReader,缓冲区要结合流才可以使用,在流的基础上对流的功能进行了增强。缓冲区的出现是为了提高流的操作效率,所以在创建缓冲区之前,必须要先有流对象,缓冲区的原理其实就是在内部封装了一个数组。只要用到缓冲区,就要记得刷新flush(),否则数据写不出去,关闭缓冲区就是在关闭缓冲区中的流对象,不用再重复关闭。BufferedWriter中的newLine()方法跨平台,在不同的操作系统中可以本地化输出换行符。BufferedReader类为了提高效率,加入了缓冲技术,构造时将字符读取流对象作为参数传递给缓冲对象的构造器,该缓冲区提供了一个依次读一行的方法readLine()方法,方便对文本数据的获取,到文件末尾返回null。
练习:通过缓冲区复制.java文件
import java.io.*; class IOdemo4{ public static void main(String[] args){ BufferedReader br=null; BufferedWriter bw=null; try{ br=new BufferedReader(new FileReader("C:\\IOdemo4.java")); bw=new BufferedWriter(new FileWriter("D:\\IOdemo4_copy.java")); String buff=null; while((buff=br.readLine())!=null){ bw.write(buff); bw.newLine(); bw.flush(); } }catch(IOException e){ throw new RuntimeException(e); }finally{ if(br!=null) try{ br.close(); }catch(IOException e){ throw new RuntimeException(e); }finally{ if(bw!=null) try{ bw.close(); }catch(IOException e){ throw new RuntimeException(e); } } } } }
readLine()方法返回的时候只返回回车符之前的数据,并不返回回车符。
下面构建MyBufferedeReader类,自定义readLine()方法和close方法。
import java.io.*; class MyBufferedReader{ private FileReader fr; public MyBufferedReader(FileReader fr){ this.fr=fr; } public void myclose(){ if(fr!=null) try{ fr.close(); }catch(IOException e){ throw new RuntimeException(e); } } public String myr de8a eadLine(){ StringBuilder sb=new StringBuilder(); int c; try{ while((c=fr.read())!=-1){ if(c=='\r') continue; if(c=='\n') return sb.toString(); else sb.append((char)c); } }catch(IOException e){ throw new RuntimeException(e); } if(sb.length()!=0) return sb.toString(); return null; } }
import java.io.*; class IOdemo5{ public static void main(String[] args){ MyBufferedReader br=null; BufferedWriter bw=null; try{ br=new MyBufferedReader(new FileReader("C:\\IOdemo5.java")); bw=new BufferedWriter(new FileWriter("D:\\IOdemo5_copy.java")); String buff=null; while((buff=br.myreadLine())!=null){ bw.write(buff); bw.newLine(); bw.flush(); } }catch(IOException e){ throw new RuntimeException(e); }finally{ if(br!=null) br.myclose(); if(bw!=null) try{ bw.close(); }catch(IOException e){ throw new RuntimeException(e); } } } }
为了确保最后一行数据也能读到,在myreadLine()方法内while循环结束后,判断如果因为读到文件尾且sb中有数据,则将最后一行数据返回。
装饰设计模式:当想要对已有的对象进行功能增强时,可以定义一个类,将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该类就称为装饰类。装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
装饰和继承的区别:继承扩展性不好,非常臃肿。装饰模式比继承模式要灵活,避免了继承体系臃肿,而且降低了类与类之间的关系。装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能,所以装饰类和被装饰类通常都是属于一个体系中的。装饰模式实际上由继承结构变成了组合结构。
自定义装饰类,继承自Reader,覆盖Reader中的抽象方法:read(char[],int off ,int len)和close()。
修改上例中的MyBufferedReader类:
import java.io.*; class MyBufferedReader extends Reader{ private Reader r; public MyBufferedReader(Reader r){ this.r=r; } public int read(char[] cbuf, int off, int len) throws IOException{ return r.read(cbuf,off,len); } public void close(){ if(r!=null) try{ r.close(); }catch(IOException e){ throw new RuntimeException(e); } } public String myreadLine(){ StringBuilder sb=new StringBuilder(); int c; try{ while((c=r.read())!=-1){ if(c=='\r') continue; if(c=='\n') return sb.toString(); else sb.append((char)c); } }catch(IOException e){ throw new RuntimeException(e); } if(sb.length()!=0) return sb.toString(); return null; } }
BufferedReader有一子类LineNumberReader,该类有getLineNumber()方法获得当前行号,setLineNumber(int lineNumber)方法设置行号,readLine()方法读取一行数据。 感觉这个程序有点小问题,如果是汉字字符,把汉字字符的两个字节拆成两次读取,可能会出现错误字符。
练习:模拟一个带行号的缓冲区对象。
代码如下:
import java.io.*; class MyLineNumberReader{ private Reader r; private int linenumber; public MyLineNumberReader(Reader r){ this.r=r; linenumber=0; } public void close() throws IOException{ if(r!=null) try{ r.close(); }catch(IOException e){ throw e; } } public String readLine(){ linenumber++; StringBuilder sb=new StringBuilder(); int c; try{ while((c=r.read())!=-1){ if(c=='\r') continue; if(c=='\n') return sb.toString(); else sb.append((char)c); } }catch(IOException e){ throw new RuntimeException(e); } if(sb.length()!=0) return sb.toString(); return null; } public int getLineNumber(){ return linenumber; } public void setLineNumber(int lineNumber){ linenumber=lineNumber; } }
import java.io.*; class IOdemo6{ public static void main(String[] args){ MyLineNumberReader br=null; BufferedWriter bw=null; try{ br=new MyLineNumberReader(new FileReader("C:\\IOdemo6.java")); br.setLineNumber(100);//从101起开始记录行数 bw=new BufferedWriter(new FileWriter("D:\\IOdemo6_copy.java")); String buff=null; while((buff=br.readLine())!=null){ bw.write(br.getLineNumber()+buff); bw.newLine(); bw.flush(); } }catch(IOException e){ throw new RuntimeException(e); }finally{ if(br!=null) try{ br.close(); }catch(IOException e){ throw new RuntimeException(e); }finally{ if(bw!=null) try{ bw.close(); }catch(IOException e){ throw new RuntimeException(e); } } } } }
程序运行结果如下:
字节流主要有两个基类InputStream和OutputStream,想要操作图片数据,这时就要用到字节流。其实字符流内部也是按照字节流进行操作,只不过将读到的字节数据临时存储,比如汉字2个字节读两次,然后一起刷新读出。而字节流不设置缓冲区,直接输入或输出,不用刷新。字节流有两种读取方式,一个一个地读,或存储到字节数组地读(该方法内部通过available()方法得到剩余文件大小,以确定还将往数组中存储多少数据),可在读取开始调用available()方法获取文件大小,建立刚刚好大小的数组读。java虚拟机启动默认占用内存是64M,可以启动时调整大小,因为若文件太大的话建立刚刚好大小的数组则不合适,会造成内存溢出。一般定义数组大小为1024整数倍最合适。
复制一个图片。思路:1、用字节读取流对象和图片关联;2、用字节数组存储获取到的图片数据3、通过循环读写完成数据存储,4、关闭资源。注意不要拿字符流处理字节文件,因为若读到未知编码数据,可能会改变数据。
import java.io.*; class IOdemo7{ public static void main(String[] args){ InputStream is=null; OutputStream os=null; try{ is=new FileInputStream("C:\\Pic.jpg"); os=new FileOutputStream("D:\\Pic_copy.jpg"); byte[] buff=new byte[1024]; int len=0; while((len=is.read(buff))!=-1) os.write(buff,0,len); }catch(IOException e){ throw new RuntimeException(e); }finally{ if(is!=null) try{ is.close(); }catch(IOException e){ throw new RuntimeException(e); }finally{ if(os!=null) try{ os.close(); }catch(IOException e){ throw new RuntimeException(e); } } } } }
字节流的缓冲区BufferedInputStream和BufferedOutStream,通过字节流的缓冲区完成复制MA3文件,记录复制时间。可调用System.currentTimeMills()方法获得系统当前时间。
import java.io.*; class IOdemo8{ public static void main(String[] args){ Long start=System.currentTimeMillis(); BufferedInputStream bis=null; BufferedOutputStream bos=null; try{ bis=new BufferedInputStream(new FileInputStream("C:\\Pic.jpg")); bos=new BufferedOutputStream(new FileOutputStream ("D:\\Pic_copy.jpg")); int data=0; while((data=bis.read())!=-1) bos.write(data); }catch(IOException e){ throw new RuntimeException(e); }finally{ if(bis!=null) try{ bis.close(); }catch(IOException e){ throw new RuntimeException(e); }finally{ if(bos!=null) try{ bos.close(); }catch(IOException e){ throw new RuntimeException(e); } } } System.out.println("复制成功!总共耗时"+(System.currentTimeMillis()-start)+" 毫秒"); } }
说明一点:MP3数据都是01代码,有可能出现第一个字节全是1,这时如果read()方法将得到的字节数据转换为int型返回,将返回-1,那么读取就将结束。而实际上为了防止出现这种情况,Java在设计read()方法时,对得到的字节数据是将其高位全部补0提升至int型返回,write()方法写入数据时只写int变量的强转,只写入数据低8位。
需求:通过键盘录入数据,当录入一行数据后,就将该行数据进行打印,如果录入的数据是over,那么就停止录入,在命令窗口按ctrl+c,实际上就是加结束标记,read此时返回-1。
import java.io.*; class IOdemo9{ public static void main(String[] args){ BufferedReader br=null; BufferedWriter bw=null; try{ br=new BufferedReader(new InputStreamReader(System.in)); bw=new BufferedWriter(new OutputStreamWriter(System.out)); String buff=null; while((buff=br.readLine())!=null){ if("over".equals(buff)) break; bw.write(buff); bw.newLine(); bw.flush(); } }catch(IOException e){ throw new RuntimeException(e); }finally{ try{ br.close(); }catch(IOException e){ throw new RuntimeException(e); }finally{ try{ bw.close(); }catch(IOException e){ throw new RuntimeException(e); } } } } }
System.out 对应的是标准输出设备—控制台,System.in对应的是标准输入设备—键盘,程序中br.read()方法是阻塞方法。为了提高读写效率,可以采用BufferedReader类的readLine()方法,但Systenm.in是InputStream,考虑使用InputStreamReader转换流,将InputStream转换为Reader对象。步骤:(1)获取键盘录入对象;(2)使用转换流将字节流对象转成字符流对象;(3)为了提高效率,将字符串进行缓冲区技术高效操作,使用装饰类包装。对应的,OutputStreamWriter类可将字节输出流转换成字符输出流。
上面代码中数据源是键盘录入,数据目的是控制台,现在想把键盘录入的数据存储到文件中。
import java.io.*; class IOdemo10{ public static void main(String[] args){ BufferedReader br=null; BufferedWriter bw=null; try{ br=new BufferedReader(new InputStreamReader(System.in)); bw=new BufferedWriter(new FileWriter("D:\\data.txt")); String buff; while((buff=br.readLine())!=null){ if("over".equals(buff)) break; bw.write(buff); bw.newLine(); bw.flush(); } }catch(IOException e){ throw new RuntimeException(e); }finally{ try{ br.close(); }catch(IOException e){ throw new RuntimeException(e); }finally{ if(bw!=null) try{ bw.close(); }catch(IOException e){ throw new RuntimeException(e); } } } } }
需求:想要将一个文件的数据打印在控制台上。
import java.io.*; class IOdemo11{ public static void main(String[] args){ BufferedReader br=null; BufferedWriter bw=null; try{ br=new BufferedReader(new FileReader("D:\\data.txt")); bw=new BufferedWriter(new OutputStreamWriter(System.out)); String buff; while((buff=br.readLine())!=null){ bw.write(buff); bw.newLine(); bw.flush(); } }catch(IOException e){ throw new RuntimeException(e); }finally{ try{ br.close(); }catch(IOException e){ throw new RuntimeException(e); }finally{ if(bw!=null) try{ bw.close(); }catch(IOException e){ throw new RuntimeException(e); } } } } }
使用流操作最纠结的就是流对象很多,不知道该用哪个。可以通过两个明确来完成。(1)明确源和目的,数据源用输入流,数据目的用输出流;(2)操作的数据是否是纯文本,是纯文本使用字符流,不是使用字节流;(3)当体系明确后再明确要使用哪个具体的流对象,通过设备来进行区分,数据源设备有内存,硬盘,键盘等,数据目的设备有内存,硬盘,控制台等。
需求:将一个文本文件中数据存储到另一个文件中,复制文件。
分析:被复制文件是数据源,使用输入流,操作纯文本文件,使用Reader,体系明确了,接下来明确要使用该体系中的哪个对象,明确数据源设备是文件(硬盘),体系中可以操作文件的对象是FileReader。
复制文件是数据目的,使用输出流,操作纯文本,使用Writer,数据目的设备是硬盘,Writer可以操作文件的类是FileWriter。
需要提高效率,加入Reader体系中的缓冲区BufferedReader。
练习:将一个图片文件中数据存储到另一个文件中,复制文件,要按照以上格式自己完成三个明确。
分析:被复制文件是数据源,使用输入流,不是操作纯文本文件,使用InputStream,体系明确了,接下来明确要使用该体系中的哪个对象,明确数据源设备是文件(硬盘),体系中可以操作文件的对象是FileInputStream。
复制文件是数据目的,使用输出流,不是操作纯文本,使用OutputStream,数据目的设备是硬盘,OutputStream类中可以操作文件的类是FileOutputStream。
需要提高效率,加入InputStream体系中的缓冲区BufferedInputStream。
需求:将键盘录入的数据保存到一个文件中
这个需求中源和目的都存在,分别分析:
键盘录入是数据源,且操作纯文本,使用Reader,文件(硬盘)是数据目的,需要提高效率,使用BufferedWriter(new FileWriter())。这里键盘System.in对应的是字节流,为了操作键盘的文本数据方便,转成字符流,用到了Reader体系中的转换流InputStreamReader。需要提高效率,用BufferedReader包装。
扩展一下,想要把录入的数据按照指定的编码表存入到文件中,比如指定编码表UTF-8,因为FileWriter使用的是默认编码表GBK,所以只有转换流OutputStreamWriter才能完成,该转换流对象要接收一个FileOutputStream对象,OutputStreamWriter对象写入数据时可以指定编码表。转换流使用场合:字符流和字节流需要转换,涉及到字符编码转换时。
练习:将一个文本数据打印在控制台上,按照以上格式完成三个明确。
改变标准输入输出设备System.setIn(),System.setOut(PrintStream)。可以通过重新设定标准输出设备,将异常信息记录到指定文件中。网上有建立日志信息的工具,比如log4j。
Properties可以和流对象结合,Properties对象的list(PrintStram)方法可以输出系统信息,通过指定PrintStream得到系统信息。
相关文章推荐
- 黑马程序员——Java反射总结
- 黑马程序员---for循环和案例
- 黑马程序员---while 和do while 循环
- 一道小小的内存申请面试题
- 中兴软创Java实习生面试题目
- 2015年面试经历
- 阿里电话面试详解
- 全新整理:微软、谷歌、百度等公司经典面试100题[第101-160题]
- (剑指Offer)面试题34:丑数
- 阿里电话面试题详解
- 新人入职培训有关职场沟通的总结分享
- 《程序员面试宝典4学习记录》
- 黑马程序员——Java IO总结二
- 求一个整形数组的和最大的连续子串 -------- 程序员面试金典
- 黑马程序员
- 7.24晚毫无准备的面试题
- (剑指Offer)面试题33:把数组排成最小的数
- 全面解析《嵌入式程序员应该知道的16个问题》
- 面试题19_二叉树的镜像——剑指offer系列
- 实习工作面试常问问题