黑马程序员——I/O流(二)

——- android培训java培训、期待与您交流! ———-

File对象与流对象区别

File:将 文件 或者 文件夹 封装成对象。方便对文件与文件夹的属性信息进行操作

流也能操作文件,那么File对象和流对象有什么不同呢?

流对象:可以对文件进行读写,可以对文件进行复制。(流只能操作数据)
File对象:可以操作文件夹,可以操作文件的属性信息。如:判断文件夹是否存在,判断文件是否存在以及获取文件名称等。

File类常见方法:
1,创建
boolean createNewFile(); 在指定位置创建文件,如果文件已经存在,则不创建,返回false。
和输出流不一样,输出流对象一建立就创建一个文件,而且文件已经存在,会覆盖。
boolean mkdir(); 创建文件夹
boolean mkdirs(); 创建多级文件夹
2,删除
boolean delete(); 删除失败返回false
void deleteOnExit(); 在程序退出时,删除指定文件

3,判断。
boolean exists(); 判断文件对象是否是文件或者目录(重点,此方法使用频率很高)
boolean isFile(); 判断是否是一个文件,exists判断是否存在,isFile判断是否是文件
boolean isDirectory(); 判断是否是一个目录
boolean isHidden(); 判断一个文件是否是隐藏的
boolean isAbsolute(); 判断路径名是否为绝对路径,不管文件是否存在,只要是绝对路径,返回true

4,获取信息。
getName(); 获取名称 如:new File(“demo.txt”) 名称为demo.txt , File(C:\abc\demo.txt) 名称为demo.txt
getPath(); 获取路径 如:new File(“demo.txt”) 路劲为demo.txt , File(C:\abc\demo.txt) 路径为C:\abc\demo.txt
getAbsolutePath(); 获取绝对路径 如:new File(“demo.txt”) (如果demo.txt存在于C:\abc目录下)绝对路径为C:\abc\demo.txt
getParent; 获取绝对路径中的父目录 如:new File(“demo.txt”) 父路径为null ,File(abc\demo.txt) 父路径为abc

long lastModified(); 返回最后一次被修改的时间
long length(); 返回文件的长度

String[] list() 返回所有的文件名称
File[] listFiles() 返回所有的文件对象。(开发中使用listFiles比List好)
static File[] listRoots() 返回文件的根目录。如:C、D、E盘
5
boolean renameTo(File dest); 重新命名此抽象路径名表示的文件

代码示例:

import java.io.*;
public class Demo {
    public static void main(String[] args) throws IOException {
        function6();
    }
    public static void function6() throws IOException{
        File f1 = new File("c:\\file.txt");
        File f2 = new File("c:\\haha.txt");
        f1.createNewFile();
        f1.renameTo(f2);    //将名字为file.txt的文件名改为haha.txt
    }
    public static void function5() throws IOException{
        File f = new File("file.txt");

        print("path:"+f.getPath());//file.txt   获取路径
        print("abspath:"+f.getAbsolutePath());//D:\java\javaCode\Demo\file.txt  获取绝对路径
        print("parent:"+f.getParent());//null   获取绝对路径中的父目录,由于获取的是相对路径,所以返回null.
                                    //如果该目录中有上一层目录,那么该目录就是返回结果

        File f1 = new File("c:\\abc\\aaa\\file.txt");
        print("parent:"+f1.getParent());//c:\abc\aaa

    }
    public static void function4() throws IOException{
        File f = new File("file.txt");

        //记住:在判断文件对象是否是文件或者目录时,必须要先判断该文件对象封装的内容是否存在,通过exists判断。
        print(f.isDirectory());//false  判断f是否是一个目录,因为不存在目录,所以false
        print(f.isFile());//false   判断f是否是一个文件,因为不存在file.txt文件,所以false

        f.createNewFile();//创建一个文件对象
        print(f.isDirectory());//false  判断f是否是一个目录,因为不存在目录,所以false
        print(f.isFile());//true    因为创建了一个文件file.txt,所以true

        File f1 = new File("abc.txt");
        f1.mkdir();//创建一个abc.txt目录
        print(f1.isDirectory());//true  存在abc.txt目录
        print(f1.isFile());//false  因为不存在abc.txt文件,所以false

        File f2 = new File("abc.txt");
        print(f2.isAbsolute());//false  判断该文件是否是绝对路径    不管文件是否存在,只要是绝对路径,那就ture
        File f3 = new File("c:\\abc.txt");
        print(f3.isAbsolute());//true   

    }
    public static void function3() throws IOException{
        File dir1 = new File("abc");
        print(dir1.mkdir());//true  该方法创建一个名字为abc的目录,如果目录已经存在,返回false

        File dir2 = new File("kkk\\aaa");
        print(dir2.mkdir());//false 但是呢,该方法不可以创建多级目录

        File dir3 = new File("aaa\\bbb\\ccc");
        print(dir3.mkdirs());//true     此方法可以创建多级文件夹

    }
    public static void function2()throws IOException{
        File f = new File("file.txt");//创建一个文件对象,但是还并没有文件存在

        //f.createNewFile();

        print("exists:"+f.exists());//exists:false  因为文件不存在,所以false,如果将上面一行代码取消注释,
                        //那么返回true,因为创建了一个file.txt文件

    }
    public static void function1() throws IOException{
        File f = new File("file.txt");//创建一个文件对象,但是还并没有文件存在

        print(f.createNewFile());//在指定位置创建文件,如果文件已经存在,则不创建,返回false

//      print(f.delete());//true    因为上面创建一个文件,删除成功,所以返回true
//      print(f.delete());//false   因为上面已经将文件删除过了,再删除时返回false

        f.deleteOnExit();//在程序退出时  删除文件
    }
    public static void print(Object obj){
        System.out.println(obj);
    }
}
/*
    File中的List方法演示
*/

