Java中的IO流主要分为:四种(8个主要相关的类,其中前4个为顶级类)

按流向分:输入流和输出流
按性质分:原始流和装饰流
按类型分:字节流和字符流

1.原始字节流,inputstream和outputstream,
  用于读取诸如图片,视频,音频等的原始字节文件,一个一个字节读,效率低。
2.在字节流的基础上使用IuputStreamReader装饰成的字符流,reader和writer。
  文件中包含汉字等非单字节字符时使用,一个字符一个字符读,效率低。
3.在原始字节流的基础上装饰的带缓存的字节流。BufferedInputStream,BufferedOutputStream。
  带缓存的一个字节一个字节读,可以用来读取原始的图片等数据,替换原始的InputStream,效率高。
4.在字符流的基础上装饰的带缓存的字符流。BufferedReader和BufferedWriter,分别新增readLine()和newLine()方法,
  一个缓存一个缓存读,效率高。



---------------------------------------------------------
输出流特例 --- 2种打印流
PrintStream(字节输出流)  -- 在其他输出流的基础上添加了许多新的功能。
  1.没有对应的输入流
  2.可以写入:基本数据类型,字符串,对象,数组等所有的数据类型
  3.带有自动行刷新功能,但必须在构造方法中开启刷新功能,默认不带有刷新功能。
  4.在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
  5.PrintStream虽然是操作字节的,但是因为具有println等换行功能,所以不能用来写入原始的图片,音频等数据。

System.out(标准输出:默认输出到控制台):返回的是PrintStream类的实例
System.in(标准输入:默认为键盘):返回的是InputStream类的实例
System.err(标准错误输出流:默认红色输出到控制台)三者均返回的是PrintStream类的实例。
这些默认的输入输出位置可以使用System类中的setIn(),setOut(),setErr()方法来更改设置:
如更改输出位置为指定文件:

//指定输出流位置:
 System.out.println("我被输出到控制台!");
 System.setOut(new PrintStream("E://test//test1.txt"));
 System.setErr(new PrintStream("E://test//test2.txt"));
 System.out.println("我被输出到指定的位置!!");
 System.err.println(我被输出到test2.txt文件);

 //指定输入流位置
 System.setIn(new InputStream("E://test//test.txt"));
 InputStream in = System.in;
 //从该输入流in中获取数据
 byte[] buffer = new byte[1024];
 int length = -1;
 while((length = in.read(buffer)) != -1){
   System.out.println(new String(buffer,0,length));
 }


----------------------------------------------------------
PrintWriter(字符输出流)  
  1.在BufferedWriter上的再一次封装,即在原始字节输出流上的三次装饰。
    PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new OutputStream("E://test.txt")));
  2.默认不带自动刷新功能,可通过第二个参数指定,如果指定为true,那么println、printf 或 format 方法将刷新输出缓冲区
  3.可以写入基本数据类型,对象,数组,字符串等所有的数据。

两者中建议使用PrintWriter

-----------------------------------------------------------
不使用Scanner,读取键盘录入???
第一种方式,一个字节一个字节读:

InputStream in = System.in;
   //读取流
   Byte[] buffer = new byte[1024];
   int length = -1;
   while((length = in.read(buffer))!=-1){
      System.out.println(new String(buffer,0,length));
   }


第二种方式:因为用户可能会输入汉字,所以应该使用字符流:

InputStream in = System.in;
    Reader reader = new InputStreamReader(in);
   //读取流
   char[] buffer = new char[1024];
   int length = -1;
   while((length = reader.read(buffer))!=-1){
      System.out.println(new String(buffer,0,length));
   }


第三种方式:带缓存的字符流,可以按行读取,并且高效

InputStream in = System.in;
   BufferedReader reader = new BufferedReader(new InputStreamReader(in));
   String str = "";
   while((str = reader.readLine())!=null){
      System.out.println(str);
   }


------------------------------------------------------------------------------------

以上获取到str之后,使用带缓存的字符流读取到的是字符串,可以实现字符串和基本数据类型之间的转换,
然后就可以获取到基本数据类型的结果,就达到了和Scanner一样的效果,查看Scanner源码实现。
---------------------------------------------------------------------------------------
模拟系统Scanner类编写自己的MyScanner类的思路:

--------------------------------------------------------------------------------------------------------
Object流:用来传输对象信息,包括字符串和数组,并且一次可以写入多个对象,在取时顺序和类型与写入时一致。
例如用户登录时可以将用户名和密码封装成一个JavaBean对象,将该对象的信息保存到本地的.tmp文件中。
使用场景:在main方法中,创建一个Person对象,给各属性赋值完了之后将该对象信息保存到本地的.tmp文件中。

使用条件:
1.该对象要实现Serializable接口可序列化
2.可以写入各种类型的数据。
3.不能序列化静态变量
4.如果存储了多个对象,那么取出顺序和存入的顺序一致

对象的写又被称为:序列化
对象的读又被称为:反序列化

对象输出流:ObjectOutputStream对象的创建:

对象输入流:ObjectInputStream
扩展:所有可序列化类都显式声明 serialVersionUID 值,原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,
     根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。因此,
     为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。
     还强烈建议使用 private 修饰符显示声明 serialVersionUID(如果可能),
     原因是这种声明仅应用于直接声明类 -- serialVersionUID 字段作为继承成员没有用处。
----------------------------------------------------------------------------------------------------
Data流:用来写出和读入基本数据类型,读取的顺序与写入的顺序一致。

------------------------------------------------------------------------------------------------------
内存流:
流的源和目的地除了可以是文件外,还可以是计算机内存。
字节输入流ByteArrayInputStream和字节输出流ByteArrayOutputStream分别使用字节数组作为流的源和目标。

//从网络获取数据使用内存流,数据大小,类型不确定

 public static byte[] read(InputStream input) throws Exception{
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
//read方法的返回值为int类型的数据,如果返回-1代表数据读取完
int len = 0;
//使用while循环将数据读取到一个字节数组中
while((len = input.read(buffer)) != -1){
//一边读一边存放数据
out.write(buffer, 0, len);
}
//关闭流
input.close();
//返回内存中的数据
return out.toByteArray();
}



使用的目的:将网络文件读取缓存到内存中,以后每次读取直接从内存中读取,节省了流浪和时间,也可以快速获取。