Java基础——IO流中的其他输入输出流

  • Post author:
  • Post category:java




1.数据输入输出流

数据输入流: DataInputStream

数据输出流: DataOutputStream

特点: 可以写基本数据类型,可以读取基本数据类型

示例代码

public class MyTest {
    public static void main(String[] args) throws IOException {
        // 数据输入和输出流:
        // 数据输入流:
        // DataInputStream
        // 数据输出流:
        // DataOutputStream
        // 特点:
        // 可以写基本数据类型, 可以读取基本数据类型
        DataInputStream in = new DataInputStream(new FileInputStream("a.txt"));
        int i = in.readInt();
        double v = in.readDouble();
        boolean b = in.readBoolean();
        char c = in.readChar();
        System.out.println(i);
        System.out.println(v);
        System.out.println(b);
        System.out.println(c);
        in.close();
    }

    private static void writeData() throws IOException {
        DataOutputStream out = new DataOutputStream(new FileOutputStream("a.txt"));
        out.writeInt(100);
        out.writeDouble(3.14);
        out.writeBoolean(true);
        out.writeChar('a');
        out.close();
    }
}



2.内存操作流

a:操作字节数组
		ByteArrayOutputStream
		ByteArrayInputStream
		此流关闭无效,所以无需关闭
b:操作字符数组
		CharArrayWrite
		CharArrayReader
c:操作字符串
		StringWriter
		StringReader		

 构造方法: public ByteArrayOutputStream()
public class MyTest {
    public static void main(String[] args) throws IOException {
        //内存操作流:内存操作流,不关联文件,只在内存中进行读写
       /* 
        操作字节数组
        ByteArrayOutputStream
        ByteArrayInputStream
        此流关闭无效,所以无需关闭*/

       /*
       * ByteArrayOutputStream
       * 此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。
       * 可使用 toByteArray() 和 toString() 获取数据。
            关闭 ByteArrayOutputStream 无效。
             此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
       * */


        // ByteArrayOutputStream()
        // 创建一个新的 byte 数组输出流。

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        bos.write("好好学习".getBytes());
        bos.write("天天向上".getBytes());
        bos.write("爱生活,爱Java".getBytes());
        //取出所有的字节数据
        byte[] bytes = bos.toByteArray();
        String s = new String(bytes);
        System.out.println(s);


        //如果你放的是,String的字节数据,你可以这样取
        String s1 = bos.toString();
        System.out.println(s1);

    }
}



3.打印流

字节流打印流

字符打印流

打印流的特点
a: 打印流只能操作目的地,不能操作数据源(不能进行读取数据)
b: 可以操作任意数据类型的数据 调用print() 方法可以写任意数据类型
c: 如果我们启用自动刷新,那么在调用println、printf 或 format 方法中的一个方法的时候,会完成自动刷新

通过以下构造创建对象 能够启动自动刷新  然后调用println、printf 或 format 方法中的一个方法的时候,会完成自动刷新
  	  public PrintWriter(OutputStream out,  boolean autoFlush)	 启动 自动刷新
  	 public PrintWriter(Writer out,  boolean autoFlush)		启动自动刷新

d: 这个流可以直接对文件进行操作(可以直接操作文件的流: 就是构造方法的参数可以传递文件或者文件路径)

PrintWriter实现自动刷新和换行

PrintWriter pw = new PrintWriter(new FileWriter("printWriter2.txt") , true) ;
	pw.println(true) ;
	pw.println(100) ;
	pw.println("中国") ;

打印流复制文本文件

这个打印流只能进行写数据,不能进行读取数据,
那么我们应该找一个可以读取文本文件中的的数据的流对象进行读取操作.
而我们非常喜欢高效的流对象,于是我们可以使用BufferedReader进行读取数据.

标准输入输出流概述和输出语句的本质

