黑马程序员—java基础—IO流

                                                            ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

 

IO流的知识,在IO流这里,在正式步入IO流的学习的时候,会学习异常,然后学习递归的语法。最后学习文件复制。然后才学习了主要包含两大流:字符流和字节流的IO流

异常:

1.异常:在程序运行期间,代码中出现的一些非正常情况;
     这种异常情况,会导致我们的程序终止;这是我们不希望看到的;
2.Java为我们提供了"异常处理机制";当使用这种"异常处理机制"之后,
  一旦异常情况出现时,起码可以使我们的程序不会崩溃,而会继续健康的运行下去;

3.有些异常情况,可以通过"判断"来避免;
   但有些情况,是无法判断的,这时就必须使用异常处理机制来编写的我们的代码;
4.异常的体系结构:
 Throwable(类):
   |--Error(错误):由JVM抛出,但不需要程序捕获,因为捕获了也没办法继续运行了;

   |--Exception(异常):Exception 类及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件。
   |--运行时异常:所有RuntimeException及其子类
   |--非运行时异常:所有非RuntimeException类型的异常;

 JVM默认处理异常的方式:
 
 JVM遇到异常情况时:
 1.先确定异常的类型;
 2.到类库中,找到此类型的"异常类",并实例化一个此异常类的对象;
 3.看我们的程序是否"捕获";
 4.如果程序没有"捕获",JVM将把此异常信息打印到控制台
   格式:
   Exception in thread "main" 异常类名: 异常信息
     at cn.itcast.demo02_JVM默认是如何处理异常.Demo.main(Demo.java:10)
 5.将我们的程序终止;

异常处理的基本语法:
  try{
  //可能出现异常的代码
 }catch(异常类型  变量名){//异常类型:是try中代码可能出现的异常类型
   //如果try中代码出现异常,将调到此处执行;
  }
 
  执行流程:
 1.无异常:
   1).try之前的代码;
   2).try中的代码;
   3).try...catch...语句之后的代码;
  2.有异常:
   1).try之前的代码;
   2).try中的代码(某一行出现异常:try中的后续代码将不会被执行)
   3).JVM判断异常类型;
       实例化异常对象;
      判断我们程序是否"捕获"?
   4).如果程序希望"捕获":catch(异常类型:就是JVM得到的异常类型)
      这时,将会执行catch...中的代码;
   5).try...catch...的后续代码

多catch语句:
 
  try{
    //多行代码,可能产生多种异常
  }catch(异常类型1  变量名){
  }catch(异常类型2  变量名){
  }....
  }catch(异常类型n  变量名){
  }
 
  注意:
  1.在多catch语句中,"异常类型"可以是"平级关系",也可以是"子父关系";
  2.如果是"子父类关系","父类类型的异常"一定要放在catch列表的末

处理异常的两种方式示例:

1,方法中处理异常:(数组工具类就不写了)

import java.text.ParseException;
import java.util.Date;

/*
 * 方法中处理异常:
 * 注意:在方法体内,可以捕获异常,但不要"处理异常",而应该将异常对象交给调用者处理;
 * 
 * 1.throws关键字:
 * 		1).在方法的声明处(形参的后面)声明throws  XxxxException,YyyyException
 * 		2).表示此方法"可能"会抛出此种类型的异常;
 * 		3).当JVM运行方法代码时,如果出现这种类型的异常,将会将异常对象抛出,给调用者;
 * 		4).throws后面可以跟多个异常类型,中间用逗号(,)隔开
 * 		5).throws的异常类型:
 * 			1>.运行时异常:调用者可以不捕获;(但是如果出现异常,JVM仍然会将异常打印到控制台,并结束程序)
 * 			2>.非运行时异常:调用者必须处理(要么继续抛出,要么try...catch...),否则不能通过编译;
 */
