IO流
1. File
1.1 File概述
- File:它是文件和目录路径名的抽象表示
- 文件和目录是可以通过File封装成对象的
 - 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已。它是可以存在的,也可以是不存在的
将来要通过具体的操作把这个路径内容转换为具体存在的 
 - 方法
- 构造方法
File(String pathname) 给定路径名字符串转换为抽象路径名来创建新的File实例,pathname可以是绝对路径或相对路径00000
File(String parent, String child) 从父路径名字符串和子路径名字符串创建新的File
File(File parent,String child) 从父抽象路径名和子路径名字符串创建新的File实例 - 创建功能
boolean createNewFile() 文件不存在时,创建一个由该抽象路径名命名的新空文件,返回true
mkdir() 创建单级目录
mkdirs() 创建此抽象路径名命名的多级目录,包括任何必须但不存在的父目录
注:创建文件就用创建文件的方式,目录就用目录的方式。当目录名和将创建的文件名同名且已存在时,是无法再创建文件的。 - 判断和获取功能
isDirectory()
isFile()
exists()
getAbsolutePath()
getPath()
getName()
list() 返回文件和目录是字符串形式数组
listFiles() 返回文件和目录是File对象形式数组 - 删除功能
delete() 目录文件都可以删除
注:如果一个目录中有内容不能直接删除,应先删除目录中的内容,最后删除目录 
 - 构造方法
 
1.2 递归
递归解决问题的思路:
把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题求解
少量的程序,就可以描述出接替过程中所需要的多次重复计算
递归解决问题要找到的两个内容:
- 递归出口:否则会出现内存溢出 StatckOverflowError
 - 递归规则:与原问题相似的规模较小的问题
 
例:遍历目录
需求给定一个路径(E:\itcast),请通过递归完成遍历该目录下的所有内容,并把所有文件的绝对路径输出到控制台
思路:
- 根据给定的路径创建一个File对象
 - 定义一个方法,用于获取该给定目录下的说有内容,参数为第一步的File对象
 - 获取给定的File目录下所有的文件或者目录File数组
 - 遍历File数组,得到每一个File对象
 - 判断该File对象是否是目录
是--递归调用
不是--获取绝对路径输出在控制台
6.调用方法 
public class RecurveDemo {
	public static void main(String[] args) {
		File f = new File("/home/Id/");
		recurve(f);
		
	}
	public static void recurve(File f){
		File[] fs = f.listFiles();
		for(File file : fs){
			if(file.isFile()){
				System.out.print(file.getAbsolutePath()+" ");
			}else{
				recurve(file);
			}
		}
	}
}
2.字节流
2.1 IO流概述和分类
- IO: 输入输出(Input/Output)
 - 流:抽象概念,数据在设备间的传输称为流
 - 常见IO流应用: 文件复制,文件上传,文件下载
例 程序运行期间,内存和硬盘之间的互动就是IO流传递
![]()
 - IO流分类:
- 按数据流向分
输入流:读数据
输出流:写数据 - 按数据类型分
字节流:字节输入流;字节输出流
字符流:字符输入流;字符输出流 - 数据通过记事本打开
能读懂内容 使用字符流
读不懂 使用字节流
不确定 使用字节流 
 - 按数据流向分
 
2.1 字节流写数据
字节流抽象类基类
- InputStream: 抽象类,字节输入流的超类
 - OuputStream: 抽象类,字节输出流的超类
 - 子类名称特点:以其父类名作为后缀
FileOutputStream:文件输出流用于将数据写入File - FileOutputStream(String name): 创建文件输出流,以指定的名称写文件
 
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
class OutputStreamDemo01 {
    public static void main(String[] args) throws IOException {
        /*
            创建字节输出流对象,做了三件事
            1.调用系统功能创建了文件
            2.创建了字节输出流对象
            3.让字节输出流对象指向创建好的文件
         */
        FileOutputStream fos = new FileOutputStream("src/fos.txt");
        fos.write(97);//a
        fos.write(57);//'9'
        fos.write(55);//'7'
        //所有和IO相关的操作,最后都要释放资源
        // close 关闭此文件输出流,并释放与此流相关联的任何系统资源
        fos.close();
    }
}
2.3 字节流写数据的3种方式
写数据步骤:创建对象,写数据,释放资源
- write(int b) 将指定的字节写入此文件输出流,一次写一个字节数据
 - write(byte[]b) 将b.length字节从指定的字节数组写入次文件输出流,一次写一个字节数组数据
 - write(byte[]b,int off,int len) 从偏移量off开始字节数组的len个字节,写入文件输出流
 
