美文网首页JavaSE
JavaSE-IO输入输出-装饰设计模式

JavaSE-IO输入输出-装饰设计模式

作者: AIGame孑小白 | 来源:发表于2021-05-12 21:53 被阅读0次

JAVA-IO

早期IO都是对字节流的操作,但是为了方便操作文本后来出现了字符流。

  1. ASCII-英文

  2. GB1312-中文(不够)

  3. GBK-中文(扩容)

  4. Unicode(无论什么字符都用两位字节) -万国码

  5. UTF-8(字节优化)-万国码

为了解决乱码问题,JAVA提供字符流,只需要设置码表即可正常查阅数据。

01 FileWriter

基本写入数据

/**
* FileWriter的父类是Writer
* FileWriter中没有方法,只有构造当需要写入操作时:调用父类的write();
* FileWriter:覆盖原文件,不管有没有都会自动创建
* 调用write()方法会把数据放入缓冲区
* 调用flush()方法会刷新缓冲区,吸入目的地
* 调用close()也会刷新缓冲区,但是关闭了流,继续写入会抛异常
*/

FileWriter fileWriter = new FileWriter("D:\\ok.txt");
fileWriter.write("嘿嘿");
fileWriter.flush();

在文件尾部追加

new FileWriter("path",true);//即可追加在文件末尾

02 FileReader

  • int read();//读取的数据,没有时为-1
FileReader fileReader = new FileReader("D:\\ok.txt");
int data = 0;
while ((data=fileReader.read())!=-1){
    System.out.println((char)data);
}
  • int read(char [] c);//返回读取的个数,没有就是-1
FileReader fileReader = new FileReader("D:\\ok.txt");
char c[] = new char[4];
int len = 0;
while ((len=fileReader.read(c))!=-1){
    System.out.println(len);
    System.out.println(c);
}
//运行结果:
4
abcd
2
efcd

new String(char[],start,lenth);

FileReader fileReader = new FileReader("D:\\ok.txt");
char c[] = new char[4];
int len = 0;
while ((len=fileReader.read(c))!=-1){
    System.out.println(len);
    System.out.println(new String(c,0,len));
}
//运行结果:
4
abcd
2
ef

03 BufferedWriter

缓冲区的出现是为了提高效率

FileWriter fileWriter = new FileWriter("D:\\ok.txt");
//其内部其实就是为FileWriter对象进行了
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
for (int i = 0; i < 10; i++) {
    bufferedWriter.write("abcdef"+i);
    //不同的操作系统不同的换行符
    bufferedWriter.newLine();
}
//其实就是对FileWriter进行关闭操作
bufferedWriter.close();

04 BufferedReader