public class Demo {
	public static void main(String[] args) {
		int[] array = new int[0];
		try{
			System.out.println("最大值:" + ArrayTools.getMax(array));
		}catch(NullPointerException e){
			System.out.println("空指针异常!");
		}catch(ArrayIndexOutOfBoundsException e){
			System.out.println("数组下标越界异常!");
		}
		
		//调用
		String str = "2015-11-15";
		
		try {
			Date date = ArrayTools.stringToDate(str);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		
		
		System.out.println("程序结束!");
      	}
  }


方法体内处理异常:

import java.text.ParseException;
import java.util.Date;

/*
 * 在方法体内,可以使用"throw(抛出)"一个异常对象:
 * 
 * 1.throw要放在方法体内,表示:"抛出"一个异常对象,
 *   异常对象,可以是由程序产生,也可以由JVM产生;
 * 2.throw后面跟的"异常对象",只能有一个;
 * 3.throw抛出的异常类型:
 * 		1).运行时异常:方法体可以不声明throws;调用者也可以不捕获;
 * 		2).非运行时异常:方法体必须声明throws此类型异常;调用者必须捕获或者继续抛出此异常,否则编译错误;
 */
public class Demo {
	public static void main(String[] args) {
		int[] intArray = {1,42,43,25,4253};
		System.out.println("数组最大值:" + ArrayTools.getMax(intArray));
		
		String str = "2015-11-15";
		try {
			Date date = ArrayTools.stringToDate(str);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		
	}
}


 

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/*
 * 对数组操作的类
 */
public class ArrayTools {
	//求int数组中的最大值:
	//由程序产生异常对象,并抛出;
	/*public static int getMax(int[] array)  {
		if(array == null){
			//抛出异常
			throw new NullPointerException("空指针异常!!");
		}
		if(array.length == 0){
			//抛出异常
			throw new ArrayIndexOutOfBoundsException("数组为零长度数组,无法获取数据!");
		}
		int max = array[0];
		for(int i = 0;i < array.length ; i++){
			max = array[i] > max ? array[i] : max;
		}
		return max;
		
	}*/
	//由JVM产生异常对象,由我们程序抛出
	public static int getMax(int[] array)  {
		try{
			int max = array[0];
			for(int i = 0;i < array.length ; i++){
				max = array[i] > max ? array[i] : max;
			}
			return max;
		}catch(NullPointerException e){
			throw e;
		}catch(ArrayIndexOutOfBoundsException e){
			throw e;
		}
		
	}
	
	//将一个字符串转换为一个Date对象:"2015-11-15"
	//此方法抛出一个"非运行时异常":
	public static Date stringToDate(String str) throws ParseException {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		Date date;
		
		try {
			date = sdf.parse(str);
		} catch (ParseException e) {
			throw e;
		}
		
		return date;
	}
	
}


throws和throw的区别:

1.throws:
  1).用在方法的声明处;
  2).throws后面跟的"异常类名",可以有多个;
  3).throws表示:此方法"可能"会产生此类型异常。如果产生异常,异常对象是由JVM产生;
  4).声明抛出的异常类型:
   1>.运行时异常:调用者可以不捕获;
   2>.非运行时异常:调用者必须捕获(或者抛出);
2.throw:
  1).用在方法体内;
  2).throw后面跟的"异常对象",只能有一个;
  3).throw表示:抛出了一个"异常对象"。异常对象已经产生了(可以由程序产生,也可以由JVM产生)
  4).抛出的异常类型:
   1>.运行时异常:方法声明处可以不声明throws此类型异常;调用者也可以不捕获;
   2>.非运行时异常:方法声明处必须声明throws此类型异常;调用者必须捕获;
用一个面试题引出finally关键字:

面试题之final,finally和finalize的区别

1.final:表示最终的
  类:表示最终类;不能被继承;
  成员方法:表示最终方法;不能被重写;
  成员变量(局部变量):表示拥有最终的值(常量);其值只能被赋值一次,之后就不能再更改;
    基本数据类型:表示"值"不能被改变
    引用数据类型:表示"引用"不能被改变(堆中的值是可以被改变的);
2.finally:是异常处理的语法,表示无论是否产生异常,都会被执行的语句;
3.finalize:是Object类中的方法,我们子类都会继承此方法;
           表示:当垃圾回收器回收此对象前,会自动调用此方法,之后再回收此对象;
File类

1.Java中操作一个文件或者目录,可以使用File类对象,它可以代表磁盘上的
  一个文件,或者目录的信息;
2.java.io.File(类):它内部封装了一些方法,可以
  1).创建文件、目录;
  2).删除文件、目录;
  3).获取文件/目录的一些属性信息:大小、最后修改日期、文件名....

  注意:
   1).一个File对象表示的"文件/目录"可能在磁盘上不存在;这不影响File对象的构造;
        后期可以通过File类中的一些方法来判断此文件/目录是否存在;
     2).通过File,只能获取文件/目录的一些属性信息,不能对文件的内容进行读、写操作;
 
java.io.File(类):
构造方法:
 public File(String pathname):使用一个路径构造一个File(此路径可以是相对路径、绝对路径、文件路径、目录路径、可以存在,也可以不存在)
 public File(String parent,String child):使用父目录名,子文件名构造一个File对象;
 public File(File parent,String child):使用代表父目录的File对象和子文件的String构造一个File对象;
