Input 输入 Output 输出

文件

java.io.File 它是对应者实际的文件或是文件夹(目录)

new File(String 文件路径)

new File("d:/1.txt");   // 可以使用 / 作为分隔符
new File("D:\\MySQL");   // 可以使用 \ 作为分隔符

或者可以使用 File.separator 获取不同操作系统的分隔符

1. 文件对象的api

.isFile() ; 返回一个布尔值代表是否是文件 .isDirectory(); 返回一个布尔值代表是否是目录

File 可以用来代表不存在的目录或文件: .mkdir() 来创建一个目录 .mkdirs() 可以用来创建多级目录

File file = new File("D:\\a\\b\\c");
file.mkdirs();

.exists() 用来判断一个文件或目录是否存在

.listFiles() 得到一个目录下的所有子目录和文件

File file = new File("D:\\6.23实训班共享");
        File[] files = file.listFiles();
        for(File f: files) {
            System.out.println(f);
        }

可以使用FilenameFilter接口来过滤需要的文件或文件夹

File[] files2 = file.listFiles(new FilenameFilter() {
    @Override
    public boolean accept(File dir, String name) {
        if (name.startsWith("笔")) {
            return true;
        } else {
            return false;
        }
    }
});
for(File f :files2) {
    System.out.println(f);
}

删除文件(立刻)

new File("D:\\6.23实训班共享\\测试.txt").delete();

删除文件(当程序运行结束)

new File("D:\\6.23实训班共享\\测试.txt").deleteOnExit();

重命名和移动

new File("D:\\6.23实训班共享\\测试.txt").renameTo(new File("c:\\测试2.txt"));

相对路径

File file = new File("D:\\6.23实训班共享\\代码\\Day16-IO\\1.txt");// 绝对路径
System.out.println(file.exists());

System.out.println(System.getProperty("user.dir")); // 当前目录
File file2 = new File("1.txt"); // 相对路径
System.out.println(file2.exists());
System.out.println(file2.getAbsoluteFile());

2.IO流

Input 输入流: 从文件读取内容,文件对我就是输入流 Output 输出流: 向文件写入内容,文件对我就是输出流

2.1 字节流(以字节为单位操作数据)

InputStream 抽象类 -> FileInputStream 文件输入流 OutputStream 抽象类 -> FileOutputStream 文件输出流

读取内容:

  1. 一次读取一个字节
// 1. 创建输入流对象
        FileInputStream fis = new FileInputStream(new File("1.txt"));
//        FileInputStream fis = new FileInputStream("1.txt")

        // 2. 读取内容
        /*int read = fis.read();// 一次读取一个字节, 返回int中只用了一个字节
        System.out.println((char)read);
        read = fis.read();
        System.out.println((char)read);
        read = fis.read();
        System.out.println((char)read);
        read = fis.read();
        System.out.println(read);*/
        while(true) {
            int read = fis.read();
            if(read == -1) {
                break;
            }
            System.out.println(read);
        }
  1. 一次读取多个字节
// 1. 创建输入流对象
FileInputStream fis = new FileInputStream(new File("1.txt"));

// 2. 一次读取多个字节
byte[] buf = new byte[2];
/*int len = fis.read(buf);// 将读到的内容填充到byte数组中,返回的是读到的字节总数, 返回-1还是表示读取完毕了
System.out.println(len);
System.out.println(Arrays.toString(buf));

len = fis.read(buf);
System.out.println(len);
System.out.println(Arrays.toString(buf));

len = fis.read(buf);
System.out.println(len);*/
while(true) {
    int n = fis.read(buf);
    if(n == -1) {
        break;
    }
    System.out.println(Arrays.toString(buf));
}

特点: 流是单向的

  1. 输出流例子:
// 1.创建了输出流
FileOutputStream fos = new FileOutputStream("2.txt");

// 2. 向输出流写入内容
fos.write(97);
fos.write(98);
fos.write(99);

// 3. 关闭输出流
fos.close();
  1. 文件的复制
FileInputStream fis = new FileInputStream("1.txt");
        FileOutputStream fos = new FileOutputStream("3.txt");

        while(true) {
            byte[] buf = new byte[1024];
            int len = fis.read(buf);
            if(len == -1) {
                break;
            }
            fos.write(buf, 0, len); // 实际读到len个字节,就写入len个字节
        }

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

2.2 字符流

以字符为单位处理流的内容

Reader 字符输入流 InputStreamReader 将字节流转为字符流 Writer 字符输出流 OutputStreamWriter 将字节流转为字符流

public static void main(String[] args) throws IOException {
    // 1. 将字节流转换为字符流
    FileInputStream fis = new FileInputStream("1.txt");
    // 注意:实际文件编码要与读取时的文件编码一致
    InputStreamReader reader = new InputStreamReader(fis, "utf-8");

    // 2. 读取
    /*while(true) {
        int c = reader.read();
        if(c == - 1) {
            break;
        }
        System.out.println((char)c);
    }*/
    while(true) {
        char[] buf = new char[1024];
        int len = reader.read(buf);
        if(len == -1) {
            break;
        }
        System.out.println(Arrays.toString(buf));
    }

    // 3. 关闭, 只需要关闭外层的流, 内部会帮我们关掉内层的流
    reader.close();
}

