天天看点

黑马程序员————基本数据流、内存操作流、打印流、标准输入输出流、序列化、properties类



------<a href="http://www.itheima.com" target="_blank" rel="external nofollow" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

IO流的其他重要类

一.数据输入输出流:操作基本数据类型

 DataInputStream:数据输入流:

  构造方法:DataInputStream(InputStream in):底层使用指定的InputStream 创建一个对象

 DataOutputStream:数据输出流:

  构造方法:DataOutputStream(OutputStream out)创建一个新的数据输出流,将数据写入指定的基础输出流

 可以读写任何的Java的基本数据类型;可以按照写入的顺序读出数据,可以起到密码保护的作用,文件中根本看不出来是什么。

二.内存操作流:(暂存)

 ByteArrayOutputStream:向内存中写入byte[]数组;

  此类实现了一个输出流,其中的数据被写入一个 byte 数组。

  缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。

 ByteArrayInputStream:从内存中读取byte[]数组;

  包含一个内部缓冲区,该缓冲区包含从流中读取的字节。

  内部计数器跟踪 read 方法要提供的下一个字节。  

 作用:

 缓存byte[]数组;就跟StringBuffer缓存String作用相同;将数据暂时存储在内存中的缓存区中,以备使用,但是一旦断电就没有了。

public class Demo {
    public static void main(String[] args) throws IOException{
  	//1.从文件中读取内容,一次读取一个字节数组;
 	 //将每次读取的字节数组存储到ByteArrayOutputStream中
 	 FileInputStream in = new FileInputStream("demo08_1.txt");
  	//定义一个byte数组的缓存区
 	 ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
 	 //一次读取一个字节数组
 	 byte[] byteArray = new byte[5];
 	 int len = 0;
 	 while((len = in.read(byteArray)) != -1){
  		 //这个时候byte数组已经被填充,但这个数组现在没有什么用,不要转换为字符串,也不需要写入到文件。可以将byte数组缓冲起来
  		 byteOut.write(byteArray, 0, len);
 	 }
  	//此循环结束后,所有的byte数组将会写入到内存中;
  	System.out.println("写入完毕!");
  
  
  	System.out.println("读取byte数组:");
  	//取出所有的byte[]数组
 	 byte[] byteArrayAll = byteOut.toByteArray();
 	 //构造一个ByteArrayInputStream
  	ByteArrayInputStream  byteIn = new ByteArrayInputStream(byteArrayAll);
  
 	 //一次读取一个字节.此方式,同遍历byteArrayAll数组效果是一样的。
  	int n = 0;
  	while((n = byteIn.read()) != -1){
   		System.out.println("读取的字节数:" + n + " 字符:" + (char)n);
  	}  
    }
}
           

  注意:此例向内存中写入的时候,是一个一个数组的写入,而取出数据是,是一个字节一个字节的读取。

三.打印流:(自动刷新)

 1.打印字符流:PrintWriter:

 2.打印字节流:PrintStream:

 3.特点:

  1).只有输出,没有输入;

  2).可以输出任何类型的数据;基本数据类和引用数据类型

  3).有自动刷新功能,如果启用,部分功能将具有自动刷新功能:

    println(),printf(),format()只有此三个方法有

  4).可以操作文件;可以写入文件

 4.构造方法

  PrintStream(OutputStream out)根据现有的 OutputStream 创建不带自动行刷新的新 PrintStream

  PrintStream(OutputStream out,boolean autoFlush)过现有的 OutputStream 创建新的 PrintStream

  PrintWriter(OutputStream out) :根据现有的 OutputStream 创建不带自动行刷新的新 PrintWriter。

  PrintWriter(OutputStream out, boolean autoFlush) 通过现有的 OutputStream 创建新的 PrintWriter。

   参数:autoFlush:true:自动刷新;

  PrintWriter(Writer out) 创建不带自动行刷新的新 PrintWriter。 

   PrintWriter(Writer out, boolean autoFlush) 创建新 PrintWriter。

  注意:如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作

  ★★★  println相当于write() + flush() + newLine()

 5.使用打印流复制文本:

  思路:

   1.复制文本,就需要输入流和输出流;

   2.打印流只有输出,没有输入;

   3.用一个字符流做输入;打印流做输出

public class Demo {
   public static void main(String[] args) throws IOException{
  	//1.输入流:
 	 BufferedReader in = new BufferedReader(new FileReader("demo16.txt"));
  	//2.输出流
  	PrintWriter out = new PrintWriter(new FileWriter("demo16_copy.txt"),true);//带自动刷新
  
  	//一次读取一行数据
  	String row = null;
  	while((row = in.readLine()) != null){
   		out.println(row);//writer() + flush() + newLine();
 	 }
  	//关闭流
 	 out.close();
  	in.close();
  
 	 System.out.println("复制完毕");
  
    }
}
           

