JAVA中的IO流

从内容上分: 字节流(Stream)和字符流(Reader/Writer)

从流向上分: 输入流(Input)和输出流(Output)

从操作上分: 节点流和处理流

IO流

节点流

直接操作文件,构造方法(File file/String filepath);

包括:InputStream、OutputStream、Reader、Writer

字节流:可读写任意文件;

字节输入流:InputStream(in.read(byte[])) 是所有的输入字节流的父类,它是一个抽象类。

字节输出流:OutputStream(out.Write(byte[], 0, length))

字符流:用来处理纯文本文件;

字符输入流:Reader(reader.read(char[]))
字符输出流:Writer(writer.write(char[], 0, length));

注意:

1、字符输出流必须手动刷出缓存(writer.flush())或者关闭流,否则无法写出;
2、所有的输出流都会自动创建文件;

处理流:对节点流进行处理,在原有流的基础上增加功能;

构造方法(节点流):
1、BufferedReader: 
new BufferedReader(Reader);
String msg = BufferedReader.readLing();

BufferedWriter: 
new BufferedWriter(Writer); 
BufferedWriter.write(String msg)
newLine()(换行);

2、
PrintWriter:能够处理字节流和字符流,而且可以自动刷出缓存;
new PrintWriter(Writer/OutputStream, true{autoflush});
PrintWriter.println(String msg);自动换行写

桥梁流、转换流(处理流):字节流转换成字符流,可以指定编码集,可以解决中文乱码;

1、对字节流进行处理:字节流---->字符流

InputStreamReader:
new InputStreamReader(InputStraem, charsetName);

OutputStreamWriter:
new OutputStreamWriter(OutputStream, charsetName);

2、对字符流进行处理:对字符流加Buffer(两个处理流相套)

BufferedReader br = new BufferedReader(InputStreamReader);

PrintWriter out = new PrintWriter(OutputStreamWriter);

对象处理流:二进制<——->对象

ObjectInputStream对象输入流:二进制------->对象
new ObjectInputStream(new FileInputStream(new File("filepath")));
ObjectInputStream.readObject():Object

ObjectOutputStream对象输出流:对象------->二进制
new ObjectOutputStream(new FileOutputStream(new File("filepath")));
ObjectOutputStream.write(Object obj);

注意:实现序列化对象的类必须实现Serizliable接口

【案例 】读取文件内容

