Java SE基础知识详解第[15]期—File、方法递归、IO流

简介: Java SE基础知识详解第[15]期—File、方法递归、IO流

File、方法递归、IO流

1.File类概述

File类在包java.io.File下、代表操作系统的文件对象(文件、文件夹)

File类提供了诸如:定位文件,获取文件本身的信息、删除文件、创建文件(文件夹)等功能。

File类作用:创建对象定位文件,可以删除、获取文件信息等。但是不能读写文件内容

File类构造器

方法名

说明

public File (String pathname)

根据文件路径创建文件对象

public File (String parent, String child)

从父路径名字符串和子路径名字符串创建文件对象

public File (File parent, String child)

根据父路径对应文件对象和子路径名字符串

创建文件对象

 

File对象可以定位文件和文件夹。

File封装的对象仅仅是一个路径名,这个路径可以是存在的,也可以是不存在的。

绝对路径和相对路径

如下图所示,绝对路径:从盘符开始,依赖于当前系统

AbsolutePath.png

如下图所示,相对路径:不带盘符,默认直接到当前工程下的目录寻找文件

RelativePath.png

示例代码如下:

publicstaticvoidmain(String[] args) {
// 1.创建File对象(指定文件路径)// 路径写法// 反斜杠\ D:\360Downloads\modiloginbg.jpgFilef=newFile("D:\\360Downloads\\modiloginbg.jpg");
// 正斜杠/ D:/360Downloads/modiloginbg.jpg//        File f = new File("D:/360Downloads/modiloginbg.jpg");// API形式生成拼接符替代正(反)斜杠 File.separator 好处:跨平台,可以自动生成Windows、Linux等不同平台的拼接符//        File f = new File("D:" + File.separator + "360Downloads" + File.separator + "modiloginbg.jpg");longsize=f.length(); // 返回文件字节大小System.out.println(size); // 612265// 2.※File创建对象,支持绝对路径也支持相对路径// 绝对路径:从盘符开始Filef2=newFile("D:\\360Downloads\\214510.jpg");
System.out.println(f2.length()); // 363322// 相对路径:不带盘符,通常用来定义模块中的某个文件,相对于工程下的路径Filef3=newFile("day10_file_io_app\\src\\2055376.jpg");
System.out.println(f3.length()); // 777576// 3.File创建对象,可以是文件,也可以是文件夹Filef4=newFile("D:\\360Downloads");
//        System.out.println(f4.length()); // 一般不通过这种方式获取文件夹的大小System.out.println(f4.exists()); // true 判断该文件对象是否存在    }

注:路径写法

①反斜杠\D:\360Downloads\modiloginbg.jpg

File f = new File("D:\\360Downloads\\modiloginbg.jpg");

②正斜杠/D:/360Downloads/modiloginbg.jpg

File f = new File("D:/360Downloads/modiloginbg.jpg");

③API形式生成拼接符替代正(反)斜杠File.separator

File f = new File("D:" + File.separator + "360Downloads" + File.separator + "modiloginbg.jpg");

好处:跨平台,可以自动生成Windows、Linux等不同平台的拼接符。

2.File类的常用API

File类的判断文件类型、获取文件信息功能常用API

方法名

说明

public boolean isDirectory()

测试此抽象路径名表示的File是否为文件夹

public boolean isFile()

测试此抽象路径名表示的File是否为文件

public boolean exists()

测试此抽象路径名表示的File是否存在

public String getAbsolutePath()

返回此抽象路径名的绝对路径名字符串

public String getPath()

将此抽象路径名转换为路径名字符串(怎么定义的就怎么拿)

public String getName()

返回由此抽象路径名表示的文件名+后缀或文件夹名

public long lastModified()

返回文件最后修改的时间毫秒值

 public long length()

返回文件字节大小

 

示例代码如下:

 

publicstaticvoidmain(String[] args) {
// 绝对路径Filef1=newFile("D:\\360Downloads\\214510.jpg");
//  1.public boolean isDirectory()  测试此抽象路径名表示的File是否为文件夹System.out.println(f1.isDirectory()); // false//  2.public boolean isFile()   测试此抽象路径名表示的File是否为文件System.out.println(f1.isFile()); // true//  3.public boolean exists()   测试此抽象路径名表示的File是否存在System.out.println(f1.exists()); // true//  4.public String getAbsolutePath()   返回此抽象路径名的绝对路径名字符串System.out.println(f1.getAbsolutePath()); // D:\360Downloads\214510.jpg//  5.public String getPath()   将此抽象路径名转换为路径名字符串(怎么定义的就怎么拿)System.out.println(f1.getPath()); // D:\360Downloads\214510.jpg//  6.public String getName()   返回由此抽象路径名表示的文件名+后缀或文件夹名System.out.println(f1.getName()); // 214510.jpg//  7.public long lastModified()    返回文件最后修改的时间毫秒值longtime=f1.lastModified();
System.out.println("最后修改时间:"+newSimpleDateFormat("yyyy-MM-ss HH:mm:ss").format(time));
// 最后修改时间:2021-04-50 13:02:50    }

File类创建文件的功能常用API

方法名

说明

public boolean createNewFile()

创建一个新的空的文件(几乎不用)

public boolean mkdir()

只能创建一级文件夹

public boolean mkdirs()

可以创建多级文件夹

 

File类删除文件的功能常用API

方法名

说明

public boolean delete ()

删除由此抽象路径名表示的文件或空文件夹(文件在被占用状态时也可以被删除)

 

示例代码如下:

publicstaticvoidmain(String[] args) throwsIOException {
Filef=newFile("day10_file_io_app\\src\\2055376.jpg");
//  1.public boolean createNewFile()    创建一个新的空的文件(几乎不用)System.out.println(f.createNewFile()); // falseFilef2=newFile("day10_file_io_app\\src\\2055376aaa.jpg");
System.out.println(f2.createNewFile()); // true//  2.public boolean mkdir()    只能创建一级文件夹Filef3=newFile("D:\\360Downloads\\abc");
System.out.println(f3.mkdir()); // trueFilef4=newFile("D:\\360Downloads\\abc\\def\\ghi");
System.out.println(f4.mkdir()); // false//  3.public boolean mkdirs()   可以创建多级文件夹Filef5=newFile("D:\\360Downloads\\abc\\def\\ghi");
System.out.println(f5.mkdirs()); // true//  4.public boolean delete ()  删除由此抽象路径名表示的文件或空文件夹(文件在被占用状态时也可以被删除)System.out.println(f2.delete()); // trueSystem.out.println(f3.delete()); // false 非空文件夹不可删除System.out.println(f5.delete()); // true 只能删除空文件夹    }

注:delete方法默认只能删除文件和空文件夹

delete方法直接删除不走回收站

File类的遍历功能

方法名

说明

public String[] list()

获取当前目录下所有的"一级文件名称"

到一个字符串数组中去返回

public File[] listFiles()(常用)

获取当前目录下所有的"一级文件对象"

到一个文件对象数组中去返回(重点)

 

示例代码如下:

publicstaticvoidmain(String[] args) {
Filef1=newFile("D:\\360Downloads");
//  1.public String[] list()//  获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回String[] nameArray=f1.list();
System.out.println(Arrays.toString(nameArray));
// [2055376.jpg, 214510.jpg, abc, addlogin.jpg, bgpic5.jpg, loginbg.jpg, loginbg1.jpg, loginbg2.jpg, modiloginbg.jpg, Software, wpcache]//  2.public File[] listFiles()(常用)//  获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)File[] fileArray=f1.listFiles();
for (Filefile : fileArray) {
System.out.println(file.getAbsolutePath());
        }
/*D:\360Downloads\2055376.jpgD:\360Downloads\214510.jpgD:\360Downloads\abcD:\360Downloads\addlogin.jpgD:\360Downloads\bgpic5.jpgD:\360Downloads\loginbg.jpgD:\360Downloads\loginbg1.jpgD:\360Downloads\loginbg2.jpgD:\360Downloads\modiloginbg.jpgD:\360Downloads\SoftwareD:\360Downloads\wpcache*/    }

listFiles方法注意事项:

当调用者不存在时,返回null。

当调用者是一个文件时,返回null。

当调用者是一个空文件夹时,返回一个长度为0的数组。

当调用者是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回。

当调用者是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏内容。

当调用者是一个需要权限才能进入的文件夹时,返回null。

3.方法递归

3.1递归的形式和特点

什么是方法递归?

方法直接调用自己或者间接调用自己的形式称为方法递归(recursion)。

递归做为一种算法在程序设计语言中广泛应用。

递归的形式

直接递归:方法自己调用自己。

间接递归:方法调用其他方法,其他方法又回调方法自己。

方法递归存在的问题?

递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出现象。

示例代码如下:

publicclassRecursionDemo1 {
publicstaticvoidmain(String[] args) {
// test(); // 进入死循环,栈内存溢出错误// test2(); // 进入死循环,栈内存溢出错误    }
// 直接递归publicstaticvoidtest() {
System.out.println("--------test执行--------");
test(); // 方法递归,直接递归    }
// 间接递归publicstaticvoidtest2() {
System.out.println("-------test2执行--------");
test3(); // 方法递归,间接递归    }
privatestaticvoidtest3() {
System.out.println("-------test3执行--------");
test2();
    }
}

3.2递归的算法流程、核心要素

案例:递归案例导学-计算n的阶乘

需求:计算n的阶乘的结果,使用递归思想解决,我们先从数学思维上理解递归的流程和核心点。

分析:

① 假如我们认为存在一个公式是 f(n) = 1*2*3*4*5*6*7*…(n-1)*n;

② 那么公式等价形式就是: f(n) = f(n-1) *n

③ 如果求的是 1-5的阶乘 的结果,手工应该应该如何应用上述公式计算。

④ f(5) = f(4) * 5f(4) = f(3) * 4f(3) = f(2) * 3f(2) = f(1) * 2f(1) = 1

递归解决问题的思路

把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。

递归算法三要素大体可以总结为:

递归的公式: f(n) = f(n-1) * n

递归的终结点:f(1)

递归的方向必须走向终结点

示例代码如下:

publicclassRecursionDemo2 {
publicstaticvoidmain(String[] args) {
/*案例:递归案例导学-计算n的阶乘需求:计算n的阶乘的结果,使用递归思想解决,我们先从数学思维上理解递归的流程和核心点。分析:① 假如我们认为存在一个公式是 f(n) = 1*2*3*4*5*6*7*…(n-1)*n;② 那么公式等价形式就是: f(n) = f(n-1) *n③ 如果求的是 1-5的阶乘 的结果,手工应该应该如何应用上述公式计算。④ f(5) = f(4) * 5   f(4) = f(3) * 4 f(3) = f(2) * 3 f(2) = f(1) * 2 f(1) = 1*/System.out.println("5的阶乘是:"+f(5)); // 5的阶乘是:120    }
publicstaticintf(intn) {
if (n==1) {
return1;
        } else {
returnf(n-1) *n;
        }
    }
}

3.3递归经典问题

案例-猴子吃桃问题

猴子第一天摘下若干桃子,当即吃了一半,觉得好不过瘾,于是又多吃了一个。第二天又吃了前天剩余桃子数量的一半,觉得好不过瘾,于是又多吃了一个。以后每天都是吃前天剩余桃子数量的一半,觉得好不过瘾,又多吃了一个。等到第10天的时候发现桃子只有1个了。

需求:请问猴子第一天摘了多少个桃子?

分析:

① 整体来看,每一天都是做同一个事件,典型的规律化问题,考虑递归三要素

② 递归公式:f(n) = 2 * (f(n + 1) + 1)

③ 递归终结点:f(10) = 1

④ 递归方向:n不断增大,向10靠拢,合理。

示例代码如下:

publicclassRecursionDemo4 {
publicstaticvoidmain(String[] args) {
/*案例-猴子吃桃问题猴子第一天摘下若干桃子,当即吃了一半,觉得好不过瘾,于是又多吃了一个。第二天又吃了前天剩余桃子数量的一半,觉得好不过瘾,于是又多吃了一个。以后每天都是吃前天剩余桃子数量的一半,觉得好不过瘾,又多吃了一个。等到第10天的时候发现桃子只有1个了。 需求:请问猴子第一天摘了多少个桃子? 分析:① 整体来看,每一天都是做同一个事件,典型的规律化问题,考虑递归三要素:② 递归公式:f(n) = 2 * (f(n + 1) + 1)③ 递归终结点:f(10) = 1④ 递归方向:n不断增大,向10靠拢,合理。*/System.out.println("第一天摘下"+eat(1) +"个桃子"); // 第一天摘下1534个桃子    }
publicstaticinteat(intn) {
if (n==10) {
return1;
        } else {
return (eat(n+1)  +1 ) *2;
        }
    }
}

3.4非规律化递归案例-文件搜索

案例-文件搜索

需求:文件搜索、从C:盘中,搜索出某个文件名称并输出绝对路径。

分析:

① 先定位出的应该是一级文件对象。

② 遍历全部一级文件对象,判断是否是文件。

③ 如果是文件,判断是否是自己想要的。

④ 如果是文件夹,需要继续递归进去重复上述过程。

示例代码如下:

publicclassRecursionDemo5 {
publicstaticvoidmain(String[] args) {
// 需求:去D盘搜索2055376.jpg文件searchFile(newFile("D:/"), "2055376.jpg");
/*文件存在,路径名称:D:\360Downloads\2055376.jpg文件存在,路径名称:D:\sss2055376.jpg*/    }
/*** create by: 全聚德在逃烤鸭、* description: 搜索某个目录下的所需文件* create time: 2022/6/3 0003 15:50** @param dir      查询目录* @param fileName 被搜索的文件名称* @return void*/publicstaticvoidsearchFile(Filedir, StringfileName) {
// 判断是否是文件夹if (dir!=nul&&dir.isDirectory()) {
File[] fileArray=dir.listFiles();
// 判断文件数组中是否不为null且有内容(数组为null时,增强for循环报错空值异常)if (fileArray!=nul&&fileArray.length>0) {
for (Filefile : fileArray) {
// 判断是否是文件if (file.isFile()) { // 是文件,判断是否是被搜索的文件if (file.getName().contains(fileName)) {
System.out.println("文件存在,路径名称:"+file.getAbsolutePath());
                        }
                    } else { // 否则为文件夹,调用递归方法searchFile继续向下搜索searchFile(file, fileName);
                    }
                }
            }
        } else {
System.out.println("当前搜索的位置不是文件夹!");
        }
    }
}

注:切记注意空值异常的问题,尽可能增加判断空值的代码!!!

3.5非规律化递归案例-啤酒问题

需求:啤酒2元1瓶,4个盖子可以换一瓶,2个空瓶可以换一瓶,请问10元钱可以喝多少瓶酒,剩余多少空瓶和盖子。

答案:15瓶 3盖子 1瓶子。

第一种方式:将空瓶、瓶盖折算为金钱,买酒方法参数为money。

示例代码如下:

publicclassRecursionDemo6 {
// 定义静态变量,代表买酒的数目,每轮买酒后增加publicstaticinttotalNumber;
publicstaticintlastBottleNumber; // 上轮剩余的空瓶数publicstaticintlastCoverNumber; // 上轮剩余的瓶盖数publicstaticvoidmain(String[] args) {
// 需求:需求:啤酒2元1瓶,4个盖子可以换一瓶,2个空瓶可以换一瓶,请问10元钱可以喝多少瓶酒,剩余多少空瓶和盖子// 答案:15瓶 3盖子 1瓶子buyBeer(10); // 可以喝15瓶酒,剩余1个空瓶,3个瓶盖    }
publicstaticvoidbuyBeer(intmoney) {
intbuyNumber=money/2;
// 判断剩余的钱是否可以买酒if (buyNumber==0) { // 若不能买酒,则退出并输出相应信息System.out.println("可以喝"+totalNumber+"瓶酒,剩余"+lastBottleNumber+"个空瓶,"+lastCoverNumber+"个瓶盖");
return;
        }
totalNumber+=buyNumber; // 更新买酒的数目// 统计本轮空瓶与瓶盖总数intbottleNumber=lastBottleNumber+buyNumber;
intcoverNumber=lastCoverNumber+buyNumber;
// 统计可以换算的用于买酒的钱intallMoney=money-buyNumber*2;
// 空瓶相当于1元一瓶,满两瓶可买一瓶啤酒,相当于2元allMoney+= (bottleNumber/2) *2; // 若不满2瓶,则无法购买,因此需要保留lastBottleNumber / 2lastBottleNumber=bottleNumber%2; // 更新本轮剩余空瓶数,用于下一轮买酒// 瓶盖相当于0.5元一个,满四个可买一瓶啤酒,相当于2元allMoney+= (coverNumber/4) *2; // 若不满4个,则无法购买,因此需要保留lastCoverNumber / 4lastCoverNumber=coverNumber%4; // 更新本轮剩余瓶盖瓶数,用于下一轮买酒// 调用递归方法,进行下一轮买酒buyBeer(allMoney);
    }
}

第二种方式:买酒方法参数为金钱、当前空瓶数量、当前瓶盖数量。

示例代码如下:

publicclassRecursionDemo7 {
// 定义静态变量,代表买酒的数目,每轮买酒后增加publicstaticinttotalNumber;
publicstaticvoidmain(String[] args) {
// 需求:需求:啤酒2元1瓶,4个盖子可以换一瓶,2个空瓶可以换一瓶,请问10元钱可以喝多少瓶酒,剩余多少空瓶和盖子// 答案:15瓶 3盖子 1瓶子buyBeer(10, 0, 0); // 可以喝15瓶酒,剩余1个空瓶,3个瓶盖    }
publicstaticvoidbuyBeer(intmoney, intlastBottleNumber, intlastCoverNumber) {
intbottleNumber=lastBottleNumber; // 本轮剩余空瓶数目intcoverNumber=lastCoverNumber; // 本轮剩余瓶盖数目// 金额足够用钱买酒,钱不够了用空瓶、瓶盖换酒// 判断剩余金额是否足够买酒if (money>=2) {
intbuyNumber=money/2;
totalNumber+=buyNumber; // 更新买酒的数目money-=buyNumber*2; // 更新剩余金额// 更新当前剩余空瓶、瓶盖数目bottleNumber+=buyNumber;
coverNumber+=buyNumber;
        } else {
if (bottleNumber>=2) {
intbuyNumber=bottleNumber/2; // 空瓶换购啤酒数目totalNumber+=buyNumber; // 更新买酒的数目// 更新当前剩余空瓶、瓶盖数目bottleNumber=bottleNumber%2+buyNumber;
coverNumber+=buyNumber;
            }
if (coverNumber>=4) {
intbuyNumber=coverNumber/4; // 瓶盖换购啤酒数目totalNumber+=buyNumber; // 更新买酒的数目// 更新当前剩余空瓶、瓶盖数目bottleNumber+=buyNumber;
coverNumber=coverNumber%4+buyNumber;
            }
        }
// 调用递归方法,进行下一轮买酒if (money>=2||bottleNumber>=2||coverNumber>=4) { // 若满足下一轮买酒条件则继续,否则结束买酒buyBeer(money, bottleNumber, coverNumber);
        } else {
System.out.println("可以喝"+totalNumber+"瓶酒,剩余"+bottleNumber+"个空瓶,"+coverNumber+"个瓶盖");
        }
    }
}