import java.io.*;
public class Demo {
    public static void main(String[] args) throws IOException {
        listFilesDemo();
    }

    //此方法中的listFiles函数是重点。
    //返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件
    public static void listFilesDemo(){
        File dir = new File("c:\\");
        File[] files = dir.listFiles();//返回c盘下的所有文件
        for(File f : files)
            print(f.getName()+"..."+f.length());
    }

    public static void ListDemo_2(){
        File f = new File("c:\\");
        //返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录 
        String[] names = f.list(new FilenameFilter() {//此方法的参数是一个接口,可以传递匿名内部类
            public boolean accept(File dir, String name) {
//              if(name.endsWith(".png"))
//                  return true;
//              else
//                  return false;
                return name.endsWith(".png");//返回后缀名为.png的文件名称
            }
        });
        for(String name : names){
            print(name);
        }
    }

    public static void listDemo(){
        File f = new File("c:\\");

        //返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。返回c盘中的所有目录
        String[] names = f.list();//调用List方法的File对象必须是封装了一个目录。如果是一个文件,会发生NullPointerException

        for(String s :names){
            print(s);
        }
    }

    public static void listRootsDemo(){
        //列出可用的文件系统根。
        File[] file = File.listRoots();

        for(File f : file)
            print(f);
    }


    public static void print(Object obj){
        System.out.println(obj);
    }
}

递归的使用


/*
 * 需求:列出指定目录下文件或者文件夹,包含子目录中的内容。
 * 也就是说列出制定目录下所有内容。
 * 
 * 因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可。
 * 在列出过程中出现的还是目录的话,还可以再次调用本功能。
 * 也就是函数自身调用自身。
 * 这种表现形式,或者编程手法,称为递归。
 * 
 * 递归要注意:
 * 1,限定条件
 * 
 * 2,要注意递归的次数,尽量避免内存溢出。
 */
import java.io.*;

public class Demo {
    public static void main(String[] args) throws IOException {
         showDir(new File("D:\\java\\java基础视频"));
        //toBin(6);
        //print(getSum(5));
    }

    // 列出制定目录下文件或者文件夹,包含子目录中的内容。也就是说列出制定目录下所有内容。
    public static void showDir(File f) {
        print(f);
        File[] files = f.listFiles();

        for (int i = 0; i < files.length; i++) {
            if (files[i].isDirectory()) {
                showDir(files[i]);
            } else {
                print(files[i]);
            }
        }
    }

    // 求某个数的二进制
    public static void toBin(int i) {
        if (i > 0) {
            toBin(i / 2);
            System.out.print(i % 2);
        }
    }

    // 获取1+到某个数的和。如输入5:1+2+3+4+5=15
    public static int getSum(int num) {
        if(num==1)
            return 1;
        else
            return num+getSum(num-1);
    }

    public static void print(Object obj) {
        System.out.println(obj);
    }
}
/*
 * 删除原理:
 *      在window中,删除目录是从里面往外面删除的。
 *      
 *      既然是从里面往外面删除。就需要用到递归
 */