- FileOutputStream的构造方法
FileOutputStream(File file)
FileoutputStream(String name) 此构造方法里面如果文件不存在,会new File(String name),直接传字符串不手动创建file,更方便 
        byte[] bys = {97,98,99,100,101};
        fos.write(bys);
        //直接用String的getBytes方法方便,不需要记字符的ASCII码
        byte[] bys1 = "abcde".getBytes();
        fos.write(bys1);
        fos.close();
2.4 字节流些数据换行追加
- 如何实现换行?
写完数据后,加换行符
windows:、\r\n
linux:\n
mac:\r
fos.write("\r\n".getBytes()); //换行符写/r/n在任何系统上都可识别为换行 - 如何实现追加写入?
FileOutputStream(File file, boolean append) //创建文件输出流以写入由指定的 File对象表示的文件
append=true时追加,字节写入文件的末尾而不是开头 
2.5 字节流写数据异常处理
finally:异常处理时提供清除操作,比如IO流中释放资源
特点:被finally控制的语句一定会执行,除非JVM退出
package io;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamDemo04 {
    public static void main(String[] args) {
        FileOutputStream fos =null;
        try {
            fos = new FileOutputStream("Z:\\src\\fos.txt");
            fos.write("hello".getBytes());
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            //在finally里释放资源,先做不为null判断,否则可能出现空指针异常
            if(fos!=null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
2.6 字节流读数据
FileInputStream:从文件系统中的文件获取输入字节
- FileInputStream(String name) 通过打开与实际文件的连接来创建一个FileInputStream,name为文件路径名
 - 字节输入流读数据步骤:创建字节输入流对象,调用读数据方法,释放资源
 
2.6.1 一次读取一个字节
int read() 读取一个字节数据
需求:把文件fos.txt的内容读取出来在控制台输出
public class FileInputStreamDemo01 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("src/fos.txt");
        int by;
        //读数据不需要换行,原文档含有换行符会被读取到,文档末尾字符是-1
        while((by=fis.read())!=-1){
            System.out.print((char)by);
        }
        fis.close();//释放资源
    }
}
例 复制文本文件
思路:
1.根据数据源穿件字节输入流对象
2.根据目的地创建字节输出流对象
3.读写数据,赋值文本文件(一次读取一个字节,一次写入一个字节)
4.释放资源
public class CopyTxtDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("src/fos.txt");
        FileOutputStream fos = new FileOutputStream("a.txt");
        int by;
        while((by = fis.read())!=-1){
            fos.write(by);
        }
        fos.close();
        fis.close();
    }
}
2.6.2 一次读一个字节数组数据
int read(byte[] b) 从该输入流读取最多 b.length个字节的数据为字节数组,返回值为读取的个数
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamDemo02 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("src/fos.txt");
        byte[] bytes = new byte[5];
        //第一次读取
        int len = fis.read(bytes);
        //读取的长度
        System.out.println(len);
        //将字节数组转化为字符串
        System.out.println(new String(bytes));
        //第二次读取
        len = fis.read(bytes);
        //读取的长度
        System.out.println(len);
        //将字节数组转化为字符串
        System.out.println(new String(bytes));
        //第三次读取
        read = fis.read(bytes);
        //读取的长度
        System.out.println(read);
        //将字节数组转化为字符串
        System.out.println(new String(bytes));
        /*
            hello\r\n
            world\r\n
            第一次 hello
            第二次 \r\nwor
            第三次 ld\r\nr(r为上次存储的,这次只有4个字符,未被覆盖掉)
         */
        fis.close();
    }
}
实际输出应该读了几个输出几个字符,byte数组的大小应是1024的整数倍,每次输出都应换做如下写法:
byte[] bys = new byte[1024];//1024及其整数倍
int len;
while((len = fis.read(bys))!=-1){
  System.out.print(new String(bys,0,len));//实际输出应该读了几个输出几个字符
}
2.7 字节缓冲流
字节缓冲流:
- BufferedOutputStream: 该类实现缓冲输出流。通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用。
 - BufferedInputStream: 创建BufferedInputStream将创建一个内部缓冲区数组。当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节
