作者简介:☕️大家好,我是intelligent_M,一个Java后端开发者!
当前专栏:intelligent_M—— Java IO流 ,CSDN博客。后续会更新Java相关技术栈以及链表哈希表二叉树…,回溯算法贪心算法…等等算法题。
创作不易 欢迎点赞评论!!!
IO流
File
创建对象
- 创建File类的对象
- 第一个构造器是最常用的
- 注意:File对象既可以代表文件,也可以代表文件夹
- File封装的对象仅仅是一个路径名,这个路径可以是存在的,也允许是不存在的
- 绝对路径:从盘符开始
- 相对路径:不带盘符,默认直接到当前工程下的目录寻找文件
/**
* 目标:掌握File创建对象,代表具体文件的方案。
*/
public class FileTest1 {
public static void main(String[] args) {
// 1、创建一个File对象,指代某个具体的文件。
// 路径分隔符
// File f1 = new File("D:/resource/ab.txt");
// File f1 = new File("D:\\resource\\ab.txt");
File f1 = new File("D:" + File.separator +"resource" + File.separator + "ab.txt");
System.out.println(f1.length()); // 文件大小
File f2 = new File("D:/resource");
System.out.println(f2.length());
// 注意:File对象可以指代一个不存在的文件路径
File f3 = new File("D:/resource/aaaa.txt");
System.out.println(f3.length());
System.out.println(f3.exists()); // false
// 我现在要定位的文件是在模块中,应该怎么定位呢?
// 绝对路径:带盘符的
// File f4 = new File("D:\\code\\javasepromax\\file-io-app\\src\\itheima.txt");
// 相对路径(重点):不带盘符,默认是直接去工程下寻找文件的。
File f4 = new File("file-io-app\\src\\itheima.txt");
System.out.println(f4.length());
}
}
常用方法1:判断文件类型,获取文件信息
- File提供的判断文件类型,获取文件信息功能
/**
目标:掌握File提供的判断文件类型、获取文件信息功能
*/
public class FileTest2 {
public static void main(String[] args) throws UnsupportedEncodingException {
// 1.创建文件对象,指代某个文件
File f1 = new File("D:/resource/ab.txt");
//File f1 = new File("D:/resource/");
// 2、public boolean exists():判断当前文件对象,对应的文件路径是否存在,存在返回true.
System.out.println(f1.exists());
// 3、public boolean isFile() : 判断当前文件对象指代的是否是文件,是文件返回true,反之。
System.out.println(f1.isFile());
// 4、public boolean isDirectory() : 判断当前文件对象指代的是否是文件夹,是文件夹返回true,反之。
System.out.println(f1.isDirectory());
// 5.public String getName():获取文件的名称(包含后缀)
System.out.println(f1.getName());
// 6.public long length():获取文件的大小,返回字节个数
System.out.println(f1.length());
// 7.public long lastModified():获取文件的最后修改时间。
long time = f1.lastModified();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
System.out.println(sdf.format(time));
// 8.public String getPath():获取创建文件对象时,使用的路径
File f2 = new File("D:\\resource\\ab.txt");
File f3 = new File("file-io-app\\src\\itheima.txt");
System.out.println(f2.getPath());
System.out.println(f3.getPath());
// 9.public String getAbsolutePath():获取绝对路径
System.out.println(f2.getAbsolutePath());
System.out.println(f3.getAbsolutePath());
}
}
常用方法2:创建文件,删除文件
- File类创建文件的功能
- File类删除文件的功能
- 注意:delete方法默认只能删除文件和空文件夹,删除后的文件不会进入回收站
/**
* 目标:掌握File创建和删除文件相关的方法。
*/
public class FileTest3 {
public static void main(String[] args) throws Exception {
// 1、public boolean createNewFile():创建一个新文件(文件内容为空),创建成功返回true,反之。
File f1 = new File("D:/resource/itheima2.txt");
System.out.println(f1.createNewFile());
// 2、public boolean mkdir():用于创建文件夹,注意:只能创建一级文件夹
File f2 = new File("D:/resource/aaa");
System.out.println(f2.mkdir());
// 3、public boolean mkdirs():用于创建文件夹,注意:可以创建多级文件夹
File f3 = new File("D:/resource/bbb/ccc/ddd/eee/fff/ggg");
System.out.println(f3.mkdirs());
// 3、public boolean delete():删除文件,或者空文件,注意:不能删除非空文件夹。
System.out.println(f1.delete());
System.out.println(f2.delete());
File f4 = new File("D:/resource");
System.out.println(f4.delete());
}
}
常用方法3:遍历文件夹
- File类提供的遍历文件夹的功能
- 使用listFiles方法时的注意事项:
- 当主调是文件,或者路径不存在时,返回null
- 当主调是空文件夹时,返回一个长度为0的数组
- 当主调是一个有内容的文件夹时,将里面所有的一级文件和文件夹的路径放在File数组中返回
- 当主调是一个文件夹,且里面有隐藏文件时,将里面所有文件和文件夹的路径放在Fule数组中返回,包含隐藏文件
- 当主调是一个文件夹,但是没有权限访问该文件夹,返回null
/**
* 目标:掌握File提供的遍历文件夹的方法。
*/
public class FileTest4 {
public static void main(String[] args) {
// 1、public String[] list():获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。
File f1 = new File("D:\\course\\待研发内容");
String[] names = f1.list();
for (String name : names) {
System.out.println(name);
}
// 2、public File[] listFiles():(重点)获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)
File[] files = f1.listFiles();
for (File file : files) {
System.out.println(file.getAbsolutePath());
}
File f = new File("D:/resource/aaa");
File[] files1 = f.listFiles();
System.out.println(Arrays.toString(files1));
}
}
前置知识
方法递归
认识递归的形式
- 什么是方法递归?
- 递归是一种算法,在程序设计语言中广泛应用
- 从形式上说:方法调用自身的形式称为方法递归(recursion)
- 递归的形式
- 直接递归:方法自己调用自己
- 间接递归:方法调用其他方法,其他方法又回调方法自己
- 使用方法递归时需要注意的问题:
- 递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出错误
/**
* 目标:认识一下递归的形式。
*/
public class RecursionTest1 {
public static void main(String[] args) {
test1();
}
// 直接方法递归
public static void test1(){
System.out.println("----test1---");
test1(); // 直接方法递归
}
// 间接方法递归
public static void test2(){
System.out.println("---test2---");
test3();
}
public static void test3(){
test2(); // 间接递归
}
}
应用,执行流程,算法思想
-
案例:计算n的阶乘
-
递归算法三要素:
/**
* 目标:掌握递归的应用,执行流程和算法思想。
*/
public class RecursionTest2 {
public static void main(String[] args) {
System.out.println("5的阶乘是:" + f(5));
}
public static int f(int n){
// 终结点
if(n == 1){
return 1;
}else {
return f(n - 1) * n;
}
}
}
其他应用:文件搜索
/**
* 目标:掌握文件搜索的实现。
*/
public class RecursionTest3 {
public static void main(String[] args) throws Exception {
searchFile(new File("D:/") , "QQ.exe");
}
/**
* 去目录下搜索某个文件
* @param dir 目录
* @param fileName 要搜索的文件名称
*/
public static void searchFile(File dir, String fileName) throws Exception {
// 1、把非法的情况都拦截住
if(dir == null || !dir.exists() || dir.isFile()){
return; // 代表无法搜索
}
// 2、dir不是null,存在,一定是目录对象。
// 获取当前目录下的全部一级文件对象。
File[] files = dir.listFiles();
// 3、判断当前目录下是否存在一级文件对象,以及是否可以拿到一级文件对象。
if(files != null && files.length > 0){
// 4、遍历全部一级文件对象。
for (File f : files) {
// 5、判断文件是否是文件,还是文件夹
if(f.isFile()){
// 是文件,判断这个文件名是否是我们要找的
if(f.getName().contains(fileName)){
System.out.println("找到了:" + f.getAbsolutePath());
Runtime runtime = Runtime.getRuntime();
runtime.exec(f.getAbsolutePath());
}
}else {
// 是文件夹,继续重复这个过程(递归)
searchFile(f, fileName);
}
}
}
}
}
字符集
常见字符集介绍
- 标准ASCII字符集
- ASCII(American Standard Code for Information Interchange):美国信息交换标准代码,包括了英文,符号等
- 标准ASCII使用一个字节存储一个字符,首位是0,总共可表示128个字符,对美国来说完全够用
- 中国用GBK(汉字内码扩展规范,国标)
- 汉字编码字符集,包含了2万多个汉字字符,GBK中一个中文字符编码成两个字节的形式存储
- 注意:GBK兼容了ASCII字符集
- Unicode字符集(统一码,也叫万国码)
- Unicode是国际组织制定的,可以容纳世界上所有文字,符号的字符集
- UTF-8是Unicode字符集的一种编码方案,采取可变长编码方案,共分四个长度区:1个字节,2个字节,3个字节,4个字节
- 英文字符,数字等只占一个字节(兼容标准ASCII编码),汉字字符占用3个字节
- 注意:技术人员在开发时都应该使用UTF-8编码
- ASCII字符集:只有英文,数字,符号等,占1个字节
- GBK字符集:汉字占2个字节,英文,数字占1个字节
- UTF-8字符集:汉字占3个字节,英文,数字占1个字节
- 注意1:字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码
- 注意2:英文,数字一般不会乱码,因为很多字符集都兼容了ASCII编码
字符集的编码,解码操作
- Java代码完成对字符的编码
- Java代码完成对字符的解码
/**
* 目标:掌握如何使用Java代码完成对字符的编码和解码。
*/
public class Test {
public static void main(String[] args) throws Exception {
// 1、编码
String data = "a我b";
byte[] bytes = data.getBytes(); // 默认是按照平台字符集(UTF-8)进行编码的。
System.out.println(Arrays.toString(bytes));
// 按照指定字符集进行编码。
byte[] bytes1 = data.getBytes("GBK");
System.out.println(Arrays.toString(bytes1));
// 2、解码
String s1 = new String(bytes); // 按照平台默认编码(UTF-8)解码
System.out.println(s1);
String s2 = new String(bytes1, "GBK");
System.out.println(s2);
}
}
IO流
IO流概述
- io流输入输出流,读写数据的
IO流的应用场景
怎么学IO流
- 怎么学IO流
- 1.先搞清楚IO流的分类,体系
- 2.再挨个学习每个IO流的作用,用法
IO流的分类
- 按流的方向分为:输入流和输出流
-
- 按流中数据的最小单位,分为字节流和字符流
- IO流总体来看就有四大流
- 字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流
- 字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流
- 字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流
- 字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流
- IO流的体系(Java.io包下的)
IO流- 字节流
文件字节输入流:每次读取一个字节
- FileInputStream(文件字节输入流)
- 作用:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去
- 注意事项:使用FileInputStream每次读取一个字节,读取性能较差,并且读取汉字输出还会出现乱码且无法避免
/**
* 目标:掌握文件字节输入流,每次读取一个字节。
*/
public class FileInputStreamTest1 {
public static void main(String[] args) throws Exception {
// 1、创建文件字节输入流管道,与源文件接通。
// InputStream is = new FileInputStream(new File("file-io-app\\src\\itheima01.txt"));
// 简化写法:推荐使用。
InputStream is = new FileInputStream(("file-io-app\\src\\itheima01.txt"));
// 2、开始读取文件的字节数据。
// public int read():每次读取一个字节返回,如果没有数据了,返回-1.
// int b1 = is.read();
// System.out.println((char)b1);
//
// int b2 = is.read();
// System.out.println((char) b2);
//
// int b3 = is.read();
// System.out.println(b3);
// 3、使用循环改造上述代码
int b; // 用于记住读取的字节。
while ((b = is.read()) != -1){
System.out.print((char) b);
}
// 读取数据的性能很差!
// 读取汉字输出会乱码!!无法避免的!!
// 流使用完毕之后,必须关闭!释放系统资源!
is.close();
}
}
文件字节输入流:每次读取多个字节
- 使用FileInputStream每次读取多个字节,读取性能得到了提升,但读取汉字输出还是会乱码
/**
* 目标:掌握使用FileInputStream每次读取多个字节。
*/
public class FileInputStreamTest2 {
public static void main(String[] args) throws Exception {
// 1、创建一个字节输入流对象代表字节输入流管道与源文件接通。
InputStream is = new FileInputStream("file-io-app\\src\\itheima02.txt");
// 2、开始读取文件中的字节数据:每次读取多个字节。
// public int read(byte b[]) throws IOException
// 每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1.
// byte[] buffer = new byte[3];
// int len = is.read(buffer);
// String rs = new String(buffer);
// System.out.println(rs);
// System.out.println("当次读取的字节数量:" + len);
//
// // buffer = [abc]
// // buffer = [66c]
// int len2 = is.read(buffer);
// // 注意:读取多少,倒出多少。
// String rs2 = new String(buffer, 0, len2);
// System.out.println(rs2);
// System.out.println("当次读取的字节数量:" + len2);
//
// int len3 = is.read(buffer);
// System.out.println(len3); // -1
// 3、使用循环改造。
byte[] buffer = new byte[3];
int len; // 记住每次读取了多少个字节。 abc 66
while ((len = is.read(buffer)) != -1){
// 注意:读取多少,倒出多少。
String rs = new String(buffer, 0 , len);
System.out.print(rs);
}
// 性能得到了明显的提升!!
// 这种方案也不能避免读取汉字输出乱码的问题!!
is.close(); // 关闭流
}
}
文件字节输入流:一次读取完全部字节
- 使用字节流读取中文,如何保证输出不乱码,怎么解决?
- 定义一个与文件一样大的字节数组,一次性读取完文件的全部字节
- 方法一:自己定义一个字节数组与被读取的文件大小一样大,人后使用该字节数组,一次性读完文件的全部字节
- 方式二:Java官方为InputStream提供了如下方法,可以直接把文件的全部字节读取到一个字节数组中返回
- 直接把文件数据全部读取到一个字节数组可以避免乱码,但是也存在问题
- 如果文件过大,创建的字节数组也会过大,可能引起内存溢出
- 读写文本内容更适合用字符流
- 字节流适合做数据的转移,如:文件复制等
/**
* 目标:使用文件字节输入流一次读取完文件的全部字节。
*/
public class FileInputStreamTest3 {
public static void main(String[] args) throws Exception {
// 1、一次性读取完文件的全部字节到一个字节数组中去。
// 创建一个字节输入流管道与源文件接通
InputStream is = new FileInputStream("file-io-app\\src\\itheima03.txt");
// 2、准备一个字节数组,大小与文件的大小正好一样大。
// File f = new File("file-io-app\\src\\itheima03.txt");
// long size = f.length();
// byte[] buffer = new byte[(int) size];
//
// int len = is.read(buffer);
// System.out.println(new String(buffer));
//
// System.out.println(size);
// System.out.println(len);
byte[] buffer = is.readAllBytes();
System.out.println(new String(buffer));
is.close(); // 关闭流
}
}
文件字节输出流:写字节出去
- FileOutputStream(文件字节输出流)
- 作用:以内存为基准,把内存中的数据以字节的形式写出到文件中去
/**
* 目标:掌握文件字节输出流FileOutputStream的使用。
*/
public class FileOutputStreamTest4 {
public static void main(String[] args) throws Exception {
// 1、创建一个字节输出流管道与目标文件接通。
// 覆盖管道:覆盖之前的数据
// OutputStream os =
// new FileOutputStream("file-io-app/src/itheima04out.txt");
// 追加数据的管道
OutputStream os =
new FileOutputStream("file-io-app/src/itheima04out.txt", true);
// 2、开始写字节数据出去了
os.write(97); // 97就是一个字节,代表a
os.write('b'); // 'b'也是一个字节
// os.write('磊'); // [ooo] 默认只能写出去一个字节
byte[] bytes = "我爱你中国abc".getBytes();
os.write(bytes);
os.write(bytes, 0, 15);
// 换行符
os.write("\r\n".getBytes());
os.close(); // 关闭流
}
}
案例:文件复制
- 字节流非常适合做一切文件的复制操作
- 任何文件的底层都是字节,字节流做复制,时一字不漏的转移完全部字节,只要复制后的文件格式一致就没有问题
- 文件复制
/**
* 目标:使用字节流完成对文件的复制操作。
*/
public class CopyTest5 {
public static void main(String[] args) throws Exception {
// 需求:复制照片。
// 1、创建一个字节输入流管道与源文件接通
InputStream is = new FileInputStream("file-io-app\\src\\itheima03.txt");
// 2、创建一个字节输出流管道与目标文件接通。
OutputStream os = new FileOutputStream("file-io-app\\src\\itheima03copy.txt");
System.out.println(10 / 0);
// 3、创建一个字节数组,负责转移字节数据。
byte[] buffer = new byte[1024]; // 1KB.
// 4、从字节输入流中读取字节数据,写出去到字节输出流中。读多少写出去多少。
int len; // 记住每次读取了多少个字节。
while ((len = is.read(buffer)) != -1){
os.write(buffer, 0, len);
}
os.close();
is.close();
System.out.println("复制完成!!");
}
}
IO流-资源释放的方式
try-catch-finally
- finally代码区的特点:无论try中的程序是正常执行了,还是出现了异常,最后都一定会执行finally区,除非JVM终止
- 作用:一般用于再程序执行完成之后进行资源的释放操作(专业级做法)
- 缺点:臃肿,不优雅
/**
* 目标:认识try-catch-finally。
*/
public class Test1 {
public static void main(String[] args) {
try {
System.out.println(10 / 2);
// return; // 跳出方法的执行
// System.exit(0); // 虚拟机
}catch (Exception e){
e.printStackTrace();
} finally {
System.out.println("===finally执行了一次===");
}
System.out.println(chu(10, 2));
}
public static int chu(int a, int b){
try {
return a / b;
}catch (Exception e){
e.printStackTrace();
return -1; // 代表的是出现异常
}finally {
// 千万不要在finally中返回数据!
return 111;
}
}
}
- 掌握释放资源的方式
/**
* 目标:掌握释放资源的方式。
*/
public class Test2 {
public static void main(String[] args) {
InputStream is = null;
OutputStream os = null;
try {
System.out.println(10 / 0);
// 1、创建一个字节输入流管道与源文件接通
is = new FileInputStream("file-io-app\\src\\itheima03.txt");
// 2、创建一个字节输出流管道与目标文件接通。
os = new FileOutputStream("file-io-app\\src\\itheima03copy.txt");
System.out.println(10 / 0);
// 3、创建一个字节数组,负责转移字节数据。
byte[] buffer = new byte[1024]; // 1KB.
// 4、从字节输入流中读取字节数据,写出去到字节输出流中。读多少写出去多少。
int len; // 记住每次读取了多少个字节。
while ((len = is.read(buffer)) != -1){
os.write(buffer, 0, len);
}
System.out.println("复制完成!!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 释放资源的操作
try {
if(os != null) os.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is != null) is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
try-with-resource
- JDK7开始提供了更简单的资源释放方案:try-with-resource
- 该资源使用完毕后,会自动调用其他close()方法,完成对资源的释放
- ()中只能放置资源,否则报错
- 什么是资源呢?
- 资源一般指的是最终实现了AutoCloseable接口
/**
* 目标:掌握释放资源的方式:try-with-resource
*/
public class Test3 {
public static void main(String[] args) {
try (
// 1、创建一个字节输入流管道与源文件接通
InputStream is = new FileInputStream("file-io-app\\src\\itheima03.txt");
// 2、创建一个字节输出流管道与目标文件接通。
OutputStream os = new FileOutputStream("file-io-app\\src\\itheima03copy.txt");
// 注意:这里只能放置资源对象。(流对象)
// int age = 21;
// 什么是资源呢?资源都是会实现AutoCloseable接口。资源都会有一个close方法,并且资源放到这里后
// 用完之后,会被自动调用其close方法完成资源的释放操作。
MyConnection conn = new MyConnection();//自定义的一个资源类
){
// 3、创建一个字节数组,负责转移字节数据。
byte[] buffer = new byte[1024]; // 1KB.
// 4、从字节输入流中读取字节数据,写出去到字节输出流中。读多少写出去多少。
int len; // 记住每次读取了多少个字节。
while ((len = is.read(buffer)) != -1){
os.write(buffer, 0, len);
}
System.out.println(conn);
System.out.println("复制完成!!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
- Myconnection类
public class MyConnection implements AutoCloseable{
@Override
public void close() throws Exception {
System.out.println("释放了与某个硬件的链接资源~~~~");
}
}