import java.io.*;

public class Demo {
    public static void main(String[] args) throws IOException {
        removeDir(new File("c:\\复制haha"));
    }

    public static void removeDir(File dir){
        System.out.println("...."+dir);
        File[] files = dir.listFiles();

        for(int i=0;i<files.length;i++){
            if(files[i].isDirectory()){//如果此文件是一个文件夹,则再调用一次此方法,进入到该文件夹中
                removeDir(files[i]);
            }
            else{//如果不是一个文件夹,则删除文件
                System.out.println(files[i]+"....."+files[i].delete());
            }
        }
        dir.delete();//当文件删除完后,就删除掉文件夹
    }

    public static void print(Object obj) {
        System.out.println(obj);
    }
}
/*
练习:
    将一个指定目录下的java文件的绝对路径,存储到一个文本文件中。
    建立一个java文件列表文件。

思路:
    1,对指定的目录进行递归
    2,获取递归过程所指定的java文件路径
    3,将这些路径存储到集合中。
    4,将集合中的数据写入到一个文件中

 */
import java.util.*;
import java.io.*;

public class Demo {
    public static void main(String[] args) throws IOException {
        File dir = new File("D:\\java\\javaCode\\Demo");

        List<File> list = new ArrayList<File>();

        fileToList(dir, list);

        File file = new File(dir,"demo.txt");
        writeToFile(list, file.toString());
    }

    public static void fileToList(File dir, List<File> list) {
        File[] files = dir.listFiles();

        for (File file : files) {
            if (file.isDirectory())
                fileToList(file, list);
            else {
                if (file.getName().endsWith(".java")) {
                    list.add(file);
                }
            }
        }
    }

    public static void writeToFile(List<File> list, String javaListFile)
            throws IOException {

        BufferedWriter bufw = null;
        try {
            bufw = new BufferedWriter(new FileWriter(javaListFile));

            for (File f : list) {
                String path = f.getAbsolutePath();
                bufw.write(path);
                bufw.newLine();
                bufw.flush();
            }
        } catch (IOException e) {
            throw e;
        } finally {
            if (bufw != null)
                try {
                    bufw.close();
                } catch (IOException e) {
                    throw e;
                }
        }
    }
}

与I/O流相结合的集合Properties

Properties是HashTable的子类。但是它不需要加泛型
也就是说它具备map集合的特点。而且它里面存储的键值对都是字符串。

是集合中和IO技术相结合的集合容器

该对象的特点:可以用于键值对形式的配置文件

那么在加载数据时,需要数据有固定格式。键=值

Properties的常用方法

Set stringPropertyNames() 将集合中的键变成Set集合

void list(PrintWriter out) 将集合中的元素输出到流中

setProperty(String key, String value) 设置集合中的键和值,其实是调用 Hashtable 的方法 put

String getProperty(String key) 通过键获取集合中的值

void load(InputStream inStream) 从流中读取数据,存入Properties集合中

void load(Reader reader) 从流中读取数据,存入Properties集合中

void store(OutputStream out, String comments) 将集合中的数据写入到流中

void store(Writer writer, String comments) 将集合中的数据写入到流中

代码示例:

import java.util.*;
import java.io.*;

public class Demo {
    public static void main(String[] args) throws IOException {
        method_2();
    }

    public static void method_2() throws IOException{
        FileInputStream fis = new FileInputStream("info.txt");
        Properties prop = new Properties();

        //将流中的数据加载进集合
        prop.load(fis);//相当于读取了info.txt文件中的数据,然后再存到Properties集合中,
                    //此时Properties集合已经具备了info.txt中的内容,此方法需要接收一个输入流

        prop.setProperty("wangwu", "90");//将Properties中的内容修改,但是这样修改只能修改Properties集合中的内容,
                            //info.txt文件中的内容并没有被修改

        FileOutputStream fos = new FileOutputStream("info.txt");//创建一个输出流,将需要的数据输出到info.txt文件中


        //此 Properties 表中的属性列表(键和元素对)写入输出流。
        prop.store(fos, "haha");//将修改后的Properties内容保存到info.txt文件中。此方法接收一个输出流和一个字符串


        System.out.println(prop);
        //prop.list(System.out);//此方法也可以将properties集合中的数据打印出来

        fis.close();
        fos.close();
    }

