Java基础_10_IO流总结


_____IO(Input Output)流_____


概述

IO流就是用来处理设备之间的数据传输的,而Java对数据的的操作时通过流的方式,其操作流的类都在IO包中。


流分类

按照操作的数据分为:字节流和字符流

按流向分为:输入流,输出流


IO流常用基类

字节流的抽象基类:InputStream,OutputStream
字符流的抽象基类:Reader,Writer

在说IO流之前,我们先要理解几个概念:
字节:一字节(byte)等于8位二进制码,在计算机中存储的信息都是以二进制形式表示的
字符:为了方便人机交互,将人类语言和计算机二进制语言之间建立一个映射表(编码表),通过不同的二进制码来表示不同的文字,所看到的人类语言字体就是字符,一个字符因为编码的不同会占用不同的字节数
编码表:人类文字在计算机中的存储映射,编码表按照时间顺序常见的有ASCII,GB2312中文对应表,扩容中文码表GBK,扩充中文码表GB18030(区分中文编码的方法是高字节的最高位不为0,从ASCII到GB18030是向下兼容的);为了整合世界文字编码,出现了unicode字符编码标准,而UTF-8编码表就是其实现方式之一
乱码:
0000 0000计算机中八位为1字节,ASCII编码表中用0100 0001来代表文字"A"。同一个文字在不同的编码表中映射可能不一致,比如在GBK编码表中文字“黑”的编码为BADA,在UTF-8中“黑”为E9BB91,当我们以GBK形式将黑(BADA)存入计算机文本文件,在读取文本文件的时候对照UTF-8编码表翻译,那么取出来的将不是文字“黑”,而是其他文字或者查询不到任何文字,这就是乱码。

字符流

概述:

对于数据而言,文本居多,所以Java单独建立了字符流来处理文本数据


文本文件流:FileWriter和FileReader

概述:

这是字符流抽象基类的两个实现子类,主要是对文本文件进行IO操作。

FileWriter写出文件实例:

FileWriter fw = null;
try {
	//只能创建文件,不能创建路径,路径必须为已有路径
    fw = new FileWriter("D:/demo.txt");  
    fw.write("黑马");
    //此类具有缓存机制,写入的数据暂时保存在流中,需要刷新才能写入到文本文件中
    //close()关闭流时会自动执行一次刷新操作,所以此处可以省略
    fw.flush();
} catch (IOException e) {  
    e.printStackTrace();
} finally {  
    if (fw != null)  
        try {
            fw.close();  
        } catch (IOException e) {  
            e.printStackTrace();
        }  
}

FileReader读入文件实例:

FileReader fr = null;
try {
	fr = new FileReader("D:/demo.txt");
	char[] cbuf = new char[1];// 读取到数据在非UTF-8编码下为2KB大小,char为2字节16位;UTF-8编码下为3KB大小
	int len = -1;
	while ((len = fr.read(cbuf)) != -1) {
		System.out.println(len + ":" + new String(cbuf, 0, len));
		System.out.println(Integer.toHexString(cbuf[0]));
		/* 
		 * Windows系统创建的UTF-8文本文件通常会在文本的开头放置一个标识字节序的编码;
		 * 所以自己通过windows系统创建的文本可能出现读取出来的第一位长度为1,内容为空,存储的是字节序
		 * 字节序分为如下两类:
		 * FFFE:Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端;
		 * FEFF:Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端; 
		 * 比如 int a = 0x05060708:
		 * 在BIG-ENDIAN的情况下存放为: 字节号 0 1 2 3 数据 05 06 07 08
		 * 在LITTLE-ENDIAN的情况下存放为: 字节号 0 1 2 3 数据 08 07 06 05
		 */
	}
} catch (FileNotFoundException e) {
	e.printStackTrace();
} catch (IOException e) {
	e.printStackTrace();
} finally {
	if (fr != null)
		try {
			fr.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
}

FileReader与FileWriter结合使用:

//读取文本文件并且写入另一个文本文件
FileReader fr = null;
FileWriter fw = null;
try {
	fr = new FileReader("D:/demo.txt");
	fw = new FileWriter("D:/demo2.txt");
	//无参数的read方法只能读取一个字符,对于硬盘磁针就好来回切换,照成时间的浪费
	//所以采用大小合适的一组数据
	char[] cbuf = new char[1024];
	int len = -1;
	while((len = fr.read(cbuf)) != -1) {
		fw.write(cbuf, 0, len);
	}
} catch (FileNotFoundException e) {
	e.printStackTrace();
} catch (IOException e) {
	e.printStackTrace();
} finally { //打开的流必须关闭,不然会占用较多资源
	if(fr != null)
		try {
			fr.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	if(fw != null)
		try {
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
}

字符流缓存装饰类:BufferedWriter和BufferedReader

概述:

针对文本IO流Java提供的字符流缓存装饰类,通过缓冲各个字符来得到更加高效的读写操作。

BufferedReader与BufferedWriter结合使用:

BufferedReader br = null;
BufferedWriter bw = null;
try {
	br = new BufferedReader(new FileReader("D:/demo.txt"));
	bw = new BufferedWriter(new FileWriter("D:/demo2.txt"));
	//BufferedReader可以直接读取文本中的一行
	String line = null;
	while((line = br.readLine()) != null) {
		//BufferedWriter可以直接将字符串写入到文本中
		bw.write(line);
		//不用写入换行符,可根据不同系统来自动换行
		bw.newLine();
		//Buffered能显著提高字符读写的效率,不过需要刷新,不然缓存在内存中不会写入硬盘
		bw.flush();
	}
} catch (FileNotFoundException e) {
	e.printStackTrace();
} catch (IOException e) {
	e.printStackTrace();
} finally {
	if(br != null)
		try {
			br.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	if(bw != null)
		try {
			bw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
}

行读取装饰类:LineNumberReader

概述:

这专门针对文本文件行读取的类,他提供了行读取,获取行号,标记等方法。

简单操作演示:

LineNumberReader lnr = null;  
try {  
    lnr = new LineNumberReader(new FileReader("./src/com/itheima/Demo.java"));  
    System.out.println(lnr.getLineNumber());  
      
    String line = null;  
    //int i=0;  
    while((line = lnr.readLine()) != null) {  
        System.out.println(lnr.getLineNumber()+": "+line);  
        /* 
        if(++i==10) { 
            lnr.mark(2);//在lnr流中标记一个位置,参数为超过多少字符后,标记“可能”会失效,失效时调用reset会抛出IOException异常 
            lnr.skip(12);//跳过多少字符,windows中回车符‘\r\n’算两个,会影响到下一行 
             
        } 
        if(i== 20) { 
            lnr.setLineNumber(0); 
            lnr.reset();//让流从新定义到mark所标记的位置。lineNumber也回到mark标记时的状态,可以在“后面”设置setLineNumber,在前面设置会被reset内部覆盖 
            lnr.setLineNumber(0); 
             
        } 
        */  
    }  
} catch (FileNotFoundException e) {  
    throw new RuntimeException("文件路径不存在失败");  
} catch (IOException e) {  
    throw new RuntimeException("读取失败");  
} finally {  
    if(lnr != null)  
        try {  
            lnr.close();  
        } catch(IOException e) {  
            throw new RuntimeException("关闭流失败");  
        }  
}  

字节流

概述:

通用的字节流,计算机中的所有数据都是按照字节的方法存储的,对于文件,音频,视频,图片等等所有数据都可以用字节流进行读写操作。


文件流:FileInputStream和FileOutputStream

概述:

这是字节流抽象基类的两个实现子类,主要是对文件进行IO操作。

FileInputStream读取文件实例:

public static void fileInputStreamDemo() throws IOException {
	FileInputStream fis = new FileInputStream("D:/demo.txt");
	/*
	 * 1.一次读取完整个文件
	 * 这种方式只适合较小的数据,JVM的默认内存为64M,读一个大小为1G的视频文件,则内存装不下。  
	 */
	//获取还没读取文件数据的长度
	int len = fis.available();
	//创建于文本数据长度相同的数组
	byte[] buf = new byte[len];
	//读取整个文件
	fis.read(buf);
	fis.close();
	//因为是文本文件,所以我们可以打印到控制台
	System.out.println(new String(buf));
	/*
	 * 2.每次读取固定长度数据
	 * 这种方式是最通用的
	 */
	fis = new FileInputStream("D:/demo.txt");
	//创建大小为1KB的缓存数组
	buf = new byte[1024];
	while((len = fis.read(buf)) != -1) {
		System.out.println(new String(buf, 0, len));
	}
	fis.close();
}


FileOutputStream写出文件实例:

public static void fileOutputStreamDemo() throws IOException {
	FileOutputStream fos = new FileOutputStream("D:/demo2.txt");
	fos.write("中关村黑马".getBytes());
	fos.close();
}

FileInputStream与FileOutputStream结合使用:

public static void fileStreamDemo() throws IOException {
	FileInputStream fis = new FileInputStream("D:/demo.txt");
	FileOutputStream fos = new FileOutputStream("D:demo2.txt");
	
	byte[] buf = new byte[1024];
	int len = -1;
	while((len = fis.read(buf)) != -1) {
		fos.write(buf, 0, len);
	}
	fis.close();
	fos.close();
}

字节流缓存装饰类:BufferedInputStream和BufferedOutputStream

概述:

与字符流一样拥有缓存装饰类,通过缓冲各个字符来得到更加高效的读写操作。

BufferedInputStream和BufferedOutputStream结合使用:

public static void bufferedStreamDemo() throws IOException {
	//我们来拷贝一份Mp3文件
	BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:/song.mp3"));
	BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:song2.mp3"));
	
	byte[] buf = new byte[1024<<3];
	int len = -1;
	while((len = bis.read(buf)) != -1) {
		bos.write(buf, 0, len);
	}
	
	bis.close();
	bos.close();
}
/** 
 * 使用read()无参数方法读取单字节的时候返回类型为int,还涉及到一个原理, 
 * 在Byte转Int的时候,有两种情况。 
 * 1.保持值不变:int i = (int)b; 
 * 2.保持最低字节不变:int i = b & 0xff; 
 * 由于read读取的是byte而返回的是int类型,如果中间有“1111-1111”字节存在 
 * 采用1:则返回同样的值“-1”也就是“11111111-11111111-11111111-11111111” 
 * 采用2:则返回“255”也就是“00000000-00000000-00000000-11111111” 
 * 抵用中方法会让值和字节流结束标识混淆,所以在read中采用的是2保持最低字节不变,其他补0 
 */  

File文件和文件夹类

概述:

在以上的操作中,我们每次都手写了文件的绝对路径,当一个文件使用多次时,维护起来就会巨困难,而且文件也是一类对象,于是File类出现了。

常用方法演示:

创建File对象和获取文件信息:

public class Demo {
	public static void main(String[] args) {
		//直接使用String绝对路径创建
		File file1 = new File("D:"+File.separator+"direc");
		//两段路径连接,中间可以没有分离符
		File file2 = new File("D:", "demo.txt");
		//也是两段连接,但是前者使用的是File对象
		File file3 = new File(file1, "kk/demo.txt");


		System.out.println("file1:"+file1); 
		System.out.println("file2:"+file2); 
		System.out.println("file3:"+file3); 
		
		System.out.println("路径:"+file3.getPath());  
		System.out.println("绝对路径:"+file3.getAbsolutePath());  
		System.out.println("文件或者文件夹名称:"+file3.getName());  
		System.out.println("父目录的路径名字:"+file3.getParent());  
		System.out.println("文件最后一次修改时间:"+file3.lastModified());  
		System.out.println("文件大小:"+file3.length());  
	}
}
/*
结果:
file1:D:\direc
file2:D:\demo.txt
file3:D:\direc\kk\demo.txt
路径:D:\direc\kk\demo.txt
绝对路径:D:\direc\kk\demo.txt
文件或者文件夹名称:demo.txt
父目录的路径名字:D:\direc\kk
文件最后一次修改时间:1453352590015
文件大小:7
*/

重命名文件:

//renameTo重命名方法内部就是拷贝和删除源文件,所以可以用来转移文件的位置
public static void main(String[] args) {
	//fi为需要重命名的文件,fo为重命名后的文件
	File fi = new File("D:"+File.separator+"demo.txt");
	File fo = new File("E:"+File.separator+"kk","demotwo.txt");
	//若源文件不存在,操作就没必要继续
	if(fi.exists()) {
		//先判断目标文件的父目录是否存在,不存在则创建
		if(!fo.getParentFile().exists())
			fo.getParentFile().mkdirs();
		//重命名并且转存文件,成功返回true
		System.out.println(fi.renameTo(fo));
	}
}


Properties键值对读取

概述:

在Java中配置文档一般使用properties和xml两种:properties读取的文档其实就是key=value的形式

简单操作演示:

/** 
 * 首位为#是注释信息不会被加载,其余必须为键值对格式 
 * @throws IOException 
 */  
public static void loadDemo() throws IOException {  
      
    Properties p = new Properties();  
    File f = new File("E:/editInfo.ini");  
      
    FileInputStream fis = new FileInputStream(f);  
    //从输入流中读取键值对
    p.load(fis);
    //将属性列表输出到指定的输出流,这里是输出到控制台 
    p.list(System.out);


    FileOutputStream fos = new FileOutputStream(f);
    //添加键值对
    p.setProperty("encoding", "GBK");
    //将此 Properties 表中的属性列表(键和元素对)写入输出字符。
    p.store(fos, "Encoding");  
      
    fis.close();  
    fos.close();  
}  

PrintStream打印流

概述:

向文本输出流打印对象的格式化表示形式,也就是格式化输出

简单操作演示:

/** 
 * 获取键盘输入 
 */  
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));  
  
PrintWriter out = new PrintWriter(System.out, true);  
//PrintWriter out = new PrintWriter("test.txt");  
//PrintWriter out = new PrintWriter(new FileWriter("test.txt"), true);  
//PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("test.txt")), true);  
  
String line = null;  
while( (line=bufr.readLine()) != null) {  
    //out.write(line.toUpperCase());  
    out.println(line);  
    //out.flush();  
}  
  
out.close();  
bufr.close();  

SequenceInputStream序列流(合并)

概述:

表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。 

文件的拆分和合并:

//拆分方法
public static void splitFile() throws IOException {
	//需要被拆分的源文件
    File source = new File("song.mp3");
    //拆分后文件的存储路径  
    File toDir = new File("splitdir");
    toDir.mkdirs();  
    BufferedInputStream bfis = new BufferedInputStream(new FileInputStream(source));  
    BufferedOutputStream bfos;  
    //定义拆分文件的大小为64KB
    byte[] bbuf = new byte[1024<<6];  
    int len = 0;  
    int count = 0;  
    while((len=bfis.read(bbuf)) != -1) {  
    	//拆分后文件的存储格式为源文件名+序号.part
        bfos = new BufferedOutputStream(new FileOutputStream(toDir.getAbsolutePath()+File.separator+"song"+count+++".part"));  
        bfos.write(bbuf, 0, len);  
        bfos.close();  
    }  
      
    bfis.close();  
}
//合并方法
public static void sequenceFile() throws IOException {
	//合并后文件的存储位置
    File toDir = new File("sequencedir");
    //需要合并的文件位置
    File sourceDir = new File("splitdir");  
      
    toDir.mkdirs();  
    //筛选出符合要求的合并文件
    File[] arr = sourceDir.listFiles(new FilenameFilter() {  
        @Override  
        public boolean accept(File file, String name) {  
            return name.startsWith("song") && name.endsWith(".part");  
        }  
    });  
    //对需要合并的文件进行排序
    Arrays.sort(arr, new Comparator() {  
        @Override  
        public int compare(File arg0, File arg1) {  
            return arg0.getName().compareTo(arg1.getName());  
        }  
    });


    BufferedInputStream bfis;  
    BufferedOutputStream bfos = new BufferedOutputStream(new FileOutputStream(new File(toDir,"song.mp3")));


    for(File f:arr) {  
        bfis = new BufferedInputStream(new FileInputStream(f));  
        byte[] bbuf = new byte[1024*2];
        int len = -1;
        while((len = bfis.read(bbuf)) != -1)
        	bfos.write(bbuf, o, len);
        bfis.close();  
    }  
    bfos.close();
}  

ObjectStream操作对象的流

概述:

被操作对象必须实现Serializable接口,此流可以将对象持久化到硬盘上。

简单操作演示:

//将对象持久化到硬盘
public static void writeObj() throws IOException {  
    File fto = new File("person.object");
    if(!fto.exists()) fto.createNewFile();
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fto, true));
      
    oos.writeObject(new Person("lis3", "男", 20));  
    oos.close();  
}
//将硬盘上的对象加载到内存中
public static void readObj() throws FileNotFoundException, IOException, ClassNotFoundException {  
    File fto = new File("person.object");  
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fto));
    Person p = null;  
    while((p = (Person) ois.readObject()) !=null) {  
        System.out.println(p);
    }
    ois.close();
}