File类的成员方法:

创建功能
 public boolean createNewFile():创建文件(如果成功创建返回true,否则(文件已经存在)返回false)
 public boolean mkdir():创建单级目录
 public boolean mkdirs():创建多级目录

public boolean delete():可以删除文件、可以删除目录(一定要是空目录)

public boolean renameTo(File dest):将当前File对象,重命名到dest表示的路径下;

注意:
1.如果dest和原文件不在同目录下,重命名操作将会相当于"剪切"操作;
2.如果dest和原文件在同目录下,相当于"重命名"判断功能
public boolean isDirectory():判断是否是一个目录;
public boolean isFile():判断是否是一个文件;
public boolean exists():判断文件/目录是否存在;
public boolean canRead():判断是否可读;
public boolean canWrite():判断是否可写;
public boolean isHidden():判断是否隐藏;

File类的基本获取功能:

public String getAbsolutePath():获取绝对路径
public String getPath():获取File封装的路径
public String getName():获取文件/目录的名称
public long length():获取文件的大小(单位:字节)
public long lastModified():最后修改时间(单位:毫秒)

File类的高级获取功能:

public String[] list():如果当前File表示的是一个目录,则返回此目录下所有的子文件/子目录的名称的String[]数组;
public File[] listFiles():如果当/File[]数组;

区别:如果仅仅想获取文件名,使用第一种;
    如果需要对子文件/子目录进行进一步的操作,可以使用第二种;
输出指定目录下指定后缀名的文件名称案例:

public class Demo {
	public static void main(String[] args) {
		File file = new File("C:\\aaa");
		//1.获取目录下所有子目录/子文件的File[]数组;
		File[] fileArray = file.listFiles();
		//2.遍历数组,取出每个File;
		for(File f : fileArray){
			//3.判断File是否是文件
			if(f.isFile() && f.getName().endsWith(".txt")){//String-->endsWith()
				System.out.println(f.getAbsolutePath());
			}
		}
	}
}

文件名称过滤器:

import java.io.File;
import java.io.FilenameFilter;

/*
 * FilenameFilter文件名称过滤器:
 * 
 * 1.之前我们讲过两个方法:
 * 		String[] list():
 * 		File[] listFiles():
 * 2.File中还有两个重载的方法:
 * 		String[] list(FilenameFilter filter):
 * 		File[] listFiles(FilenameFilter filter):
 */
public class Demo {
	public static void main(String[] args) {
		File file = new File("C:\\aaa");
		String[] fileNameArray = file.list(new FilenameFilter(){
			@Override
			public boolean accept(File dir, String name) {
			//	return false;//任何文件都不匹配
			//	return true;//全部匹配
				//写我们自己的逻辑
				System.out.println("过滤器:dir = " + dir + " name = " + name);
				File file = new File(dir,name);//new File("C:\\aaa","1.mp4")-->相当于:new File("C:\\aaa\\1.mp4");
				if(file.isFile() && file.getName().endsWith(".txt")){
					return true;
				}else{
					return false;
				}
			}});
		System.out.println("打印:");
		for(String s : fileNameArray){
			System.out.println(s);
		}
	}
}

批量修改文件名称的案例:

import java.io.File;
import java.io.FilenameFilter;

/*
 * 批量修改文件名称案例
 * 例如:将C:\\20151018\\day18目录下所有的.avi文件重命名至:C:\\20151018\\day18\\avi目录下,
 *     并将文件名中的"集合框架"字样去掉;
 * 
 * 思路:
 * 1.使用File封装初始目录:new File("C:\\20151018\\day18");
 * 2.封装目标目录:new File("C:\\20151018\\day18\\avi");
 * 3.判断目标目录是否存在?否:创建目录;
 * 4.获取原目录下所有的.avi文件:使用过滤器:File[] listFiles(过滤器)
 * 5.遍历数组,获取每个File对象;
 * 		1).处理文件名:将原名中"集合框架"字样去掉;
 * 		2).renameTo()重命名到目标目录中;
 * 
 */