构造方法: - 字节缓冲输出流:BufferedOutputStream(OutputStream out)
 - 字节缓冲输入流:BufferedInputStream(InputStream in)
为什么构造方法需要使用字节流,而不是具体的文件或者路径呢? - 字节缓冲流仅仅提供缓冲区(大小8192),而真正的读写数据依靠基本的字节流对象进行操作
读写方法和用的FileOutputStream/FileInputStream相同
例:复制视频
思路:
1.根据数据源创建字节输入流对象
2.根据目的地创建字节输出流对象
3.读写数据,复制视频
4.释放资源
可以通过4种方式来复制,带/不带 buffer,一次读写一个byte/byte[]来实现。
其中带buffer且一次读写一个数组最快 
public class CopyAviDemo {
    public static void main(String[] args) throws IOException {
        long startTime = System.currentTimeMillis();
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\拓新\\腾讯课堂java高级阶段资料.avi"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src/new.avi"));
        byte[] bys = new byte[1024];
        int len;
        while((len=bis.read(bys))!=-1){
            bos.write(bys,0,len);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("共耗时"+(endTime-startTime)+"毫秒");
        bos.close();
        bis.close();
    }
}
3 字符流
3.1 为什么会出现字符流
- 字符流 = 字节流+编码表
用字节流复制文本时,文本文件也会有中文,但是显示没有问题,原因是最终底层操作会自动进行字节拼接成中文.
如何识别中文的呢? - 汉字在存储的时候,无论选择哪种编码,第一个字节都是负数
GBK存储汉字占两个字节,UTF-8占3个字节。解析时根据当时的编码来判断用几个字节组合起来解析
String s ="中国";
byte[] bys = s.getBytes();//[-28,-72,-83,-27,-101,-67]
byte[] bys = s.getBytes("UTF-8");//[-28,-72,-83,-27,-101,-67]
byte[] bys = s.getBytes("GBK");//[-42,-48,-71,-6]
System.out.println(Arrays.toString(bys)); 
3.2 编码表
- 计算机存储信息采用二进制
 - 编码是将字符转换存储到计算机中,而相应的解码是将二进制数按照某种规则解析显示出来。
注意编码和解码必须用同一套规则,否则会乱码
字符编码:一套自然语言的字符与二进制数之间的对应规则 - 字符集
- 一个系统支持的所有字符的集合,包括国家文字,标点符号,图形符号,数字等
 - 一套字符集必然至少有一套字符编码,常见的字符集有ASCII字符集,GBXXX字符集,Unicode字符集
 
 - ASCII字符集
- American Standard Code for Infomation Interchange美国信息交换便准代码,主要用于显示英语及控制字符,数字,标点等
 - 基本ASCII字符7位表示一个字符,共128字符。扩展ASCII采用8位表示一个字符,共256字符,支持欧洲常用字符
 
 - GBXXX字符集
- GB2312:简体中文码表。小于127的字符意义与原来相同,大于127的字符连在一起表示一个汉字。
ASCII码原有的数字标点字幕重新编了两个字节长的编码,这就是常说的“全角”字符,而原来127以下的叫“半角” - GBK:最常用的中文码表,兼容GBK2312,在其基础上扩展。使用双字节码表方案,支持繁体汉字及日韩汉字等
 - GB18030:最新的中文码表,支持中国国内少数民族文字,每个字可以由1个2个4个字节组成。
 
 - GB2312:简体中文码表。小于127的字符意义与原来相同,大于127的字符连在一起表示一个汉字。
 - Unicode字符集
