IO流
目录
一、File类的使用
二、IO流原理及流的分类
三、节点流
四、缓冲流
五、转换流
六、标准输入、输出流
七、打印流
八、数据流
九、对象流
十、随机存储文件流
十一、NIO.2中Path、 Paths、Files类的使用
一、File类的使用
(一)、File类的使用
- 1、File类的一个对象,代表一个文件或一个文件目录(俗称文件夹)
- 2、File类声明在Java.io包下
- 3、相对路径:
绝对路径: - 4、并未涉及到写入湖泊读取问价内容的的操作,如果需要读取或写入文件 内容,必须使用IO流来完成
- 5、后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的“终点”
- 6路径分隔符
(二)如何创建File类的对象
@Test
public void test1(){
//构造器1
File file1 = new File("hello.txt");//相对路径
File file2 = new File("F:\\Java\\IntelliJ IDEA\\Java Study\\day21");//绝对路径
System.out.println(file1);
System.out.println(file2);
//构造器2
File file3 = new File("F:\\Java\\IntelliJ IDEA","Java Study");
System.out.println(file3);
//构造器3
File file4 = new File(file2,"hello.txt");
}
(三)File类的常用方法
练习
练习1
1. 利用File构造器, new 一个文件目录file
1)在其中创建多个文件和目录
2)编写方法,实现删除file中指定文件的操作
```java
@Test
public void test1() throws IOException {
File file1 = new File("F:\\Java\\IntelliJ IDEA\\Java Study\\day21\\src\\IOExer\\hello.txt");
//在其中创建多个文件和目录
File destFile1 = new File(file1.getParent(),"haha1.txt");
File destFile2 = new File(file1.getParent(),"haha2.txt");
boolean newFile1 = destFile1.createNewFile();
boolean newFile2 = destFile1.createNewFile();
if(newFile1){
System.out.println("创建成功");
}
boolean deleteFile = destFile1.delete();
if(deleteFile){
System.out.println("删除成功");
}
}
练习2
2. 判断指定目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称
@Test
public void test2(){
File srcFile = new File("d:\\code");
File[] listFiles = srcFile.listFiles();
for(File file : listFiles){
if(file.getName().endsWith(".jpg")){
System.out.println(file.getAbsolutePath());
}
}
}
/*
* File类提供了两个文件过滤器方法
* public String[] list(FilenameFilter filter)
* public File[] listFiles(FileFilter filter)
*/
@Test
public void test3(){
File srcFile = new File("d:\\code");
File[] subFiles = srcFile.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".jpg");
}
});
for(File file : subFiles){
System.out.println(file.getAbsolutePath());
}
}
练习3
3. 遍历指定目录所有文件名称,包括子文件目录中的文件。
拓展1:并计算指定目录占用空间的大小
拓展2:删除指定文件目录及其下的所有文件
public static void main(String[] args) {
// 递归:文件目录
/** 打印出指定目录所有文件名称,包括子文件目录中的文件 */
// 1.创建目录对象
File dir = new File("E:\\teach\\01_javaSE\\_尚硅谷Java编程语言\\3_软件");
// 2.打印目录的子文件
printSubFile(dir);
}
public static void printSubFile(File dir) {
// 打印目录的子文件
File[] subfiles = dir.listFiles();
for (File f : subfiles) {
if (f.isDirectory()) {// 文件目录
printSubFile(f);
} else {// 文件
System.out.println(f.getAbsolutePath());
}
}
}
// 方式二:循环实现
// 列出file目录的下级内容,仅列出一级的话
// 使用File类的String[] list()比较简单
public void listSubFiles(File file) {
if (file.isDirectory()) {
String[] all = file.list();
for (String s : all) {
System.out.println(s);
}
} else {
System.out.println(file + "是文件!");
}
}
// 列出file目录的下级,如果它的下级还是目录,接着列出下级的下级,依次类推
// 建议使用File类的File[] listFiles()
public void listAllSubFiles(File file) {
if (file.isFile()) {
System.out.println(file);
} else {
File[] all = file.listFiles();
// 如果all[i]是文件,直接打印
// 如果all[i]是目录,接着再获取它的下一级
for (File f : all) {
listAllSubFiles(f);// 递归调用:自己调用自己就叫递归
}
}
}
// 拓展1:求指定目录所在空间的大小
// 求任意一个目录的总大小
public long getDirectorySize(File file) {
// file是文件,那么直接返回file.length()
// file是目录,把它的下一级的所有大小加起来就是它的总大小
long size = 0;
if (file.isFile()) {
size += file.length();
} else {
File[] all = file.listFiles();// 获取file的下一级
// 累加all[i]的大小
for (File f : all) {
size += getDirectorySize(f);// f的大小;
}
}
return size;
}
// 拓展2:删除指定的目录
public void deleteDirectory(File file) {
// 如果file是文件,直接delete
// 如果file是目录,先把它的下一级干掉,然后删除自己
if (file.isDirectory()) {
File[] all = file.listFiles();
// 循环删除的是file的下一级
for (File f : all) {// f代表file的每一个下级
deleteDirectory(f);
}
}
// 删除自己
file.delete();
}
二、IO流原理及流的分类
三、节点流
read()的使用
- 1、read()方法的理解:返回读入的一个字符,如果达到文件末尾,返回-1,底层自带迭代器
- 2、为了保证流资源一定可以关闭,我们用 try-catch-finally来处理
- 3、读入的文件一定要存在,否则就会报FileNotFoundException的异常
@Test
public void testFileReader1() {
FileReader fileReader = null;//先声明对象
try {
//1、实例化File类的对象,指明要操作的文件,没有实例化,会导致close()方法空指针
File file = new File("hello.txt");//相较于当前Module
//如果现在main方法里,就是相对于当前工程的
//2、提供具体的功能
fileReader = new FileReader(file);
//3、数据的读入
//read():返回读入的一个字符,如果达到文件末尾,返回-1
//read():底层自带迭代器
int data = fileReader.read();
while (data != -1) {
System.out.print((char) data);
data = fileReader.read();
}
} catch (IOException e){
e.printStackTrace();
}finally{
try {
//4、流的关闭操作
if(fileReader != null){//判断是否实例化,防止空指针
fileReader.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
@Test//对read()操作的升级:使用read()的重载方法
public void testFileReader2(){
//1、File的实例化
File file = new File("hello.txt");
FileReader fileReader = null;
try {
//2、FileReader的实例化
fileReader = new FileReader(file);
//3、读入操作
char[] charBuffer = new char[5];
int len;
while((len = fileReader.read(charBuffer)) != -1){
//难点:循环条件
//方法一
for (int i = 0; i < len; i++) {
System.out.print(charBuffer[i]);
}
}
while((len = fileReader.read(charBuffer)) != -1){
//难点:循环条件
//方法二
String str = new String(charBuffer,0,len);
System.out.print(str);
}
}catch (IOException e){
e.printStackTrace();
}finally{
//资源的关闭
try{
if(fileReader != null){
fileReader.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
write()的使用
- 1、输出操作:对应File可以不存在
- 2、如果不存在,在输出的过程中,会自动创建此文件
- 3、如果存在
① 若FileWrietr()构造器中append中写的是true,则在原有文本的基础上追加新内容
② 若append中写的是false,则覆盖原有的内容
③ 默认是false
@Test
public void testFileWriter(){
//1、File的实例化墓志铭写出的文件
File file = new File("hello.txt");
//2、FileWriter 的实例化,用于数据的写出,怕出现空指针
FileWriter fileWriter = null;
try{
fileWriter = new FileWriter(file,true);
//3、数据的写出
String str = "上山打老虎\n";
fileWriter.write(str);
fileWriter.write("you are a sutudents");
}catch (IOException e){
e.printStackTrace();
}finally{
try{
//4、流资源的关闭操作
fileWriter.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
使用FileWriter 和FileReader 实现文本的复制
- 1、写入时,还可以用重载的构造器:
- 2、流资源的关闭,尽量先开后关,后开先关
- 3、注意:流资源关闭的写法
fileWriter.write(charBuffer,0,len);
@Test
public void tset_FileWriter_FileReader(){
//1、示例化File类,指明读入文件和写出文件
File inputFile = new File("hello.txt");
File outputFile = new File("hello2.txt");
//2、声明FileWriter和FileReader类
FileReader fileReader = null;
FileWriter fileWriter = null;
try{
fileReader = new FileReader(inputFile);
fileWriter = new FileWriter(outputFile,true);
int len;
char[] charBuffer = new char[10];
while((len = fileReader.read(charBuffer)) != -1){
fileWriter.write(charBuffer,0,len);
}
//2.1、实例化FileWriter和FileReader类
}catch (IOException e){
e.printStackTrace();
}finally{
//4、流资源的关闭
try {
if(fileReader != null)
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}try {
if(fileWriter != null)
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用FileOutputStream 和FileInputStream 实现图片的复制
- 1、把char[]数组 改为 byte[]数组
- 2、把FileWriter和FileReder改为FileOutputStream和FileInputStream
总结
- 1、对于文本文件(.txt.java.c.cpp),使用字符流处理
- 2、对于非文本文件(.jpg.mp3.mp4.avi.doc.ppt),使用字节流处理
- 3、如果只是复制,也可以用字节流复制文本(注意:不能在内存空间看),byte[]数组长度太小会造成截断,中文复制会出现乱码
四、缓冲流
1、缓冲流的分类
- BefferedInputStream
- BefferedOutputStream
- BefferedReader
- BefferedWriter
2、 作用:提供流的读取,写入的速度
- 提高读写素的的原因:内部提供了一个缓冲区
说明
- 1、把节点流放在缓冲流的构造器里,
- 2、read()方法 和 write()方法 用 buffered~~去调用(否则缓冲流就没有意义了)
- 3、流资源的关闭,要求:先关闭外层的流,再关闭内层的流
- 4、说明:在关闭外层流时,内层的流会自动关闭,所以内层流的关闭,我们可以省略
- 处理流:就是“套接”在已有的流的基础上
- 5、flush()方法:清空缓存区
Beffered中独有方法
- 1、readline():读取一行,没有返回null(不会读入换行符)
- 2、newline():输出一行,自动加上换行符
String data;
while((data = befferedInputstream.readline(byteBeffer)) != unll){
// bufferedOutputStream.write(byteBeffer+"\n");//data中不包含换行符
bufferedOutputStream.newline(byteBeffer);
}
@Test
public void BufferedStream_Test(){
//1.造文件
File source_File = new File("idea.jpg");
File destination_File = new File("idea2.jpg");
//2.造流
//2.1 造节点流
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
//2.2 造缓冲流
BufferedInputStream bufferedInputStream = null;
BufferedOutputStream bufferedOutputStream =null;
try{
//2.3实例化
fileInputStream = new FileInputStream(source_File);
fileOutputStream = new FileOutputStream(destination_File);
bufferedInputStream = new BufferedInputStream(fileInputStream);
bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
//3、复制的细节:读取、写入
byte[] byteBuffer = new byte[10];
int len;
//3.1 用bufferedInputStream去调用 read()方法
while((len = bufferedInputStream.read(byteBuffer)) != -1){
//3.2 用bufferedOutputStream去调用 write()方法
bufferedOutputStream.write(byteBuffer,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally{
//4、流资源的关闭
//要求:先关闭外层的流,再关闭内层的流
//说明:在关闭外层流时,内层的流会自动关闭,所以内层流的关闭,我们可以省略
try {
bufferedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
练习
练习1
实现图片加密操作。
int b = 0;
while(b = fis.read() != -1){
fos.write(b ^ 5);
}
练习2
获取文本上每个字符出现的次数
提示:遍历文本的每一个字符;字符及出现的次数保存在Map中;将Map中数据
写入文件
/**
* 练习3:获取文本上字符出现的次数,把数据写入文件
*
* 思路:
* 1.遍历文本每一个字符
* 2.字符出现的次数存在Map中
*
* Map<Character,Integer> map = new HashMap<Character,Integer>();
* map.put('a',18);
* map.put('你',2);
*
* 3.把map中的数据写入文件
*
* @author shkstart
* @create 2019 下午 3:47
*/
public class WordCount {
/*
说明:如果使用单元测试,文件相对路径为当前module
如果使用main()测试,文件相对路径为当前工程
*/
@Test
public void testWordCount() {
FileReader fr = null;
BufferedWriter bw = null;
try {
//1.创建Map集合
Map<Character, Integer> map = new HashMap<Character, Integer>();
//2.遍历每一个字符,每一个字符出现的次数放到map中
fr = new FileReader("dbcp.txt");
int c = 0;
while ((c = fr.read()) != -1) {
//int 还原 char
char ch = (char) c;
// 判断char是否在map中第一次出现
if (map.get(ch) == null) {
map.put(ch, 1);
} else {
map.put(ch, map.get(ch) + 1);
}
}
//3.把map中数据存在文件count.txt
//3.1 创建Writer
bw = new BufferedWriter(new FileWriter("wordcount.txt"));
//3.2 遍历map,再写入数据
Set<Map.Entry<Character, Integer>> entrySet = map.entrySet();
for (Map.Entry<Character, Integer> entry : entrySet) {
switch (entry.getKey()) {
case ' ':
bw.write("空格=" + entry.getValue());
break;
case '\t'://\t表示tab 键字符
bw.write("tab键=" + entry.getValue());
break;
case '\r'://
bw.write("回车=" + entry.getValue());
break;
case '\n'://
bw.write("换行=" + entry.getValue());
break;
default:
bw.write(entry.getKey() + "=" + entry.getValue());
break;
}
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关流
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
五、转换流
(一)转换流分类:属于字符流
- 1、InputStreamReader:将InputStream转换为Reader(解码)
- 2、OutputStreamWriter:将Writer转换为OutputStream(编码)
(二)作用:提供字节流于字符流之间的转换
- 1、解码:字节 byte[]数组 --> 字符 char[]数组
- 2、编码:字符 char[]数组 --> 字节 byte[]数组
(三)字符集
@Test
public void test1(){
InputStreamReader inputStreamReader = null;//没有显式地说明,使用系统默认的字符集
try {
FileInputStream fileInputStream = new FileInputStream("hello.txt");
//具体使用那个字符集取决于hello.txt保存时使用的字符集
inputStreamReader = new InputStreamReader(fileInputStream,"UTF-8");
char[] charBuffer = new char[5];
int len;
while((len = inputStreamReader.read(charBuffer)) != -1){
String str = new String(charBuffer,0,len);
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStreamReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
六、标准输入、输出流
概述
- 1、System.in:标准的输入流,从键盘输入
- 2、System.out:标准的输出流,默认从控制台(显示器)输出
方法
- 1、可以通过System类的setIn(InputStream is)\serTut(OutputStream os)重新指定输入和输出的流
练习1
从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续
进行输入操作,直至当输入“e”或者“exit”时,退出程序。
- 方式一:用Scanner实现,调用next~()方法,返回一个字符串
- 方式二:用System.in实现——>BufferedReader里的readline()
public static void main(String[] args) {
BufferedReader br = null;
try {
InputStreamReader isr = new InputStreamReader(System.in);
br = new BufferedReader(isr);
while(true){
String data = br.readLine();
//注意点:把data写在括号里,避免空指针
if("e".equalsIgnoreCase(data)||"exit".equalsIgnoreCase(data)){
System.out.println("结束输入");
break;
}
String upperCase = data.toUpperCase();
System.out.println(upperCase);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(br != null)
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
练习2
Create a program named MyInput.java: Contain the methods for reading int,
double, float, boolean, short, byte and String values from the keyboard.
public class MyInput {
// Read a string from the keyboard
public static String readString() {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// Declare and initialize the string
String string = "";
// Get the string from the keyboard
try {
string = br.readLine();
} catch (IOException ex) {
System.out.println(ex);
}
// Return the string obtained from the keyboard
return string;
}
// Read an int value from the keyboard
public static int readInt() {
return Integer.parseInt(readString());
}
// Read a double value from the keyboard
public static double readDouble() {
return Double.parseDouble(readString());
}
// Read a byte value from the keyboard
public static double readByte() {
return Byte.parseByte(readString());
}
// Read a short value from the keyboard
public static double readShort() {
return Short.parseShort(readString());
}
// Read a long value from the keyboard
public static double readLong() {
return Long.parseLong(readString());
}
// Read a float value from the keyboard
public static double readFloat() {
return Float.parseFloat(readString());
}
}
七、打印流
练习
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream(new File("D:\\IO\\text.txt"));
// 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
ps = new PrintStream(fos, true);
if (ps != null) {// 把标准输出流(控制台输出)改成文件
System.setOut(ps);
}
for (int i = 0; i <= 255; i++) { // 输出ASCII字符
System.out.print((char) i);
if (i % 50 == 0) { // 每50个数据一行
System.out.println(); // 换行
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ps != null) {
ps.close();
}
}
八、数据流
作用:用于读取或写出基本数据类型的变量(可以单独区分开)
- 注意1:用 DataOutputStream 写出的文件只能用 DataInputStream 读取
- 注意2:读取不同数据类型的数据的顺序要与当初写入文件时,保存数据的顺序一致
- 可以调用flush()方法,将内存(相当于缓冲区)中已有的数据写入到文件里
注意:此处应使用try-catch-finally
DataOutputStream dos = new DataOutputStream(new FileOutputStream("destData.dat"));
dos.writeUTF("我爱北京天安门"); // 写UTF字符串
dos.writeBoolean(false); // 写入布尔值
dos.writeLong(1234567890L); // 写入长整数
dos.close();
DataInputStream dis = new DataInputStream(new FileInputStream("destData.dat"));
String info = dis.readUTF();
boolean flag = dis.readBoolean();
long time = dis.readLong();
System.out.println(info);
System.out.println(flag);
System.out.println(time);
dis.close();
九、对象流
对象流的使用
- 1、ObjectInputstream 和 ObjectOutputStream
- 2、作用:用于储存和读取基本数据类型或对象的处理流,它的强大之处就是可以把java中的对象写入数据源中,也能把对象从数据源中还原回来
序列化的过程
- 将内存中的Java对象爆粗到次哦按中国或通过网络传输出去,使用ObjectOutoutStream实现
//序列化:将内存中的对象保存到磁盘里
//使用ObjectOutputStream实现
@Test
public void test1 (){
ObjectOutputStream oos = null;
try {
//不用再实话File类了
oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
oos.writeObject(new String("我爱北京"));
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//反序列化:将磁盘文件中的对象还原为内存中的对象
//使用ObjectInputStream实现
@Test
public void test2 (){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("object.dat"));
Object obj = ois.readObject();
String str = (String)obj;
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
自定义类的序列化
自定义类需要满足以下要求,方可序列化
- 1、需要实现接口:Serialzable
- 2、当前类提供一个全局变量:serialVersionUID
- 3、除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性也是可序列化的(默认情况下,基本数据类型是可序列化的)
- 4、即,如果内部结构是其他自定义类,则这些自定义类也要是可序列化的(重复1、2、3)
- 5、ObjectOutputStream和ObjectInputStream不能序列化static和transient修
饰的成员变量
十、随机存储文件流(RandomAccessFile 类)
RandomAccessFile的使用
- 1、RandomAccessFile直接继承于java.lang.Object,实现了DAteInput和DateOutput
- 2、RandomAccessFile既可以作为一个输入流,也可以作为一个输出流,
- 3、注意:RandomAccessFile同时作为输入输出流时,要创建两个对象,一个负责输入,一个负责输出
- 4、如果RandomAccessFile作为输出流时,若写出到的文件已存在,则会对原有文件进行覆盖(默认情况下是,从头开始覆盖)
- 5、可以用RandomAccessFile实现插入效果
使用RandomAccessFile实现插入效果
@Test
public void tset(){
RandomAccessFile raf1 = null;
try {
raf1 = new RandomAccessFile(new File("hello.txt"),"rw");
raf1.seek(3);//将指针调到角标为3 的位置
//保存指针3 后面所有的数据到StringBuffer中
StringBuilder builder = new StringBuilder((int)new File("hello.txt").length());//获取文件的长度
byte[] byteBuffer = new byte[20];
int len;
while((len = raf1.read(byteBuffer)) != -1){
builder.append(new String(byteBuffer,0,len));
}
//调回指针,写入“xyz”
raf1.seek(3);
raf1.write("xyz".getBytes());//把字符串——>字节数组
//将StringBuilder中的数据写入文件中
raf1.write(builder.toString().getBytes());//因为builder没有getBytes()方法,所以把它变成字符串,用String的getBytes()方法
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(raf1 != null)
raf1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}