------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------
学习概述:装饰设计模式的含义,IO流体系中的第二种字节流的用法,File类的用法,Properties的用法
学习目标:熟悉装饰设计模式的流程和原理,熟练掌握字节流体系对象的用法,对一些常用的对象必须烂熟于心,对于文件操作类File也要熟练掌握,Properties也是一个极其重要的类,了解RandomAccessFile以及管道流。
1.装饰设计模式
(1)装饰设计模式定义:当想要对已有对象的功能进行增强时,可将已有对象传入,基于已有对象的功能,并提供增强功能,那么自定义的该类称为装饰类。装饰类通常会通过构造方法接收被装饰对象。关键词:构造函数传递!
(2) 装饰和继承的区别:装饰由继承结构编程组合结构,装饰模式比比继承模式更加灵活,降低了类之间的耦合度,避免了继承体系的臃肿,装饰类因为增强已有对象,只是单纯的增强功能,所以装饰类和被装饰类通常在一个体系中。
练习:模拟一个带行号的缓冲区对象
mport java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
//简化版
class MyLineNumberReader extends MyBufferedReader
{
private int lineNumber;
MyLineNumberReader(FileReader r)
{
super(r);
}
public String myReaderLine()throws IOException
{
lineNumber++;
return super.myReadLine();
}
public void setLineNumber(int lineNumber)
{
this.lineNumber = lineNumber;
}
public int getLineNumber()
{
return lineNumber;
}
}
/*
class MyLineNumberReader
{
private Reader r;
private int lineNumber;
MyLineNumberReader(Reader r)
{
this.r = r;
}
public String myReadLine()throws IOException
{
lineNumber++;
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch=r.read())!=-1)
{
if(ch=='\r')
continue;
if(ch=='\n')
return sb.toString();
sb.append((char)ch);
}
if(sb.length()!=0)
return sb.toString();
return null;
}
public void setLineNumber(int LineNumber)
{
this.lineNumber= lineNumber;
}
public int getLineNumber()
{
return lineNumber;
}
public void myClose()throws IOException
{
r.close();
}
}
*/
class MyLineNumberReaderDemo {
public static void main(String[] args)throws IOException
{
FileReader fr = new FileReader("PersonDemo.java");
MyLineNumberReader mnr = new MyLineNumberReader(fr);
String line = null;
while((line = mnr.myReadLine())!=null)
{
System.out.println(mnr.getLineNumber()+":"+line);
}
mnr.myClose();
}
}
2.字节流的File操作
字节流的一系列操作方式与字符流几乎完全一样,区别是操作的数据单元不一样,要注意字节流的read和write方法参数中的数组是字节数组,不再是字节数组。字节输出流的write()方法中不能包括String字符串,这是与字符输出流很大的不同,在以后的使用过程中要注意。如果没有指定字节流缓冲区,字节流是不需要刷新的,但是close()一样必须要写,因为你必须要关闭资源。为什么会这样?因为本质上字符流是基于字节流的。
思考:对于FileOuputputStream的available方法,该方法返回一个整型值,得到文件的字节长度,是不是我们可以定义byte数组的时候指定byte数组长度,无需利用缓冲循环读取呢?理论上是可行的的,但是要考虑硬件条件,如果你要操作一个很大的文件,比如说电影文件,该文件大小超过了内存大小,那么采用这种方法将会出现内存溢出的情况,所以这种方法要慎用,还是以循环为主!
练习 拷贝文件:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyPic {
public static void mian(String[] args){
FileOutputStream fos =null;
FileInputStream fis=null;
try{
fos = new FileOutputStream("E:\\copy.jpg");
fis = new FileInputStream("D:\\test.jpg");
byte[] temp = new byte[1024];
int len=0;
while((len=fis.read(temp))>0){
fos.write(temp, 0, len);
}
}
catch(IOException e){
e.printStackTrace();
}
finally{
try{
if(fis!=null){
fis.close();
}
if(fos!=null){
fos.close();
}
}
catch(IOException e){
e.printStackTrace();
}
}
}
}
思考1:用字符流是否可以实现?
回答:字符流可以完成该操作,但是操作完之后就会发现,复制过去的图片往往打不开。因为字符流的本质也是依赖于字节流。
思考2:为什么read()方法的返回值一定是int?
Java 下 IO 中 FileReder 和 FileInputStream 分别是以字符和字节的形式来完成数据的读取的,然而返回值确是 int 类型的数据,这样做的核心目的只是要取到到一个 int 类型下的 -1 来表示数据流的末尾。为什么要这样做?又是怎么实现的呢?
首先看 FileReder :
FileReader fr = new FileReader("src.txt");
int ch = fr.read();
如上面的代码,FileReader 的 read 方法返回值是一个 int 类型的变量来接收的,然而 read 方法在实际中却是以字符形式来进行数据的读取的。通过上面的基本数据类型的取值范围我们能发现 char 类型数据的取值范围为 0 ~ 65535 ,也就是说 char 类型数据是取不到负值的;int 类型数据的取值范围为 -2147483648 ~ 2147483647 ,可以取到负值;同时 int 的取值范围又包含 char 的取值范围,这就为使用 int 作为返回值类型提供了可能,因为流需要一个特殊的值来表示流末尾,这个值不应该在 char 的取值范围内,如果使用 char 取值范围内的值作为流末尾标志,那么这个值同样有可能出现在数据流中间作为数据来传输,流在读到这个值的时候会认为已经到达流末尾,后面未读取的数据将被截断。所以 Java 中选择了使用 -1 来作为流末尾,这个值不在 char 的取值范围内,所以不存在数据截断,然而 -1 又在 int 的取值范围内,同时 int 的取值范围包含 char 的取值范围,所以 FileReader 下 read 方法返回的 char 类型数据直接转为了 int 类型。
再看 FileInputStream :
FileInputStream fis = new FileInputStream("src.txt");
int b = fis.read();
同理 FileInputStream 也需要一个自己取不到的值来作为流末尾的标志,Java 同样使用 -1 来作为字节流的流末尾,从上面基本数据类型的取值范围我们可以看到 byte 的取值范围为 -128 ~ 127 ,这就意味走着 byte 可以取到 -1 ,如果把 -1 直接当作 int 作为流末尾,那么就无法区分这个读到的结果是流末尾还是流中的数据了,那么 Java 是如何实现取值 -1 的呢?在 Java 内部,Java 通过高位补 0 来实现数据从 byte 到 int 的转换,举个例子:
-1 在 byte 类型和 int 类型中都可以取到,-1 在 byte 类型下的二进制存储形式为 11111111 ,然而使用 read 方法的时候,Java 内部将 byte 的高位补 0 将 byte 转为 int 类型,所以 byte 类型的 -1 在 int 类型下的二进制存储形式为 00000000 00000000 00000000 11111111,对应的 int 值为 255,通过高位补 0 ,所有 byte 类型的负数都转为了正数。然而在使用这些读到的 byte 数据时,只要将这些数据从 int 强转回 byte 即可得到原有的数据。所以就可以使用 -1 来作为流末尾的标志,因为 Java 内部将 byte 的负数通过高位补 0 将其转换为了负数。
3. 字节流的缓冲区原理和使用方式和字符流缓冲区几乎完全相同
4.File
File类是Java IO体系中一个重要的对象主要有以下几个作用
<1>用来将文件或者是文件夹封装成一个对象
<2>很方便的对文件夹进行操作,File类的出现弥补了流的不足,流不能操作文件,流只能操作数据
<3>File对象可以作为参数传递给流对象
<4> 了解File类的常用方法
练习1:使用递归方式得到目录下的所有内容
package com.lee;
import java.io.File;
/**
*
* @李亮亮
*递归方式得到文件夹所有内容
*/
public class FileDemo {
private static void showDir(File file){
//首先打印出文件夹内容
System.out.println(file);
File[] files = file.listFiles();
for(int i=0;i<files.length;i++){
if(files[i].isDirectory()){
//递归
showDir(files[i]);
}
System.out.println(files[i]);
}
}
public static void main(String[] args) {
File file = new File("test");
showDir(file);
}
}
练习2:删除一个带内容的文件夹(从里往外删除,如果文件夹为空,那么直接将文件夹删除,注意文件夹也要删除)
private static void deleteDir(File file){
File[] files = file.listFiles();
for(int i=0;i<files.length;i++){
if(files[i].isDirectory()){
//递归调用
deleteDir(files[i]);
}
System.out.println(files[i]);
files[i].delete();
}
//删除文件夹
System.out.println(file);
file.delete();
}
注意:File类的删除方法是不走回收站的,删除时一定要注意,以免防止误删。
5.Properties
Properties是Hashtable的子类,所以存放方式具有map结构,它里面存储的键值都是String类型,是集合与IO流结合的的集合容器,在配置文件中被广泛使用,所以要熟练掌握这个类的用法。
下面这个练习基本上已经把Properties的各种方法都涉及到了,
练习:编写一个程序,在硬盘上生成配置文件记录程序的运行次数
package com.lee;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;
public class PropertiesDemo {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
Properties props = new Properties();
File file = new File("E:\\run2.txt");
if(!file.exists()){
file.createNewFile();
}
FileInputStream ips = new FileInputStream(file);
props.load(ips);
int count =0;
String value= props.getProperty("time");
if(value!=null){
count = Integer.parseInt(value);
}
count++;
props.setProperty("time", count+"");
FileOutputStream fops = new FileOutputStream(file);
props.store(fops, "");
fops.close();
ips.close();
}
}
学习总结:
1.熟练掌握了有关字节流的各种流对象,包括FileInputStream,FileOutputStream,以及字节流对象的缓冲流对象BufferredInputStream和BufferredOuputStream,
2.虽然字符流和字节流的使用步骤差别不大,但是也要注意这两种流的区别,它们在什么环境中合适使用,特别要注意字符输出流FileWriter和字节输出流FileOutputStream的一点区别,就是FileWriter需要刷新,但是FileOutputStream不需要。
3.除了IO体系的两大分支:字节流和字符流之外,对于Properties和RandAccessFile也有所了解