public class Demo {
	public static void main(String[] args) {
		//1.封装初始目录
		File srcFile = new File("C:\\20151018\\day18");
		//2.封装目标目录
		File destFile = new File("C:\\20151018\\day18\\avi");
		//3.判断目标目录是否存在
		if(!destFile.exists()){
			destFile.mkdir();
		}
		//4.获取原目录下所有的.avi文件
		File[] fileArray = srcFile.listFiles(new FilenameFilter(){
			@Override
			public boolean accept(File dir, String name) {
				File file = new File(dir,name);
				if(file.isFile() && file.getName().endsWith(".avi")){
					return true;
				}else{
					return false;
				}
			}});
		//5.遍历数组
		for(File f : fileArray){
			//获取文件名
			String fileName = f.getName();//17.19_集合框架(产生10个1-20之间的随机数要求随机数不能重复).avi
			fileName = fileName.replace("集合框架", "");//17.19_(产生10个1-20之间的随机数要求随机数不能重复).avi
			//重命名
			f.renameTo(new File(destFile,fileName));//f.renameTo("C:\\20151018\\day18\\avi\\17.19_(产生10个1-20之间的随机数要求随机数不能重复).avi");
			
		}
		System.out.println("执行完毕!收队!!");
	}
}

递归:

1.递归:方法可以调用本身,叫:方法的递归调用;
2.递归的注意事项:
  1).递归必须要有出口,否则就是死递归;
  2).递归的层次不能太深,否则堆栈溢出;
  3).构造方法不能递归调用:编译错误

递归的经典用法有求乘积,代替for循环,但是递回损耗内存。这里只演示用递归操作文件夹的经典案例:

import java.io.File;

/*
 * 递归删除带内容的目录案例
 * 
 * 1.我们知道删除文件或者目录使用File-->delete()方法,但此方法只能删除文件和空目录;
 *   所以我们需要使用递归,遍历目录,先删除目录下所有文件,然后再删除目录;
 * 思路:
 * 
 * 1.封装起始目录(也可能是文件);
 * 2.判断是文件还是目录:
 * 		是文件:删除
 * 		是目录:
 * 			1).获取目录下所有子文件和子目录;
 * 			2).遍历数组
 * 			3).回到步骤2(从步骤2到这里,可以形成递归调用)
 */
public class Demo {
	public static void main(String[] args) {
		//1.封装起始目录
		File file = new File("C:\\20151018 - 副本");
		//2.调用递归方法
		deleteFile(file);
	}
	private static void deleteFile(File file) {
		if(file == null){
			return;
		}
		//2.判断文件或目录
		if(file.isFile()){//删除
			file.delete();
			System.out.println("删除文件:" + file.getAbsolutePath());
		}else{//目录
			//1).获取目录下所有子文件和子目录;
			File[] fileArray = file.listFiles();
			if(fileArray!= null){
				for(File f : fileArray){
					deleteFile(f);
				}
			}
			//删除目录
			file.delete();
			System.out.println("删除目录:" + file.getAbsolutePath());
		}
		
	}
}

接下来,正式步入IO流的学习:

先把IO流的主要流的大纲画出来,分两部分学习,第一部分是重点流,第二部分是其他流

1.字节流:可以操作二进制文件、文本文件
  1).输出流:OutputStream(抽象类)
     |--FileOutputStream(类):
  2).输入流:InputStream(抽象类)
     |--FileInputStream(类):
2.字符流:只能操作文本文件
  1).输出流:Writer(抽象类)
  2).输入流:Reader(抽象类)

一,基本字节流:

字节输出流(写):

java.io.FileOutputStream(类):
构造方法:文件可以不存在,会自动创建
  FileOutputStream(String fileName) :创建一个向具有指定名称的文件中写入数据的输出文件流。
  FileOutputStream(File file) : 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
  FileOutputStream(String name, boolean append)  创建一个向具有指定 name 的文件中写入数据的输出文件流。
  FileOutputStream(File file, boolean append)创建一个向指定 File 对象表示的文件中写入数据的文件输出流。

后两种是追加写入

输出的方法:
  write(int n):输出一个字节;
  write(byte[] b):输出一个字节数组;
  write(byte[] b,int off,int len):输出一个字节数组的一部分;
成员方法啊:
  close():关闭流

字节输入流(读):

java.io.FileInputStream(类):
构造方法:文件必须存在,否则运行时异常;
  FileInputStream(String name) 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
  FileInputStream(File file) :通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
读取的方法:

int read():读取一个字节
int read(byte[] byteArray):读取若干个字节,填充参数的byte[]数组;
经典案例:基本字节流复制文本文件:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/*
 * 复制文件:
 * 
 * 1.输入流:FileInputStream
 * 2.输出流:FileOutputStream:
 * 
 * 读写的方式:
 * 		1).一次读写一个字节;
 * 		2).一次读写一个字节数组:
 * 
 * 此例:可以用复制任何的文件,包括二进制文件,只需要将文件名更改为指定的名称即可;
 */
