侧边栏壁纸
博主头像
coydone博主等级

记录学习,分享生活的个人站点

  • 累计撰写 306 篇文章
  • 累计创建 51 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

打印流和序列化流

coydone
2021-02-26 / 0 评论 / 0 点赞 / 279 阅读 / 12,659 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-05-01,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

打印流

字节打印流:PrintStream;字符打印流:PrintWriter。

优势:支持向文件中输出任意的数据类型。自动刷新的功能,不需要调用flush()或close(),操作更加简洁。

方法:

  • void print(String str):输出任意类型的数据。

  • void println(String str):输出任意类型的数据,自动写入换行操作。

可以通过构造方法,完成文件数据的自动刷新功能。

构造方法:开启文件自动刷新写入功能

public PrintWriter(OutputStream out, boolean autoFlush)
public PrintWriter(Writer out, boolean autoFlush)
/*打印流:
 *  PrintWriter:是Writer一个子类,只能写入,不能读取
 *  
 *  PrintWriter(OutputStream out) 
    PrintWriter(Writer out) 

 *  void print(boolean b) 
 *  void print(int i) 
  
 *  void println(int x) 
    void println(String x) 
   PrintWriter特点:
     1.保证数据原样写入
     2.PrintWriter底层也使用了缓冲区,利用PrintWriter写数据,其实也是
            写入了PrintWriter底层的缓冲区,我们需要通过手动刷新或者close()
     3.自动刷新
       PrintWriter(OutputStream out, boolean autoFlush) 
       PrintWriter(Writer out, boolean autoFlush) 
       当autoFlush为true的时候会启动自动刷新,自动刷新只针对println有效
 */
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class Demo {
    public static void main(String[] args) throws IOException {
        //PrintWriter自带刷新,需要在构造方法中开启
        PrintWriter pw = new PrintWriter(new FileWriter("print.txt"),true);//默认不自动刷新
        pw.print(12);
        pw.println(12.22);//自动刷新,println()
        pw.print("aaaaaa");
        pw.println();//flush()
    }
}

重定向标准I/O

System.in和System.out。它们是Java提供的两个标准的输入/输出流,主要用于从键盘接收数据以及向屏幕输出数据。

System.in常见方法:

  • int read(),此方法从键盘接收一个字节的数据,返回值是该字符的ASCII码。

  • int read(byte []buf),此方法从键盘接收多个字节的数据,保存至buf中,返回值是接收字节数据的个数。

System.out常见方法:

  • print(),向屏幕输出数据,不换行,参数可以是java的任意数据类型。

  • println(),向屏幕输出数据,换行,参数可以是java的任意数据类型。

System类提供了3个重定向标准输入/输出的方法:

方法 说明
static void setErr(PrintStream) 重定向标准错误输出流
static void setIn(InputStream in) 重定向标准输入流
static void setOut(PrintStream out) 重定向标准输出流

程序向文件输出,System.out.println()直接输出,不要重复创建FileWriter。

import java.io.FileInputStream;
import java.util.Scanner;