在System这个类中存在两个静态的成员变量:
public static final InputStream in: 标准输入流, 对应的设备是键盘
public static final PrintStream out: 标准输出流 , 对应的设备就是显示器
  	System.in的类型是InputStream.
  	System.out的类型是PrintStream是OutputStream的孙子类FilterOutputStream 的子类.

字节打印流

public class MyTest {
    public static void main(String[] args) throws IOException {
        //打印流:单个的,没有成对出现,只能写数据,不能读数据
        //字节打印流
        // 自己创建的打印流,关联的是文件
        PrintStream printStream = new PrintStream("hehe.text");
        printStream.write("aaaa".getBytes());
        printStream.write("\r\n".getBytes());
        printStream.println("asfasdfasdfasdfffff");
        printStream.print(true);
        printStream.println(3.14);

        printStream.close();

        //System.out 返回的 这个字节打印流,关联的设备是屏幕
        PrintStream out = System.out;
        out.println(200);
    }
}

PrintWriter使用

public class MyTest2 {
    public static void main(String[] args) throws FileNotFoundException {
        PrintWriter printWriter = new PrintWriter(new FileOutputStream("haha.txt"),true);
        //如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作,而不是每当正好输出换行符时才完成。
        //printWriter.write("asfasdfasdfasdf");
        printWriter.flush();
        printWriter.println("asdfasdfasdf");
       printWriter.println("asdfasdfasdfasdf");
        //printWriter.flush();
        printWriter.close();
    }
}

案例演示:打印流复制文本文件

public class MyTest3 {
    public static void main(String[] args) throws IOException {
       
        BufferedReader reader = new BufferedReader(new FileReader("haha.txt"));
        PrintWriter printWriter = new PrintWriter(new FileOutputStream("ccc.txt"), true);

        while (true){
            String s = reader.readLine();
            if (s == null) {
                break;
            }
            printWriter.println(s);
        }
        reader.close();
        printWriter.close();
    }
}

二种方式实现键盘录入

public class MyTest {
    public static void main(String[] args) throws IOException {
    //第一种,Scanner实现键盘录入
       // Scanner scanner = new Scanner(System.in);
     //第二种,BufferedReader的readLine方法。
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while (true){
            System.out.println("请输入数据");
            String s = reader.readLine();
            if("886".equals(s)){
                break;
            }
            System.out.println(s);
        }
    }
}

输出语句用字符缓冲流改进

  获取System下的in成员变量
	InputStream in = System.in ;
	 in是一个字节输入流对象,那么我们就可以通过这个字节输入流对象进行读取键盘录入的数据.
	 那么我们既然要读取数据,之前我们讲解了两种读取数据的方式:
	  1. 一次读取一个字节
	  2. 一次读取一个字节数组
那么我们在这个地方使用那种读取方式. 经过分析,这两种读取方式都不太合适.
因为数据是客户通过键盘录入 进来的,而我们希望直接读取一行数据.
而既然要读取一行数据,那么我们就需要使用readLine方法,而这个方法是属于BufferedReader的方法,
而我们就需要创建一个BufferedReader对象进行读取数据.而我们这in有属于字节流,而创建BufferedReader对象的时候需要一个字符流,
而我们就需要将这个字节流转换成字符流,那么既然要对其进行转换,
那么就需要使用转换流. 需要使用InputStreamReader.



4.随机访问流

RandomAccessFile概述 最大特点 能读能写
RandomAccessFile类不属于流,是Object类的子类。但它融合了InputStream和OutputStream的功能。
支持对随机访问文件的读取和写入。