四.标准输入输出流:(重要)(输入方式,输出见打印流)

 1.System.in : 返回的是InputStream类型的,实际上是InputStream子类对象的引用

   Inputstream in=System.in

 2.System.out: 返回的是PrintStream类型的,这是一个OutputStream的子类

   PrintStream out =System.out;

   System.out.println("输出流:" + out);//PrintStream

   System.out.println("输入流:" + in);//BufferedInputStream

   通过打印可以知道输出流的的确是printStream字节打印流打印流

   而输入流却是一个带缓冲的字节输入流

  out.println()方法打印字符数组名称的,得到的是数组元素,而打印其他的却是地址值。

 3.将标准输入流转换为带缓冲的字符输入流:

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

  如何实现的呢?

  InputStreamReader streamReader = new InputStreamReader(System.in);

  BufferedReader bufReader = new BufferedReader(streamReader);

 4.三种方式实现获取控制台数据:

  1).Scanner();

  2).main()方法的形参;

  3).System.in;(可以经过转换方法,一次读取一行数据:带缓冲的字符流。见下面实例)

 5.一点小知识

InputStream in = System.in;
  //一次读取一个字节。从控制台
  int n = in.read();//程序会在read()方法阻塞,等待用户输入
  System.out.println("读取的字节:" + n + " 读取的字符:" + (char)n);
  BufferedReader bufReader= new BufferedReader(new InputStreamReader(System.in));   
  String s = bufReader.readLine();
  System.out.println("读取的内容:" + s);
★★★ 若是在控制台输入一些文字,按下回车键,n会存储第一个字节的信息,而s会接收之后的信息。说明其内部有个光标机制,会自动移动读取位置。
           

五.随机访问流:(指针特性)

 1.RandomAccessFile:

 2.它虽然在IO包但是不是流,直接继承自Object

 3.它内部结合了InputStream和OutputStream可以对文件进行读写;

 4.构造方法的mode参数:

  RandomAccessFile(File file, String mode)  创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。

    RandomAccessFile(String name, String mode):创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。

  "r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。 

  "rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。 

  "rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。 

  "rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。

 5.getFilePointer()获取文件指针;

   seek():设置文件指针;

   其文件的写入和读出和Data数据操作流差不多,都具有文件加密功能,按照顺序来读写才可以,并且可以获取和设置指针,来选择读取位置。

六.合并流

 1.构造方法

public SequenceInputStream(InputStream s1,InputStream s2)
   InputStream is1 = new FileInputStream("a.txt");
   InputStream is2 = new FileInputStream("b.txt");
   SequenceInputStream sis = new SequenceInputStream(is1, is2);

   //将多个文件写入一个文件中
   InputStream is1 = new FileInputStream("a.txt");
   InputStream is2 = new FileInputStream("b.txt");
   Vector<InputStream> v = new Vector<InputStream>();
   v.add(is1);
   v.add(is2);
   Enumeration<InputStream> en = v.elements();//返回的 Enumeration 对象将生成此向量中的所有项。生成的第一项为索引 0 处的项,然后是索引 1 处的项,依此类推。
   SequenceInputStream sis = new SequenceInputStream(en);//将集合中所有的输入流都添加进来。

   // 复制数据
   FileOutputStream out = new FileOutputStream("copy.txt");

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

   out.close();
   is1.close();
   is2.close();
   sis.close();
           

七.序列化和反序列化:(重要)

 1.序列化和反序列化的概述

   序列化:是可以将一个内存中的"对象"写入到一个文件中,包括这个对象中的所有属性的值。

     也可以将一个对象,通过网络传输到另一台机器上。这个过程就叫:序列化;

  反序列化:当将一个对象"序列化"后,我们可以将其"反序列化",将对象的信息读入到程序;例如:从一个文件中读取一个"对象的信息";从网络接收到一个对象的信息。

 2.序列化和反序列化的类:

  ObjectOutputStream:序列化输入流;

   构造方法:public ObjectOutputStream(OutputStream out):

   成员方法:public final void writeObject(Object obj):

   注意:需要被序列化的类,必须实现Serializable接口。这个接口是一个"标识性"的接口,没有抽象方法,子类不需要重写方法。

      实现此接口的类,表示可以被序列化。

 3.ObjectInputStream:反序列化输出流:

   构造方法:ObjectInputStream(InputStream in) 创建从指定 InputStream 读取的 ObjectInputStream

   成员方法:public final Object readObject(),返回的是一个引用,用的时候需要强转一下。可以取出对象的有关属性。

   注意:此时会产生一个全新的对象,和之前的对象毫无关系的,地址值并不一样。

 4.每个被序列化的类,都隐式的有一个属性:serialVersionUID,而且有一个默认值。

   如果我们不显示定义这个属性,java会自动添加一个这个属性。

   这时,如果我们将此类的对象序列化后,如果更改了类结构,这个属性的值也会随之改变,那么就与已经序列化的对象的这个属性值不匹配了。

   当我们反序列化时,Java会验证这个属性值,如果不匹配,则抛出:InvalidClassException异常;

   建议:在需要序列化的类中显示的定义serialVersionUID属性,并赋予一个值。如果这个值不发生变化,反序列化就不会有问题。

 5.transient关键字

  防止有关成员变量被序列化。

  没有被序列化的变量,再次被取出时,其值就是个默认值,0或者null.

 6.注意:

  1).被序列化的对象,必须实现Serializable 接口;

  2).被序列化的对象,建议显示的定义成员常量:serialVersionUID

  3).transient可以修饰成员变量不被序列化;

