io流

1 file文件

1 file引入

1 File概念: 文件和目录路径名的抽象表示形式。
路径的抽象表现形式,这个文件可以不存在

static String pathSeparator
与系统有关的路径分隔符,为了方便,它被表示为一个字符串。

static char pathSeparatorChar
与系统有关的路径分隔符。

static String separator
与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。

static char separatorChar
与系统有关的默认名称分隔符。

File(File parent, String child)
根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。

File(String pathname)
通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。

File(String parent, String child)
根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。

File(URI uri)
通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。

public class FileDemo01 {
	public static void main(String[] args) throws IOException {
		System.out.println(File.pathSeparator); // 路径分隔符 ; 配置环境变量
		System.out.println(File.pathSeparatorChar);
		
		System.out.println(File.separator); // \ 路径名分隔符 D:\Java_0221\02_JavaSE\day02
		System.out.println(File.separatorChar);
		
		// 表示一个路径
		String pathName = "D:\\Java_0221\\02_JavaSE\\day02"; // 可能针对不同的操作系统不适配
		String pathName2 = "D:" + File.separator + "Java_0221"; // 可能针对不同的操作系统不适配
		
		// 创建一个File对象
		File f = new File("D:\\hello.txt"); // 创建了一个文件指针指向了D:\\hello.txt所对应的位置
		// 凡是对文件的操作的第一步,必须封装文件路径
		f.createNewFile();
		// D:\Java_0221\02_JavaSE\day15\视频\02.ArrayList类讲解.mp4
		// File(String parent, String child) 
		File f2 = new File("D:\\Java_0221\\02_JavaSE\\day15\\视频", "02.ArrayList类讲解.mp4");
		System.out.println(f2);
		// File(File parent, String child) 
		File f3 = new File(new File("D:\\Java_0221\\02_JavaSE\\day15\\视频"), "02.ArrayList类讲解.mp4");
		System.out.println(f3);
		File f4 = new File("java.txt"); // 相对于项目路径下
		// D:\Java_0221\CodeLibraryForJava0221\day0318_filedemo\java.txt
		System.out.println(f4.getAbsolutePath());
		System.out.println(f4.createNewFile());
		
	}
}

2 file 的创建

文件的创建
public boolean createNewFile() 若文件不存在则创建新文件
public boolean mkdir() 创建目录
public boolean mkdirs() 创建目录及不存在的父目录
注意
1.操作文件第一步一定创建文件指针指向对应文件位置
2.如果需要对某一个文件进行操作,那么改文件的父路径一定要存在

public class FileDemo02 {
	public static void main(String[] args) throws IOException {
		// 在项目文件夹下面创建一个a.txt
		File f1 = new File("a.txt");
		System.out.println("createNewFile: " + f1.createNewFile());
		// 在项目文件夹filedemo下面创建一个a.txt
		File f2 = new File("src/com/sxt/filedemo/a.txt");
		System.out.println("createNewFile: " + f2.createNewFile());
//		File f3 = new File("X:/a.txt");
//		System.out.println("createNewFile: " + f3.createNewFile());
		
		// 在项目路径下创建aaa文件夹
		File f4 = new File("aaa");
		System.out.println(f4.mkdir());
		
		// 在项目路径下创建 bbb/a.txt
		File f5 = new File("bbb");
		File f6 = new File(f5,"x.txt");
		System.out.println(f5.mkdir());
		System.out.println(f6.createNewFile());
		
		// 在项目路径下创建 xxx/yyy/zzz/haha.txt
//		File f7 = new File("xxx");
//		File f8 = new File("xxx/yyy");
//		File f9 = new File("xxx/yyy/zzz");
//		File f10 = new File("xxx/yyy/zzz/haha.txt");
//		System.out.println(f7.mkdir());
//		System.out.println(f8.mkdir());
//		System.out.println(f9.mkdir());
//		System.out.println(f10.createNewFile());
		File f11 = new File("xxx/yyy/zzz");
		File f12 = new File(f11, "haha.txt");
		System.out.println(f11.mkdirs());
		System.out.println(f12.createNewFile());
	}
}

3 file的删除

文件的删除
1.public boolean delete() 删除文件及空目录
只能一步一步的删除 如下代码
2.递归删除
删除带内容文件夹需要使用递归删除,

public class FileDemo03 {
	public static void main(String[] args) {
		
		File f1 = new File("xxx/yyy/zzz/haha.txt");
		File f2 = new File("xxx/yyy/zzz");
		File f3 = new File("xxx/yyy");
		File f4 = new File("xxx");
		System.out.println(f1.delete());
		System.out.println(f2.delete());
		System.out.println(f3.delete());
		System.out.println(f4.delete());
	}
}

4 file的重命名

文件的重命名
public boolean renameTo(File dest) 重命名为dest
如果源文件和目标文件父路径保持一致,表示更名
如果源文件和目标文件父路径不一致,表示先剪切然后更名

public class FileDemo04 {
	public static void main(String[] args) {
		// 将java.txt修改成android.txt
		File f = new File("java.txt");
		File dest = new File("android.txt");
		System.out.println(f.renameTo(dest));
		
		// 将android.txt修改成 php.txt
		File f2 = new File("android.txt");
		File dest2 = new File("src/com/sxt/filedemo/php.txt");
		System.out.println(f2.renameTo(dest2));
	}
}

5 file 的相关属性功能

文件的相关属性功能

public boolean isFile() 是否文件

public boolean isDirectory() 是否目录

public boolean canRead() 是否可读

public boolean canWrite() 是否可写

public boolean exists() 是否存在

public boolean isHidden() 是否隐藏

public long length() 长度

public String getAbsolutePath() 绝对路径

public String getPath() 定义的路径

public String getName() file名字

public long lastModified() 最后一次修改时间

public class FileDemo05 {
	public static void main(String[] args) {
		File f = new File("a.txt");
		System.out.println("isFile:" + f.isFile());
		System.out.println("isDirectory:" + f.isDirectory());
		System.out.println("canRead:" + f.canRead());
		System.out.println("canWrite:" + f.canWrite());
		System.out.println("exists:" + f.exists());
		System.out.println("isHidden:" + f.isHidden());
		System.out.println("length:" + f.length());
		System.out.println("getAbsolutePath:" + f.getAbsolutePath());
		System.out.println("getPath:" + f.getPath());
		System.out.println("getName:" + f.getName());
		System.out.println("lastModified:" + f.lastModified());
		
		char ch = '\n';
		char ch2 = '\r';
		String ch3 = "\r\n";
		
		Date d = new Date(1552892143776L);
		System.out.println(d);
		
		Calendar c = Calendar.getInstance();
		c.setTimeInMillis(1552892143776L);
		System.out.format("现在是北京时间%d/%s/%d %d:%d:%d",c.get(Calendar.YEAR),
				((c.get(Calendar.MONTH) + 1 < 10) ? "0" + (c.get(Calendar.MONTH) + 1) : c.get(Calendar.MONTH) + 1),c.get(Calendar.DAY_OF_MONTH)
				,c.get(Calendar.HOUR), c.get(Calendar.MINUTE), c.get(Calendar.SECOND));
	}
}