实现Serializable接口类中会出现序列化标记 

 * 给类一个序列化标记,不显示申明则依据类中属性算:访问修饰符不同都不能读取 
 *  
 * static 和transient修饰的不能被持久化 
 */  
private static final long serialVersionUID = 1L;  
这个标记可以由用户显示赋值,也可以由Java根据类的字段和属性等等计算出来,主要是标记是否为同一个类

PipedStream管道流

概述:

管道流就是将输入和输出绑定到一根管道上,有输入才有输出,没输入则输出等待。

简单操作演示:

//创建一个输入的多线程对象
class Read implements Runnable {
	private PipedInputStream in;
	public Read(PipedInputStream in) {
		super();
		this.in = in;
	}
	@Override
	public void run() {
		//与常规无异的读取操作
		try {
			byte[] buf = new byte[1024];
			System.out.println("Read:等待数据写入");
			int len = in.read(buf);
			System.out.println("Read:数据读取成功");
			String s = new String(buf, 0, len);
			System.out.println(s);
			in.close();
		} catch (IOException e) {}
	}
}
//创建一个输出的多线程对象
class Write implements Runnable {
	private PipedOutputStream out;
	public Write(PipedOutputStream out) {
		super();
		this.out = out;
	}
	@Override
	public void run() {
		//与常规无异的写入操作
		try {
			System.out.println("Write:准备写入数据,等待6秒");
			Thread.sleep(6000);
			out.write("大眼猫".getBytes());
			System.out.println("Write:数据写入成功");
			out.close();
		} catch (Exception e) {}
	}
}
public class Demo {
	public static void main(String[] args) throws IOException {
		PipedInputStream in = new PipedInputStream();
		PipedOutputStream out = new PipedOutputStream();
		//通过此方法将输入和输出绑定到一根管道上面
		in.connect(out);


		//绑定后,输入输出就会形成线程之间的相互等待,有输入才有输出,就像我们在学习多线程的时候举例用到的售票模式,只是这里的同步在内部就已经封装好了
		Read r = new Read(in);
		Write w = new Write(out);
		new Thread(w).start();
		new Thread(r).start();
	}
}

RandomAccessFile随机访问流

概述:

此类封装了读和写的方法;它内部有个大型的byte类型数组,能对数据在任意点开始读取和修改:用它就可以实现多线程下载。

你可能感兴趣的:(Java,Base)