BufferedReader 在InputStreamReader的基础上以行为单位处理字符流 与 InputStreamReader是平级关系, 父类都是Reader

FileInputStream fis = new FileInputStream("1.txt");
InputStreamReader reader = new InputStreamReader(fis, "utf-8");
BufferedReader reader2 = new BufferedReader(reader);

/*System.out.println(reader2.readLine()); // 以行为单位读取数据
System.out.println(reader2.readLine()); // 以行为单位读取数据
System.out.println(reader2.readLine()); // 以行为单位读取数据*/

while(true) {
    String line = reader2.readLine();
    if(line == null) {
        break;
    }
    System.out.println(line);
}

reader2.close();

BufferedReader与InputStreamReader体现的是装饰者模式

装饰者模式 装饰者与被装饰者需要有一个共同的父类 装饰者和被装饰者之间体现的是组合的关系,而不是继承的关系(目的是为了更加灵活) 装饰者会对被装饰者做功能上的增强

Effective(高效的) JAVA 
	组合优于继承
	字节流
InputStream
	(*) FileInputStream  从文件读取字节
	(*) BufferedInputStream 加入缓冲功能,提高文件的读取效率
	ByteArrayInputStream 从字节数组变成输入流
OutputStream
	(*) FileOutputStream 向文件写入字节
	(*) BufferedOutputStream 加入缓冲功能, 提高文件的写入效率
	ByteArrayOutputStream 把流的内容写入字节数组
	PrintStream 实际上就是 System.out

字符流 Reader () InputStreamReader 转换字节流为字符流 () BufferedReader 功能增强,以行为单位读取数据 (装饰器模式) FileReader 是InputStreamReader子类,将字符编码固定为操作系统的默认编码,不能手工改动 Writer () OutputStreamWriter 转换字节流为字符流 () PrintWriter 以行为单位写入数据 write 当成字符写入 print print就是将参数转为字符串后写入 FileWriter 是OutputStreamWriter的子类,也是固定了字符编码

java中的序列化

问题:如何将对象中的信息永久保存 1.将来将对象信息存入数据库 2.java 中提供的序列化方式来永久保存数据

首先要让类实现Serializable序列化接口 第二使用ObjectOutputStream 写入要序列化的对象

public class Student implements Serializable{

    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
```java
Student student = new Student();
student.setId(1);
student.setName("张三");

// 序列化就是将对象变为输出字节流
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("d:\\student.obj"));
os.writeObject(student);
os.close();

如果没有实现Serializable接口,会出现NotSerializableException

注意: 1)要求对象中的所有属性也都是可以序列化 2)如果某个属性不想序列化,可以在属性上加transient关键字

反序列化

把字节内容读取进来,还原为java对象 ObjectInputStream用来读取字节内容,还原(反序列化)为java对象

ObjectInputStream is = new ObjectInputStream(new FileInputStream("d:\\student.obj"));
        Student s = (Student)is.readObject();
        System.out.println(s.getId());
        System.out.println(s.getName());
        System.out.println(s.getAddress());

除了可以写入和读取对象以外,还可以写入和读取基本类型(int,long,boolean...) ,读取和写入的顺序要保持一致 如果不一致,出现EOFException 如果没有更多内容,也会出现EOFException 建议在写入时最后一个对象使用null,这样读取时就可以根据null来判断是否读取完毕

序列化和反序列化其实也是java中的一种数据传输的机制

创建对象的几种方式:

  1. 反序列化
  2. clone 是一种
  3. new 是一种

克隆需要实现Cloneable接口,并覆盖clone方法,克隆举例:

public class Teacher implements Cloneable{ // 可克隆的

    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
	    // 必须调用Object父类的克隆方法
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher t = new Teacher();
        t.setId(1);
        t.setName("满老师");

        Teacher t2 = (Teacher)t.clone();
        System.out.println(t2.getId());
        System.out.println(t2.getName());

        System.out.println(t2 == t);
    }
}

克隆对应的设计模式:原型(prototype)模式 ,不走构造方法,根据一个已有对象创建新的对象。

使用Cloneable接口和clone克隆的对象,仅仅是浅拷贝,如果属性为引用类型,复制的仅是地址。没有为这个属性创建新的对象

深拷贝 利用序列化和反序列化生成新的对象,也会为属性创建新的对象 例:

public class Teacher2 implements Serializable{

    private int id;
    private String name;
    private Date birthday;

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Teacher2 clone() {
        try {
            // 序列化
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream os = new ObjectOutputStream(bos);
            os.writeObject(this); // 写入当前对象
            byte[] bytes = bos.toByteArray(); // 字节结果

            // 反序列化
            ObjectInputStream is = new ObjectInputStream(new ByteArrayInputStream(bytes));
            Teacher2 t = (Teacher2)is.readObject();
            return t;
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        Teacher2 t = new Teacher2();
        t.setId(1);
        t.setName("李老师");
        t.setBirthday(new Date());

        Teacher2 t2 = t.clone();

        System.out.println(t == t2);
        System.out.println(t.getBirthday() == t2.getBirthday());
    }
}