FileReader fileReader = new FileReader("D:\\ok.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line = "";
/**
*bufferedReader.readLine()
*只返回有效数据内容,并不返回回车等终止符
*其原理就在于:遇到\n就返回字符数组,遇到\r就continue,将字符装入数组
*/
while ((line = bufferedReader.readLine())!=null) {
    System.out.println(line);
}
bufferedReader.close();//其实就是对fileReader进行关闭

05 LineNumberReader

这两个类都相当于是装饰类,和BufferedReader、BufferedWriter一样,只不过增加了设置行数和获取行数

06 LineNumberWriter

在FileReader的基础上,装饰扩展出读取一行数据的功能,并且可以设置初始行号和读取到的当前行号

  • setLineNumber(int l);
  • getLineNumber();

07 装饰设计模式

增强已有的对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰类都属于一个体系

最经典的例子就是BufferedWriter与FileWriter的设计关系:

  • BufferedWriter其实是对FileWriter对象的功能增强,增加了使用数组做缓冲区,一次性写入的高效方法,其原理就是通过构造器传入FileWriter对象,在其功能的基础上进行功能扩展。

  • BufferedWriter的构造器只能传入Writer类型,这里之所以使用多态,是为了提高程序的可扩展性,否则如果每次针对一个类似于FileWriter的类型就要增加一种构造器,这样程序的可扩展性极差。

设计方式:

  • 首先你需要使得各个功能类似的类继承同一个基类
  • 那么,装饰类只需要把构造器对这个基类开放即可

装饰模式的优点:

  • 比继承灵活,避免了继承体系的臃肿
  • 降低类与类之间的关系

08 IO异常处理方式

如果有多个IO操作对象,则需要在finally中进行多次独立的if(obj!=null)的判断操作,这才是最标准的异常捕捉!

FileWriter fileWriter = null;
try {
    fileWriter = new FileWriter("D:\\ok.txt");
    fileWriter.write("嘿嘿");
} catch (IOException e) {
    e.printStackTrace();
}finally {
    try {
        if(fileWriter!=null){
            fileWriter.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

快速自定义抛出异常:

在catch块当中:throw new RuntimeException("自定义异常");

09 InputStream

假如有一个文件:

abcde
f

使用FileInputStream读取,获得available()时,其返回值是8!

因为在windows操作系统中,回车"\r\n"占据两个字节,到字节流后变成两个字节

10 OutputStream

FileInputStream fileInputStream = new FileInputStream("path");
FileOutputStream fileOutputStream = new FileOutputStream("path");
byte b[]  = new byte[1024];
int len = 0;
while ((len = fileInputStream.read(b))!=-1){
    fileOutputStream.write(b,0,len);
}
fileInputStream.close();
fileOutputStream.close();

11 BufferedInputStream

装饰FileInputStream的类,用法和字符流相似

12 键盘输入流

在java.lang包下面System类中in是一个被静态InputStream描述的字节读取流对象,out和err是PrintStream对象。

//读取一个字符
String str =  ""+System.in.read();
System.out.println(str);
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
System.out.println(num);

13 转换流

  • InputStreamReader:把输入字节流转换成字符流操作
  • OutputStreamWriter:把输出字节流转换成字符流操作
//字节流输入
InputStream inputStream = System.in;
//输出的的时候操作字符流对象
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
//使用包装类,调用readLine();
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
//读取一行数据,如果输入exit就退出
String line = "";
while (!(line = bufferedReader.readLine()).equals("exit")){
    System.out.println(line);
}
//关闭
bufferedReader.close();

还有一个好玩的装饰类:

//字节流输入
InputStream inputStream = System.in;
//输出的的时候操作字符流对象
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
//使用包装类,调用readLine();
LineNumberReader lineNumberReader = new LineNumberReader(inputStreamReader);
//读取一行数据,如果输入exit就退出
String line = "";
while (!(line = lineNumberReader.readLine()).equals("exit")){
    System.out.println(lineNumberReader.getLineNumber()+":"+line);
}
//关闭
lineNumberReader.close();

14 File

  • boolean createNewFile();//存在文件返回false
  • boolean delete();
  • int compareTo(File f);//字母顺序比较名称
  • boolean exists();
  • boolean mkdir();//创建目录
  • boolean isDirectory();//是否目录
  • boolean isFile();//是否文件
  • boolean isHidden();//是否隐藏
  • String getPath();
  • String getAbsolutePath();
  • long length();

15 Properties

位于 java.util.Properties ,是Java 语言的配置文件所使用的类, Xxx.properties 为Java 语言常见的配置文件,如数据库的配置 jdbc.properties, 系统参数配置 system.properties。 这里,讲解一下Properties 类的具体使用。继承了Hashtable 类,以Map 的形式进行放置值, put(key,value) get(key),以key=value 的 键值对的形式进行存储值。 key值不能重复。

Properties properties=new Properties();
//用绝对路径
InputStream input=new BufferedInputStream(new FileInputStream("D:\\workspace\\JavaLearn\\src\\jdbc.properties"));
properties.load(new InputStreamReader(input,"utf-8"));
//多添加几个值。
properties.setProperty("name","两个蝴蝶飞");
properties.setProperty("sex","男");

OutputStream output=new FileOutputStream("D:\\jdbc.properties");
OutputStreamWriter out=new OutputStreamWriter(output,"utf-8");
properties.store(out,"填充数据");

16 对象序列化

对象持久化存储:找到可长久保存对象的介质,叫做持久化,想要一个对象被序列化,该类必须实现io下的接口:Serializable

标记接口:没有任何方法的接口,例如:Serializable,其原理就是为对象加个UID值,但是也可以自定义一个UID

ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("a.txt"));
objectOutputStream.writeObject(new People("嘿嘿","男"));
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("a.txt"));
People people = (People) objectInputStream.readObject();
System.out.println(people.getName()+":"+people.getSex());

17 管道流

import java.io.*;

class MyReader implements Runnable {
    public PipedInputStream pipedInputStream;
    public MyReader(PipedInputStream input){
        this.pipedInputStream = input;
    }
    @Override
    public void run() {
        try {
            byte b[] = new byte[1024];
            int len = pipedInputStream.read(b);
            String str = new String(b,0,len);
            System.out.println(str);
            pipedInputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class MyWriter implements Runnable{
    private PipedOutputStream pipedOutputStream;
    public MyWriter(PipedOutputStream out){
        this.pipedOutputStream = out;
    }
    @Override
    public void run() {
        try {
            Thread.sleep(6000);
            pipedOutputStream.write("我爱你".getBytes());
            pipedOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class Out {
    public static void main(String[] args) throws Exception {
        PipedInputStream pipedInputStream = new PipedInputStream();
        PipedOutputStream pipedOutputStream = new PipedOutputStream();
        MyReader myReader = new MyReader(pipedInputStream);
        MyWriter myWriter = new MyWriter(pipedOutputStream);
        pipedInputStream.connect(pipedOutputStream);//管道流关联
        new Thread(myReader).start();
        new Thread(myWriter).start();
    }
}

18 RandomAccessFile

RandomAccessFile既可以读取文件内容,也可以向文件输出数据。同时,RandomAccessFile支持“随机访问”的方式,程序快可以直接跳转到文件的任意地方来读写数据。

  • 可以自由访问文件的任意位置,所以如果需要访问文件的部分内容,而不是把文件从头读到尾,使用RandomAccessFile将是更好的选择。
  • 允许自由定义文件记录指针(OutputStream、Writer等输出流不同),RandomAccessFile可以不从开始的地方开始输出,因此RandomAccessFile可以向已存在的文件后追加内容。如果程序需要向已存在的文件后追加内容,则应该使用RandomAccessFile。
  • 它有一个最大的局限,就是只能读写文件,不能读写其他IO节点。RandomAccessFile的一个重要使用场景就是网络请求中的多线程下载及断点续传。

四种模式:

**"r" : ** 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw": 打开以便读取和写入。
"rws": 打开以便读取和写入。相对于 "rw","rws" 还要求对“文件的内容”或“元数据”的每个更新都同步写入到基础存储设备。
"rwd" : 打开以便读取和写入,相对于 "rw","rwd" 还要求对“文件的内容”的每个更新都同步写入到基础存储设备。

特殊方法:

long getFilePointer( );//返回文件记录指针的当前位置
void seek(long pos);//将文件指针定位到pos位置

利用RandomAccessFile实现文件的多线程下载,即多线程下载一个文件时,将文件分成几块,每块用不同的线程进行下载。当RandomAccessFile向指定文件中插入内容时,将会覆盖掉原有内容。如果不想覆盖掉,则需要将原有内容先读取出来,然后先把插入内容插入后再把原有内容追加到插入内容后。

下面是一个利用多线程在写文件时的例子,其中预先分配文件所需要的空间,然后在所分配的空间中进行分块,然后写入:

/** 
 * 测试利用多线程进行文件的写操作 
 */  
public class Test {  
  
    public static void main(String[] args) throws Exception {  
        // 预分配文件所占的磁盘空间,磁盘中会创建一个指定大小的文件  
        RandomAccessFile raf = new RandomAccessFile("D://abc.txt", "rw");  
        raf.setLength(1024*1024); // 预分配 1M 的文件空间  
        raf.close();  
          
        // 所要写入的文件内容  
        String s1 = "第一个字符串";  
        String s2 = "第二个字符串";  
        String s3 = "第三个字符串";  
        String s4 = "第四个字符串";  
        String s5 = "第五个字符串";  
          
        // 利用多线程同时写入一个文件  
        new FileWriteThread(1024*1,s1.getBytes()).start(); // 从文件的1024字节之后开始写入数据  
        new FileWriteThread(1024*2,s2.getBytes()).start(); // 从文件的2048字节之后开始写入数据  
        new FileWriteThread(1024*3,s3.getBytes()).start(); // 从文件的3072字节之后开始写入数据  
        new FileWriteThread(1024*4,s4.getBytes()).start(); // 从文件的4096字节之后开始写入数据  
        new FileWriteThread(1024*5,s5.getBytes()).start(); // 从文件的5120字节之后开始写入数据  
    }  
      
    // 利用线程在文件的指定位置写入指定数据  
    static class FileWriteThread extends Thread{  
        private int skip;  
        private byte[] content;  
          
        public FileWriteThread(int skip,byte[] content){  
            this.skip = skip;  
            this.content = content;  
        }  
          
        public void run(){  
            RandomAccessFile raf = null;  
            try {  
                raf = new RandomAccessFile("D://abc.txt", "rw");  
                raf.seek(skip);  
                raf.write(content);  
            } catch (FileNotFoundException e) {  
                e.printStackTrace();  
            } catch (IOException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            } finally {  
                try {  
                    raf.close();  
                } catch (Exception e) {  
                }  
            }  
        }  
    }  
  
}  

相关文章

网友评论

    本文标题:JavaSE-IO输入输出-装饰设计模式

    本文链接:https://www.haomeiwen.com/subject/hdszdltx.html