public class Demo {
	public static void main(String[] args)  {
		FileInputStream in = null;
		try {
			in = new FileInputStream("C:\\aaa\\哥有老婆.mp4");
			FileOutputStream out = new FileOutputStream("哥有老婆_copy.mp4");
			
			//一次读写一个字节
			/*int n = 0;
			while((n = in.read()) != -1){
				out.write(n);//直接将字节输出
			}*/
			//一次读写一个字节数组
			
			byte[] byteArray = new byte[1024];
			int n = 0; 
			long start = System.currentTimeMillis();
			
			while((n = in.read(byteArray)) != -1){
				//	System.out.println("n = " + n);
				out.write(byteArray,0,n);
			}
			long end = System.currentTimeMillis();
			System.out.println("复制时间:" + (end - start) + " 毫秒");
			//释放资源
			in.close();
			out.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		System.out.println("复制完毕!");
	}
}

与基本字节流对应的两个流分别是高效字节流,也叫缓冲流,其效率比基本流高,方法简单的列举一下:

字节流:
  1).输出流:OutputStream(抽象类):
     |--FileOutputStream(类--基本字节输出流)
     |--FilterOutputStream(不学):
      |--BufferedOutputStream(类--缓冲字节输出流):
       构造方法:
       1).BufferedOutputStream(OutputStream out) :
       输出的方法:
       (无特有的,都是继承的:三种输出的方法)

  2).输入流:InputStream(抽象类):
     |--FileInputStream(类--基本字节输入流)
     |--FilterInputStream(不学):
      |--BufferedInputStream(类--缓冲字节输入流):
       构造方法:
       1).BufferedInputStream(InputStream in):
       读取的方法:
       (无特有的,都是继承的:两种读取的方法)
案例:字节流读写文件的四种方式的效率比较:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/*
 * 字节流四种读写方式效率的对比:
 * 
 * 基本字节流:
 * 		FileOutputStream:
 * 		FileInputStream:
 * 		1).一次读写一个字节:			method1();	54986 毫秒
 * 		2).一次读写一个字节数组:		method2();	97 毫秒
 * 缓冲字节流:
 * 		BufferedOutputStream:	
 * 		BufferedInputStream:	
 * 		3).一次读写一个字节:			method3();	666 毫秒
 * 		4).一次读写一个字节数组:		method4();	38 毫秒
 */
public class Demo {
	public static void main(String[] args) throws IOException {
		long start = System.currentTimeMillis();
	//	method1();//54986 毫秒
	//	method2();//97 毫秒
	//	method3();//666毫秒
		method4();//38 毫秒
		long end = System.currentTimeMillis();
		System.out.println("执行时间:" + (end - start) + " 毫秒");
	}
	//基本流:一次读写一个字节
	private static void method1() throws IOException {
		//1.输入流:
		FileInputStream in = new FileInputStream("C:\\aaa\\哥有老婆.mp4");
		//2.输出流
		FileOutputStream out = new FileOutputStream("哥有老婆_copy1.mp4");
		//一次读写一个字节
		int n = 0;
		while((n = in.read()) != -1){
			out.write(n);
		}
		//释放资源
		in.close();
		out.close();
	}
	//基本流:一次读写一个字节数组
	private static void method2() throws IOException{
		//1.输入流:
		FileInputStream in = new FileInputStream("C:\\aaa\\哥有老婆.mp4");
		//2.输出流
		FileOutputStream out = new FileOutputStream("哥有老婆_copy2.mp4");
		//一次读写一个字节数组
		byte[] byteArray = new byte[1024];
		int n = 0;
		while((n = in.read(byteArray)) != -1){
			out.write(byteArray,0,n);
		}
		in.close();
		out.close();
	}
	//缓冲流:一次读写一个字节
	private static void method3() throws IOException{
		//1.缓冲输入流
		BufferedInputStream bufIn = new BufferedInputStream(new FileInputStream("C:\\aaa\\哥有老婆.mp4"));
		//2.缓冲输出流
		BufferedOutputStream bufOut = new BufferedOutputStream(new FileOutputStream("哥有老婆_copy3.mp4"));
		//一次读写一个字节
		int n = 0;
		while ((n = bufIn.read()) != -1){
			bufOut.write(n);
		}
		bufIn.close();
		bufOut.close();
	}
	//缓冲流:一次读写一个字节数组
	private static void method4() throws IOException{
		//1.缓冲输入流
		BufferedInputStream bufIn = new BufferedInputStream(new FileInputStream("C:\\aaa\\哥有老婆.mp4"));
		//2.缓冲输出流
		BufferedOutputStream bufOut = new BufferedOutputStream(new FileOutputStream("哥有老婆_copy4.mp4"));
		//一次读写一个字节数组
		byte[] byteArray = new byte[1024];
		int n = 0;
		while((n = bufIn.read(byteArray)) != -1){
			bufOut.write(byteArray , 0 , n);
		}
		bufIn.close();
		bufOut.close();
	}
}