八.Properties类(及其重要)

 1.它是Map的实现类,直接继承自:Hashtable。所以它就是一个Map集合。

 2.其内部直接继承了输入输出流,可以用它来读写配置文件信息;

 3.配置文件:一般软件运行时都会记录一些用户的"设置信息"、"操作信息"等一些信息,记录的目的是为了下次再启动软件时,

    可以根据用户上次的设置或使用情况,来显示软件;

    配置文件就是一个"文本文件"。

    它内部一般以"键值对"的形式存储信息。

 4.其为map类

    Map的方法:

        put(Object key ,Object value):存储元素

        keySet():获取键的集合

        get(Object key):通过键获取值

  特有方法:

       public Object setProperty(String key,String value): 用来存储元素,类似于put(),返回原来的值。

       public String getProperty(String key):    获取键的值相当于get()

       public Set<String> stringPropertyNames():   获取所有键的值集合keySet()

   注意,其中键和值的类型都是string类的。

   5.与IO流一起使用,读写配置文件:

            public void load(Reader reader):从配置文件中读取属性信息

            public void store(Writer writer,String comments):向配置文件中写入信息。comments,文件头信息。

private static void write() throws IOException{
  	Properties pro = new Properties();
  	//填充集合
 	pro.setProperty("疲劳值", "1200");
  	pro.setProperty("经验", "5000");
  	pro.setProperty("金币", "500");
 	 pro.setProperty("敏捷", "20");
  
 	 //写入到文件
 	 FileWriter out = new FileWriter("demo23.properties");
 	 pro.store(out, "demo23");
  
 	 out.close();

private static void read() throws IOException{
 	 Properties pro = new Properties();
 	 //读取
 	 FileReader in = new FileReader("demo23.properties");
 	 pro.load(in);//将会把文件中的所有配置信息,读取到Properties中
 	 in.close();
  
 	 //解析Properties
  	Set<String> keySet = pro.stringPropertyNames();
 	for(String key : keySet){
   		System.out.println(key + "---" + pro.getProperty(key));
  	}
  
 }  可见Properties只是一个集合而已,若是要将文件信息写入文本之中,还以要已开字符的输入输出流呢。

           

★★6.练习:修改指定键的值:

 需求,将属性写入到配置文件,遍历properties文件看是否有什么值,并修改

 * 思路分析:

 * 1.配置文件一般是以键值对的形式存储,那么考虑使用Properties

 * 2.创建字符输入流,将文件信息读入

 * 3.遍历集合,寻找键值,然后修改

 * 4.输出修改后的,创建输出流。

 *

 * */

public class Demo {
 public static void main(String[] args) throws IOException {
  //创建集合
  Properties pro=new Properties();
  //填充集合
  String str1=(String)pro.setProperty("技术", "100");
  pro.setProperty("力量","20001" );
  pro.setProperty("速度", "2000");
  System.out.println(str1+pro.setProperty("敏捷", "2234"));
  //创建输入流
  FileWriter out=new FileWriter("demo.properties");
  //集合写入文件
  pro.store(out, "demo--name");
  //out.close();
  
  //读取配置文件中的内容
  //创建文件输入流
  FileReader in =new FileReader("demo.properties");
  //读取文件信息进入集合
  pro.load(in);
  in.close();
  //遍历集合
  //创建键值对的集合
  Set<String> proset=pro.stringPropertyNames();
  for(String pstr:proset){
  
   System.out.println("键为--"+pstr+"--的,值为--"+pro.getProperty(pstr));
   if (pstr.equals("技术")){
    pro.setProperty(pstr, "10000");
    System.out.println("键为---"+pstr+"---的值修改为---"+pro.getProperty(pstr));
   }
  }
  //将修改后的再次写入文件
  pro.store(out,"修改后的技术");
  out.close();
 }
} 

           

九.新IO的方法:

 Path:与平台无关的路径。

 Paths类:包含了返回Path的静态方法。

  public static Path get(URI uri):根据给定的URI来确定文件路径。

 Files类:操作文件的工具类。提供了大量的方法,简单了解如下方法

  public static long copy(Path source, OutputStream out) :复制文件

  public static Path write(Path path, Iterable<? extends CharSequence> lines,Charset cs,OpenOption... options):

  把集合的数据写到文件。

public class Demo {
 	public static void main(String[] args) throws IOException{
 		OutputStream out = new FileOutputStream("demo25_copy.txt");
  		Files.copy(Paths.get("demo16_copy.txt"), out);
 		out.close();
 		System.out.println("拷贝完成");
  
  		//将集合的数据写入到文件
 		ArrayList<String> strList = new ArrayList<>();
 		strList.add("张学友");
 		strList.add("刘德华");
 		strList.add("章子怡");
  
  		Files.write(Paths.get("demo25_2.txt"), strList, Charset.forName("GBK"));
 		System.out.println("写入完毕!");
 	}
}
           



继续阅读