4.字符集

4.1常见字符集介绍

字符集基础知识

计算机底层不可以直接存储字符的。计算机中底层只能存储二进制(0、1)。

二进制是可以转换成十进制的。

结论:计算机底层可以表示十进制编号,计算机给人类字符进行编号存储的编号规则就是字符集

ASCII字符集

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):包括了数字、英文、符号。

ASCII使用1个字节存储一个字符,一个字节是8位,总共可以表示128个字符信息,对于英文,数字来说是够用的。

GBK

Windows系统默认的码表。兼容ASCII码表,也包含了几万个汉字,并支持繁体汉字以及部分日韩文字。

注意:GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字。

Unicode码表

Unicode(又称统一码、万国码、单一码)是计算机科学领域里的一项业界字符编码标准,容纳世界上大多数国家的所有常见文字和符号。

由于Unicode会先通过UTF-8,UTF-16,以及UTF-32的编码成二进制后再存储到计算机,其中最为常见的就是UTF-8(UTF-8是Unicode的一种编码方式)

注:

Unicode是万国码,UTF-8编码后一个中文一般以三个字节的形式存储

UTF-8也要兼容ASCII编码表。

技术人员都应该使用UTF-8的字符集编码。

编码前和编码后的字符集需要一致,否则会出现中文乱码。