字节流的知识以上,接下来就是字符流的知识,在写字符流的知识的时候,要明白转换流:

字符流:
  1).输出流:Writer
     |--OutputStreamWriter(类--转换流):是字符流通向字节流的桥梁
      构造方法:
      OutputStreamWriter(OutputStream s):使用平台默认字符集
      输出的方法(五种):
      1).write(int n):输出一个字符
      2).write(char[] c):输出一个字符数组
      3).write(char[] c ,int off, int len):输出字符数组的一部分;
      4).write(String str):输出一个字符串
      5).write(String str,int off, int len):输出字符串的一部分;
  2).输入流:Reader
     |--InputStreamReader(类--转换流):是字节流通向字符流的桥梁
      构造方法:
      InputStreamReader(InputStream in):使用平台默认字符集
      读取的方法(两个):
      int read():读取一个字符:返回值:读取的"字符"
      int read(char[] c):读取一个字符数组;返回值:读取的"字符数量"

基本字符流:

字符流:
  1.输出流:Writer
    |--OutputStreamWriter(类--转换流):
      |--FileWriter(类--纯字符流):
       构造方法:
       1).FileWriter(File file)根据给定的 File 对象构造一个 FileWriter 对象。
       2).FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。 追加
       3).FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。
       4).FileWriter(String fileName, boolean append) 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
      输出的方法:
      (无特有方法,都是继承:五种)
     
  2.输入流:Reader
    |--InputStreamReader(类--转换流):
      |--FileReader(类--纯字符流):
       构造方法:
       1).FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader。
       2).FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader
      读取的方法:
      (无特有方法,都是继承:两种)
高效字符流:也叫缓冲流:

2.字符流:
  1).输出流:Writer(抽象类):
     |--OutputStreamWriter(转换流):
       |--FileWriter(字符流--基本流):
     |--BufferedWriter(字符流--缓冲流):
      构造方法:
      1).BufferedWriter(Writer out):创建一个使用默认大小输出缓冲区的缓冲字符输出流。
      输出方法:
      (无特有方法,都是从父类继承的:五种)
      特有成员方法:
      void newLine():输出一个换行符;
  2).输入流:Reader(抽象类):
     |--InputStreamReader(转换流):
       |--FileReader(字符流--基本流):
     |--BufferedReader(字符流--缓冲流):
      构造方法:
      1).BufferedReader(Reader in) :创建一个使用默认大小输入缓冲区的缓冲字符输入流。
      读取的方法:
      (继承父类:两种)
      String readLine():读取一行数据(不读取换行符)
经典案例:使用IO流复制多级文件夹:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/*
 * 复制多级文件夹案例:
 * 例:将C:\\aaa目录复制到C:\\xxxx目录下
 * 
 * 
 * 大体思路:
 * 1.要遍历原目录下的所有目录-->使用递归
 * 2.要针对每个文件分别建立输入、输出流,分别复制;
 * 
 * 步骤:
 * 1.封装原目录;C:\\aaa
 * 2.封装目标目录:C:\\xxxx
 * 3.判断目标目录下,是否包含"原目录名称",如果不包含,创建:C:\\xxxx\\aaa
 * 4.获取原目录下所有的子文件/子目录;并遍历
 *   获取每个File对象,判断是否是文件:
 *   是:复制;
 *   否:回到3(从步骤3,到这里形成递归调用)
 */
public class Demo {
	public static void main(String[] args) throws IOException{
		//1.封装原目录
		File srcFile = new File("C:\\aaa");
		//2.封装目标目录
		File destFile = new File("C:\\xxxx");
		//递归调用
		copyDir(srcFile,destFile);
		System.out.println("复制完毕!");
	}