    /*
     * 演示如何将文件中的数据存储到集合中
     * 想要将info.txt中键值数据存储到集合中进行操作。
     * 
     * 思路:
     *      1,用一个流和info.txt文件关联
     *      2,读取一行数据,将该行数据用“=”进行分割
     *      3,等号左边为键,右边为值。存入到Properties集合中即可
     * 
     * 前提是必须有一个info.txt文件,否发会找不到指定的文件
     */
    public static void method_1() throws IOException{
        BufferedReader bufr = new BufferedReader(new FileReader("info.txt"));

        String line = null;
        Properties prop = new Properties();

        while((line=bufr.readLine())!=null){
            String[] arr = line.split("=");
            prop.setProperty(arr[0], arr[1]);
        }
        bufr.close();

        System.out.println(prop);//{zhangsan=30, lisi=50, wangwu=40}
    }

    // 设置和获取元素
    public static void setAndGet() {

        Properties prop = new Properties();

        prop.setProperty("zhangsan", "30");// 设置键值对
        prop.setProperty("lisi", "25");// 设置键值对

//      System.out.println(prop);//{zhangsan=30, lisi=25}

        Set<String> names = prop.stringPropertyNames();// 将Properties集合转换成Set集合,集合中存在的都是键
        for (String s : names) {
            System.out.println(s + "...." + prop.getProperty(s));//输出集合中的键和值,getProperty(s)是获取值
            /*
             结果:
                zhangsan....30
                lisi....25
             */
        }
    }
}

Properties配置文件

用于记录应用程序运行次数
如果使用次数已到,那么给出注册提示。

很容易想到的是:计数器。
可是计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增。
可是随着应用程序的退出,该计数器在内存中消失了。

下一次再启动该程序时,又重新开始从0计数。
这样不是我们想要的。

程序即使结束,该计数器的值也存在。
下次程序再启动,会先加载该计数器的值,并加1后重新存储起来。

所以要建立一个配置文件。用于记录该软件的使用次数。

该配置文件使用键值对的形式。
这样便于阅读数据,并操作数据。

键值对数据是map集合。
数据是以文件形式存储,使用io技术。
那么map+io –> Properties。

配置文件可以实现应用程序数据的共享

代码示例:

import java.util.*;
import java.io.*;

public class Demo {
    public static void main(String[] args) throws IOException {
        Properties prop = new Properties();

        File file = new File("count.ini");//以后开发时,最好将文件封装成一个文件对象,这样可以对文件进行判断
        if(!file.exists())//如果文件不存在,则创建一个文件
            file.createNewFile();
        FileInputStream fis = new FileInputStream(file);//读取文件中的内容

        prop.load(fis);

        int count = 0;

        String value = prop.getProperty("time");

        if(value!=null){
            count = Integer.parseInt(value);
            if(count>=5){
                System.out.println("使用次数已到,交钱");
                return;
            }

        }

        count++;

        prop.setProperty("time", count+"");//将读取到的次数写入Properties 中

        FileOutputStream fos = new FileOutputStream(file);

        prop.store(fos, "");//再将Properties中的数据写入文件中

        fos.close();
        fis.close();

    }
}
/*
    dom4j工具,也是用来配置文件信息的。它比Properties配置更方便。这个工具以后会学到
*/

打印流

该流提供了打印方法,可以将各种数据类型的数据都原样打印。

字节打印流:
printStream
构造函数可以接收的参数类型:
1,file对象。File
2,字符串路径。String
3,字节输出流。OutputStream

字符打印流
printWriter
构造函数可以接收的参数类型:
1,file对象。File
2,字符串路径。String
3,字节输出流。OutputStream
4,字符输出流。Writer
注意:字节打印流既可以接收字节流,也可以接收字符流

System.in 默认设输入备:键盘输入
System.out 默认输出设备:控制台

代码示例:

import java.util.*;
import java.io.*;

public class Demo {
    public static void main(String[] args) throws IOException {

        BufferedReader bufr = new BufferedReader(new InputStreamReader(
                System.in));

//      PrintWriter out = new PrintWriter(System.out,true);//将打印字节流打印到控制台上,并且自动刷新
        PrintWriter out = new PrintWriter(new FileWriter("demo.txt"),true);//将打印字节流打印到控制台上,并且自动刷新;true为自动刷新,
                                        //注意,只要操作流对象了才具备自动刷新
                                        //如果new PrintWriter("demo.txt",true);是错误的。必须操作流才有刷新动作

        String line = null;

        while((line=bufr.readLine())!=null){
            if(line.equals("over"))
                break;
            out.println(line.toUpperCase());//用字符打印流的输出方法println
//          out.flush();//因为设置了自动刷新,所以此处可以不用设置
        }
        out.close();
        bufr.close();
    }
}