- 为表达任意语言的任意字符而设计,是业界的一种标准同一码,万国码。最多使用4个字节表达每个字母、符号或文字。
有三种编码方案,UTF-8,UTF-16和UTF32,最常用UTF-8编码.
(字符集是字符在集合里的排序,而编码涉及的是在内存中的存储表达方式) - UTF-8优先采用的编码,IETF(互联网工程小组)要求所有互联网协议必须支持UTF-8。
使用1到4个字节编码:- 128个US-ASCII字符,只需要一个字节编码
 - 拉丁文等字符,需要二个字节编码
 - 大部分常用字(含中文),使用三个字节编码
 - 其他极少使用的Unicode辅助字符,使用四个字节编码
 
 
 - 为表达任意语言的任意字符而设计,是业界的一种标准同一码,万国码。最多使用4个字节表达每个字母、符号或文字。
 
3.3 字符串中的编码解码问题
编码 String->byte:
- byte[] getBytes():使用平台默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中(IDEA采用)
 - byte[] getBytes(String charsetName):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
解码 byte->String: - String(byte[]bytes): 通过使用平台默认字符集解码指定的字节数组来构造String(IDEA采用)
 - String(byte[]bytes,String charsetName)用指定的字符集解码字节来构造String.
 
3.4 字符流中的编码解码问题
- 字符流抽象基类
- Reader:字符输入流的抽象类
 - Writer:字符输出流的抽象类
 
 - 字符流中编码解码问题相关的两个类:
- InputstreamReader
 - OutputStreamWriter
- OutputStreamWriter(OutputStream out)
 - OutputStreamWriter(OutputStream out, Charset cs)
例: 
 
 
public class ConversionStreamdemo {
	public static void main(String[] args) throws IOException {
		OutputStreamWriter osw = 
                    new OutputStreamWriter(new FileOutputStream("src/fos.txt"),"GBK");
		osw.write("果存");
		osw.close();
	
		int ch;
                //用GBK编码的字符存入文件虽然我们看不懂,但是用编码规则的输入流可以读取里面的字符
		InputStreamReader isr = new InputStreamReader(new FileInputStream("src/fos.txt"),"GBK");
                //对于read方法此处一次读取的是一个字符,不再是一个字节
		while((ch=isr.read())!=-1){
			System.out.println((char)ch);
		}
	}
}
3.5 字符流写数据的5种方式
- 分为写字符,字符数组,或者写字符串
- void write(int c) 写一个字符
 - void write(char[] cbuf)写一个字符数组
 - void write(char[]cbuf,int off,int len)写字符数组的一部分
 - void write(String str) 写一个字符串
 - void write(String str, int off,int len) 写一个字符串的一部分
 
 - 每次调用write()方法都会使编码转换器在给定字符上被调用.所得到的字节在写入底层输出流之前累积在缓冲区中。
可以调用flush()刷新缓冲区,将字节写入文件。close()方式在释放资源前,也可以刷新缓冲区。一旦close在这之后不能追加写操作。 
3.6 字符流读数据的2种方式
- int read() 一次读取一个字符
 - int read(char[] cbuf) 一次读一个字符数组
字符流读取方式和字节流读取方式一样的,只不过一个读的是字符,一个读的是字节
例:复制java文件 
public class CopyJavaDemo1 {
	public static void main(String[] args) throws IOException {
		InputStreamReader isr= new InputStreamReader(new FileInputStream("src/io/OutputStreamWriterDemo.java"));
													//new FileInputStream("src/io/OutputStreamWriterDemo.java"));
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("src/new.java"));
		char[] cbuf = new char[1024];
		int len;
		while((len = isr.read(cbuf))!=-1){
			osw.write(cbuf, 0, len);
			osw.flush();
		}
		osw.close();
		isr.close();
	}
}
例:复制java文件改进版
- InputStreamReader/OutputStreamWriter转换流的名字比较长,采用简化类FileWriter/Filereader更方便。
简化类方便刻写文件,都是按照本地默认编码字符集实现的- 构造器简化 FileReader(String fileName) 创建一个新的 FileReader ,给定要读取的文件的名称。
 
 - 当我们转换时涉及编码问题,还得用InputstreamReader/OutputStreamWriter
 