汉字存储和展示过程如下图所示。

UTF-8.png

知识点汇总:

字符串常见的字符底层组成是什么样的?

英文和数字等在任何国家的字符集中都占1个字节。

GBK字符中一个中文字符占2个字节

UTF-8编码中一个中文1般占3个字节

编码前的字符集和编码后的字符集有什么要求?

必须一致,否则会出现中文字符乱码。

英文和数字在任何国家的编码中都不会乱码。

4.2字符集的编码、解码操作

String编码常用API

方法名

说明

byte[] getBytes()

使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中

byte[] getBytes(String charsetName)

使用指定的字符集将该 String编码为一系列字节,

将结果存储到新的字节数组中

 

String解码常用构造器

方法名

说明

String (byte[] bytes)

通过使用平台的默认字符集解码指定的字节数组来构造新的String

String (byte[] bytes, String charsetName)

通过指定的字符集解码指定的字节数组

来构造新的String

String(byte bytes[], int offset, int length)

解码指定的字节数组,指定开始位置与解码长度

 

示例代码如下:

publicclassTest {
publicstaticvoidmain(String[] args) throwsUnsupportedEncodingException {
// 1.编码   把文字转换为字节(可以使用指定编码)Stringname="abc我爱你中国";
byte[] bytes=name.getBytes(); // 以当前代码默认字符集进行编码(当前默认UTF-8)// 英文、数字在UTF-8字符集中为正数,汉字为负数System.out.println(Arrays.toString(bytes)); // [97, 98, 99, -26, -120, -111, -25, -120, -79, -28, -67, -96, -28, -72, -83, -27, -101, -67]System.out.println(bytes.length); // 18// 指定编码(字符集)为GBKbyte[] bytes2=name.getBytes("GBK");
System.out.println(Arrays.toString(bytes2)); // [97, 98, 99, -50, -46, -80, -82, -60, -29, -42, -48, -71, -6]System.out.println(bytes2.length); // 13// 2.解码 把字节转换为文字(编码前和编码后的字符集必须一致,否则出现乱码)Stringrs=newString(bytes); // 以当前代码默认字符集进行解码(当前默认UTF-8)System.out.println(rs); // abc我爱你中国// 指定解码(字符集)为GBKStringrs2=newString(bytes2, "GBK");
System.out.println(rs2); // abc我爱你中国    }
}

5.IO流概述

IO流也称为输入、输出流,就是用来读写数据的。

I表示intput,是数据从硬盘文件读入到内存的过程,称之输入,负责读。

O表示output,是内存程序的数据从内存到写出到硬盘文件的过程,称之输出,负责写。

IO流的分类

字节输入流以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流称为字节输入流。

字节输出流以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流称为字节输出流。

字符输入流以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流称为字符输入流。

字符输出流:以内存为基准,把内存中的数据以字符的形式写出到磁盘文件或者网络中去的流称为字符输出流。

IO流体系如下图所示。

IOStream.png

6.字节流的使用

6.1文件字节输入流:每次读取一个字节

文件字节输入流:FileInputStream

作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去。

FileInputStream构造器

方法名

说明

public FileInputStream(File file)

创建字节输入流管道与源文件对象接通

public FileInputStream(String pathname)

创建字节输入流管道与源文件路径接通

 

FileInputStream常用API

方法名

说明

public int read()

每次读取一个字节将读取的数据内容的编号返回

如果字节已经没有可读的返回-1

public int read(byte[] buffer)

每次读取一个字节数组将读取的数据数目返回读取的数据内容保存在字节数组buffer中,如果字节已经没有可读的返回-1

 

示例代码如下;

publicstaticvoidmain(String[] args) throwsException {
// 1.创建一个文件字节输入流管道与源文件接通InputStreamis=newFileInputStream("day10_file_io_app\\src\\data.txt");
// 读取第一个字节返回intb1=is.read();
System.out.println(b1); // 97   字符编号System.out.println((char) b1); // a  转为字符型intb2=is.read();
System.out.println(b2); // 98System.out.println((char) b2); // bintb3=is.read();
System.out.println(b3); // 99System.out.println((char) b3); // c// 如果字节已经没有可读的返回-1intb4=is.read();
System.out.println(b4); // -1    }

注:每次读取一个字节会不可避免的存在一些问题。

性能较慢。

读取中文字符输出无法避免乱码问题(每次读取1个字节,1个中文字符在UTF-8中占3个字节)。

6.2文件字节输入流:每次读取一个字节数组

示例代码如下:

publicstaticvoidmain(String[] args) throwsException {
// 1.创建一个文件字节输入流管道与源文件接通InputStreamis=newFileInputStream("day10_file_io_app\\src\\data2.txt");
// 2.定义一个字节数组,用于读取字节数组中的数据byte[] buffer=newbyte[3]; // 每次最多读取3B的数据intnumber=is.read(buffer);
System.out.println("读取了几个字节:"+number); // 读取了几个字节:3// 解码 把字节转换为文字Stringrs=newString(buffer);
System.out.println(rs); // abcintnumber2=is.read(buffer);
System.out.println("读取了几个字节:"+number2); // 读取了几个字节:3Stringrs2=newString(buffer);
System.out.println(rs2); // 我intnumber3=is.read(buffer);
System.out.println("读取了几个字节:"+number3); // 读取了几个字节:2// String rs3 = new String(buffer);// System.out.println(rs3); // 12�/*第28行后,此时字节数组buffer没有清空,其中存储的是代表汉字"我"的三个字节,再次进行读取时,仅能读取2个字节,因此将"12"代表的2个字节存入至buffer的前2位中第3位仍为代表汉字"我"的3个字节中的第3位,因此乱码*/// 改进   读取多少数据,转换多少数据Stringrs3=newString(buffer, 0, number3); // 指定开始位置与解码长度System.out.println(rs3); // 12// 如果字节已经没有可读的返回-1intnumber4=is.read(buffer);
System.out.println("读取了几个字节:"+number4); // 读取了几个字节:-1    }

注:

第28行后,此时字节数组buffer没有清空,其中存储的是代表汉字"我"的三个字节,再次进行读取时,仅能读取2个字节,因此将"12"代表的2个字节存入至buffer的前2位中,第3位仍为代表汉字"我"的3个字节中的第3位,因此乱码。

每次读取一个字节数组会不可避免的存在一些问题。

读取中文字符输出无法避免乱码问题(读取字节数组容易将代表每个中文字符的3个字节截断,在转换为文本时会出现乱码)。

如何使用字节输入流读取中文内容输出不乱码呢?

定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。

示例代码如下:

publicstaticvoidmain(String[] args) throwsException {
Filef=newFile("day10_file_io_app\\src\\data3.txt");
InputStreamis=newFileInputStream(f);
// 获取文件大小并强转为int型intlength= (int) f.length();
byte[] buffer=newbyte[length];
intnumber=is.read(buffer);
System.out.println("读取了多少字节:"+number); // 读取了多少字节:139System.out.println("文件大小:"+f.length() +"字节"); // 文件大小:139字节Stringrs=newString(buffer);
System.out.println(rs);
/*abc我12dsadasd撒大声地所多sdggabc我12dsadasd撒大声地所多sdggabc我12dsadasd撒大声地所多s的dggsfSDAS是RQ示范法123*/    }

注:直接把文件数据全部读取到一个字节数组可以避免乱码,是否存在问题?

如果文件过大,定义的字节数组可能引起内存溢出

6.3文件字节输出流:写字节数据到文件

文件字节输出流:FileOutputStream

作用:以内存为基准,把内存中的数据以字节的形式写出到磁盘文件中去的流。

FileOutputStream构造器

方法名

说明

public FileOutputStream (File file)

创建字节输出流管道与源文件对象接通

public FileOutputStream (File file,boolean append)

创建字节输出流管道与源文件对象接通,可追加数据

public FileOutputStream (String filepath)

创建字节输出流管道与源文件路径接通

public FileOutputStream (String filepath,boolean append)

创建字节输出流管道与源文件路径接通,可追加数据

 

FileOutputStream常用API

方法名

说明

public void write(int a)

写一个字节出去

public void write(byte[] buffer)

写一个字节数组出去

public void write(byte[] buffer , int pos , int len)

写一个字节数组的一部分出去

 

流的关闭与刷新

方法名

说明

flush()

刷新流,还可以继续写数据

close()

关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据

 

示例代码如下:

publicclassFileOutputStreamDemo4 {
publicstaticvoidmain(String[] args) throwsException {
// 1.创建一个文件字节输出流管道与目标文件接通//        OutputStream os = new FileOutputStream("day10_file_io_app\\output04.txt"); // 写路径,自动创建路径中的文件(会先清空之前的数据,再将新数据写入)OutputStreamos=newFileOutputStream("day10_file_io_app\\output04.txt", true); // 追加数据,不会先清空之前的数据//        public void write(int a)  写一个字节出去os.write('a');
os.write(98);
//        public void write(byte[] buffer)  写一个字节数组出去byte[] buffer= {'a', 97, 98};
os.write(buffer);
os.write("\r\n".getBytes()); // 换行byte[] buffer2="我爱你中国".getBytes();
os.write(buffer2);
//        public void write(byte[] buffer , int pos , int len)  写一个字节数组的一部分出去byte[] buffer3= {'d', 58, 28, 39};
os.write(buffer3, 0, 3); // 将buffer数组中第0位开始的前3位写入到文件中// 刷新流,写数据必须刷新数据os.flush();
// 用完后一定要关闭流,释放资源(关闭流中包含刷新流)os.close();
    }
}

注:os.write("\r\n".getBytes());是换行。

6.4文件拷贝

需求:把某个视频复制到其他文件目录下。

思路:

① 根据数据源创建字节输入流对象。

② 根据目的地创建字节输出流对象。

③ 读写数据,复制视频。

④ 释放资源。

示例代码如下:

publicclassFileCopyDemo05 {
publicstaticvoidmain(String[] args) {
try {
// 1.创建一个字节输入流管道与原文件接通InputStreamis=newFileInputStream("E:\\录屏\\学生作业相关知识学习.mp4");
// 2.创建一个字节输出流管道与目标文件接通OutputStreamos=newFileOutputStream("C:\\Users\\Administrator\\Desktop\\CopyMp4.mp4"); // 一定要加上目标文件名// 3.定义一个字节数组,用于文件复制byte[] buffer=newbyte[1024]; // 每次读取1kb的数据// 不能用此种方式,每次循环is.read(buffer)输入流执行了两次,需要定义变量来记录读取长度/* while (is.read(buffer) != -1) {os.write(buffer, 0, is.read(buffer)); // 读多少倒多少}*/// 定义变量记录读取的字节数intlen;
while ((len=is.read(buffer)) !=-1) {
os.write(buffer, 0, len); // 读多少倒多少            }
System.out.println("复制结束");
// 切记关闭流os.close(); // 一般从里向外关is.close();
        } catch (FileNotFoundExceptione) {
e.printStackTrace();
        } catch (IOExceptione) {
e.printStackTrace();
        }
    }
}

7.资源释放的方式

try-catch-finally

finally:在异常处理时提供finally块来执行所有清除操作,比如说IO流中的释放资源。

特点:被finally控制的语句最终一定会执行,除非JVM退出。

异常处理标准格式:try….catch…finally

注:遇到return语句时,先执行finally代码块中的语句,再执行return语句。

try-catch-resource

finally虽然可以用于释放资源,但是释放资源的代码过于繁琐,有没有办法简化?

JDK 7中简化资源释放的改进如下图所示。

TryCatchResource.png

注:JDK 7的()中只能放置资源对象,,否则报错,try代码块执行结束后自动调用close()方法,释放资源

什么是资源?

资源都是实现了Closeable/AutoCloseable接口的类对象,如下图示例所示。

ColseableAndAutoCloseable.png

8.字符流的使用

8.1文件字符输入流—一次读取一个字符

文件字符输入流:FileReader

作用:以内存为基准,把磁盘文件中的数据以字符的形式读取到内存中去。

Reader构造器

方法名

说明

public FileReader(File file)

创建字符输入流管道与源文件对象接通

public FileReader(String pathname)

创建字符输入流管道与源文件路径接通

 

Reader常用API

方法名

说明

public int read()

每次读取一个字符,返回字符对应的编码

如果字符已经没有可读的返回-1

public int read(char[] buffer)

每次读取一个字符数组返回读取的字符个数

如果字符已经没有可读的返回-1

 

示例代码如下:

publicstaticvoidmain(String[] args) throwsException {
// 1.创建一个字符输入流管道与源文件接通Readerr=newFileReader("day10_file_io_app\\src\\data3.txt");
// 2.每次读取一个字符返回,如果字符已经没有可读的返回-1intcode=r.read();
// 打印第一个字符对应的字符集(UTF-8)编码System.out.println(code); // 25105// 将字符集(UTF-8)编码转换为对应的字符System.out.println((char) code); // 我    }

注:字符流的好处:读取中文字符不会出现乱码(代码文件编码一致的前提下

8.2文件字符输入流—一次读取一个字符数组

示例代码如下:

publicstaticvoidmain(String[] args) throwsException {
Readerr=newFileReader("day10_file_io_app\\src\\data3.txt");
char[] buffer=newchar[3]; // 每次读取三个字符intlen; // 定义变量记录读取字符数while ((len=r.read(buffer)) !=-1) {
// 将数组内容以字符串形式拼接起来System.out.print(newString(buffer, 0, len)); // 读取多少拼接多少        }
    }

注:最终打印时使用public String(char value[])而不是public static String toString(char[] a),前者是将数组中的字符/字节拼接为字符串返回,后者是将数组内容转换为字符串返回(带[])。

8.3文件字符输出流

文件字符输出流:FileWriter

作用:以内存为基准,把内存中的数据以字符的形式写出到磁盘文件中去的流。

FileWriter构造器

方法名

说明

public FileWriter(File file)

创建字符输出流管道与源文件对象接通

public FileWriter (File file,boolean append)

创建字符输出流管道与源文件对象接通,

可追加数据

public FileWriter (String filepath)

创建字符输出流管道与源文件路径接通

public FileWriter (String filepath,

boolean append)

创建字符输出流管道与源文件路径接通,

可追加数据

 

FileWriter常用API

方法名

说明

void write(int c)

写一个字符

void write(char[] buffer)

写入一个字符数组

void write(char[] buffer, int pos, int len)

写入字符数组的一部分

void write(String str)

写一个字符串

void write(String str, int pos, int len)

写一个字符串的一部分

 

流的关闭与刷新

方法名

说明

flush()

刷新流,还可以继续写数据

close()

关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据

 

示例代码如下:

publicstaticvoidmain(String[] args) {
// 创建一个文件字符输出流管道与目标文件接通try (
//                Writer w = new FileWriter("day10_file_io_app\\src\\data_out.txt")Writerw=newFileWriter("day10_file_io_app\\src\\data_out.txt", true); // 不清空原文件数据,在其基础上添加数据        ) {
w.write("\r\n"); // 换行// 1.void write(int c)  写一个字符w.write(97);
w.write('a');
w.write('中');
// 2.void write(char[] buffer)  写入一个字符数组char[] chars="abc我爱你北京".toCharArray();
w.write(chars);
// 3.void write(char[] buffer, int pos, int len)    写入字符数组的一部分w.write(chars, 1, 5); // len代表字符数// 4.void write(String str) 写一个字符串w.write("123我爱你中国");
// 5.void write(String str, int pos, int len)   写一个字符串的一部分w.write("123我爱你中国", 3, 5);
        } catch (IOExceptione) {
e.printStackTrace();
        }
    }

注:w.write("\r\n");是换行。

字节流、字符流如何选择使用?

字节流适合做一切文件数据的拷贝(音视频,文本),不适合读取中文内容输出。

字符流适合做文本文件的操作(读,写)。

相关文章
|
17天前
|
消息中间件 Java Kafka
在Java中实现分布式事务的常用框架和方法
总之,选择合适的分布式事务框架和方法需要综合考虑业务需求、性能、复杂度等因素。不同的框架和方法都有其特点和适用场景,需要根据具体情况进行评估和选择。同时,随着技术的不断发展,分布式事务的解决方案也在不断更新和完善,以更好地满足业务的需求。你还可以进一步深入研究和了解这些框架和方法,以便在实际应用中更好地实现分布式事务管理。
|
23天前
|
Java
java小工具util系列5:java文件相关操作工具,包括读取服务器路径下文件,删除文件及子文件,删除文件夹等方法
java小工具util系列5:java文件相关操作工具,包括读取服务器路径下文件,删除文件及子文件,删除文件夹等方法
58 9
|
15天前
|
Java
java 中 IO 流
Java中的IO流是用于处理输入输出操作的机制,主要包括字节流和字符流两大类。字节流以8位字节为单位处理数据,如FileInputStream和FileOutputStream;字符流以16位Unicode字符为单位,如FileReader和FileWriter。这些流提供了读写文件、网络传输等基本功能。
38 9
|
15天前
|
安全 Java 开发者
Java中WAIT和NOTIFY方法必须在同步块中调用的原因
在Java多线程编程中,`wait()`和`notify()`方法是实现线程间协作的关键。这两个方法必须在同步块或同步方法中调用,这一要求背后有着深刻的原因。本文将深入探讨为什么`wait()`和`notify()`方法必须在同步块中调用,以及这一机制如何确保线程安全和避免死锁。
29 4
|
15天前
|
Java
深入探讨Java中的中断机制:INTERRUPTED和ISINTERRUPTED方法详解
在Java多线程编程中,中断机制是协调线程行为的重要手段。了解和正确使用中断机制对于编写高效、可靠的并发程序至关重要。本文将深入探讨Java中的`Thread.interrupted()`和`Thread.isInterrupted()`方法的区别及其应用场景。
21 4
|
13天前
|
Java 数据处理 数据安全/隐私保护
Java处理数据接口方法
Java处理数据接口方法
20 1
|
1月前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
88 4
|
4月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
5月前
|
Java 大数据
解析Java中的NIO与传统IO的区别与应用
解析Java中的NIO与传统IO的区别与应用
|
3月前
|
Java 大数据 API
Java 流(Stream)、文件(File)和IO的区别
Java中的流(Stream)、文件(File)和输入/输出(I/O)是处理数据的关键概念。`File`类用于基本文件操作,如创建、删除和检查文件;流则提供了数据读写的抽象机制,适用于文件、内存和网络等多种数据源;I/O涵盖更广泛的输入输出操作,包括文件I/O、网络通信等,并支持异常处理和缓冲等功能。实际开发中,这三者常结合使用,以实现高效的数据处理。例如,`File`用于管理文件路径,`Stream`用于读写数据,I/O则处理复杂的输入输出需求。
224 12