合并流

/*
 * 将多个流合并成一个流,然后再存到一个文件中
 */
import java.util.*;
import java.io.*;
public class Demo {
    public static void main(String[] args) throws IOException {
        Vector<FileInputStream> v = new Vector<FileInputStream>();

        v.add(new FileInputStream("c:\\1.txt"));
        v.add(new FileInputStream("c:\\2.txt"));
        v.add(new FileInputStream("c:\\3.txt"));

        Enumeration<FileInputStream> en = v.elements();

        SequenceInputStream sis = new SequenceInputStream(en);//SequenceInputStream构造方法接收的参数是Enumeration

        FileOutputStream fos = new FileOutputStream("c:\\4.txt");

        byte[] byt = new byte[1024];
        int len = 0;
        while((len=sis.read(byt))!=-1){
            fos.write(byt, 0, len);
        }

        sis.close();
        fos.close();
    }
}
/*
    切割文件,然后合并
 */
import java.util.*;
import java.io.*;

public class Demo {
    public static void main(String[] args) throws Exception {
        splitFile();
        heBing();
    }

    //将已经分解的碎片文件合并成一个文件
    public static void heBing() throws Exception {
        Vector<FileInputStream> v = new Vector<FileInputStream>();

        for (int i = 1; i <= 3; i++) {
            v.add(new FileInputStream("D:\\spliteFile\\" + i + ".suipian"));
        }

        Enumeration<FileInputStream> en = v.elements();

        SequenceInputStream sis = new SequenceInputStream(en);
        FileOutputStream fos = new FileOutputStream("D:\\spliteFile\\2.png");

        byte[] buf = new byte[1024];
        int len = 0;
        while((len=sis.read(buf))!=-1){
            fos.write(buf,0,len);
            fos.flush();
        }
        sis.close();
        fos.close();
    }

    //将图片分解成碎片
    public static void splitFile() throws Exception {
        FileInputStream fis = new FileInputStream("D:\\1.png");// 将流和一个图片相关联

        File dir = new File("D:\\spliteFile");
        if (!dir.exists()) {
            dir.mkdir();// 如果目录不存在,则创建一个目录
        }

        byte[] buf = new byte[1024 * 100];// 定义字节数组为100kb
        int len = 0;
        int count = 1;

        while ((len = fis.read(buf)) != -1) {
            FileOutputStream fos = new FileOutputStream("D:\\spliteFile\\"
                    + (count++) + ".suipian");// 将图片切割成碎片文件

            fos.write(buf, 0, len);

            fos.close();
        }
    }
}

操作对象的流ObjectOutputStream

可以将对象存放到硬盘上,
在需要使用到该对象时,可以直接从银盘上读取

代码示例:

/*
 * 操作 对象 的流ObjectOutputStream和ObjectInputStream 
 *
 * 可以将堆内存中的对象存放到硬盘上
 *
 * 这两个对象需要成对使用
 */

import java.util.*;
import java.io.*;

class Person implements Serializable {// person类得实现Serializable接口,才可以被ObjectOutputStream序列化,
    // 但是Serializable中什么方法都没有,所以Serializable接口相当于是一个标记,具备此标记就可以被序列化

     static final long serialVersionUID = 42L;
    // //加入此行代码后,序列化的坐标都相同,所以改动此类不会导致读取失败。
    // 如果不加入此行代码,当改变此对象时,会导致此对象的坐标不同,当读取到不同坐标的对象时会发生错误

    public String name;
    private int age;
    static String guoji = "cn";// 定义为静态的,不能被序列化

    public Person(String name, int age, String guoji) {
        this.name = name;
        this.age = age;
        this.guoji = guoji;
    }

    public String toString() {
        return name + "..." + age + "..." + guoji;
    }
}

public class Demo {
    public static void main(String[] args) throws Exception {
//       writeObj();
        readObj();
    }

    public static void readObj() throws Exception {// 将对象从文件中读取出来,当写入文件后,将原有的文件代码改动后再读取,
        // 会发生异常。除非在被读取的文件中加入static final long serialVersionUID = 42L。或者再重新
        // 写入一次文件,以保证读取和写入的文件一样

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("demo.txt"));