public static void main(String[] args){
    File file = new File("test.txt");
    try {
        fileHandle(file);
        copyFile(file);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public void fileHandle(File file) throws IOException{
    file.createNewFile(); //创建文件
    if(file.exists()){  //判断文件是否存在
        file.mkdir(); //创建文件夹
        String fileName = file.getName(); //获取文件名
        long fileSize = file.length(); //获取文件大小
        String filePath = file.getPath(); //获取文件路径
        System.out.println("文件名:" + fileName + "\t" + "文件大小:" + fileSize + "文件路径:" + filePath);

        File f = new File("e://xxx//yyy");  
        System.out.println(f.mkdirs());//生成所有目录  
        //f.mkdir();  必须xxx目录存在才能生成yyy目录  
    } else {
        System.out.println("文件不存在!");
    }
    //file.delete(); //删除文件
}

public static void copyFile(File file) throws IOException{

    System.out.println("----------字节流复制-------------");
    File streamFile = new File("streamTest.txt");
    FileInputStream sin = new FileInputStream(file);
    FileOutputStream sout = new FileOutputStream(streamFile);

    byte[] sbuffer = new byte[10];
    int slen = 0;
    while((slen = sin.read()) != -1){
        sout.write(buffer, 0, slen);
        for(byte b : sbuffer){
            System.out.println((char)b)
        }
    }
    System.out.println("复制成功!");
    if(sout != null) sout.close();
    if(sin != null) sin.close();



    System.out.println("----------字符流复制-------------");
    File charFile = new File("charTest.txt");
    FileReader rin = new FileReader(file);
    FileWriter wout = new FileWriter(charFile);

    char[] cbuffer = new char[10];
    int clen = 0;
    while((clen = rin.read(cbuffer)) != -1){
        wout.write(cbuffer, 0, clen);
    }
    System.out.println("复制成功!");
    rin.close(); //必须得要关闭流,不然内容消失,字符输出流在关闭的时候会自动刷出缓存
    wout.close(); //流的关闭顺序:先开后关

    System.out.println("----------处理流复制Buffer-------------");
    //BufferedReader 由Reader类扩展而来,提供通用的缓冲方式文本读取,
    //而且提供了很实用的readLine,读取一个文本行,从字符输入流中读取文本,
    //缓冲各个字符,从而提供字符、数组和行的高效读取。

    File bufferFile = new File("bufferTest.txt");
    BufferedReader bin = new BufferedReader(new FileReader(file)); //装饰器模式
    BufferedWriter bout = new BufferedWriter(new FileWriter(bufferFile));

    String bmsg = null; 
    while((bmsg = bin.readLine()) != null){
        bout.write(bmsg);
        bout.newLine();
        bout.flush();
    }
    System.out.println("复制成功!");


    System.out.println("----------处理流复制PrintWriter-------------");
    //字节流字符流通吃,而且还能自动刷出缓存
    File printFile = new File("printTest.txt");
    BufferedReader pin = new BufferedReader(new FileReader(file)); //装饰器模式

    //第一个true为:文件追加,在文件原来的基础上进行追加
    //第二个true为:自动刷新缓存
    PrintWriter pout = new PrintWriter(new FileWriter(printFile, true), true);

    String pmsg = null; 
    while((pmsg = bin.readLine()) != null){
        pout.println(pmsg);
    }
    System.out.println("复制成功!");


    System.out.println("----------桥梁流复制-------------");

    InputStreamReader streamReader = 
        new InputStreamReader(new FileInputStream(file), "GBK"); 

    OutputStreamWriter streamWriter = 
        new OutputStreamWriter(
            new FileOutputStream(new File("streamCharTest.txt")), "GBK");

    BufferedReader br = new BufferedReader(streamReader);
    PrintWriter p_out = new PrintWriter(streamWriter);

    String p_msg = null;
    while((p_msg = br.readLine()) != null){
        p_out.println(p_msg);
    }
    System.out.println("复制成功!");
}

字符集(密码本):一个字符对应一个整数;

支持中文的字符集:
    GB2312 ----> GBK(一个汉字占两个字节,一个英文两个字节)

    Unicode:字符集(一个中文两个字节)

    UTF-8是Unicode字符集编码形式:一个中文3个字节,一个英文还是1字节;

不支持中文:ASCII------->ISO-8859-1

编码:

String -----------字符集------------->byte[]: String.getBytes(charsetName);;
"abc" ------>97 98 99

解码:

前提是编码时需要编码对应的字符集
byte[] ------- 字符集------->String: new String(byte[],,charsetName);

乱码:

编码解码不一致,如果编码格式不支持中文,该乱码无法解决;

思路:反编译:用乱码找到byte,在进行GBK解码;

UTF-8编码,gbk解码,该种乱码可解决;GBK编码,UTF-8解码,该种不可逆;
UTF-8/GBK编码,ISO-8859-1解码,该种可逆

【案例 】文件编码解码

public static void main(String[] args){
    String msg = "这是需要转换的内容";
    //编码:String.getBytes():用系统默认的编码集去编码,windows:GBK
    byte[] bytes1 = msg.getBytes();
    //解码
    String newMsg1 = new String(bytes1, "ISO-8859-1");

    byte[] bytes2 = bytes.getBytes("ISO-8859-1");
    String newMsg2 = new String(bytes2, "GBK");
    System.out.println(newMsg2);
}

对象流:

对象序列化:把对象变成二进制
    1、数据存储到文件中   
    2、通过网络进行发送;

反序列化:把二进制变成对象:
    1、把文件中二进制数据读成对象;
    2、通过网络接收的二进制数据读成对象;

Serializable:可序列化接口,要实现序列化该类必须实现Serializable

【案例 】对象流

//创建要写入磁盘的类,这个类需要实现接口 Serializable(可序列化的)
class Student implements Serializable{
    String name;
    int age;
    transient String sex; //transient修饰属性,表示暂时的,则这个属性不会被写入磁盘

    public Student(String name, String sex, int age){
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
}

public class ObjectDemo{
    public static void main(String[] args) 
        throws FileNotFoundException, IOException, ClassNotFoundException {
        createObj();
        readObj();
    }

    //写入对象
    public static void createObj() throws IOException{
        //1.创建目标路径
        File file =  new File("objTest.txt");
        //2.创建流通道
        FileOutputStream out = new FileOutputStream(file);
        //3.创建对象输出流
        ObjectOutputStream objOut = new ObjectOutputStream(out);
        //4.创建类对象,并初始化
        Student stu = new Student("鸣大人", "男", 30);
        //5.向目标路径文件写入对象
        objOut.writeObject(stu);  //把对象写成二进制
        //6.关闭资源
        objOut.close();
    }

    //读取对象
    public static void readObj() throws IOException, ClassNotFoundException {
        File file = new File("objTest.txt");
        FileInputStream in = new FileInputStream(file);
        ObjectInputStream objIn = new ObjectInputStream(in);
        //读取对象数据,需要将对象流强制转换为 要写入对象的类型
        Student stu = (Student)objIn.readObject();  //把文件中的二进制数据读成对象
        System.out.println("\n name:"+stu.name+"\n sex:"+stu.sex+"\n age:"+stu.age);
        objIn.close();  
    }
}

注意:

1.如果想将一个对象写入到磁盘中,那么对象所属的类必须要进行序列化,实现Serializable 接口,Serializable接口没有任何方法 ,是一个标记接口

2.如果对象所属的类的成员变量发生改变,你在读取原来的对象是就会报错,如果想要解决报错,保证serialVersionUID是唯一。

3.如果你不想将某些信息存入到磁盘 就可以同过transient关键字修饰成员变量

4.如果一个类中引用了另外的一个类,那么另外的这个类也要实现Serializable接口。

5. writeObject(Object o);  //向磁盘写入对象
   readObject();          //读取磁盘的对象,注意这里需要强制类型

【案例 】文件追加的三种方式

public static void main(String[] args) throws IOException{
        File file = new File("text.txt");

        if(file.createNewFile()){
            System.out.println("创建成功!");
        }

        method1(file, "123");
        method2(file, "456");
        method3(file, "789");
    }

//处理流
public static void method1(File file, String msg) 
    throws IOException{
    BufferedWriter out = 
        new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
    out.write(msg);
    if(out != null) out.close();
}

//追加文件:使用FileWriter   
public static void method2(File file, String msg) throws IOException{
     // 打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件
    FileWriter out = new FileWriter(file, true);
    out.write(msg);
}

//追加文件:使用RandomAccessFile   
public static void method3(File file, String msg) throws IOException{
     // 打开一个随机访问文件流,按读写方式  
    RandomAccessFile out = new RandomAccessFile(file, "rw");
    long fileLength = out.length();
    // 将写文件指针移到文件尾。  
    out.seek(fileLength);
    // 把msg追加到文件末尾
    out.writeBytes(msg);
}

Java RandomAccessFile用法

RandomAccessFile的优点:

1、支持任意访问的方式,程序可以直接跳转到任意地方读写数据。
2、如果只想访问文件的部分内容,而不是把文件从头读到尾,使用RandomAccessFile
    将会带来更简洁的代码以及更好的性能。

RandomAccessFile类中比较重要的2个方法,其他的和普通IO类似.

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

RandomAccessFile中的第二个参数mode用于打开文件的访问模式,可以写为:

"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。  
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。  
"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的
      每个更新都同步写入到底层存储设备。  
"rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更
      新都同步写入到底层存储设备。  

【案例 】RandomAccessFile操作方法

public static void main(String[] args) throws IOException {
    File file = new File("test.txt");
    if(file.createNewFile()){
        System.out.println("创建成功!");
    }

    addition(file, "123");

    Scanner s = new Scanner(System.in);
    readRandom(file, s.nextInt());

    insert(file, s.next());
}
private static void addition(File file, String string) throws IOException {
    // TODO Auto-generated method stub
    RandomAccessFile rand = new RandomAccessFile(file, "rw");

    //将指针移到文件尾
    long length = rand.length();
    rand.seek(length);
    rand.writeBytes(string);
}

public static void readRandom(File file, int pos) throws IOException{
    RandomAccessFile rand = new RandomAccessFile(file, "r");
    System.out.println("getFilePointer():" +  rand.getFilePointer());
    rand.seek(pos);

    byte[] bytes = new byte[10];
    int index = 0;
    String msg = null;
    while((index = rand.read(bytes)) > 0){
        msg = new String(bytes, 0, index);
        System.out.println(msg);
    }
}

额外功能:

    还可以实现一个多线程断点下载的功能,下载前都会建立两个临时文件:
        1、一个是与被下载文件大小相同的空文件
        2、一个是记录文件指针的位置文件
    每次暂停的时候,都会保存上一次的指针,然后断点下载的时候,
    会继续从上一次的地方下载,从而实现断点下载或上传的功能
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值