	private static void copyDir(File srcFile, File destFile) throws IOException {
		//3.判断目标目录下,是否包含"原目录名称",如果不包含,创建:C:\\xxxx\\aaa
		destFile = new File(destFile,srcFile.getName());
		if(!destFile.exists()){
			destFile.mkdir();
		}
		//4.获取原目录下所有的子文件/子目录;并遍历
		
		File[] fileArray = srcFile.listFiles();
		if(fileArray != null){
			for(File f : fileArray){
				if(f.isFile()){//是文件,直接复制
					//复制
					BufferedInputStream in = new BufferedInputStream(new FileInputStream(f));
					BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(new File(destFile,f.getName())));
					//一次读写一个字节数组
					byte[] byteArray = new byte[1024];
					int n = 0;
					while((n = in.read(byteArray)) != -1){
						out.write(byteArray,0,n);
					}
					in.close();
					out.close();
					
				}else{//是目录,递归调用
					copyDir(f,destFile);
				}
			}
		}
		
	}
}

经典案例:把一个文件中的字符串排序后再写入另一个文件案例

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;

/*
 * 
 * 
 * 1.将文件中的数据读取到一个StringBuffer中:
 * 2.遍历StringBuffer取出每个字符,并存储到TreeSet中;
 * 3.遍历TreeSet,将集合中的数据写入到另一个文件中;
 */
public class Demo {
	public static void main(String[] args) throws IOException {
		BufferedReader in = new BufferedReader(new FileReader("demo17.txt"));
		Set set = new TreeSet<>();
		//一次读取一行数据,并存储到一个StringBuffer中
		StringBuffer buf = new StringBuffer();
		String row = null;
		while((row = in.readLine()) != null){
			buf.append(row);
		}
		in.close();
		//遍历StringBuffer
		for(int i = 0;i < buf.length() ; i++){
			set.add(buf.charAt(i));
		}
		//将数据作为字符数组写入到文件
		FileWriter out = new FileWriter("demo17_output.txt");
		for(char c : set){
			out.write(c);
		}
		out.close();
		System.out.println("写入完毕!");
	}
}

以下都是IO流的其他流:做基本介绍

 LineNumberReader类:
 
1.跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),
      它们可分别用于设置和获取当前行号。 默认情况下,行编号从 0 开始。

2.数据输入输出流:

DataInputStream:可以读取Java的"基本数据类型";readXxxx()
DataOutputStream:可以输出Java的"基本数据类型";writeXxxx()

此类,可以读写Java基本数据类型,是按"写入",同时要按"字节"读取;即读取的数据必须和写入的数据顺序相同,否则读出乱码
3,字节数组缓冲流

ByteArrayOutputStream:此类实现了一个输出流,其中的数据被写入一个 byte 数组
ByteArrayInputStream:从"byte[]数组缓存区"中读取数据;它可以模仿字节输入流的方式,一次读取一个字节或一个字节数组;
4,打印流

1.字节打印流:PrintStream:
2.字符打印流:PrintWriter:

打印流的特点:
 只能操作目的地,不能操作数据(只有输出流,没有输入流)
可以操作任意类型的数据。
如果启动了自动刷新,能够自动刷新。--PrintWriter
可以操作文件的流
可以将PrintWriter作为普通Writer使用(五种输出的方法)

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

out.println("hello");//write() + flush() + newLine()

字节打印流:输出语句的本质:

import java.io.PrintStream;

/*
 * 打印流:
 * 
 * 1.PrintStream:字节打印流:System.out的类型
 * 2.PrintWriter:字符打印流(Demo08)
 */
public class Demo {
	public static void main(String[] args) {
		PrintStream out = System.out;
		out.println("HelloWorld");
		
		int[] intArray = {1,432,432,432};
		out.println(intArray);//地址:println(Object obj):
		
		char[] charArray = {'a','b','c'};
		out.println(charArray);//abc:println(char[] c):
	}
}

随机访问流:

RandomAccessFile:
RandomAccessFile类不属于流,是Object类的子类。但它融合了InputStream和OutputStream的功能。支持对随机访问文件的读取和写入。

getFilePointer():获取文件指针;
seek():设置文件指针

序列化流 案例:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