        Person p = (Person) ois.readObject();

        System.out.println(p);// 结果:zhangsan...30...cn。发现结果的国籍并没有被序例化,那是因为国籍定义为静态的,
        // 导致写入文件时没有将国籍改动,所以输出的国籍还是原来的国籍

        ois.close();

    }

    public static void writeObj() throws IOException {// 将对象写入一个文件中

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("demo.txt"));

        oos.writeObject(new Person("zhangsan", 30, "haha"));// 将序列化的对象存入到一个文件中

        oos.close();
    }
}

管道流PipedOutputStream(需要与多线程结合使用)

管道流可以理解为一根水管,
在水管的一端输入数据,在另一端读取数据。

代码示例:

/*
 * 管道流的演示。管道流需要与线程相结合使用
 *  PipedOutputStream和PipedInputStream
 */
import java.util.*;
import java.io.*;

import javax.management.RuntimeErrorException;
import javax.swing.plaf.SliderUI;

class ReadPiped implements Runnable{
    PipedInputStream in;
    public ReadPiped(PipedInputStream in){
        this.in = in;
    }
    public void run() {
        try {
            byte[] bs = new byte[1024];
            int len = 0;

            System.out.println("读取前没有数据,阻塞");
            len = in.read(bs);
            System.out.println("读取到数据");

            String str = new String(bs,0,len);
            System.out.println(str);

        } catch (Exception e) {
            throw new RuntimeException("管道读取流失败");
        }finally{
            if(in!=null)
                try {
                    in.close();
                } catch (Exception e2) {
                    throw new RuntimeException("管道读取流关闭失败");
                }
        }
    }
}

class WritePiped implements Runnable{
    public PipedOutputStream out;
    public WritePiped(PipedOutputStream out){
        this.out = out;
    }
    public void run(){
        try {

            System.out.println("等待4秒,然后再写入数据");
            Thread.sleep(4000);
            out.write("guandaoliu zhixing".getBytes());


        } catch (Exception e) {
            throw new RuntimeException("管道输出流失败");
        }finally{
            if(out!=null)
                try {
                    out.close();
                } catch (Exception e2) {
                    throw new RuntimeException("管道输出流关闭失败");
                }
        }
    }
}

public class Demo {
    public static void main(String[] args) throws Exception {
        PipedInputStream in = new PipedInputStream();//创建管道输入流
        PipedOutputStream out = new PipedOutputStream();//创建管道输出流

        in.connect(out);//将管道输入流和管道输出流连接

        ReadPiped rp = new ReadPiped(in);
        WritePiped wp = new WritePiped(out);

        new Thread(rp).start();
        new Thread(wp).start();

    }
}

RandomAccessFile

随机访问文件RandomAccessFile

该类不算是IO体系中子类。
而是直接继承Object。

但是它是IO包中成员,因为它具备读和写功能。
内部封装了一个数组,而且通过指针对数组的元素进行操作。
可以通过getFilepointer获取指针位置,
同时可以通过seek改变指针的位置。

其实完成读写原理就是内部封装了字节输入流和输出流

通过构造函数可以看出,该类只能操作文件。
而且操作文件还有模式: 只读:r 读写:rw等

如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。
如果模式rw。 要操作的文件不存在,会自动创建。如果文件存在,不会发生覆盖

此类需要重点掌握。
1,掌握它的模式 r 强调内容和rw
2,掌握它能写入基本数据类型 writeInt、writeDouble等
3,掌握能够改变指针位置的方法 seek

代码示例:

/*
    RandomAccessFile代码示例
*/
import java.util.*;
import java.io.*;

import javax.management.RuntimeErrorException;
import javax.swing.plaf.SliderUI;

public class Demo {
    public static void main(String[] args) throws Exception {
        writeFile();
        readFile();
    }

    public static void readFile() throws IOException {
        RandomAccessFile raf = new RandomAccessFile("demo.txt", "r");// r代表只能读

        // 调整对象中指针,使得指针从第8个字节开始读,那么读取到的就是下一个人的姓名和年龄
        raf.seek(8 * 1);

        // 跳过指定的字节数
        // raf.skipBytes(8); //和seek的区别是,此方法指针只能往末尾移动而不能往回移动

        byte[] buf = new byte[4];

        raf.read(buf);

        String name = new String(buf);

        int age = raf.readInt();

        System.out.println("name=" + name);
        System.out.println("age=" + age);

        raf.close();
    }