public class CopyJavaDemo02 {
	public static void main(String[] args) throws IOException {
		FileReader fread = new FileReader("src/io/OutputStreamWriterDemo.java");
		FileWriter fwrite = new FileWriter("src/new.java");
		
		char[] cbuf = new char[1024];
		int len;
		while((len = fread.read(cbuf))!=-1){
			fwrite.write(cbuf,0,len);
		}
		fwrite.close();
		fread.close();
		
	}
}
3.7 字符缓冲流
- BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。
可以指定缓冲区大小,或者可以接受默认大小,默认值足够大,基本满足需求
注:InputStreamWriter里面内置的缓冲区,缓存的是转码后的字节,和此处提到的缓冲流字符不同 - BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符数组和行的高效读取。
缓冲区大小可指定,通常采用默认大小 - 构造方法
- BufferedWriter(Writer out)
 - BufferedReader(Reader in)
 
 
public class BufferedStreamDemo01 {
    public static void main(String[] args) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("src/writer.txt"));
        bw.write("hello everyone\r\n");
        bw.write("hello world\r\n");
        bw.close();
    }
}
3.8 字符缓冲流特有功能
BufferedWriter
- void newLine():写一行行分隔符,行分隔符字符串由系统属性定义
BufferedReader - public String readLine():读一行文字,结果包含行的内容的字符串,不包括任何行终止符。
如果流的结尾已经到达,返回null 
public class BufferedStreamDemo02 {
    public static void main(String[] args) throws IOException {
       BufferedWriter bw = new BufferedWriter(new FileWriter("src/1.txt"));
        for (int i = 0; i < 10; i++) {
            bw.write("hello"+i);
           // bw.write("\r\n"); 此换行符针对windows
            bw.newLine();//使用newLine自适应操作系统换行符
            bw.flush();
        }
        bw.close();
        BufferedReader br = new BufferedReader(new FileReader("src/1.txt"));
        String s;
        while( (s = br.readLine())!=null){
            System.out.println(s);//不读换行符号,要自己添加换行
        }
        br.close();
    }
}
例:利用bufferedWriter/bufferedReader 特殊方法复制java文件
public class BufferedStreamDemo03 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("src/io/CopyAviDemo.java"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("new.java"));
        String line;
        while((line = br.readLine())!=null){
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
        bw.close();
        br.close();
    }
}
3.9 小结
例 集合到文件
需求:把ArrayList集合中的字符串数据写到文件中。要求:每个字符串元素作为文件中的一行数据
public class ArrayListToTxtDemo {
    public static void main(String[] args) throws IOException {
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("hello");
        arrayList.add("world");
        arrayList.add("java");
        BufferedWriter bw = new BufferedWriter(new FileWriter("arr.txt"));
        for(String s: arrayList) {
            bw.write(s);
            bw.newLine();
            bw.flush();
        }
        bw.close();
    }
}
同理 数据目的源也不一定是文件,可以把数据从文件读取到数组中去(使用bufferedReader)
例 点名器
需求:有一个文件里面存储了班级同学的姓名,没一个姓名占一行,要求通过程序实现随机点名
思路:将数据从文件读取到集合中,并释放流资源
随机产生0到集合长度的随机数,根据随机数找到索引对应的数据
public class CallNameDemo {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("src/name.txt"));
        ArrayList<String> names = new ArrayList<String>();
        String line;
        while((line = br.readLine())!=null){
            names.add(line);
        }
        br.close();//重点
        Random random = new Random();
        int index = random.nextInt(names.size());//[0-size)之间的数
        
        System.out.println(names.get(index));
    }
}
例 把ArrayList集合中的学生数据写入到文本文件
把每个学生对象的数据作为文件中的一行数据,格式:学号,姓名,年龄,居住地 略
ArrayList从学生对象,拼接是使用StringBuilder
略
例 复制单级文件夹
需求:把“E:\itcast"把这个文件夹复制到模块目录下
思路:
1.创建数据源目录File对象,路径是E:\itcast
2.获取数据源目录File对象的名称(itcast)
3.创建目的地目录File对象,路径名是新的模块名+itcast组成
4.判断目的地目录对应的File是否存在,如果不存在就创建
5.获取数据源目录下所有文件的File数组
6.遍历File数组,得到每一个file对象,该File对象,其实就是数据源文件
7.获取数据源文件File对象名称
8.创建目的地文件File对象
9.复制文件(文件有文本,图片视频,采用二进制形式复制)
package io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyFolderDemo {
	public static void main(String[] args) throws IOException {
		File source = new File("/home/userID/source");
		File desination = new File("/home/userId/tmp");
		
		String sourceName = source.getName();
			
		
		//File dir = new File(desination.getPath()+"/"+sourceName);
		File dir = new File(desination.getPath(),sourceName);
		
		if(!dir.exists()){
			dir.mkdirs();
		}
		String filename;
		File desName;
		File[] fileList = source.listFiles();
		for(File file:fileList){
			filename = file.getName();
			//desName = new File(dir.getPath()+"/"+filename);
			desName = new File(dir.getPath(),filename);
			copyFile(file,desName);
		}
		System.out.println(sourceName);
		
	}
	public static void copyFile(File a, File b) throws IOException{
		BufferedInputStream bis = new BufferedInputStream( new FileInputStream(a));
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(b));
		
		byte[] b1 = new byte[1024];
		int len;
		while((len=bis.read(b1))!=-1){
			bos.write(b1, 0, len);
		}
		bos.close();
		bis.close();
	}
	
}
3.10 exception 处理
- try...catch...finally做法:
try{
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
}finally{
执行所有清除操作;
} - JKD7改进方案,自动释放资源:
try(定义流对象){
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
} - JDK9改进方案,自动释放资源:
定义输入流对象
定义输出流对象
try(输入流对象;输出流对象){
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
} 
4 特殊操作流
4.1标准输入输出流
- System类的两个静态的成员变量
 