/*
 * 序列化流:
 * 
 * 1.序列化:是指将一个"对象(包含属性值)"存储到一个文件中,或者通过网络进行传输;
 * 
 * 序列化:ObjectOutputStream:
 * 		 构造方法:
 * 		ObjectOutputStream(OutputStream out) :
 * 		序列化的方法:
 * 		void writeObject(Object obj) :将指定的对象写入 ObjectOutputStream。 
 * 
 * 注意:当某个对象需要被"序列化"时,此类必须实现:Serializable(接口):
 *     (在Serializable接口中,没有任何方法,这种接口叫:标记接口;它类似于一个标记,某个类如果实现了这样的接口,表示这个类就具有了某种权限)
 * 
 * 
 * 反序列化:ObjectInputStream:
 * 		构造方法:
 * 		ObjectInputStream(InputStream in) 
 * 		反序列化的方法:
 * 		Object readObject() :从 ObjectInputStream 读取对象。 
 * 
 * 关于"序列号":
 * 		1.当一个类实现了Serializable后,系统会自动为这个类添加一个属性--它表示"序列号"的信息;
 *        这个"序列号"会被写入到文件中;
 *      2.当我们修改这个类的结构时,系统会自动更改此"序列号";所以这就导致了"类中的序列号"和"文件中的序列号"不匹配的问题;
 *      3.建议:每个需要被"序列化"的类,都要显示的指定"序列号",并且由程序员自己维护序列号的信息;
 *      
 * 使用transient关键字,声明不需要被序列化的成员变量:
 * 		
 */
public class Demo {
	public static void main(String[] args) throws IOException, ClassNotFoundException{
		//序列化
		/*Student stu = new Student("刘德华",20);
		//实例化一个序列化流
		ObjectOutputStream objOut = new ObjectOutputStream(new FileOutputStream("demo13.txt"));
		objOut.writeObject(stu);
		objOut.close();
		System.out.println("写入完毕!");*/
		
		//反序列化
		ObjectInputStream in = new ObjectInputStream(new FileInputStream("demo13.txt"));
		Object obj = in.readObject();//会在内存中再产生一个Student对象
		Student stu = (Student)obj;
		System.out.println("反序列化:name = " + stu.getName() + ", age = " + stu.getAge());
	}
}

操作配置文件--Properties类:

1.配置文件:我们在使用软件的过程中,都会记录一些用户的设置信息,目的是当用户再次启动
         并且使用这个软件时,可以为用户应用之前的配置;记录这种配置信息的文件就是:配置文件
         配置文件就是一个"文件";
2.一般配置文件的格式:
  疲劳值=120
  钻石=1000
  HP=20000
  ...
3.这种格式可以被描述为:"键值对"的格式;这种格式的配置文件很适合存储到Map中;
4.Java为了我们方便的操作配置文件,为我们提供了一个类:Properties类;它就是一个Map集合;
  但是,它可以结合IO流,很方便将集合中的数据写入到配置文件中,也可以将配置文件中的内容读取到
  集合中。

Properties的特殊功能
public Object setProperty(String key,String value):相当于Map.put(key,value)
public String getProperty(String key):相当于Map.get(key):
public Set stringPropertyNames():相当于Map.keySet():

Properties的load()和store()功能:

public void load(Reader reader):通过字符流,一次性的将配置文件中的数据读取到集合中;
public void store(Writer writer,String comments):通过字符流,将集合中的键值对,写入到文件中;

案例:修改配置文件中的值;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

/*
 * 修改配置文件demo16.txt中"钻石"的值为:5000
 * 
 * 1.将配置文件中的内容读取到集合中;
 * 2.直接使用setProperties(key)方法将钻石的值,改为:5000;
 * 3.将集合中的数据写入到文件中;
 */
public class Demo {
	public static void main(String[] args) throws IOException {
		Properties pro = new Properties();
		FileReader in = new FileReader("demo16.txt");
		pro.load(in);
		in.close();
		pro.setProperty("钻石", "5000");
		//写入
		FileWriter out = new FileWriter("demo16.txt");
		pro.store(out, null);
		out.close();
		System.out.println("修改完毕!");
	}
}

NIO的知识:

JDK7要了解的新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 lines, Charset cs, OpenOption... options):
       把集合的数据写到文件。

 

 


Properties的特殊功能
public Object setProperty(String key,String value):相当于Map.put(key,value)
public String getProperty(String key):相当于Map.get(key):
public Set stringPropertyNames():相当于Map.keySet():
Properties的特殊功能
public Object setProperty(String key,String value):相当于Map.put(key,value)
public String getProperty(String key):相当于Map.get(key):
public Set stringPropertyNames():相当于Map.keySet():
Properties的特殊功能
public Object setProperty(String key,String value):相当于Map.put(key,value)
public String getProperty(String key):相当于Map.get(key):
public Set stringPropertyNames():相当于Map.keySet():
Properties的特殊功能
public Object setProperty(String key,String value):相当于Map.put(key,value)
public String getProperty(String key):相当于Map.get(key):
public Set stringPropertyNames():相当于Map.keySet():

 

你可能感兴趣的:(黑马日志)