    public static void writeFile() throws IOException {
        RandomAccessFile raf = new RandomAccessFile("demo.txt", "rw");// rw代表不仅可以读,还可以写

        raf.write("李四".getBytes());//李四

        raf.writeInt(97);//a

        raf.write("王五".getBytes());//王五
        raf.writeInt(99);//c

        raf.close();
    }
}

用于操作数据的流DataInputStream(一般用于操作基本数据类型)

记住:只要是只操作基本数据类型就用它
代码示例:

import java.util.*;
import java.io.*;

import javax.management.RuntimeErrorException;
import javax.swing.plaf.SliderUI;

public class Demo {
    public static void main(String[] args) throws IOException {
        // writedata();
        // readData();

        // writeUTFDemo();
        readUTFDemo();

        // 转换流的utf-8编码中,一个中文代表3个字节
        // OutputStreamWriter osw = new OutputStreamWriter(new
        // FileOutputStream("utf.txt"),"utf-8");
        // osw.write("你好");
        // osw.close();
    }

    public static void readUTFDemo() throws IOException {
        DataInputStream dis = new DataInputStream(new FileInputStream(
                "utfdata.txt"));

        String s = dis.readUTF();// 新版的utf-8编码只能用此方法读取。如果用此方法读取utf.txt文件会发生异常。
        // 因为utf.txt文件的utf-8编码和新版的不同,旧版的utf-8一个中文代表3个字节,新版的一个中文代表4个字节

        System.out.println(s);

        dis.close();
    }

    public static void writeUTFDemo() throws IOException {
        DataOutputStream dos = new DataOutputStream(new FileOutputStream(
                "utfdata.txt"));

        dos.writeUTF("你好");// 用新版的utf-8写入中文,一个中文代表4个字节

        dos.close();
    }

    public static void readData() throws IOException {
        DataInputStream dis = new DataInputStream(new FileInputStream(
                "data.txt"));

        int i = dis.readInt();
        boolean b = dis.readBoolean();
        double d = dis.readDouble();

        System.out.println(i);
        System.out.println(b);
        System.out.println(d);

    }

    public static void writedata() throws IOException {
        DataOutputStream dos = new DataOutputStream(new FileOutputStream(
                "data.txt"));

        dos.writeInt(234);
        dos.writeBoolean(true);
        dos.writeDouble(1234.4567);

        dos.close();
    }
}

操作字节数组的流ByteArrayInputStream

代码示例:

/*
    用于操作字节数组的流对象。

    ByteArrayInputStream:在构造的时候,需要接收数据源。而且数据源是一个字节数组

    ByteArrayOutputStream:在构造的时候,不用定义数据目的,因为该对象已经在内存封装了可变长度的字节数组。
    这就是数据目的地。

    因为这两个流对象操作的都是数组,并没有使用系统资源。所以,不用进行close关闭

    源设备:
        键盘:System.in        硬盘:FileStream       内存:ArrayStream
    目的设备:
        控制台:System.out  硬盘:FileStream       内存:ArrayStream


    该对象没有涉及到硬盘的操作,只是将一些数据写入一个数组中而已,就好比在int类型数组中写入数据一样。
    只不过该对象的写入使用的是流的思想
 */
import java.util.*;
import java.io.*;

import javax.management.RuntimeErrorException;
import javax.swing.plaf.SliderUI;

public class Demo {
    public static void main(String[] args) throws IOException {
        //数据源
        ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFG".getBytes());

        //数据目的
        ByteArrayOutputStream bos = new ByteArrayOutputStream();//可以理解为创建了一个可变长度的byte[]数组

        int ch = 0;
        while((ch=bis.read())!=-1){
            bos.write(ch); //可以理解为 直接往byte[]数组中写入数组。
        }

        System.out.println(bos.size());//7
        System.out.println(bos.toString());//ABCDEFG

        //byte[] bys = bos.toByteArray();//将对象中的数据变为byte[]数组。可以理解为 将一个byte类型的数组赋值给bys

//      bos.writeTo(new FileOutputStream(("demo.txt")));//将此 byte 数组输出流的全部内容写入到指定的文件中
    }
}

文件编码表(编码、解码)

/*
文件的编码表

简单的说:
    "你好"用gbk编的码,用utf-8解的码,会出现"??"。
    "你好"用utf-8编的码,用gbk解的码,会出现"浣犲ソ"。
 */
