首页 > 代码库 > I/O流

I/O流

基础:
  Unix:严格区分大小写
  Windows:默认不区分大小写
  路径分割符,属性分割符
  Unix: 使用 "/"
  Windows:使用"\"分割目录路径,但是在Java中一个"\"表示转移,在Windows平台的Java代码中表示一个路径,就得使用两个"\\",Windows也支持"/"

==>因此File类提供了两类常量,分别来表示路径分割符和属性分隔符
//获取属性分割符
  String pathSeparator = File.pathSeparator;
  char pathSeparatorChar = File.pathSeparatorChar;
//获取路径分割符
  String separator = File.separator;
  char separatorChar = File.separatorChar;

1,File类:
  只能设置和获取文件本身的信息,不能设置和获取文件的内容
  createNewFile() >>只能创建文件
  mkdir() 创建此抽象路径名指定的目录。>>只能创建单级目录
  mkdirs() 创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。>>创建多级目录

//列出文件的分层结构
  String str = "F:\\test\\aa\\xxx\\helloword.txt";
  File fil = new File(str);
  List<String> list = new ArrayList<String>();
  listAllParent(list,fil);
  Collections.reverse(list);//颠倒list元素排序
  StringBuffer sb = new StringBuffer(30);
  for (String name:list){
    sb.append(name).append(">");
  }
  sb.deleteCharAt(sb.length()-1);
  System.out.println(sb);
}
private static void listAllParent(List<String> list, File fil) {
  if (!("".equals(fil.getParentFile().getName()))){
    list.add(fil.getParentFile().getName());
  }
  if (fil.getParentFile().getParentFile()!=null){
    listAllParent(list, fil.getParentFile());
   }    
}
//test>aa>xxx
//列出所有的文件
public
static void listAllFile(File file) {   File[] files = file.listFiles();   for (File f:files){     System.out.println(f);     if (f.isDirectory()){       listAllFile(f);     }   } }
//批量修改文件名称
public static void main(String[] args) {
  File file = new File("F:\\test");
  File[] files = file.listFiles();
  String delText = "技术之";
  for (File fil : files) {
    if (fil.getName().contains(delText)){
        String newName = fil.getName().replace(delText, "哈哈哈");
        fil.renameTo(new File(file,newName));//File(父目录, 子目录) 
      }
  }
}

2,FilenameFilter 文件过滤器
boolean accept(File dir, String name)
测试指定文件是否应该包含在某一文件列表中。

File[] files = file.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
  if (new File(dir,name).isFile() && name.endsWith(".java")){
    return true;
  }
    return false;
  }
});    //只返回Java文件

3,InputStream
OutputStream
Reader
Writer

//流关闭
finally{
  try {
    if (is!=null){
      is.close();
    }
  } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  }
  try {
     if (os!=null){
     os.close();
     }
  }
catch (IOException e) {     // TODO Auto-generated catch block     e.printStackTrace();   } }

>>flush()方法对部分字节流有作用,对字符流都有作用
调用close()方法前会先调用flush()方法
4,字符编码发展历程
计算机只认识数字,计算机里一切数据都是以数字来表示,因为英文符号有限,所以规定使用的字节的最高位时0,每一个
字节都是以0-127之间的数字来表示---ASCIL
两个字节表示一个汉字,中文字符每个字节的最高位为1 --GB2312---后出现 GBK
解决各国本地字符的影响,收录全球国家的符号---Unicode
  ASCII:占一个字节,只能包含128个字符,不能表示汉字
  ISO-8859-1占一个字节,收录西欧语言,不能表示汉字
  ANSI:占两个字节,在简体中文的操作系统,ANSI就指的是GB2312
  GB2312/GBK/GB18030:占两个字节,支持中文
  UTF-8:万国码
  UTF-8 BOM:微软使用的编码,默认占三个字节(即使空文件)
存储字母和数字无论是什么字符,都占1个字节
存储汉字,GBK家族占两个字节,UTF-8家族占3个字节
>>不能使用单字节存储中文
字符的编码和解码操作
  编码:把字符串转换为byte数组 //byte[] data = http://www.mamicode.com/str.getBytes("GBK");
  解码:把byte数组转换为字符串 //String ret = new String(data, "ISO-8859-1");
  乱码:编码和解码的字符不同
解决方案:
//乱码解决方案
  data = http://www.mamicode.com/ret.getBytes("ISO-8859-1");
  System.out.println(Arrays.toString(data));
  ret = new String(data, "GBK");
  System.out.println(ret);
5,包装流:创建对象时需要传入另一个都对象
new 包装流(流对象)
缓冲流:是一个包装流,目的起缓冲作用
缓冲流的目的:
操作流的时候,习惯定义一个byte/char数组
int read():每次都从磁盘文件读取一个字节,直接操作磁盘文件性能极地
解决方案:
定义一个数组作为缓冲区
  byte[] buffer = new byte[1024];//该数组其实就是一个缓冲区
  一次性从磁盘文件读取1024个字节,如此以来,操作磁盘文件的次数少了,性能得以提升

>>>缓冲流内置缓冲区大小:8192
  private static int defaultCharBufferSize = 8192;
6,节点流和缓冲流性能对比
操作字符和字节都习惯使用缓冲流包装起来,提高性能

7,转换流
把字节流转换成字符流
InputStreamReader:把字节输入流转成字符输入流
OutputStreamWriter:把字节输出流转成字符输出流
为什么有字节转字符,没有字符转字节?
字节流可以操作一切文件(纯文本和二进制文件)
字符流是用来操作中文纯文本使用的,本身是对字节流的增强
8,内存流(数组流)
把数据临时存在数组中,待会再从数组中获取出来
1):字节内存流:ByteArrayInputStream / ByteArrayOutputStream
2):字符内存流:CharArrayReader / CharArrayWriter
3):字符串流:StringWriter / StringReader
9,合并流(顺序流) SequenceInputStream
把多个输入流合并成一个(没有字符的合并流)