public class Demo {
    public static void main(String[] args) throws Exception {
        //InputStream in = System.in;//Console控制台
        //Scanner(InputStream)
        //Scanner读取任意数据类型
        //Scanner input = new Scanner(System.in);
        //Scanner从文件中读取数据aa.txt
        Scanner input = new Scanner(new FileInputStream("aa.txt"));
        //Scanner具有从console获取用户输入数据的功能
        String content = input.nextLine();
        System.out.println(content);
        //Scanner:控制台的数据-->java程序   Input
        input.close();
    }
}
public class Demo {
    public static void main(String[] args) throws IOException {
        //模仿Scanner从控制台获取用户输入的数据
        //InputStream-->Reader  转换流
        InputStreamReader isr = new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(isr);
        //接收用户输入
        System.out.print("请输入您的姓名:");
        String name = br.readLine();
        System.out.println("您的姓名是:"+name);
    }
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;

public class Demo {
    public static void main(String[] args) throws FileNotFoundException {
        //重定向:输出或输入的地点修改为其它的位置
        //System.in默认从控制台读取数据,改成G:\\1.txt
		//修改System.in获取数据的位置
        System.setIn(new FileInputStream("g:\\1.txt"));

        //System.out.println("hello");
        //重定向System.out
        PrintStream out = new PrintStream(new FileOutputStream("system.txt"));
        System.setOut(out);

        System.out.print("hello");
        System.out.println(12);
        System.out.println(12.23);
    }
}

字节数组流

ByteArrayInputStream和ByteArrayOutputStream经常用在需要流和数组之间转化的情况。

字节数组输入流

说白了,FileInputStream是把文件当做数据源。ByteArrayInputStream则是把内存中的“字节数组对象”当做数据源。

public static void main(String[] args) {
    byte[] arr = "abcde".getBytes();
    ByteArrayInputStream bais = null;
    try {
        bais = new ByteArrayInputStream(arr);
        StringBuilder sb = new StringBuilder();
        int temp = 0;
        while ((temp = bais.read()) != -1) {
            sb.append((char) temp);
        }
        System.out.println(sb.toString());
    } finally {
        try {
            bais.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

字节数组输出流

ByteArrayOutputStream流对象是将流中的数据写入到字节数组中。

public static void main(String[] args) {
    ByteArrayOutputStream baos = null;
    StringBuilder sb = new StringBuilder();
    try {
        baos = new ByteArrayOutputStream();
        baos.write('a');
        baos.write('b');
        baos.write('c');
        byte[] bytes = baos.toByteArray();
        for (byte b : bytes) {
            sb.append((char)b);
        }
        System.out.println(sb);
    } finally {
        try {
            if (baos != null) {
                baos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

序列化流(对象流)

概述

序列化和反序列化:字节流。核心:对象。场景:图形的存储。

序列化:程序中的对象保存文件中。ObjectOutputStream:输出流

反序列化:将文件中保存的对象读取到程序中。ObjectInputStream:输入流。

当两个进程远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。比如,我们可以通过http协议发送字符串信息;我们也可以在网络上直接发送Java对象。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象才能正常读取。

把Java对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为Java对象的过程称为对象的反序列化。

对象序列化的作用有如下两种:

  • 持久化:把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中。

  • 网络通信:在网络上传送对象的字节序列。比如:服务器之间的数据通信、对象传递。

使用序列化保存对象信息

在Java中,只有实现了java.io.Serializable接口的类的对象才能被序列化,Serializable表示可串行的、可序列化的,所以,对象序列化在某些文献中也叫串行化。JDK类库中有些类,如String类、包装类和Date类等都实现了Serializable接口。

序列化算法规则:

  • 所有保存到磁盘中的对象都有一个序列号。

  • 当程序试图序列化一个对象时,将会检查是否已经被序列化,只有序列化后的对象才能被转换成字符序列输出。

  • 如果对象已经被序列化,则程序直接输出一个序列化编号,而不再重新序列化。

实现序列化步骤:

  • 准备对象,可以任意对象。一般自定义对象。

  • 创建对象ObjectOutputStream,指定保存对象位置。。

  • 实现序列化:writeObject(对象)

  • 释放资源:close()。

好处:序列化完毕之后,对象保存在文件中。程序停止运行,对象不会丢失。

实现反序列化步骤:

  • 创建对象ObjectInputStream,指定读取保存对象位置。

  • 实现反序列化:Object o = readObject()。

  • 释放资源:close()。

序列化注意事项

1、加static修饰的成员变量不会被序列化,静态成员随着类的加载而加载,当没有对象的时候,静态成员已经存在静态成员不属于对象,而属于类。

2、如果非静态成员不想序列化:加一个transient,一旦使用该关键该成员变量就不会被序列化。transient关键字:如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。(在序列化中被忽略掉)

import java.io.Serializable;

public class Person implements Serializable {
    /*
    * 版本编号,序列化为防止程序代码变化。给java类定义版本号。
    * 文件中号码与java类号码一致。反序列化不会出错。
    * */
    private static final long serialVersionUID = 1L;
    public int id;
    private String name;
    private int age;
	//省略name和age的构造方法、getter()、setter()、toString()

import java.io.*;
public class Demo01 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //method01();
        //method02();
    }
    private static void method01() throws IOException {
        //序列化
        //1、创建Person对象
        Person p = new Person("张三",20);
        //2、创建ObjectOutputStream对象
        ObjectOutputStream oos =  new ObjectOutputStream(new FileOutputStream("object.txt"));
        //3、调用writeObject()
        oos.writeObject(p);
        //4、释放资源
        oos.close();
    }
    private static void method02() throws IOException, ClassNotFoundException {
        //反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
        Object o = ois.readObject();
        //Person p =(Person)o;
        System.out.println(o);
        ois.close();
    }
}

当第一次序列化对象存储到文件中,修改Person类中的代码,反序列化会出错。即序列化的版本不一致。

Exception in thread "main" java.io.InvalidClassException: com.coydone.demo02.Person; local class incompatible: stream classdesc serialVersionUID = -4254320201896902916, local class serialVersionUID = -2943045096116274627

序列化和反序列化实现注册和登录

import java.io.Serializable;
public class User implements Serializable {
    private String userName;
    private String userPwd;
	//省略有参构造方法、getter()、setter()、toString()
}

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

public class Demo {
    public static void main(String[] args)  {
        regist();
        login();
    }

    private static void login() {
        Scanner input = new Scanner(System.in);
        System.out.print("账号:");
        String userName = input.nextLine();
        System.out.print("密码:");
        String userPwd = input.nextLine();

        //判断用户名和密码与注册信息是否一致
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("userInfo.dat"));
            User o = (User)ois.readObject();
            if (userName.equals(o.getUserName())&&userPwd.equals(o.getUserPwd())){
                System.out.println("登录成功!");
            }else {
                System.out.println("登录失败!");
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("请先注册,再登录!");
        }finally {
            if (ois!=null){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void regist() {
        //注册
        Scanner input = new Scanner(System.in);
        System.out.print("账号:");
        String userName = input.nextLine();
        System.out.print("密码:");
        String userPwd = input.nextLine();
        System.out.print("确认密码:");
        String reUserPwd = input.nextLine();

        //两次密码输入一致
        if (!userPwd.equals(reUserPwd)){
            System.out.println("两次密码输入不一致,注册失败!");
            return;
        }
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("userInfo.dat"));
            oos.writeObject(new User(userName,userPwd));

            System.out.println("注册成功!");
        } catch (Exception e) {
            System.out.println("注册失败!");
        }finally {
            if (oos!=null){
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

随机访问流

RandomAccessFile可以实现两个作用:

1、实现对一个文件做读和写的操作。

2、可以访问文件的任意位置。不像其他流只能按照先后顺序读取。

在开发某些客户端软件时,经常用到这个功能强大的可以“任意操作文件内容”的类。比如,软件的使用次数和使用日期,可以通过本类访问文件中保存次数和日期的地方进行比对和修改。Java很少开发客户端软件,所以在Java开发中这个类用的相对较少。

RandomAccessFile的绝大多数功能,已经被JDK1.4的nio的“内存映射文件(memory-mapped files)”给取代了。

三个核心方法:

1、RandomAccessFile(String name,String mode):name用来确定文件,mode取r(读)或rw(可读写),通过mode可以确定流对文件的访问权限。

2、seek(long a):用来定位流对象读写文件的位置,a确定读写位置距离文件开头的字节个数。

3、getFilePointer():获得流的当前读写位置。

public static void main(String[] args) {
    RandomAccessFile raf = null;
    try {
        raf = new RandomAccessFile("d:/a.txt","rw");
        //写入数据
        int[] arr = {10, 20, 30, 40, 50, 60};
        for (int i : arr) {
            raf.writeInt(i);
        }
        raf.seek(4);
        System.out.println(raf.readInt()); //20
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (raf != null) {
                raf.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Properties集合

概述

Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。

特点:

1、Hashtable的子类,map集合中的方法都可以用。

2、该集合没有泛型。键值都是字符串。

3、它是一个可以持久化的属性集。键值可以存储到集合中,也可以存储到持久化的设备(硬盘、U盘、光盘)上。键值的来源也可以是持久化的设备。

4、有和流技术相结合的方法。

/*Properties方法使用
 * Properties经常用来操作key和value都是String类型的数据
 *    Object setProperty(String key, String value):向Properties中存放key和value 
      String getProperty(String key):根据key获取value
      Set<String> stringPropertyNames():将properties中所有的key放到一个set集合中,返回这个set集合
 */

读取文件中的数据保存到集合

void load(InputStream inStream)//从输入流中读取属性列表(键和元素对)。
void load(Reader reader)//按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)

将集合中内容存储到文件

//以适合使用load(InputStream)方法加载到Properties表中的格式,将此Properties表中的属性列表(键和元素对)写入输出流。
void store(OutputStream out,String comments)
    
//以适合使用load(Reader)方法的格式,将此Properties表中的属性列表(键和元素对)写入输出字符。
void store(Writer writer,String comments)

使用场景

Properties 一般与配置文件一起使用。

配置文件常见格式:.xml、.properties。

.properties文件:对应java程序:Properties集合。

集合类:Properties,继承(派生) Map<K,V>。Properties不支持泛型。默认存储都是字符串。Properties<String,String>

案例:

MySQL:登录的账号,密码:root、root。

创建db.properties配置文件保存用户名和密码。

userName=root
password=root

Java代码从配置文件中获取用户名和密码

import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
public class Demo {
    public static void main(String[] args) throws IOException {
        //获取登录的账号、密码IO
        //1、创建Properties集合
        Properties prop = new Properties();
        //2、加载配置文件信息到prop对象中
        prop.load(new FileReader("db.properties"));
        //3、查看结果
        System.out.println("账号:"+prop.getProperty("userName"));
        System.out.println("密码:"+prop.getProperty("password"));
        //账号:root
        //密码:root
    }
}

文件的压缩与解压

压缩文件的本质是把存储文件的二进制,用一定的规则表示来减少原文件的二进制长度,从而减少文件的大小。而这个规则不同的压缩软件规则不同。

Java中实现zip的压缩与解压缩

zipOutputStream:实现文件的压缩

  • ZipOutputStream(OutputStream out):创建新的ZIP输出流。

  • void putNextEntry(ZipEntry e):开始写入新的ZIP文件条目并将流定位到条目数据的开始处。

  • ZipEntry(String name):使用指定名称创建新的ZIP条目。

ZiplnputStream:实现文件的解压

  • ZipInputStream(lnputStream in):创建新的ZIP输入流。

  • ZipEntry getNextEntry():读取下一个ZlP文件条目并将流定位到该条目数据的开始处。

代码实现

import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class Test {
    public static void main(String[] args) {
        compression("G:\\01.zip",new File("G:\\01"));
//        decompression("D:\\test.zip","D:\\test\\");
    }

    //压缩
    private static void compression(String zipFileName, File targetFile) {
        try {
            //要生成的压缩文件
            ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFileName));
            BufferedOutputStream bos = new BufferedOutputStream(out);
            zip(out,targetFile,targetFile.getName(),bos);
            bos.close();
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private static void zip(ZipOutputStream zOut, File targetFile, String name, BufferedOutputStream bos) throws IOException {
        if (targetFile.isDirectory()) { //处理目录
            File[] files = targetFile.listFiles();
            if (files.length == 0) { //空文件夹
                zOut.putNextEntry(new ZipEntry(name + "/"));
            }
            for (File file : files) {
                //递归处理
                zip(zOut,file,name + "/" + file.getName(),bos);
            }
        } else {
            zOut.putNextEntry(new ZipEntry(name));
            InputStream in = new FileInputStream(targetFile);
            BufferedInputStream bis  = new BufferedInputStream(in);
            byte[] bytes = new byte[1024];
            int len  = -1;
            while ((len = bis.read(bytes)) != -1) {
                bos.write(bytes,0,len);
            }
            bis.close();
        }
    }

    //解压
    private static void decompression(String targetFileName,String parent) {
        try {
            //构建解压输入流
            ZipInputStream zIn = new ZipInputStream(new FileInputStream(targetFileName));
            ZipEntry entry = null;
            File file = null;
            while ((entry = zIn.getNextEntry()) != null && !entry.isDirectory()) {
                file = new File(parent,entry.getName());
                if (!file.exists()) {
                    new File(file.getParent()).mkdirs(); //创建此文件的上级目录
                }
                OutputStream os = new FileOutputStream(file);
                BufferedOutputStream bos = new BufferedOutputStream(os);
                byte[] bytes = new byte[1024];
                int len = -1;
                while ((len = zIn.read(bytes)) != -1) {
                    bos.write(bytes,0,len);
                }
                bos.close();
                System.out.println(file.getAbsolutePath() + "解压成功");
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
0

评论区