- public static final InputStream in:默认已经打开,通常对应键盘输入
 - public static final PrintStream out 默认已经打开,通常对应显示输出
 
- 键盘录入
自己写键盘录入数据 
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
可是写起来太麻烦了,java提供了一个类实现键盘录入 - Scanner sc = new Scanner(System.in)
例 
package io;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
public class SystemInDemo {
	public static void main(String[] args) throws IOException {
	//	InputStream is = System.in;
		
        /*		
                int by;
		while((by=is.read())!=-1){
			System.out.print((char)by);
		}*/
                //如何把字节流转换为字符流?用转换流
		//InputStreamReader isr = new InputStreamReader(is);
                //使用字符流不能实现一次读取一行数据
                //需要用缓冲流对字符流包装下,但是读取的是字符串
		//BufferedReader br = new BufferedReader(isr);
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		System.out.println("please input string");
		String line = br.readLine();
		System.out.println("the string is "+line);
		
                //想要读取非字符串,要用包装类对其进行转换
		System.out.println("please input int data");
		int i = Integer.parseInt(br.readLine());
		System.out.println("the string is "+i);
		
		//自己写键盘录入太麻烦了,java提供Scanner
		Scanner sc = new Scanner(System.in);	
	}
}
- 输出语句的本质:是一个标准的字节输出流
 
- PrintStream ps = System.out
 - PrintStream类有的方法,System.out都可以使用
 
4.2 打印流
- 字节打印流: PrintStream
 - 字符打印流: PrintWriter
 - 打印流的特点:
- 只负责输出数据,不负责读取数据
 - 有自己的特有方法(除了write还有print)
 
 
4.2.1 字节打印流
- PrintStream(String fileName):使用指定的文件名创建新的打印流
 - 使用write方法(继承父类字节流的方法),查看的时候会转码,使用自己特有的方法print,数据会原样输出
例 
	PrintStream ps = new PrintStream("fos.txt");
	ps.write(97);
	ps.print(98);
	ps.println();
	ps.println(48);
	ps.close();
输出:
a98
48
4.2.2 字符打印流
- 构造方法
- PrintWriter(String fileName)使用指定的文件名创建,没有自动刷新,手动调用flush()
 - PrintWriter(Writer out, boolen autoFlush)