//合并流
SequenceInputStream sis = new SequenceInputStream(new FileInputStream("F:\\test\\a.txt"),
new FileInputStream("F:\\test\\hello.txt"));
byte[] info = new byte[1024];
int len = -1;
while ((len=sis.read(info))!=-1){
  System.out.println(new String(info,0,len));
}
sis.close();

10,序列化和反序列化
序列化:把堆内存中的Java对象,通过某种方式把对象存储到磁盘文件中或者传递给其他网络的节点
反序列化:把磁盘文件中的对象数据或者把网络节点上的对象数据恢复成Java对象
为什么要做序列化:
1):在分布式系统中,需要共享的数据的javabean对象,都得做序列化,此时需要把对象再网络上传输,
此时就得把对象数据转换为二进制形式.以后存储在HttpSession中的对象,都应该实现序列化接口
2):服务钝化:
需要序列化,必须实现序列化接口:java.io.Serializable
对象流:
ObjectOutputStream
ObjectInputStream

File file = new File("F:\\test\\args.txt");
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(new User(1001, "格格", "longde"));
oos.close();
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
User user = (User)ois.readObject();
System.out.println(user.toString());
ois.close();

1):如果某些数据不需要做序列化,比如密码,添加 transient 关键字
  transient private String pwd;
2):反序列化的版本问题:
反序列化Java对象时必须提供该对象的class文件,现在的问题是,随着项目的升级,系统的class文件也会升级(如增加一个字段或删除一个字段),
如何保证两个class文件的兼容性?
如果不显示定义serialVersionUID类变量,该变量的值由jvm根据类相关信息计算,而修改后类的计算方式和之前往往不同,从而
造成了对象反序列化因为版本不兼容而失败的问题
  解决方案:在类中提供一个固定的serialVersionUID
    public class User implements Serializable{
      ** private static final long serialVersionUID = 1L;

11,打印流
打印流只能是输出流
PrintStream:字节打印流
与其他输出流不同,PrintStream 永远不会抛出 IOException;而是,异常情况仅设置可通过 checkError 方法测试的内部标志。
另外,为了自动刷新,可以创建一个 PrintStream;这意味着可在写入 byte 数组之后自动调用 flush 方法,可调用其中一个 println 方法,
或写入一个换行符或字节 (‘\n‘)。
PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
PrintWriter:字符打印流
PrintWriter(OutputStream out, boolean autoFlush)
与 PrintStream 类不同,如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作,
而不是每当正好输出换行符时才完成。这些方法使用平台自有的行分隔符概念,而不是换行符。
如果没有开启自动刷新,则需要手动刷新或者当缓冲区满的时候再自动刷新

>>>使用打印流作为输出流,此时的输出流操作会特别简单,因为在打印流中:
提供了print方法:打印不换行
提供了println方法:打印再换行
print和println方法支持打印/输出各种数据类型的数据
>>>打印流中的格式化(printf)输出

System.out.println() 等价于 PrintStream ps = System.out ps.println();
格式化输出
//传统做法
String name = "格格";
int age = 17;
String str = "姓名:"+name+",年龄:"+age;
System.out.println(str);
//格式化
String format = "姓名:%s,年龄:%d";
Object[] data = {"格格",17};
System.out.printf(format, data);
//简化
System.out.println();
System.out.printf("姓名:%s,年龄:%d","格格",18);

**标准的IO:
标准的输入:通过键盘录入数据给程序
标准的输出:在屏幕上显示程序数据
在System类中有两个常量
  InputStream in = System.in;
  PrintStream out = System.out;
重定向标准
  System.setIn(new FileInputStream("F:\\test\\a.txt"));
重新分配标准的输入流,System.in的数据来源由System.setIn指定

System.setOut(new PrintStream("F:\\test\\aCopy.txt"));
重新分配标准的输出流

12,扫描器类,Scanner

//扫描文件中的数据
Scanner input = new Scanner(new File("F:\\test\\a.txt"),"UTF-8");
while (input.hasNextLine()){
  String line = input.nextLine();
  System.out.println(line);
}
input.close();

>>扫描键盘输入的数据
  Scanner input = new Scanner(System.in):
>>扫描字符串中的数据
  Scanner sc = new Scanner("格格");

13,资源文件(以.properties作为扩展名的文件)/属性文件
  Properties类:Hashtable的子类,Map接口的实现类

//创建properties对象
Properties ps = new Properties();
InputStream is = new FileInputStream("src/db.properties");
//加载输入流中的数据
ps.load(is);
System.out.println("账号是:"+ps.getProperty("username"));
System.out.println("密码是:"+ps.getProperty("password"));
is.close();

14,数据流,提供可以读写任意数据类型的方法
DataOutputStream: 提供 writeXxx()方法
DataInputStream: 提供 readXxx()方法

15,随机访问文件(RandomAccessFile):表示可以在任意位置读取文件
public RandomAccessFile(File file, String mode)
值 含意
"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
"rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
>>>作用:多线程断点下载
16,管道流
PipedInputStream
PipedOutputStream
PipedWriter
PipedReader

17,新IO, NIO
jdk1.4版本开始,可以把一块磁盘文件映射到内存中,再去读取内存中的数据
可以替代标准的 java IO API,现在主要运用于服务器中
jdk1.7中提取出更新的IO, NIO2.0

I/O流