IO流大本营

第一节 转换流

1.作用:
    a.实现字节流到字符流的转换
    b.解决中文乱码的问题
2.中文编码
    GB2312   (采用两个字节保存字符汉字,英文数字一个字节)
    GBK    (采用两个字节保存字符汉字,英文数字一个字节)
    GB18030   (英文数字都是一个字节,中文是两个或四个字节)
    ANSI    在简体中文Windows操作系统中, ANSI 编码代表 GBK 编码
    Big5    繁体中文
    ISO-8859-1    收录除ASCII外,还包括西欧,希腊语,泰语,阿拉伯语,希伯来语对应的文字符号
3.Unicode字符集(包含每个国家的所有字符)国际通用
    unicode编码  使用两个字节---65536个字符,浪费空间
    为了节省空间使用转码形式
    utf-8     使用 1 、2、3个字节(EF BB BF:记事本添加的BOM(Byte Order Mark)头,编码的标记)
    utf-16    使用两个字节---65536个字符(FF FE 小端(尾) FE FF 大端(尾))
    utf-32    使用4个字节
4.只有转换流才能指定读取和写入的字符集

1.1 InputStreamReader类

InputStreamReader:把外部设备的二进制字节流转换成字符流读取到内存,并指定编码

代码实现:

public class InputStreamReaderDemo {
    public static void main(String[] args) throws IOException {
        //1.实例化File的对象
        //File file = new File("file/input1.txt");
        
        //2.实例化转换输入流的对象
        //注意:当一个流的存在的意义是为了实例化另外一个流,则这个流不需要手动进行关闭
        //InputStream input = new FileInputStream(file);
        //InputStreamReader reader = new InputStreamReader(input);
        //使用默认的字符集【GBK】进行实例化转换流
        //InputStreamReader reader = new InputStreamReader(new FileInputStream(new File("file/input1.txt")));
        //使用指定字符集进行实例化转换流
        //字符集一般使用字符串直接传参,不区分大小写,但是,如果字符集书写有误的话,则会跑出java.io.UnsupportedEncodingException
        InputStreamReader reader = new InputStreamReader(new FileInputStream(new File("file/input1.txt")),"UTF-8");
        
        //3.读取
        char[] arr = new char[16];
        int len = 0;    
        while((len = reader.read(arr)) != -1) {
            String string = new String(arr, 0, len);
            System.out.println(string);
        }
        reader.close();
    }
}

1.2 OutputStreamWriter类

OutputStreamWriter:把内存中的字符使用指定的编码,转换成二进制字节流存储到外部设备

代码实现:

public class OutputStreamWriterDemo {
    public static void main(String[] args) throws IOException {
        //需求:将一段文本以utf-8的格式写入到文件中【注,文件格式为默认格式】
        //1.实例化FIle对象
        //注意:对于所有的输出流而言,文件可以不存在,在进行写入的过程中可以自动进行创建
        //但是,对于所有的输入流而言,文件必须先存在,然后才能操作,否则,会抛FileNotFounedException
        File file = new File("file/output1.txt");
        
        //2.实例化转换输出流
        //如果不想覆盖源文件中的内容时,则在传参的时候,设置一个参数为true
        OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file,true), "utf-8");
​
        //3.写入
        writer.write("客户放假,刚回家");
        //4.刷新
        writer.flush();
        //5.关闭
        writer.close();
    }
}

 

第二节 缓冲流

作用:主要是为了增强基础流的功能,提高流的读写效率,减少访问磁盘的次数
​
注意:如果使用记事本创建的文件,文件是utf-8或者unicode编码,文件的前面有一个BOM(Byte Order Mark)头,BOM头作用是指定文件使用的编码类型。GBK编码没有添加bom头。
utf-8:EF BB BF 
unicode 小端: FF FE     66 00
unicode 大端 :FE FF    00 66

2.1 BufferedInputStream类

public class BufferedInputStreamDemo {
    public static void main(String[] args) throws IOException {
        //1.实例化一个File对象
        File file = new File("file/test22.txt");        
        //2.实例化一个缓冲字节输入流的对象
        BufferedInputStream input = new BufferedInputStream(new FileInputStream(file));     
        //3.读取
        byte[] arr = new byte[1024];
        int len = 0;
        while((len = input.read(arr)) != -1) {
            String string = new String(arr, 0, len);
        }
    }
}

2.2 BufferedOutputStream类

    BufferedOutputStream内部默认的缓存大小是8KB,每次写入时存储到缓存中的byte数组中,当数组存满时,会把数组中的数据写入文件,并且缓存下标归零。
public class BufferedOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        //实例化FIle对象
        File file = new File("test33.txt");     
        //实例化换种字节输出流 
        BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(file)); 
        //写
        output.write("你好hello".getBytes());     
        //刷新
        output.flush(); 
        //关闭
        output.close();
    }
}