out为字符输出流
autoFlush:一个布尔值,真则在println,printf或者format时将刷新输出缓冲区
字符流需要刷新才会执行底层写数据
例 把模块目录下的java文件复制到另一个模块目录下
打印流只负责写操作,输入流可以用BufferedReader,readline 
 
package io;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class CopyJavaDemo3 {
	public static void main(String[] args) throws IOException {
		
		BufferedReader br = new BufferedReader(new FileReader("src/io/PrintStreamDemo.java"));
		char[] cs = new char[1024];
		String line;
		PrintWriter pw = new PrintWriter(new FileWriter("src/collections/1.java"), true);
		while((line =br.readLine())!=null){
			pw.println(line);
		}
		pw.close();
		br.close();
	}
}
4.3 对象序列化流
- 
对象序列化:将对象保存在磁盘中,或者在网络中传输对象
使用一个字节序列表示一个对象,该字节序列包括:对象的类型,对象的数据和对象中存储的属性等信息
字节序列写到文件之后,相当于文件中持久保存了一个对象的信息
反之,该字节序列还可以从文件中读取回来重构对象,对它进行反序列化 - 
要实现序列化和反序列化就要用对象序列化流和对象反序列化流
- 对象序列化流:ObjectOutputStream
 - 对象反序列化流:ObjectInputStream
 
 
4.3.1 对象序列化流:ObjectOutputSteam
- 将Java对象的原始数据类型和图形写入OjbectOutputStream.可以使用ObjectInputStream读取(重构)对象,可以通过使用流的文件来实现
对象的持久存储。如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象。 - 构造方法
- ObjectOutputStream(OutputStream out)创建一个写入指定的OutputStream的ObjectOutputStream
 
 - 序列化对象的方法:
- void writeObject(Object obj):将指定的对象写入ObjectOutputStream
 
 - 注意:
- 一个对象要想被序列化,对应的类必须实现Serializable接口
 - Serializable是一个标记接口,实现该接口不需要重写任何方法
例:
Student.java 
 
public class Student implements Comparable,Serializable {
	private String name;
	private int age;
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
...略
ObjectOutputSreamDemo.java
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import collections.Student;
public class ObjectOutputStreamDemo {
	public static void main(String[] args) throws FileNotFoundException, IOException {
		ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("src/io/oos.txt"));
		Student s = new Student("xiaowang",21);
		oos.writeObject(s);
		oos.close();
	}
}
NotSerializableException 抛出一个实例需要一个Serializable接口。 序列化运行时或实例的类可能会抛出此异常
类的序列化由实现java.io.Serializable接口的类启用。 不实现此接口的类将不会使任何状态序列化或反序列化( 序列化接口没有方法或字段,仅用于标识可串行化的语义)
4.3.2 对象反序列化流ObjectInputStream
- ObjectInputStream可以反序列化先前使用ObjectOutputStream编写的原始数据和对象
 - ObjectInputStream(InputStream in) 构造方法创建从指定的InputStream读取的ObjectInputSteram
 - readObject()从ObjectInputStream读取一个对象
 
public class ObjectInputStreamDemo {
	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src/io/oos.txt"));
		Student s = (Student)ois.readObject();
		System.out.println(s.getName()+" is "+ s.getAge());
		ois.close();
	}
}
4.3.3 对象序列化流的三个问题
- 用对象序列化流序列化一个对象后,如果我们修改了对象所属的类文件,读取数据会不会出问题呢?
- 会抛出InvalidClassException异常
 
 - 如果出问题,如何解决呢?
- 在对像所对应的类例显示声明serialVersionUID
private static final long serialVersionUID = 42L 
 - 在对像所对应的类例显示声明serialVersionUID
 - 如果一个对象的某个成员变量的值不想被序列化,又如何实现呢?
- 给改成变量加transient关键字,标记成员变量不参加序列化。
反序列化时可以读取该变量值,但是是常规默认值,不是实际值 
 - 给改成变量加transient关键字,标记成员变量不参加序列化。
 - InvalidClassException当序列化运行时检测到类中的以下问题之一时抛出。
- 类的串行版本与从流中读取的类描述符的类型不匹配
 - 该类包含未知的数据类型
 - 该类没有可访问的无参数构造函数
 
 - serialVersionUID