6 文件的遍历

文件的遍历

public String[] list() 返回file下的文件和目录名称(字符串)

public File[] listFiles() 返回file下的文件和目录(文件)

public String[] list(FilenameFilter filter) 根据filter过滤返回file下文件和目录名称

public File[] listFiles(FileFilter filter) 根据filter过滤返回file下文件和目录

public class FileDemo06 {
	public static void main(String[] args) {
		// 希望遍历com.sxt.filedemo目录下的所有文件
		File f1 = new File("src/com/sxt/filedemo");
		/*String[] fileNames = f1.list();
		for (String fileName : fileNames) {
			System.out.println(fileName);
		}
		
		System.out.println("================");
		File[] files = f1.listFiles();
		for (File f : files) {
			System.out.println(f.getAbsolutePath() + "|" + f.length());
		}*/
		// 希望遍历com.sxt.filedemo目录下的所有的Java文件
		/*String[] fileNames = f1.list(new FilenameFilter() {
			
  	 	 // 写一个接口  参数列表 返回值类型 方法名
    	// 利用返回值去继续往下写我的需求
			@Override
			public boolean accept(File f, String name) {
//				System.out.println(f + "|" + name);
				File file = new File(f, name);	
				if (file.getName().endsWith(".java")) {
					return true;
				} else {
					return false;
				}
				
			}
		});
		
		for (String fileName : fileNames) {
			System.out.println(fileName);
		}*/
		File[] files = f1.listFiles(new FileFilter() {
			
			@Override
			public boolean accept(File f) {
				return f.isFile() && f.getName().endsWith(".java");
			}
		});
		for (File file : files) {
			System.out.println(file.getAbsolutePath());
		}
	}
}

7 递归的批量操作

批量操作 (递归)
批量创建
批量删除
批量修改
批量查询

递归:
1.必须存在方法,自己调自己
2.递归必须要有出口,否则容易出现死递归
1 批量查找文件如下

 /*需求: 获取D:\Java_0221\CodeLibraryForJava0221文件夹下面所有的Java文件
  分析:
  	1.封装文件路径指向需要操作的文件位置  D:\Java_0221\CodeLibraryForJava0221
   2.获取该目录下所有的文件或者文件夹
   3.获取到每一个文件或者文件夹
  4.判断该对象是文件还是文件夹 (if-else)
   		是文件
   			判断该文件是否以后缀名.java结尾 (单if)
   				是
   					输出文件名称
   				否
   					不处理
   		是文件夹
   			回到第2步
*/ 
public class BatchFileDemo01 {
	public static void main(String[] args) {
		// 1.封装文件路径指向需要操作的文件位置  D:\Java_0221\CodeLibraryForJava0221
		File srcFile = new File("D:\\Java_0221\\CodeLibraryForJava0221");
		getAllJavaFile(srcFile);
	}
	
	public static void getAllJavaFile(File srcFile) throws NullPointerException {
		if (srcFile == null) {
			throw new NullPointerException("传入的文件对象不能为null!!!");
		}
		
		// 2.获取该目录下所有的文件或者文件夹
		File[] files = srcFile.listFiles();
		
		// 3.获取到每一个文件或者文件夹
		if (files != null && files.length != 0) {
			for (File f : files) {
				// 4.判断该对象是文件还是文件夹 (if-else)
				if (f.isDirectory()) {
					// 回到第2步
					getAllJavaFile(f);
				} else {
					// 判断该文件是否以后缀名.java结尾 (单if)
					String fileName = f.getName();
					if (fileName.endsWith(".java")) {
						// 输出文件名称
						System.out.println(fileName);
					}
				}
			}
		}
	}
}

**2 批量查找视屏文件 **如下

/*统计视频文件的个数 
 * D:\Java_0221
 */
public class BatchFileDemo02 {
	
	public static int mp4FileCount = 0;
	public static List<File> list = new ArrayList<File>();
	
	public static void main(String[] args) {
		System.out.println("MP4文件的个数是: " + getAllMp4FileCount(new File("D:\\Java_0221\\02_JavaSE")));
		
		for (File f : list) {
			System.out.println(f.getName());
		}
	}
	
	public static int getAllMp4FileCount(File srcFile) {
		
		if (srcFile == null) {
			throw new NullPointerException("传入的文件对象不能为null!!!");
		}
		
		File[] files = srcFile.listFiles();
		if (files != null && files.length != 0) {
			for (File f : files) {
				if (f.isDirectory()) {
					getAllMp4FileCount(f);
				} else {
					String fileName = f.getName();
					if (fileName.endsWith(".mp4")) {
						mp4FileCount ++;
						list.add(f);
					}
				}
			}
		}
		return mp4FileCount;
	}
}

3 批量删除如下

/*
 * 批量删除
 * D:\Java_1227\day01
 * 思路:
 * 1.封装文件指针指向需要操作的文件位置
 * 2.遍历到源路径下所有的文件或者文件夹
 * 3.获取到每一个文件或者文件夹
 * 4.判断该对象是文件还是文件夹
 * 		是文件夹		
 * 			回到第二步
 * 		是文件
 * 			直接删除
 * 5.删除空目录
 */