2.3 BufferedReader类

public class BufferedReaderDemo {
    public static void main(String[] args) throws IOException {
        //1.实例化FIle对象
        File file = new File("test33.txt");     
        //2.实例化缓冲字符流的对象
        BufferedReader reader = new BufferedReader(new FileReader(file));   
        
        //方式一:read循环读取
        char[] arr = new char[8];
        int len = 0;
        while((len = reader.read(arr)) != -1) {
            String string = new String(arr, 0, len);
        }
        
        //方式二:readLine循环读取
        String result = null;
        while((result = reader.readLine()) != null) {
            System.out.println("第一行:" + result);
        }   
        reader.close();
    }
}

2.4 BufferedWriter类

public class BufferedWriterDemo {
    public static void main(String[] args) throws IOException {
        // 实例化FIle对象
        File file = new File("test33.txt");
        // 实例化缓冲字符输出流
        BufferedWriter writer = new BufferedWriter(new FileWriter(file,true));
        // 写
        writer.write("今天天气还可以");
        // 作用:主要就是为了换行
        writer.newLine();
        // 刷新
        writer.flush();
        // 关闭
        writer.close();
    }
}

 

第三节 内存流

3.1ByteArrayIn(Out)putStream类

输入和输出都是从文件中来的,当然,也可将输出输入的位置设置在内存上,这就需要ByteArrayInputStream和ByteArrayOutputStream


ByteArrayInputStream:将内容写入到内存中,是Inputstream的子类
ByteArrayOutputStream:将内存中数据输出,是OutputStream的子类
注:此时的操作应该以内存为操作点    

注意:内存操作流的操作对象,一定是以内存为主准,不要以硬盘为准。

案例:完成一个字母大小写转换的程序

public class TextDemo02 {
    public static void main(String[] args) throws IOException {
        //定义一个字符串,全部由大写字母组成
        String string = "HELLOWORLD";
        
        //1.内存输入流
        //向内存中输出内容,注意:跟文件读取不一样,不设置文件路径
        ByteArrayInputStream bis  = new ByteArrayInputStream(string.getBytes());
        //2.内存输出流
        //准备从内存中读取内容,注意:跟文件读取不一样,不设置文件路径
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        
        int temp = 0;
        //3.read()方法每次只读取一个字符
        while((temp = bis.read()) != -1) {
            //将读取的数字转为字符
            char c = (char)temp;
            //将字符变为大写
            bos.write(Character.toLowerCase(c));
        }
        //循环结束之后,所有的数据都在ByteArrayOutputStream中
        //取出内容,将缓冲区内容转换为字符串
        String newString = bos.toString();
        
        //4.关闭流
        bis.close();
        bos.close();
        System.out.println(newString);
    }
}
 

第四节 标准输入输出流

4.1PrintStream和PrintWriter

Java的标准输入/输出分别通过System.in和System.out实现,默认情况下分别代表是键盘和显示器

PrintStream类:PrintStream为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。

PrintWriter类:向文本输出流打印对象的格式化表示形式。此类实现在 PrintStream中的所有 print方法。它不包含用于写入原始字节的方法。

代码实现:

public static void main(String[] args)throws Exception {
        //1创建PrintStream
        //PrintStream ps=new PrintStream("d:\\print.txt");
        PrintWriter pw=new PrintWriter("d:\\print.txt");
        //2打印
        pw.println(true);
        pw.println(3.14);
        pw.println(100);
        pw.println("我爱北京");
        //3刷新
        pw.flush();
        //4关闭
        pw.close();
}


public class PrintStreamDemo {
    public static void main(String[] args) throws FileNotFoundException {
        //System.out.println("hello world");
        //创建打印流的对象
        //注意:默认打印到控制台,但是,如果采用setOut方法进行重定向之后,将输出到指定的文件中
        PrintStream print = new PrintStream(new FileOutputStream(new File("test33.txt")));
        static void setErr(PrintStream err) //重新分配“标准”错误输出流。 
        static void setIn(InputStream in) //重新分配“标准”输入流。 
        static void setOut(PrintStream out) //重新分配“标准”输出流。 
        //将标准输出重定向到print的输出流
        System.setOut(print);
        System.out.println("hello world");
    }
}
public class InputStreamDemo {
    public static void main(String[] args) throws FileNotFoundException {
        //重新初始化System.out
        PrintStream ps = new PrintStream("Taylor.txt");
        System.setOut(ps);//输出重定向,输出到Taylor.txt文件中
        for (int i = 0; i < 5; i++) {
            System.out.println("好久不见,Taylor");
        }
        //关流
        ps.close();
    }
}

 

第五节 对象流

5.1ObjectInputStreamh和ObjectOutputStream