RandomAccessFile的父类是Object , 这个流对象可以用来读取数据也可以用来写数据.可以操作任意数据类型的数据.
我们可以通过getFilePointer方法获取文件指针,并且可以通过seek方法设置文件指针
public class MyTest4 {
    public static void main(String[] args) throws IOException {
        //writeData();
        //怎么写的就怎么读
        RandomAccessFile out = new RandomAccessFile("e.txt", "rw");
        int i = out.readInt();
        //获取文件指针位置
        long filePointer = out.getFilePointer();
        System.out.println("文件指针位置:"+filePointer);
        boolean b = out.readBoolean();
        filePointer = out.getFilePointer();
        System.out.println("文件指针位置:" + filePointer); 
        double v = out.readDouble();
        filePointer = out.getFilePointer();
        System.out.println("文件指针位置:" + filePointer); 
        String s = out.readUTF();
        filePointer = out.getFilePointer();
        System.out.println("文件指针位置:" + filePointer);
        System.out.println(i);
        System.out.println(b);
        System.out.println(v);
        System.out.println(s);
        //设置文件指针的位置
        out.seek(13);
        s = out.readUTF();
        filePointer = out.getFilePointer();
        System.out.println("文件指针位置:" + filePointer);
        System.out.println(s);
    }

    private static void writeData() throws IOException {
        RandomAccessFile out= new RandomAccessFile("e.txt", "rw");
        out.writeInt(100);
        out.writeBoolean(true);
        out.writeDouble(3.2);
        out.writeUTF("呵呵");
        out.close();
    }
}



5.序列化流和反序列化流

所谓的序列化:就是把对象通过流的方式存储到文件中.注意:此对象 要重写Serializable 接口才能被序列化
	反序列化:就是把文件中存储的对象以流的方式还原成对象
	序列化流:	ObjectOutputStream
	反序列化流:	ObjectInputStream

像这样一个接口中如果没有方法,那么这样的接口我们将其称之为标记接口(用来给类打标记的,相当于猪肉身上盖个章)

一个对象可以被序列化的前提是这个对象对应的类必须实现Serializable接口


序列化和反序列化

序列化:把对象转换为字节序列的过程称为对象的序列化.

反序列化:把字节序列恢复为对象的过程称为对象的反序列化.


什么时候需要用到序列化和反序列化呢?

当我们只在本地JVM里运行下Java实例, 这个时候是不需要什么序列化和反序列化的, 但当我们需要将内存中的对象持久化到磁盘, 数据库中时,

当我们需要与浏览器进行交互进行网络传输时, 这个时候就需要序列化和反序列化了


只要我们对内存中的对象进行持久化或网络传输, 这个时候都需要序列化和反序列化.



序列化与反序列化的案例演示1

public class MyTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //   序列化:把java对象的字节数据保存到硬盘上
        //   反序列化:java对象的字节数据读取回内存
        // void writeObject (Object obj)
        // 将指定的对象写入 ObjectOutputStream。

        //writeData();
        readData();
    }

    private static void readData() throws IOException, ClassNotFoundException {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("student.txt"));
        //反序列化
        Student object = (Student) in.readObject();
        System.out.println(object);
    }

    private static void writeData() throws IOException {
        Student student = new Student("张三",23);
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("student.txt"));
        //序列化
        out.writeObject(student);
        out.close();
    }
}



序列化与反序列化案例演示2

public class MyTest2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //writeData();
       readData();
    }

    private static void readData() throws IOException, ClassNotFoundException {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("student.txt"));
        //反序列化
        Student object = (Student) in.readObject();
        System.out.println(object);
        object = (Student) in.readObject();
        System.out.println(object);
    }

    private static void writeData() throws IOException {
        Student student = new Student("张三",23);
        Student student2 = new Student("李四", 24);
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("student.txt"));
        //序列化
        out.writeObject(student);
        out.writeObject(student2);
        out.close();
    }
}



6.如何解决序列化时候的黄色警告线问题?

实现Serializable接口, 为什么还要显示指定serialVersionUID的值?

如果不显示指定serialVersionUID, JVM在序列化时会根据属性自动生成一个serialVersionUID, 然后与属性一起序列化, 再进行持久化或网络传输. 在反序列化时, JVM会再根据属性自动生成一个新版serialVersionUID, 然后将这个新版serialVersionUID与序列化时生成的旧版serialVersionUID进行比较, 如果相同则反序列化成功, 否则报错.