import java.util.*;
import java.io.*;

import javax.management.RuntimeErrorException;
import javax.swing.plaf.SliderUI;

public class Demo {
    public static void main(String[] args) throws IOException {
//      writeTextGBK();
//      readTextGBK();
        readTextUTF();
//      writeTextUTF();
    }
    public static void readTextGBK() throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"gbk");//由于写入的文件编码是gbk,
        //读取时编码也是gbk,那么读取时结果为 "你好" 。如果将读取的编码改为utf-8,那么读取结果为"??"。
        //那是因为:gbk编码中一个中文字符代表2个字节,那么"你好"共有4个字节。utf-8编码中一个中文代表3个字节,那么"你好"共6个字节。
        //用utf-8编码读取gbk编码的文件时,会将gbk中的3个字节字节去查找utf-8表,查找不到则返回一个"?",
        //然后再将剩下的1个字节去查找utf-8表,查找不到也返回一个"?",所以导致结果为"??"

        char[] chs = new char[10];
        int len = 0;
        len = isr.read(chs);
        String str = new String(chs,0,len);
        System.out.println(str);
    }

    public static void writeTextGBK() throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");
        osw.write("你好");
        osw.close();
    }

    public static void readTextUTF() throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"utf-8");//由于写入的文件编码是utf-8,
        //读取时编码也是utf-8,那么读取时结果为 "你好" 。如果将读取的编码改为gbk,那么读取结果为"浣犲ソ"。
        //那是因为:utf-8编码中一个中文字符代表3个字节,那么"你好"共有6个字节。gbk编码中一个中文代表2个字节,那么"你好"共4个字节。
        //用gbk编码读取utf-8编码的文件时,会将utf-8中的2个字节字节去查找gbk表,查找到一个"浣"。
        //然后再将剩下的4个字节中的2个字节去查找gbk表,查找到一个"犲"。再将最后的2个字节去查找gbk表,查找到一个"ソ"。
        //所以结果为:浣犲ソ



        char[] chs = new char[10];
        int len = 0;
        len = isr.read(chs);
        String str = new String(chs,0,len);
        System.out.println(str);
    }

    public static void writeTextUTF() throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"utf-8");
        osw.write("你好");
        osw.close();
    }
}
/*
编码:字符串变成字节数组

解码:字节数组变成字符串

String --> byte[];      str.getBytes("编码"); 如:str.getBytes("utf-8")

bate[] --> String;      new String(byte[],"编码");    new String(byte[],"iso8859-1")
 */
import java.util.*;

public class Demo {
    public static void main(String[] args) throws Exception {
        method_2();
    }

    //演示  GBK编码iso8859-1解码 产生的乱码如何还原
    public static void method_1() throws Exception{
        String s = "你好";

        //用GBK编码
        byte[] bs_gbk = s.getBytes("GBK");
        System.out.println(Arrays.toString(bs_gbk));//[-60, -29, -70, -61]

        //用iso8859-1解码
        String str_iso = new String(bs_gbk,"iso8859-1");
        System.out.println(str_iso);//????


        //还原的方法是,再对iso8859-1进行编码,然后用gbk解码

        //用iso885891编码
        byte[] bs_iso = str_iso.getBytes("iso8859-1");
        System.out.println(Arrays.toString(bs_iso));//[-60, -29, -70, -61]

        //用GBK解码
        String str_gbk = new String(bs_iso,"GBK");
        System.out.println(str_gbk);//你好
    }

    //演示  GBK编码utf-8解码 产生的乱码不可还原
    public static void method_2() throws Exception{
        String s = "你好";

        //用GBK编码
        byte[] bs_gbk = s.getBytes("GBK");
        System.out.println(Arrays.toString(bs_gbk));//[-60, -29, -70, -61]

        //用utf-8解码
        String str_iso = new String(bs_gbk,"utf-8");
        System.out.println(str_iso);//???



        //用utf-8编码
        byte[] bs_iso = str_iso.getBytes("utf-8");
        System.out.println(Arrays.toString(bs_iso));//[-17, -65, -67, -17, -65, -67, -17, -65, -67]

        //用GBK解码
        String str_gbk = new String(bs_iso,"GBK");
        System.out.println(str_gbk);//锟斤拷锟?
    }
}

总结

这里写图片描述
这里写图片描述
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值