1.流中流动的数据是对象
    将一个对象写入到本地文件中,被称为对象的序列化(Serializable)
    将一个本地文件中的对象读取出来,被称为对象的反序列化(Deserializable)
2.ObjectInputStream/ObjectOutputStream:增强了读写8种基本数据类型,字符串,对象的功能
    注意:
    a.序列化对象的类型必须实现Serializable接口。否则不能序列化。
    b.如果向将多个对象序列化到本地,可以借助于集合,【思路:将多个对象添加到集合中,将集合的对象写入到本地文件中,再次读出来,获取到的仍然是集合对象,遍历集合】。    
3.对象中以下字段不能序列化:
    1)transient 修饰的字段
    2)static 静态字段
4.在要序列化类中添加serialVersionUID字段,保证序列化和反序列化是同一个类   
private static final long serialVersionUID = 100L;
5.读取到文件尾部的标志:java.io.EOFException    (End Of File Exception)
public class ObjectStreamDemo {
    public static void main(String[] args) {
        //objectOutputStreamUsage();
        objectInputStreamUsage();
    }
​
    //写:将对象进行序列化
    public static void objectOutputStreamUsage() {
        //1.实例化一个Person的对象
        Person person =  new Person("张三", 10, 'B'); 
        //2.实例化一个对象输出流的对象
        ObjectOutputStream output = null;
        try {
            output = new ObjectOutputStream(new FileOutputStream(new File("file/person.txt")));     
            //3.将对象写入到流中
            output.writeObject(person);         
            //4.刷新
            output.flush();     
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
​
    //读:反序列化
    public static void objectInputStreamUsage() {
        //1.实例化对象输入流的对象
        try {
            ObjectInputStream input = new ObjectInputStream(new FileInputStream(new File("file/person.txt")));      
            //2.读取
            Object object = input.readObject();         
            //3.对象的向下转型
            if(object instanceof Person) {
                Person p = (Person)object;
                System.out.println(p);
            }
        
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}   
注意:在使用对象流的时候,用于初始化对象流的参数只能是字节流(将对象转换为二进制的形式,然后再把二进制写入文件)

补充:

1.合并流 SequenceInputStream

2.字符串流 StringReader / StringWriter

3.管道流 PipedInpurStream / PipedOutputStream

 

第六节 RandomAccessFile类

    RandomAccessFile是用来访问那些保存数据记录的文件的,可以用seek( )方法来访问记录,并进行读写。这些记录的大小不必相同;但是其大小和位置必须是可知的。但是该类仅限于操作文件。

案例一:RandomAccessFile类的应用

public class TextDemo01 {
    public static void main(String[] args) throws Exception {
        RandomAccessFile file = new RandomAccessFile("file.txt", "rw");
        // 以下向file文件中写数据
        file.writeInt(20);// 占4个字节
        file.writeDouble(8.236598);// 占8个字节
        //这个长度写在当前文件指针的前两个字节处,可用readShort()读取
        file.writeUTF("这是一个UTF字符串");
        file.writeBoolean(true);// 占1个字节
        file.writeShort(395);// 占2个字节
        file.writeLong(2325451l);// 占8个字节
        file.writeUTF("又是一个UTF字符串");
        file.writeFloat(35.5f);// 占4个字节
        file.writeChar('a');// 占2个字节
        //把文件指针位置设置到文件起始处
        file.seek(0);
​
        // 以下从file文件中读数据,要注意文件指针的位置
        System.out.println("——————从file文件指定位置读数据——————");
        System.out.println(file.readInt());
        System.out.println(file.readDouble());
        System.out.println(file.readUTF());
        
        //将文件指针跳过3个字节,本例中即跳过了一个boolean值和short值。
        file.skipBytes(3);
        System.out.println(file.readLong());
        
        //跳过文件中“又是一个UTF字符串”所占字节
        //注意readShort()方法会移动文件指针,所以不用写2。
        file.skipBytes(file.readShort()); 
        System.out.println(file.readFloat());
​
        // 以下演示文件复制操作
        System.out.println("——————文件复制(从file到fileCopy)——————");
        file.seek(0);
        RandomAccessFile fileCopy = new RandomAccessFile("fileCopy.txt", "rw");
        int len = (int) file.length();// 取得文件长度(字节数)
        byte[] b = new byte[len];
        //全部读取
        file.readFully(b);
        fileCopy.write(b);
        System.out.println("复制完成!");
    }
}

 

第七节 Properties集合

Properties是Map接口的一个实现类,并且是Hashtable的子类
Properties集合中元素也是以键值对的形式存在的
Properties特点:
1.存储属性名和属性值
2.属性名和属性值都是字符串
3.和流有关系
4.没有泛型
public class PropertiesDemo {
    public static void main(String[] args) {
        //1.实例化一个Properties的对象
        Properties pro = new Properties();
        System.out.println(pro);
        
        //2.把文件userlist.properties中的键值对同步到集合中
        //实质:读取
        /*
        void load(InputStream inStream); //从输入流中读取属性列表(键和元素对)。 
        */
        try {
            pro.load(new BufferedInputStream(new FileInputStream(new File("file/userlist.properties"))));
        } catch (Exception e) {
            e.printStackTrace();
        }   
        System.out.println(pro);
        
        //3.向集合中添加一对键值对
        /*
         *  Object setProperty(String key, String value) 
            调用 Hashtable 的方法 put。 
         */
        pro.setProperty("address", "china");        
        System.out.println(pro);
        
        try {
            //4.store
            //实质:写入
            //comments:工作日志
            pro.store(new BufferedOutputStream(new FileOutputStream(new File("file/userlist.properties"))), "add a pair of key and value");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

第八节 装饰者设计模式

    1.装饰模式指的是在不必改变原类文件和继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是通过装饰类来包裹真实的对象。(真实对象变成了装饰类的成员变量!)
    2.应用场景:需要扩展一个类的功能,或给一个类添加附加职责。
案例:
1.抽象类 ReadFile -->read抽象方法
2.定义一些ReadFile的实现子类
    ReadTextFile 读取文本文件
    ReadMusicFile 读取音乐文件
    ReadVideoFile 读取视频文件
3.要求:提高三个子类的功能:带缓冲    
   3.1继承
     BufferedReadTextFile继承ReadTextFile 重写 read方法
     BufferedReadMusicFile继承ReadMusicFile 重写 read
     BufferedReadVideoFile继承ReadVideoFile 重写 read
     缺点:1.类体系太庞大 2.耦合性太高
   3.2装饰者设计模式 :采用组合的关系
     BufferedReadFile{
         private ReadFile readFile;//被强化的类
         public BufferedReadFile(ReadFile readFile){
           this.readFile=readFile;
         }
         public void read(){
           ///重写方法实现
         }
     }
     优点:耦合性低,提高重用性

代码实现:

/**
 * 抽象人类
 */
public abstract class AbstractPerson {
    public abstract void eat();
}
/**
*子类1
*/
public class Person extends AbstractPerson {
    String name;    
    public void eat() {
        System.out.println(name+"正在吃东西.........");
    }
    
}
/**
 * 子类2
 */
public class Person2 extends AbstractPerson{
    String name;
    @Override
    public void eat() {
        System.out.println(name+"吃......"); 
    }
}
​
import java.io.BufferedInputStream;
/**
 * 增强Person
 * @author wgy
 */
public class StrongPerson extends AbstractPerson {
    //使用person创建一个变量
    AbstractPerson p;
    public StrongPerson(AbstractPerson p) {
        this.p = p;
    }
    
    public void eat() {
        p.eat();
        System.out.println("抽一口");
        System.out.println("眯一会");
        System.out.println("写会java代码"); 
    }
}


//测试类
public class Test {
    public static void main(String[] args) {
        Person benwei=new Person();
        benwei.name="本伟";       
        Person2 zhengshuai=new Person2();
        zhengshuai.name="郑帅";
        
        //装饰者设计
        StrongPerson strongPerson=new StrongPerson(benwei);
        StrongPerson strongPerson2=new StrongPerson(zhengshuai);
        strongPerson.eat();
        strongPerson2.eat();
    }
}

 

面试题

1.BufferedReader属于哪种流,它主要是用来做什么的,它里面有那些经典的方法?
//字符缓冲流,主要用来增强基础流,提高读写效率。read()读取字符数组,readLine()一次读取一行。
2.怎么样把输出字节流转换成输出字符流,说出它的步骤?
//OutputStreamWriter把字节数据指定编码后转换成字符流。
3.流一般需要不需要关闭,如果关闭的话在用什么方法,一般要在那个代码块里面关闭比较好,处理流(过滤流)是怎么关闭的,如果有多个流互相调用传入是怎么关闭的?
//关闭用close()方法,在finally代码块里面关闭比较好。最先创建的流最后关,关闭调用的流时,默认关闭传入的流。
4.什么叫对象序列化,什么是反序列化,实现对象序列化需要做哪些工作?什么时候对象需要被序列化呢?
//将对象写入本地文件中,叫做对象序列化。
//将对象从本地文件中读取出来,叫做反序列化。
//实现序列化,需要对象所对应的类实现Serializable接口
//1.需要把对象保存到文件中存储到物理介质(比如:硬盘)的时候  2.对象需要在网络上进行传输的时候
5.在实现序列化接口的时候一般要生成一个serialVersionUID字段,它叫做什么,一般有什么用
//叫做序列化版本ID,用于鉴别序列化与反序列化是否是同一个类
6.什么是内存流?有什么作用
//以内存为操作点,对数据进行读写操作

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值