public class BatchFileDemo03 {
	public static void main(String[] args) {
		// 1.封装文件指针指向需要操作的文件位置
		File srcFile = new File("D:\\Java_1227\\day01");
		try {
			deleteFolder(srcFile);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static void deleteFolder(File srcFile) throws NullPointerException {
		if (srcFile == null) {
			throw new NullPointerException("传入的文件不能为null");
		}	
		// 2.遍历到源路径下所有的文件或者文件夹
		File[] files = srcFile.listFiles();
		
		// 3.获取到每一个文件或者文件夹
		if (files != null && files.length != 0) {
			for (File f : files) {
				// 4.判断该对象是文件还是文件夹
				if (f.isDirectory()) {
					deleteFolder(f);
				} else {
					System.out.println("\t删除文件:" + f.getName() + ":" + (f.delete() ? "成功" : "失败"));
				}
			}
		}
		// 5.删除空目录
		System.out.println("删除文件夹:" + srcFile.getName() + ":" + (srcFile.delete() ? "成功" : "失败"));		
	}		
}

2 io

1 io 的引入

IO流的本质
用于数据的传输

IO流的分类
1 按照数据类型分类
字节流
字符流

2 按照流向分类
输入流
输出流

3 流向:
内存 -> 外界 输出流
外界 -> 内存 输入流

4 分类
字节流
字节输入流 XXXInputStream
字节输出流 XXXOutputStream
字符流
字符输入流 XXXReader
字符输出流 XXXWriter

流的三要素

1.数据源:数据从哪里到内存
2.目的地:数据从内存到哪里
3.交通工具:选择IO流框架中对应的输入流或者输出流来处理

处理对象

字节流: 主要用来处理字节或二进制对象。例如 视频 音频 图片
字符流: 主要用来处理字符、字符数组或字符串文件 例如 html css txt js xml json
字节流是万能流,能够处理任何文件,但是在处理字符方面不太方便,所以有字符流

2 字节流

1. OutputStream (io字节流的输出) 抽象的

方法

void close()
关闭此输出流并释放与此流有关的所有系统资源。
void flush()
刷新此输出流并强制写出所有缓冲的输出字节。
void write(byte[] b)
将 b.length 个字节从指定的 byte 数组写入此输出流。
void write(byte[] b, int off, int len)
将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
abstract void write(int b)
将指定的字节写入此输出流。

Exception in thread “main” java.io.IOException: Stream Closed
流已经关闭了就不能够再次使用了

//   需求:向文本里写入“HelloWorld”。
//  注意:
//  	1.IO流的创建会默认创建文件
  
public class IODemo02 {
	public static void main(String[] args) throws IOException {
		/*
		 * 数据源: 内存
		 * 目的地: 文本文件 a.txt
		 * 交通工具: 选择输出流 OutputStream 
		 * FileOutputStream(File file)
		 */
		String s = "HelloWorld";
		File f = new File("D://qwe/a.txt");
		OutputStream os = new FileOutputStream(f);
		byte[] bys = s.getBytes(); // 编码的方法
//		os.write(bys); 
//		os.write(bys, 5, 5);
//		os.write(97);
		os.write("97".getBytes());
		os.flush();
		os.close();
//		os.write(100);
	}
}

构造方法
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。

FileOutputStream(File file, boolean append)
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
FileOutputStream(String name)
创建一个向具有指定名称的文件中写入数据的输出文件流。
FileOutputStream(String name, boolean append)
创建一个向具有指定 name 的文件中写入数据的输出文件流。

注意:

1.换行符在不同的操作系统中不适配
windows \r\n
linux \n
max \r
所以字节流在某些时候在操作字符或者字符数组或者字符串不方便,后面字符流可简单处理
2.追加写入 append参数
3.IO流中加入异常处理

public class IODemo03 {
	public static void main(String[] args) throws Exception {
		OutputStream os = new FileOutputStream("b.txt", true);
//		os.write("Hello".getBytes());
		for (int i = 1; i <= 10; i++) {
			os.write(("HelloWorld" + i).getBytes());
			os.write("\r\n".getBytes());
			os.flush();
		}
		os.close();
	}
}

2 Inputstream(io字节流的输入) 抽象的

需求:将文本文件的数据读取出来显示在控制台
InputStream
FileInputStream

FileInputStream的构造方法
FileInputStream(File file) 创建一个从file读取数据的输入流
FileInputStream(String name) 创建一个从name文件读取数据的输入流
FileInputStream的成员方法

注意:

1.使用输出流的时候,如果文件不存在,系统会帮我们自动创建
2.使用输入流的时候,文件必须存在

public class IODemo01 {
	public static void main(String[] args) throws Exception {
		/*
		 * 数据源: 文本文件
		 * 目的地: 控制台
		 * 交通工具:
		 *  	文本文件 -> 内存 输入流
		 *  	内存 -> 控制台 输出流 暂时不需要考虑 System.out.println();
		 */
		InputStream is = new FileInputStream("is.txt");

		String s = "中国";
		System.out.println(Arrays.toString(s.getBytes()));
		
		int by = 0;
		while((by = is.read()) != -1) {
			System.out.print((char)by);
		}
	}
}

int read() 一次读取一个字节

1.每次读取一个字节
2.每读取一个字节,指针向后移动一个字节单位
3.读到文件末尾返回-1
4.一次性读取一个字节存在的问题: 如果是中文或者其他文字会出现乱码
方案一:处理一些字符不太方便,建议使用字符流
方案二:或者一次性不要读取一个字节
int read(byte[] b) 一次读取一个字节数组
int read(byte[] b, int off, int len) 一次读取一个字节数组的一部分 基本不用
void close() 关闭此文件输入流并释放与此流有关的所有系统资源


/*
 * int read(byte[] b)  一次读取一个字节数组
 * 1.每次读取一个字节数组,读取出来保存到形参定义的字节数组中
 * 2.返回的是每次读取的实际长度
 */
public class IODemo02 {
	public static void main(String[] args) throws Exception {
		File f = new File("jys.txt");
		FileInputStream fis = new FileInputStream(f);	
//		标准写法
		int len = 0;
		byte[] bys = new byte[1024];
		while ((len = fis.read(bys)) != -1) {
			System.out.print(new String(bys, 0, len));
		}
	}
}

3 .io流异常处理

/*
 * 练习: 写一首诗到文件中 数据源来自于集合
 */
public class IODemo05 {
	public static void main(String[] args) {
		List<String> list = new ArrayList<>();
		list.add("窗前明月光");
		list.add("疑似地上霜");
		list.add("举头望明月");
		list.add("低头思故乡");
		
		/*
		 * 数据源: 内存中的集合
		 * 目的地: 文件
		 * 交通工具: 内存->文件 FileOutputStream
		 */
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream("jys.txt");
			for (String poem : list) {
				fos.write((poem + "\r\n").getBytes());
				fos.flush();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}		
		System.out.println("Over");
	}
}

4 字节流拷贝文件

第一种方式
一次性读取一个字节数组拷贝文件

public class IODemo03 {
	public static void main(String[] args) {
		copyFile1("D:\\Java_0221\\02_JavaSE\\day06\\视频\\01.方法的重载.mp4", "haha.mp4");
	}	
	// 一次性读取一个字节数组拷贝文件
	public static void copyFile2(String srcFile, String descFile) {
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream(srcFile);
			fos = new FileOutputStream(descFile);
			
			int len = 0;
			byte[] bys = new byte[1024];
			while ((len = fis.read(bys)) != -1) {
				fos.write(bys, 0, len);
				fos.flush();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}		
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}	

第二种方式
一次性读取一个字节拷贝文件

/*
	 * 数据源: 文件
	 * 目的地: 文件
	 * 交通工具
	 * 		文件 -> 内存  输入流 FileInputStream
	 * 		内存 -> 文件  输出流 FileOutputStream
	 */
public class IODemo03 {
	public static void main(String[] args) {
		copyFile2("D:\\Java_0221\\02_JavaSE\\day06\\视频\\01.方法的重载.mp4", "haha.mp4");
	}
	public static void copyFile(String srcFile, String descFile) {
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream(srcFile);
			fos = new FileOutputStream(descFile);
						
			int b = 0;
			while ((b = fis.read()) != -1) {
				fos.write(b);
				fos.flush();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}			
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

5 字节缓冲输入流和输出流

字节流一次读写一个字节数组,
大小为1024的速度明显比一次读写一个字节的速度要快的多的多,
因为这里加入了数组缓冲区的思想,
Java考虑到流读写效率的提升所以为我们提供了字节缓冲流。
字节缓冲输出流
BufferedOutputStream
字节缓冲输入流
BufferedInputStream

代码演示加入字节缓冲流之后的视频拷贝的效率区别

public class IODemo04 {
	public static void main(String[] args) throws Exception {
		// write();
		// read();
		long start = System.currentTimeMillis();
//		copyFile2("D:\\Java_0221\\02_JavaSE\\day06\\视频\\01.方法的重载.mp4", "haha.mp4");// 231
//		copyFile3("D:\\Java_0221\\02_JavaSE\\day06\\视频\\01.方法的重载.mp4", "hehe.mp4");// 718
		copyFile("jys.txt", "jys4.txt");// 718
		long end = System.currentTimeMillis();
		
		System.out.println(end - start);

	}

	// 一次性读取一个字节拷贝文件
	/*
	 * 数据源: 文件 目的地: 文件 交通工具 文件 -> 内存 输入流 FileInputStream 内存 -> 文件 输出流
	 * FileOutputStream
	 */
	public static void copyFile(String srcFile, String descFile) {
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream(srcFile);
			fos = new FileOutputStream(descFile);

			int b = 0;
			while ((b = fis.read()) != -1) {
				fos.write(b);
				fos.flush();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	// 一次性读取一个字节数组拷贝文件
	public static void copyFile2(String srcFile, String descFile) {
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream(srcFile);
			fos = new FileOutputStream(descFile);

			int len = 0;
			byte[] bys = new byte[8192];
			while ((len = fis.read(bys)) != -1) {
				fos.write(bys, 0, len);
				fos.flush();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	/*
	 * 使用高效缓冲读取一个字节
	 */
	public static void copyFile3(String srcFile, String descFile) throws Exception {
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(descFile));

		int len = 0;
		byte[] bys = new byte[1024];

		while ((len = bis.read(bys)) != -1) {
			bos.write(bys, 0, len);
			bos.flush();
		}
		bis.close();
		bos.close();
	}
	public static void read() throws FileNotFoundException, IOException {
		InputStream is = new FileInputStream("bis.txt");
		BufferedInputStream bis = new BufferedInputStream(is);

		byte[] bys = new byte[10];
		int len = is.read(bys);
		System.out.println(new String(bys, 0, len));

		bis.close();
	}
	public static void write() throws FileNotFoundException, IOException {
		OutputStream os = new FileOutputStream("bos.txt");
		BufferedOutputStream bos = new BufferedOutputStream(os);

		bos.write("hello".getBytes());
		bos.flush();
		bos.close();
	}
}

3 乱码现象

计算机是如何进行编码 解码的?

编码
文本信息 -通过编码表-> 十进制 --> 二进制 --> 传输
解码
二进制 --> 十进制 -通过编码表-> 文本信息

编码表:

ASCII 美国标准信息交换码 1个字节存储一个字符
Unicode 国际标准码 2个字节储存储字符 0~65535 前0~256正好和ASCII码表兼容
UTF-8 最多用三个字节来表示一个字符。 目前项目最常用的编码表
ISO-8859-1 拉丁码表。欧洲码表。 不支持中文的 Tomcat7包括之前的服务器版本/FTP服务器都是使用这个编码
GB2312/GBK/GB18030 中文编码表。 Java平台中默认采用的编码方式是GBK。

系统支持的编码有哪些?

1.字符编码解码 char ch = ‘a’;
2.字符串编码解码 getBytes(String charsetName) new String(byte[] bys, String charsetName)
3.流的编码解码 OutputStreamWriter InputStreamReader

计算机是如何处理中文的?

计算处理中文的流程
首先判断当前这个字符是英文还是中文
英文是整数,中文是负数
如果是英文
就查找当前系统的编码表,根据编码表转换成对应的字符
如果是中文
就查找当前系统的编码表
如果是GBK,就等待下一个字节拼接组成一个字符
如果是UTF-8,就等待下两个字节拼接组成一个字符

乱码原因有哪些?

1.人为的转换
2.编码和解码使用的编码表不一致
3.可能和系统编码产生冲突 (Tomcat7是ISO-8859-1,Tomcat8是UTF-8)
4.考虑浏览器编码,浏览器会使用URL编码 讲解Tomcat服务器讲解

4 字符流

1 字符流的引入

为什么引入字符流?因为字节流有如下缺点:

1.处理换行符不方便
2.处理编码不方便
3.处理中文不方便
4.读取一行不方便

需求:使用字符流向文本里写入“HelloWorld”。
Writer(抽象类,不能直接使用,选择子类)
OutputStreamWriter(转换流,是字符流通向字节流的桥梁,可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认字符集编码。)

构造方法

OutputStreamWriter(OutputStream out) 创建使用默认字符编码的字符输出转换流
OutputStreamWriter(OutputStream out, Charset cs) 创建使用cs字符集的字符输出转换流
OutputStreamWriter(OutputStream out, CharsetEncoder enc) 创建使用enc字符集编码器的
OutputStreamWriter(OutputStream out, String charsetName) 创建使用指定字符集的

其他方法

Writer append(char c)
将指定字符添加到此 writer。
Writer append(CharSequence csq)
将指定字符序列添加到此 writer。
Writer append(CharSequence csq, int start, int end)
将指定字符序列的子序列添加到此 writer.Appendable。
abstract void close()
关闭此流,但要先刷新它。
abstract void flush()
刷新该流的缓冲。
void write(char[] cbuf)
写入字符数组。
abstract void write(char[] cbuf, int off, int len)
写入字符数组的某一部分。
void write(int c)
写入单个字符。
void write(String str)
写入字符串。
void write(String str, int off, int len)
写入字符串的某一部分。

public class IODemo02 {
	public static void main(String[] args) throws Exception {
//		write();
//		write2();
		write3();
	}
	public static void write3() throws Exception{
		Writer w = new OutputStreamWriter(new FileOutputStream("osw.txt"));
//		w.append("abc").append("123").append("hello");
//		w.write(97);
		w.write(new char[] {'a','b','c'});
		w.flush();
		w.close();
	}
	public static void write2() throws UnsupportedEncodingException, FileNotFoundException, IOException {
		String s = "HelloWorld中国";
		Writer w = new OutputStreamWriter(new FileOutputStream("osw.txt"), "UTF-8");
		w.write(s);
		w.flush();
		w.close();
	}
	private static void write() throws FileNotFoundException, IOException {
		String s = "HelloWorld";
		Writer w = new OutputStreamWriter(new FileOutputStream("w.txt"));
		w.write(s);
		w.flush();
		w.close();
	}
}

2 字符流拷贝文件

方式一
字节流可以处理所有文件,但是文本文件建议使用字符流
字符流只能够处理文本文件,不能够处理二进制文件

public class IODemo04 {
	public static void main(String[] args) throws Exception {
		copy("D:\\Java_0221\\02_JavaSE\\day06\\视频\\01.方法的重载.mp4", "ok.mp4");
	}

	public static void copy(String srcFile, String descFile) throws Exception{
		Reader r = new InputStreamReader(new FileInputStream(srcFile));
		Writer w = new OutputStreamWriter(new FileOutputStream(descFile));
				
		int len = 0;
		char[] chs = new char[1024];
		
		while ((len = r.read(chs))!=-1) {
			w.write(chs, 0, len);
			w.flush();
		}		
		r.close();
		w.close();
	}
}

方式二
FileWriter:

用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个OutputStreamWriter。

FileReader:

用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。

构造方法类似FileInputStream和FileOutputStream
成员方法完全继承自父类OutputStreamWriter和InputStreamReader

代码演示使用FileWriter和FileReader复制文件内容

public class IODemo05 {
	public static void main(String[] args) throws Exception {
		FileReader fr = new FileReader("IODemo03.java");
		FileWriter fw = new FileWriter("demo.java");
		
		int len = 0;
		char[] chs = new char[1024];
		
		while ((len = fr.read(chs))!=-1) {
			fw.write(chs, 0, len);
			fw.flush();
		}		
		fr.close();
		fw.close();		
	}
}

3 字符缓冲流

类似于字节流的BufferedOutputStream和BufferedInputStream,字符流同样存在字符字符缓冲流
同理,字符缓冲流也是为了提高字符流的效率。
BufferedWriter和BufferedReader继承自Writer和Reader,所以具备原有的读取方法,但是还存在自己特有的方法。
特有的方法:
BufferedWriter:void newLine()
BufferedReader:String readLine()

代码演示字符缓冲流拷贝文件内容

字节流: BufferedInputStream + BufferedOutputStream
字符流: BufferedReader + BufferedWriter

public class IODemo06 {
	public static void main(String[] args) throws Exception {
//		write();
//		read();
		copy();
	}

	public static void copy() throws Exception {
		BufferedReader br = new BufferedReader(new FileReader("IODemo03.java"));
		BufferedWriter bw = new BufferedWriter(new FileWriter("Hello.txt"));
		
		String line = null;
		while ((line = br.readLine()) != null) {
			bw.write(line);
			bw.newLine();
			bw.flush();
		}		
			br.close();
			bw.close();
		
	}


	public static void read() throws Exception{
		BufferedReader br = new BufferedReader(new FileReader("IODemo01.java"));

		String line = null;
		while ((line = br.readLine()) != null) {
			System.out.println(line);
		}		
	}	


	private static void write() throws IOException {
		BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
		bw.write("Hello");
		bw.newLine();
		bw.flush();
		
		bw.write("World");
		bw.newLine();
		bw.flush();
		
		bw.write("Java");
		bw.newLine();
		bw.flush();
		
		bw.close();
	}
}

3 Other IO 流

1 可以读写基本数据类型io流

数据输入流:DataInputStream
DataInputStream(InputStream in)

数据输出流:DataOutputStream
DataOutputStream(OutputStream out)

特点:

1.基本数据类型流是字节流,就能够具备字节流的功能
2.基本数据类型一般都是一个类的成员变量,是类的隐私数据,提高数据的安全性
3.读取数据的顺序和类型必须保持一致
4.除了能够读写字节,还能够读写基本类型,但是保存到文件中是看不懂的

public class IODemo01 {
	public static void main(String[] args) throws Exception {
//		write();
//		read();
	}
		//读
	private static void read() throws Exception {
		DataInputStream dis = new DataInputStream(new FileInputStream("dos.txt"));
		byte by = dis.readByte();
		short sh = dis.readShort();
		int i = dis.readInt();
		long l = dis.readLong();
		float f = dis.readFloat();
		double d = dis.readDouble();
		char ch = dis.readChar();
		boolean bool = dis.readBoolean();
		System.out.println(by);
		System.out.println(sh);
		System.out.println(i);
		System.out.println(l);
		System.out.println(f);
		System.out.println(d);
		System.out.println(ch);
		System.out.println(bool);
	}
		//写
	private static void write() throws FileNotFoundException, IOException {
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("dos.txt"));
		dos.writeByte(10);
		dos.writeShort(20);
		dos.writeInt(30);
		dos.writeLong(40L);
		dos.writeFloat(2.5f);
		dos.writeDouble(3.0);
		dos.writeChar('a');
		dos.writeBoolean(false);
//		dos.write("100".getBytes());
		dos.close();
	}
}

2 PrintWriter

概述:向文本输出流打印对象的格式化表示形式。此类实现在 PrintStream 中的所有 print 方法。
特点:
1.只能写数据,不能读取数据。
2.能够读写任意类型,都是写入任意类型的String.valueOf(Object obj)
3.可以调用println实现自动换行
4.如果启动了自动刷新,能够自动刷新。
5.能够实现将字节流转换成字符流
6.能够解决IO流的编码问题
7.如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作

代码演示打印流复制文件内容
PrintStream和PrintWriter完全兼容
如果要读写二进制文件,只能够用PrintStream
三种方法复制文本

		// 当做字符流使用
public class IODemo02 {
	public static void main(String[] args) throws Exception {
	
	//	write();
		copy1();
		copy2();
		copy3();
	}
	private static void write() throws FileNotFoundException {
		PrintWriter pw = new PrintWriter("pw.txt");
		pw.write("Hello");
		pw.flush();
		pw.close();
	}	
	//copy1() 方法一
	public static void copy() throws Exception {
		BufferedReader br = new BufferedReader(new FileReader("IODemo01.java"));
		PrintWriter pw = new PrintWriter("pw.txt");
		
		int len = 0;
		char[] chs = new char[1024];
		while ((len = br.read(chs)) != -1) {
			pw.write(new String(chs, 0, len));
			pw.flush();
		}		
		br.close();
		pw.close();
	}	
	//copy2() 方法二
	public static void copy2() throws Exception {
		BufferedReader br = new BufferedReader(new FileReader("IODemo01.java"));
		PrintWriter pw = new PrintWriter("pw.txt");
		
		String line = null;
		while ((line = br.readLine()) != null) {
//			pw.write(line);
			pw.println(line);
			pw.flush();
		}		
		br.close();
		pw.close();
	}
	//copy3() 方法三
	
	// 如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作
	public static void copy3() throws Exception {
		BufferedReader br = new BufferedReader(new FileReader("IODemo01.java"));
		// PrintWriter(Writer out, boolean autoFlush) 
		PrintWriter pw = new PrintWriter(new FileWriter("pw2.txt"), true);
		
		String line = null;
		while ((line = br.readLine()) != null) {
			pw.println(line);
		}
		
		br.close();
		pw.close();
	}
}

3 RandomAccessFile

概述:此类的实例支持对随机访问文件的读取和写入。

特点
1.RandomAccessFile类不属于流,是Object类的子类。
2.包含了InputStream和OutputStream的功能。
3.能够读写基本类型
4.支持对随机访问文件的读取和写入。 getFilePointer/seek
long getFilePointer()
long length()
void seek(long pos)
void setLength(long newLength)

public class IODemo04 {
	public static void main(String[] args) throws Exception {
		read();
//		write();
	}
	private static void write() throws Exception {
		RandomAccessFile raf = new RandomAccessFile("IODemo01.java", "rw");
		raf.seek(raf.length());
		raf.write("End".getBytes());
		raf.close();
	}
	private static void read() throws Exception {
		RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");
		raf.seek(6);
		int i = raf.readInt();
		System.out.println(i);
		System.out.println(raf.getFilePointer());
		raf.close();
	}
}

4 键盘输入 IO 流

 System类中的静态变量:in,out 。
“标准”输入流:static InputStream in 。
“标准”输出流:static PrintStream out 。 
它们各代表了系统标准的输入和输出设备。
默认输入设备是键盘,输出设备是显示器
public class IODemo03 {
	@SuppressWarnings("resource")
	public static void main(String[] args) throws IOException {
	/*	 
		 *	标准的输出流
		 * 向控制台输出HelloWorld
		 * 数据源: 内存
		 * 目的地: 控制台
		 */
		/*PrintStream ps = System.out;
		ps.println("12313");*/
		/*System.out.println("31313");
		PrintStream err = System.err;
		err.println("djlaka");*/
		
		/*
		 * 标准的输入流
		 * 数据源: 键盘输入
		 * 目的地: 内存
		 */
		/*InputStream is = System.in;
		byte[] bys = new byte[10];
		System.out.println("请输入字符串:");
		int len = is.read(bys); // 该方法是一个阻塞方法
		String result = new String(bys, 0, len);
		System.out.println(result);*/
		
		MyScanner input = new MyScanner(System.in);
		System.out.print("请输入字符串:");
		String line = input.next();
		System.out.println("您输入的字符串是: " + line);
		
		System.out.println("请输入小数:");
		double d = input.nextDouble();
		System.out.println("您输入的小数是: " + d);
	}
}

// 自定义Scanner类
class MyScanner {
	
	private InputStream is;
	
	private MyScanner() {
		throw new Error("系统错误!!!");
	}
	
	public MyScanner(InputStream is) {
		this.is = is;
	}
	
	public String read() {
		InputStreamReader isr = new InputStreamReader(is);
		BufferedReader br = new BufferedReader(isr);
		String line = null;
		try {
			line = br.readLine();
			return line;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	public String next() {
		return read();
	}
	
	public double nextDouble() {
		String line = read();
		try {
			double d = Double.parseDouble(line);
			return d;
		} catch (Exception e) {
			throw new InputMismatchException("兄弟,这个是数字类型啊!!!" + e.getMessage());
		}
	}
	
}

5 序列化和反序列化流

序列化: 将对象写入到文件当中
反序列化: 讲对象从文件中还原

序列化流和反序列化流
ObjectInputStream
ObjectOutputStream
异常

1,java.io.NotSerializableException: com.sxt.otheriodemo.Student
异常名称: 未序列化异常
产生原因: 需要写入到文件的对象没有实现Serializable接口,Serializable接口是一个标记接口
标记接口本身就是一种功能的扩展
解决办法: 实现Serializable接口

transient关键字: 防止类的成员变量序列化到本地,保护成员的安全性

2 , java.io.InvalidClassException:
异常名称: 无效类异常
产生原因: 流中的序列化ID和本地类文件的序列化ID不一致
第一次写入到文件 stream classdesc serialVersionUID = 123456789L
第一次读取文件中对象 local class serialVersionUID = 123456789L
还原对象的时候会去本地字节码文件id匹配
如果相等: 就还原对象
如果不相等: 就报错
修改了源文件之后, local class serialVersionUID = 11111111L
第二次读取还原对象,123456789L != 11111111L
自动生成序列化ID

public class IODemo07 {
	public static void main(String[] args) throws Exception {
		/*PrintWriter pw = new PrintWriter(new FileWriter("test.txt"), true);
		pw.println(new Student("张三", 18));
		pw.close();*/
				
//		write();
		read();
	}
	private static void read() throws Exception {
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt"));
		Object obj = ois.readObject();
		System.out.println(obj);
		ois.close();
	}

	private static void write() throws IOException, FileNotFoundException {
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt"));
		oos.writeObject(new Student("李四", 45));
		oos.close();
	}
}

/*
 * 类: 组织代码,封装数据
 */
class Student implements Serializable {
	private static final long serialVersionUID = -6596699813872332589L;
	private String name;
	private transient int age;
	public Student() {
		super();
	}
	
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}	
}

6 Properties

概述: Properties 类表示了一个持久的属性集;

Properties 可保存在流中或从流中加载;属性列表中每个键及其对应值都是一个字符串。
Properties可以当做Map集合类使用
Properties的特殊遍历功能
public Object setProperty(String key,String value)
public String getProperty(String key)
public Set stringPropertyNames()
Properties和IO流结合使用
public void load(Reader reader)
public void store(Writer writer,String comments)

配置文件:
1.Properties 轻量级,在网络中传输带宽小,本地存储数据量简单,就是键值对结构,理解为一个类似于map的文件
2.XML 重量级,结构化清晰,可读性强,能够存储复杂结构的数据

持久化:
1.txt
2.Properties
3.XML
4.json
5.数据库

public class IODemo11 {
	public static void main(String[] args) throws Exception, IOException {
		Properties prop = new Properties();
		// 当做Map使用
//		prop.put("name", "zhangsan");
//		prop.put("age", "123");
//		prop.put("address", "shenzhen");
//		
//		Set<Object> keys = prop.keySet();
//		for (Object key : keys) {
//			Object value = prop.get(key);
//			System.out.println(key + "=" + value);
//		}
		
		// 专属存储属性集的方法 public Object setProperty(String key,String value)
		
//		prop.setProperty("name", "zhangsan");
//		prop.setProperty("age", "123");
//		prop.setProperty("address", "shenzhen");
		
		/*
		 * public String getProperty(String key)
		 * public Set<String> stringPropertyNames()
		 */
//		Set<String> keys = prop.stringPropertyNames();
//		for (String key : keys) {
//			String value = prop.getProperty(key);
//			System.out.println(key + "=" +value);
//		}
		
		// 当做流使用
		// public void load(Reader reader)
//		prop.load(new FileReader("prop.properties"));
//		System.out.println(prop);
		
		prop.put("name", "zhangsan");
		prop.put("age", "123");
		prop.put("address", "nanchang");
		// public void store(Writer writer,String comments)
		prop.store(new FileWriter("MyProperties.properties"), "This is wonderful!!!");	
	}

7 合并流

合并流

打印流只有输出流
随机访问文件不是流框架的流,即使读流,也是写流
合并流只是用来读取没有写的流

分割文件

如何上传大容量视频文件
多线程分割视频
将视频文件分割成5个部分 RandomAccessFile raf.length / 5 开始索引 start 结束索引 end
每个部分开启一个线程拷贝

public class IODemo06 {
	public static void main(String[] args) throws Exception {
		Vector<InputStream> v = new Vector<InputStream>();
		v.add(new FileInputStream("IODemo01.java"));
		v.add(new FileInputStream("IODemo02.java"));
		v.add(new FileInputStream("IODemo03.java"));
		Enumeration<InputStream> elements = v.elements();
		SequenceInputStream sis = new SequenceInputStream(elements);
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("sis2.txt"));
		
		int len = 0;
		byte[] bys = new byte[1024];
		
		while ((len = sis.read(bys)) != -1) {
			bos.write(bys, 0, len);
			bos.flush();
		}
		
		sis.close();
		bos.close();
	}
}

8 行号流

行号流
引出NIO

void setLineNumber(int lineNumber)
设置当前行号。
int getLineNumber()
获得当前行号

public class IODemo12 {
	public static void main(String[] args) throws Exception {
		LineNumberReader lnr = new LineNumberReader(new FileReader("IODemo01.java"));
		/*String line = null;
		while ((line = lnr.readLine()) != null) {
			System.out.println(line + "|" + lnr.getLineNumber());
		}*/
		lnr.setLineNumber(3);
		System.out.println(lnr.readLine() + "|" + lnr.getLineNumber());
		lnr.close();
	}
}

9 内存流

内存流

数据源: 文件 网络 键盘输入 内存
目的地: 文件 网络 控制台 内存
之前经常创建字节数组/字符数组/字符串来做传输的中间
能不能提供一个可以自动增长的字节数组供我们使用

字节数组内存流

ByteArrayInputStream
ByteArrayOutputStream
其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长
可使用 toByteArray() 和 toString() 获取数据

public class IODemo08 {
 public static void main(String[] args) throws IOException {
 	/*
 	 * 数据源: 内存
 	 * 目的地: 内存,自动维护的字节数组中
 	 * 自动卫华的字节数组会随着程序的结束自动回收
 	 */
 	ByteArrayOutputStream baos = new ByteArrayOutputStream();
 	String s = "HelloWorld";
 	baos.write(s.getBytes());
 	byte[] bys = baos.toByteArray();
 	
 	/*
 	 * 数据源: 来自于内存
 	 */
 	ByteArrayInputStream bais = new ByteArrayInputStream(bys);
 	byte[] bytes = new byte[10];
 	int len = bais.read(bytes);
 	System.out.println(new String(bytes, 0, len));
 }
}

字符数组内存流

CharArrayReader
CharArrayWriter

字符串内存流

StringReader
StringWriter

public class IODemo {
	public static void main(String[] args) throws Exception {
		CharArrayWriter caw = new CharArrayWriter();
		caw.write("今晚打老虎");
		caw.flush();
		caw.close();
		
		char[] chs = caw.toCharArray();
		CharArrayReader car = new CharArrayReader(chs);
		char[] charArr = new char[5];
		int len = car.read(charArr);
		System.out.println(new String(charArr, 0, len));
		
		StringWriter sw = new StringWriter();
		sw.write("HelloWorld");
		
		String result = sw.toString();
		StringReader sr = new StringReader(result);
		len = sr.read(charArr);
		System.out.println(new String(charArr, 0, len));		
	}
}

4 NIO

1 NIO的引入

NIO在JDK1.4后引入的
NIO是面向块(缓冲区)编程,旧IO是面向流编程

IO NIO
面向流 面向缓冲区
阻塞IO 非阻塞IO
无 选择器

Java中针对IO的一些核心的包和接口、类

java.nio 主要包含了各种与Buffer相关的类
java.nio.channel 主要包含了与Channel和Selector相关的类和接口
java.nio.charset 主要包含了与编码相关的类接口
java.nio.channels.spi 主要包含了与Channel相关的服务提供者编程接口
javan.nio.charset.spi 主要包含了与charset相关的服务提供者编程接口

目前需要掌握的核心就是四个包
Buffer
Channel
CharSet
Selector

面向缓冲区编程:
数据的读写必须经过缓冲区
我们可以使用Buffer所对应的子类来数据从通道(Channel)流向缓冲区
从缓冲区写到通道叫做读取缓冲区

2 Buffer

Buffer是一个抽象类,针对缓冲区封装的一个类,提供相应的方法来操作这个缓冲区
子类有
ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer

核心类
ByteBuffer和CharBuffer
ByteBuffer有一个子类 MappedByteBuffer
MappedByteBuffer类能够将文件直接映射到内存中,那么这样我们就可以像访问内存一样访问文件,非常方便

获取Buffer
获取ByteBuffer
static ByteBuffer allocate(int capacity)
分配一个新的字节缓冲区。
static ByteBuffer allocateDirect(int capacity)
分配新的直接字节缓冲区。
二者获取Buffer的区别
1.创建普通Buffer成本低,读写的效率不高
2.因为创建直接Buffer成本高,所以我们一般用在Buffer生存周期较长的时候使用
3.只有ByteBuffer才能够创建直接Buffer,其他的Buffer对象是不能够创建
4.如果创建了直接Buffer但是我又想要使用其他Buffer的功能,可以将ByteBuffer转换成其他Buffer
asIntBuffer()
四个非常重要的概念

capacity: 缓冲区的容量,不可以为负数,一旦创建了就不能够改变
limit: 无效缓冲区的第一个位置索引,limit后面的数据既不可读,也不可写
position : 下一个可以被读取或者写入的缓冲区位置索引
mark: 标记索引,该索引能够用于下次读取或者写入,它只能够在0-position之间

四个系数的关系:
0 < mark < postion < limit < capacity

五个方法

flip(): 将写模式切换为读模式, 将limit的值改为postion的值,同时将postion归0
特点: 就是为下一次数据的读取做好准备
clear(): 将读模式切换为写模式,将limit改为capacity的值,同时将postion归0
特点: 就是为下一次数据的写入做好准备
put(): 相对读取,向Buffer中存储数据
get(): 相对读取,从Buffer中获取数据
mark(): 设置标记位
reset(): 重置
hasRemaining(): 判断当前位置和limit之间是否还有元素可处理

绝对读取: get(index) 不会影响position的位置
相对读取: put() get() 会影响,每次读取一次,指针后移

public class NIODemo02 {
	public static void main(String[] args) {
		// ByteBuffer buffer = ByteBuffer.allocate(5);
		ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
		CharBuffer buffer = CharBuffer.allocate(8);
		// 已经准备好了向Buffer中写数据 写模式
		System.out.println("capacity:" + buffer.capacity()); // 8
		System.out.println("limit:" + buffer.limit()); // 8
		System.out.println("position:" + buffer.position()); // 0

		buffer.put('x');
		buffer.put('y');
		buffer.put('r');
		System.out.println("------------------------");

		System.out.println("capacity:" + buffer.capacity()); // 8
		System.out.println("limit:" + buffer.limit()); // 8
		System.out.println("position:" + buffer.position()); // 3

		System.out.println("------------------------");
		// 切换模式 ,limit变为position的位置然后将position变为0
		buffer.flip();
		System.out.println("capacity:" + buffer.capacity()); // 8
		System.out.println("limit:" + buffer.limit()); // 3
		System.out.println("position:" + buffer.position()); // 0

		System.out.println("------------------------");
		char ch = 0;
		ch = buffer.get();
		System.out.println(ch);

		buffer.mark();// mark: 1

		ch = buffer.get();
		System.out.println(ch);

		System.out.println("------------------------");
		System.out.println("capacity:" + buffer.capacity()); // 8
		System.out.println("limit:" + buffer.limit()); // 3
		System.out.println("position:" + buffer.position()); // 2

		System.out.println("------------------------");
		ch = buffer.get();
		System.out.println(ch);

//		ch = buffer.get();
//		System.out.println(ch);
		System.out.println("---------++---------------");
		buffer.reset();
		
		ch = 0;
		while (buffer.hasRemaining()) {
			ch = buffer.get();
			System.out.println((char) ch);
		}

		System.out.println("------------------");
		buffer.clear(); // 将postion 清 0 ,将limit = capacity

		System.out.println("capacity:" + buffer.capacity()); // 8
		System.out.println("limit:" + buffer.limit()); // 8
		System.out.println("position:" + buffer.position()); // 0

		// 注意: 调用clear方法只是将读模式改为写模式,并不会清空缓冲区的数据
		System.out.println(buffer.get(1));
		System.out.println("执行绝对读取之后Buffer的position位置:" + buffer.position());
	}
}

3 Channel 通道

Channel原理类似于传统的流对象, FileInputStream FileOutputStream
但是有3个主要的区别

1.程序如果想要读取Channel中的数据,不能够直接读写,必须经过Buffer 【唯一性】
2.通过Channel通道既能够读取也能够写入数据 【双向性】
3.Channel能够将指定的部分或者全部文件映射到内存中
全部映射
MappedByteBuffer
部分文件映射

Java中为Channel提供了如下常用的类
FileChannel 和文件相关的通道
DatagramChannel 和UDP协议传输数据相关的通道
SocketChannel 针对TCP协议客户端Socket提供的通道
ServerSocketChannel 针对TCP协议服务器端Socket提供的通道

获取FileChannel对象
和文件相关的普通流有哪些?
FileInputStream FileOutputStream RandomAccessFile
常用的方法
read() : 将Channel中的数据读取到Buffer中
write() : 向Buffer中写入数据
map(): 将channel中的数据全部或者部分映射到Buffer中
inChannel.map(mode, position, size)
MappedByteBuffer mappBuffer = inChannel.map(MapMode.READ_ONLY, 0, srcFile.length());

public class NIODemo03 {
	public static void main(String[] args) throws IOException {
		File srcFile = new File("nio.txt");
		FileInputStream fis = new FileInputStream(srcFile);
		FileOutputStream fos = new FileOutputStream(new File("nio4.txt"));
		
		// 获取Channel对象
		FileChannel inChannel = fis.getChannel();
		FileChannel outChannel = fos.getChannel();
		
		// 获取Buffer对象
		ByteBuffer buffer = ByteBuffer.allocate(10);
		
		// 将inChannel中的数据读取到Buffer中
		int len = inChannel.read(buffer);
		System.out.println(len);
		byte[] bys = buffer.array();
		System.out.println(new String(bys, 0, len));
		//	切换成读模式
		buffer.flip();
		len = inChannel.read(buffer);
		System.out.println(len);
		System.out.println(new String(buffer.array(), 0, len));
		buffer.flip();		
	}
}

4 Charset

理解为现实生活的编码表对象

public class NIODemo05 {
	public static void main(String[] args) throws UnsupportedEncodingException, CharacterCodingException {
		String s = "Hello中国";
		// 编码
		byte[] bys = s.getBytes("utf-8");
		// [72, 101, 108, 108, 111, -42, -48, -71, -6]
		// [72, 101, 108, 108, 111, -28, -72, -83, -27, -101, -67]
		System.out.println(Arrays.toString(bys));
		
		// 解码
		// Hello涓浗
		String data = new String(bys, "gbk");
		System.out.println(data);
		
	
//		编码 字符/字符数组/字符串 --> 数值
//		1.创建编码表对象
		Charset utf8 = Charset.forName("utf-8");
		Charset gbk = Charset.forName("gbk");
		/*
		 * 2.创建编码器对象或者解码器对象
		 */
		CharsetEncoder utf8Encoder = utf8.newEncoder();
		CharsetDecoder utf8Decoder = utf8.newDecoder();
		
		CharsetEncoder gbkEncoder = gbk.newEncoder();
		CharsetDecoder gbkDecoder = gbk.newDecoder();
		
		// 3.创建需要编码或者解码的数据源,数据源一定要是Buffer
		CharBuffer charBuffer = CharBuffer.allocate(10);
		charBuffer.put("Hello中国");
		charBuffer.flip();
		System.out.println("======编码========");
		ByteBuffer byteBuffer = utf8Encoder.encode(charBuffer);
		
		for (int i = 0; i < byteBuffer.limit(); i++) {
			System.out.println(byteBuffer.get(i));
		}
		
		System.out.println("======解码========");
		CharBuffer charBuffer2 = gbkDecoder.decode(byteBuffer);
		System.out.println(charBuffer2);
	}
}

5 NIO来遍历

利用NIO来遍历文件
基于事件驱动的方式遍历文件

public class NIODemo07 {
	public static void main(String[] args) throws IOException {
		FileVisitor<Path> visitor = new SimpleFileVisitor<Path>() {
			@Override
			public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes attrs) throws IOException {
				System.out.println("正准备访问" + path + "文件");
				return FileVisitResult.CONTINUE;
			}
			
			@Override
			public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
				System.out.println("正在访问" + path + "文件");
				if (path.endsWith("NIODemo03.java")) {
					System.out.println("恭喜你找到了我想要的Java文件,你可以停止查找了");
					return FileVisitResult.TERMINATE;
				}
				return FileVisitResult.CONTINUE;
			}
			
			@Override
			public FileVisitResult visitFileFailed(Path path, IOException exc) throws IOException {
				return FileVisitResult.SKIP_SIBLINGS;
			}
			
//			@Override
//			public FileVisitResult postVisitDirectory(Path path, IOException exc) throws IOException {
//				return FileVisitResult.CONTINUE;
//			}
		};
		
		Files.walkFileTree(Paths.get("D:\\Java_0408\\JavaSE\\day20"), visitor);
	}
}

6 WatchService监控文件系统

使用NIO的WatchService监控文件系统变化

public class NIODemo08 {
	public static void main(String[] args) throws Exception {
		// 获取到文件系统的WatchService对象
		WatchService watchService = FileSystems.getDefault().newWatchService();

		Paths.get("c:").register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
				StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY,
				StandardWatchEventKinds.OVERFLOW);
		// 通过wachService来监听文件系统
		while (true) {
			WatchKey key = watchService.take();
			List<WatchEvent<?>> pollEvents = key.pollEvents();
			for (WatchEvent<?> watchEvent : pollEvents) {
				System.out.println(watchEvent.context() + "发生了" + watchEvent.kind() + "事件");
			}

			boolean reset = key.reset();
			if (!reset) {
				break;
			}
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值