如果显示指定了serialVersionUID, JVM在序列化和反序列化时仍然都会生成一个serialVersionUID, 但值为我们显示指定的值, 这样在反序列化时新旧版本的serialVersionUID就一致了.

在实际开发中, 不显示指定serialVersionUID的情况会导致什么问题? 如果我们的类写完后不再修改, 那当然不会有问题, 但这在实际开发中是不可能的, 我们的类会不断迭代, 一旦类被修改了, 那旧对象反序列化就会报错. 所以在实际开发中, 我们都会显示指定一个serialVersionUID, 值是多少无所谓, 只要不变就行.



7.如何让对象的成员变量不被序列化

使用transient关键字声明不需要序列化的成员变量

private transient int age ;// 可以阻止成员变量的序列化使用transient

注意:静态变量也不会序列化,因为序列化是针对对象而言的, 而static属性优先于对象存在, 随着类的加载而加载, 所以不会被序列化。



8.Properties的概述和作为Map集合的使用

Properties 类表示了一个持久的属性集。

Properties 可保存在流中或从流中加载。

属性列表中每个键及其对应值都是一个字符串。

Properties父类是Hashtable

属于双列集合,这个集合中的键和值都是字符串 Properties不能指定泛型


Properties的特殊功能使用


public Object setProperty(String key,String value)

public String getProperty(String key)

public Set stringPropertyNames()


案例演示

public class MyTest3 {
    public static void main(String[] args) {
        //Properties extends Hashtable<Object,Object>

        Properties properties = new Properties();
         //properties.put("name", "zhangsan");
        //默认键值存为String
        properties.setProperty("name","zhangsan");
        String name = properties.getProperty("name");
        //第二个参数,是默认值,如果键没有找到对应的值,返回默认值
        String property = properties.getProperty("name2", "lisi");
    }
}


Properties的load()和store()功能

Properties和IO流进行配合使用:

public void load(Reader reader): 读取键值对数据把数据存储到Properties中

public void store(Writer writer, String comments)把Properties集合中的键值对数据写入到文件中, comments注释

public class MyTest4 {
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        properties.setProperty("username","王老虎");
        properties.setProperty("password","654321");
        //键=值
        //把集合中的键值数据保存到文件中
        properties.store(new FileWriter("user.properties"),"用户的信息");
    }
}
public class MyTest5 {
    public static void main(String[] args) throws IOException {
        //读取配置文件,文件中的键值是=号拼接的
        Properties properties = new Properties();
        properties.load(new FileReader("user.properties"));
        System.out.println(properties);
    }
}



9.SequenceInputStream

SequenceInputStream 
	表示其他输入流的逻辑串联。
	它从输入流的有序集合开始,
	并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,
	依次类推,直到到达包含的最后一个输入流的文件末尾为止
	a:构造方法
	SequenceInputStream(InputStream s1, InputStream s2) 
	通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2),
	以提供从此 SequenceInputStream 读取的字节。
	b:构造方法
	SequenceInputStream(Enumeration<? extends InputStream> e) 
	 通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。

案例演示:将两首MP3歌曲合并到一个MP3文件中

public class MyTest {
    public static void main(String[] args) throws IOException {
        FileInputStream in1 = new FileInputStream("叶丽仪 - 新上海滩.mp3");
        FileInputStream in2 = new FileInputStream("烟花易冷Live_林志炫.mp3");

        SequenceInputStream in = new SequenceInputStream(in1, in2);
        FileOutputStream out = new FileOutputStream("C:\\Users\\Desktop\\歌曲大连唱3.mp3");
        int len = 0;
        byte[] bytes = new byte[1024 * 8];
        while ((len = in.read(bytes)) != -1) {
            out.write(bytes, 0, len);
        }
        in.close();
        out.close();
    }
}



版权声明:本文为weixin_43496491原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。