- 默认根据类的各个方面按照一定规则计算该值,可能不同编译器计算的值存在差异,导致误报InvalidClassException。
 - 为保证不同Java编译器间一致,通常手动定义private static final long serialVersionUID = 42L
 
 
4.4 Properties
4.4.1 Properties 概述
- Properties是一个Map体系的集合类
 - Properties可以保存到流中或从流中加载
 
public class PropertiesDemo01 {
	public static void main(String[] args){
	// properties不同与map不能写泛型
	//	Properties<String,String> p = new Properties<String,String>();
		Properties prop = new Properties();
		prop.put("name1", "zhangsan");
		prop.put("name2", "lisi");
		prop.put("name3","wanger");
		
		//按照集合的方式遍历properties,但注意返回的键和值都是Object类型
		Set<Object> keySet = prop.keySet();
		for(Object key : keySet){
			System.out.println(key+" is "+prop.get(key));
		}
	}
}
4.4.2 特有方法(针对string)
- Object setProperty(String key,String value) 包装put方法,使其直接收string类型参数
 - String getProperty(String key)
 - Set
stringPropertyName();返回键集,对应值是字符串  
4.4.3 Properties和IO流结合的方法
- void load(InputStream inStream) 字节流读取属性列表(键和元素对)
 - void load(Reader reader)字符流读取属性列表
 - void store(OutputStream out, String comments) 字节流写入属性列表
 - void store(Writer writer, String comments) 字符流写入属性列表
例1 
public class PropertiesDemo02 {
	public static void main(String[] args) throws IOException {
		//myStore();
		myLoad();
	}
	private static void myStore() throws IOException{
		Properties prop = new Properties();
		prop.setProperty("zhangsan", "24");
		prop.setProperty("lisi", "29");
		prop.setProperty("wangwu", "18");
		prop.store(new FileWriter("src/1.txt"), "store");
	}
	private static void myLoad() throws FileNotFoundException, IOException{
		Properties prop = new Properties();
		prop.load(new FileReader("src/1.properties"));
		Set<String> keySet = prop.stringPropertyNames();
		for(String key : keySet){
			System.out.println(key+" , "+prop.getProperty(key));
		}
	}
}
例2 猜数字小游戏,只能试玩3次。如果还想玩,提示试玩已结束,想玩请充值
1.写一个游戏类,里面有个猜数字的小游戏
2.写一个测试类,main()方法按下面步骤完成:
A.从文件中读取数据到Properties集合,用load()方法实现
文件已存在:game.txt里面数据count=0(注意map的格式等于)
B.通过Properties集合获取到玩游戏的次数
C.判断次数是否到达3次
如果到了,给出提示,游戏试玩已经输,想玩请充值
如果不到3次
玩游戏
次数+1,重新写回文件,用Properties的store()方法实现
Game.java
package io;
import java.util.Random;
import java.util.Scanner;
public class Game {
	public static void play(){
		Random random = new Random();
		int value = random.nextInt(10)+1;
		System.out.println("please guess the number from 1~ 10");
		
		while(true){
			Scanner sc = new Scanner(System.in);
			int input = sc.nextInt();
			if(input>value){
				System.out.println("your input is bigger");
			}else if(input<value){
				System.out.println("your input is smaller");
			}else{
				System.out.println("Success"+value+"="+input);
				break;
			}
		}
	}
}
TestGame.java
package io;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
public class TestGame {
	public static void main(String[] args) throws FileNotFoundException,
			IOException {
		Properties prop = new Properties();
		FileReader fr = new FileReader("src/game.txt");
		prop.load(fr);
		fr.close();// 注意关闭流close!!!
		int count = Integer.parseInt(prop.getProperty("count"));
		boolean trival = true;
		if (count >=3 && trival) {
			System.out
					.println("have tried 3 times,please buy it www.baiduChargeMoney.com");
		} else {
			Game.play();
		}
		count++;
		prop.setProperty("count", String.valueOf(count));
		FileWriter fw = new FileWriter("src/game.txt");
		prop.store(fw, null);
		fw.close();
	}
}
                    
                

                
            
        
浙公网安备 33010602011771号