File类
PS:尽量不在直接操作C盘,在代码操作的时候回出现权限问题
File文件路径分隔符号
File类提供四个属性,这个四个属性是对文件路径进行分隔使用【\ 和 ,】
-
-
返回值类型 根据不同系统,动态选择路径分隔符 static String
pathSeparator
系统依赖路径分隔符的字符,表示为方便的字符串。static char
pathSeparatorChar
系统依赖路径分隔符字符。static String
separator
系统依赖的默认名称分隔符字符,表示为方便的字符串。static char
separatorChar
系统依赖的默认名称分隔符字符。
-
在给File类提供路径参数的时候,需要注意路径分隔的使用
Unix系统是严格区分大小写,并给使用【/】来分隔路径
Windows系统中使用【\】,来分隔路径
PS:因为 【\】在代码中是转移字符【\n、\t】,所以在写路径的时候需要对【\】进行转移【\ \】
使用当前【\】的路径 --》称为 【绝对路径】
绝对路径: 从盘符开始的路径 例如【C:\Java\jdk1.8_211】
Windows系统还支持另外一种路径,【相对路径】—》需要使用路径分隔【/】
PS:相对路径需要使用子工程中,不能再外部使用
相对路径必须有【参考路径】,它不是从盘符开始
这个参考路径,不是程序猿手动定义,一般是直接参考当前项目工程路径
String path = "C:\Demo";//绝对
String path1 = "\file.txt"//相对
//此时path1不会参考path,相对路径它参考的式路径,不是字符串
String paht2 = "\file\file.txt";
//此时这个路径参考的就是当前项目下的路径
File类
File类是唯一可以抽象表示,磁盘文件或磁盘文件夹的一个对象
PS:我们可以用过系统一个路径将当前文件或文件夹,封装成一个File类的对象,然后通过这个对象的方法可以对文件或文件夹进行操作【即通过代码操作文件或文件夹】
该类提供了包括【创建、删除、重名、判断文件权限、以及检查文件是否存在等功能】
但是这个类不一共对文件内容操作方法,如果需要操作文件内容,那么就需要使用到【IO流】
File类可以获取到文件的【元数据】
ps:元数据指的是描述文件的数据
File类中的常用方法
package com.qfedu.File;
import java.io.File;
import java.io.IOException;
import java.util.Date;
/*
* File类的API
*/
public class FileAPIDemo {
public static void main(String[] args) {
//1.文件分隔符号
Separator();
//2.获取封装到File对象中路径
showPath();
//3.检查文件的状态
checkFileState();
//4.文件操作
OperationFile();
//5.文件夹操作
OperationDirectory();
}
public static void Separator() {
//这些分隔符号是File的静态属性是Java动态提供给系统用来获取路径符号使用,作用是方便程序猿使用【呵呵。。。】
//第一个 \ 分隔 【除了返回值类型不一样之外,都是一样的】
System.out.println(File.separator); //返回值是String
System.out.println(File.separatorChar); //返回值类型是char
//第二个 以 ; 号作为结尾 【除了返回值类型不一样之外,都是一样的】
System.out.println(File.pathSeparator);
System.out.println(File.pathSeparatorChar);
//例如需要写一个绝对路径C:\Program Files\Java\jdk1.8.0_221\bin
String path = "C:\\Program Files\\Java\\jdk1.8.0_221\\bin";
String path2 =
"C:"+File.separator+"Program Files"+File.separator+"Java"+File.separator+"jdk1.8.0_221"+File.separator+"bin";
System.out.println(path);
System.out.println(path2);
}
public static void showPath() {
//1.如果创建一个File对象 参数是一个String类型的路径
//这个路径即可以是绝对路径也可以是相对路径
//将c盘下123.txt文件封装成File对象
File f1 = new File("C:\\abc\\xxx\\123.txt");
//1.获取File对象的绝对路径
System.out.println(f1.getAbsolutePath());// 返回的是一个String对象
System.out.println(f1.getAbsoluteFile());// 返回的是一个File对象
//2. 获取文件路径
System.out.println(f1.getPath());//返回值是一个String类型
//3.[记忆]获取文件的名字
System.out.println(f1.getName());//返回值是一个String类型
//4.获取上级目录
System.out.println(f1.getParent());// 返回的是一个String对象
System.out.println(f1.getParentFile());// 返回的是一个File对象
}
public static void checkFileState() {
File f1 = new File("C:\\abc\\xxx\\123.txt");
//判断文件是否是隐藏文件
System.out.println(f1.isHidden());
//判断文件是否可读
System.out.println(f1.canRead());
//判断文件是否可写
System.out.println(f1.canWrite());
//获取文件最后修改时间
System.out.println(new Date(f1.lastModified()));
//文件大小【字节】
System.out.println(f1.length());
}
public static void OperationFile() {
//1.除了直接给文件路径的方式创建File对象之外
//2.还可以使用两个两个参数进行拼接,第一个参数对象是父类路径,第二个参数对象是子类路径
File dir = new File("C:\\abc\\xxx");//当前这个File对象的路径只到文件夹
File f1 = new File(dir,"123.txt");
System.out.println(f1);
//1.判断是不是文件[记忆]
System.out.println(f1.isFile());
//2.判断文件或文件夹是否存在[记忆]
System.out.println(f1.exists());
//需求:如果c:\abc\xxx\123.txt文件不存在则新建,否在删除
if(!f1.exists()) {
//创建文件
try {
f1.createNewFile();//[记忆]
} catch (IOException e) { //它是所有IO包下类所抛出的根异常
System.out.println(e.getMessage()+"文件创建失败");
}
}else {
f1.delete();//删除[记忆]
//集重命名,移动复制剪切粘贴与一身[记忆]
// f1.renameTo("这里传递的是一个File对象,这个对象路径就是移动位置路径最后的文件门就是修改之后的名字")
}
}
public static void OperationDirectory() {
File dir = new File("C:\\123\\456\\789");
//PS:文件夹和目录是一个概念
//1.判断是否是一个文件夹[记忆]
System.out.println(dir.isDirectory());
//2.判断文件夹是否是否存在[记忆]
System.out.println(dir.exists());
//3.创建文件夹【两个方法:1.mkdir()【只能创建一级目录】 2.mkdirs()【可以递归创建目录】】
// 创建成功返回true,失败则返回false
boolean b = dir.mkdirs();//[记忆]
System.out.println(b);
//4.1列出所有文件名字 【IDEA中快捷得到返回值 在当前调用方法的最后写.var回车,就会自动生成定义返回值】
String[] fileNames = dir.list();//[记忆]
//4.2列出所有文件对象
File[] files = dir.listFiles();//[记忆]
//4.3列出所有盘符
File[] files1 = File.listRoots();//[记忆]
}
}
递归
递归是一种【方法自身循环调用的一种方式】
递归调用时是一种特殊的调用形式【方法自身调用自身】,一个方法体的内部调用自己的方法,这种方式就称之为【递归】,方法递归包含了一个隐式的循环,它会重复执行某段代码,但是这个重复需要进行控制,否则会出现JVM包错【JVM蹦溃】
如果理解递归?
从前有座山,山里有座庙,庙里有一个老和尚和一个小和尚,老和尚给小和尚讲故事,。。。。。。。。。。
PS:无论如何书写递归都需要设定一个出口【递归停止的条件】
案例:使用递归,求出任意数的和
package com.qfedu.RecursiveMethod;
import java.util.Scanner;
/**
* 基础递归
* @author jkmaster
*
*/
public class RecursiveDemo {
public static void main(String[] args) {
//1.正常解法
// Scanner input = new Scanner(System.in);
// System.out.println("请输入要计算的范围:");
// int n = input.nextInt();
// int sum = 0;//求和
// for(int i = 1; i<=n;i++) {
// sum += i;
// }
// System.out.println(sum);
//
System.out.println(toSum(5));
}
}
案例2:有6个人, 6人 说 5人 大3岁 5人 说 4人 大3岁。。。。。 1人说自己13岁 问第6人多大
public static int sumAge(int PersonNumber) {
if(PersonNumber == 1) {
return 13;
}else {
return sumAge(PersonNumber-1)+3;
}
}
总结
1.写递归必须要有结束点【递归的停止】,否则会出现StackOverflowError 栈溢出
2.明确这个方法在重复做什么事情,是否有重复做事情的条件,是否可以重复做到这个事情
3.简单的递归都是可以使用【循环解决】,但是深层次的遍历就无法使用循环必须使用【递归】
4.在项目中有一定要在【确认】的前提下在写递归,不然一但出错,后果严重
PS:递归的执行效率是真的高,原因是在同一个时间内会将所有资源都用到这个计算中。
进阶案例:遍历某个盘符打印所有文件和文件夹
package com.qfedu.RecursiveMethod;
import java.io.File;
/**
* 递归遍历盘符下的所有文件
* @author jkmaster
*
*/
public class RecursiveFilesDemo {
public static void main(String[] args) {
//将C盘中的所有文件和目录以树装结构进行打印
File file = new File("C:\\");
showLayers(file,0);
}
/**
* 显示指定目录的层级结构
* @param file 目录的路径
* @param level 层级结构显示等级 0是最高级,依次类推 得到层级拼接
*/
public static void showLayers(File file,int level) {
//1.创建一个字符串构建器
StringBuilder bs = new StringBuilder("|--");
for(int i = 0; i<level ;i++) {
bs.insert(0, "|");
}
//判断是文件还是文件夹【文件就不需要在向下查找了,文件夹需要继续查找】
File[] files = file.listFiles();
if(files == null) {
return ;
}
for(File tempFile : files) {
//判断数组中存储是文件还是文件夹
//如果是文件 就拼接 |-- 文件名
System.out.println(bs.toString().concat(tempFile.getName()));
//如果是目录 就向内部查找
if(tempFile.isDirectory()) {
showLayers(tempFile,level+1); //递归遍历文件夹
}
}
}
}
IO流
PS:IO流不难,它只需要记住【读取】和【写出】,但是你需要判断在什么情况下使用哪种流
一定要记住缓冲流如何使用,很重要
什么是流?
流在Java中被称之为IO–》翻译过来 In 和 Out ,就相当于是 【内存】与 【存储设备】之间 的【数据通道】
在程序中所有的数据都是以流的方式进行传输和保存的,程序需要数据时使用输入流读取数据,而当程序需要保存数据时使用输出流将数据写入到磁盘文件进行保存
IO流是数据通道,架设起内容到磁盘,磁盘到内存之间第一个数据通信桥梁,数据可以通过和这个流进行传输
流的分类
1.以流的流向分类
输入流: 将【存储设备】中内容读取到【内存】中
输出流: 将【内存】中的数据写入到【存储设备】中
2.以流中流动的数据类型分类
字节流:以字节(byte)为单位,可以读写【所有】数据。
字符流:以字符(char)为单位,可以读写【文本】数据。
3.以流的功能分类
节点流:具有实际传输数的读写功能
过滤流: 在节点流的基础上增加了功能。
字节输入输出流
PS:现在使用的流是字节流,证明这个流中数据类型必须是【byte】
InputStream【字节输入流】
InputStream是所有字节输入流的父类,这个父类提供了流的操作方式,这些方式都会被子类所继承
InputStream是一个抽象类,所以是不可以直接创建对象,只能使用其子类
正常情况下数据都是从**【存储设备】读取到系统【内存】中**,所以提供一个字节输入流的子类【FileInputStream】
FileInputStream【文件字节输入流】
顾名思义就是和文件打交道的一个流,Java中流只能对**【纯文本文件】**读取
PS:我们可以操作文件一般就是txt或properties文件,如果要读取Office文件需要三方jar
-
-
Constructor FileInputStream(File file)
通过当前File对象来构建文件字节输入流对象FileInputStream(String name)
通过String对象【String对象是一个文件路径】来构建文件字节输入流对象
-
-
-
void
close()
关闭此文件输入流并释放与流关联的任何系统资源。
-
-
-
int
read()
从这个输入流读取一个字节的数据。int
read(byte[] b)
从输入流中读取字节数组长度的数据int
read(byte[] b, int off, int len)
从输入流读取字节数组的长度的数据,从off位置开始到 len长度结束
-
案例
package com.qfedu.IO.FileInputStram;
import java.io.*;
import java.util.Arrays;
/*
文件字节输入流
*/
public class FileInputStreamDemo {
public static void main(String[] args) {
InputStream is = null;
try {
//1. 先明确流的流向【是读取还是写出】 读取文件中内容
//需要选择一个输入流【文件字节输入流】--》创建文件字节输入流对象
//PS:这个参数有两种方式 一种:是String类型的路径 另外一种:File对象【File对象中封装也是一个路径】
//InputStream is = new FileInputStream(new File("C:\\IdeaProjects\\Java2003_Day_19\\dir\\File.txt"));
//InputStream is = new FileInputStream("C:\\IdeaProjects\\Java2003_Day_19\\dir\\File.txt");
//读取工程内的数据,会使用相对路径,因为在工程中读取文件是相对
is = new FileInputStream("C:\\IdeaProjects\\Java2003_Day_19\\src\\com\\qfedu\\IO\\File\\FileAPIDemo.java");
//is = new FileInputStream("dir/File.txt");
//2.选择什么方式读取数据【要明确现在使用的是什么流【字节】和【字符】】
//FileInputStream是一个字节流,所以这个流中数据类型 需要是 byte
//字节输入流一共提供三种读取数据的方式
/*
1. 读取一个字节数据 read()
2. 读取字节数组长度的数据 read(byte[] b)
3. 读取多个字节的数据,从数据off所以位置开始存储,存储的长度是len read(byte[] b , int off, int len)
PS: 着三个方法都是使用int作为返回值类型,这个返值的作用有两个
1.返回实际读取数据的长度
2.当读取到文件的末尾时,会返回-1表示读取文件完毕【没有数据可以在读取了】
文件是顺序读取,第一个读取完毕之后,后续会接着第一次的末尾继续读取
*/
//1.一次读取一个字节数据【因为是byte,所以无法存字符,所以自动转换为Unicode中编码(十进制)】
// System.out.println(is.read());//【少】
//
// //2.一次读取多个字节数据,读取多少个,是字节数组长度决定
// byte[] buffer = new byte[10];
// System.out.println("没有读取数据之前:"+Arrays.toString(buffer));
// /*
// 1.读取数据的操作,不是读取buffer数组中的数据【这里什么都没有读取什么?】
// 是使用buffer数组的长度读取文件中数据,并存在到buffer数组中
// */
// //[97, 98, 99, 100, 101, 102, 103, 104, 105, 106]
// is.read(buffer);//读取数据【最多的】
// System.out.println("读取数据之后:"+Arrays.toString(buffer));
// //读取数据之后可以通过字符串将数据打印出来【仅限打印】
// System.out.println("读取出来的数据:"+new String(buffer));
//
//
// //一次读取多个数据,可执行从数据什么位置开始写,和写的长度
//
// /*
// 无论是 字节数组、还是字符数组,每次读取数据存到buffer数组中,他是覆盖
// 是从0开始从新覆盖,所以在读取数据之后,写出数据时一定要注意实际读取长度
// */
// is.read(buffer,0,5);
// System.out.println("读取之后数据中数据存方式:"+Arrays.toString(buffer));
// System.out.println("读取数据内容:"+new String(buffer));
// 如何循环读取数据
//创建一个字节数组,数组的大小是经过计算和验证的到, --》 1024K -> 1KB
byte[] bs = new byte[1024];
//所有read方法都会后去到一个实际读取数据的长度,这个长度就是判断依据,因为读取到最后一个数据位置时返回-1
int len;
while((len = is.read(bs))!= -1){ //主要不读取到-1就不会得到到文件的末尾,就可以继续读取
/*
读取数据的时候,每次想字节数组中存储数据的时候,都是从0开始覆盖书写,所以最后一次读取数据如果不满足1024
就会出现 数组中有上一次读取数据残留,这样打印会出现一些多于的信息,所以无论是打印还是写出数据我们都是使用
实际读取到数据长度 --》 这个实际读取数据的长度从哪来,就是 read方法的返回值
一般就是在判断位置所定义哪个变量-->len
*/
// System.out.println(new String(bs));
System.out.println(new String(bs,0,len));
}
} catch (FileNotFoundException e) { //没有找到路径文件
e.printStackTrace();
} catch (IOException e) { //流异常,是操作IO包下所有类所需要处理第一个异常
e.printStackTrace();
}finally { //资源使用
//流属于“长连接”,一直保持连接,所以在流使用完毕之后必须关闭流,流一旦关闭不能再使用
//所有流在使用完毕之后 都需要关闭
//通过 流对象.close()
if(is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
OutputStream【字节输出流】
OutputStream是所有字节输出流父类,所有字节输出流都是直接或间接继承
OutputStream是一个抽象类是无法直接创建对象,所以需要使用其子类才可以创建
一般流操作的都是文本文件,所以使用它子类FileOutputStream
FileOutputStream【文件字节输出流】
是操作文件的,是将内存中数据写入到文件中【磁盘中的文本文件】
-
-
Constructor and Description FileOutputStream(File file)
创建一个文件输出流写入指定的File
对象表示的文件。FileOutputStream(File file, boolean append)
创建一个文件输出流写入指定的File
对象表示的文件。其实这个四个构造方法都是相同就是参数传入的对象不同,在于一个是File对象,另外一个是String对象【String对象是一个字符(是一个文件路径)】,文件字节输出流多了一个构造参数,这个参数的作用是可以在写出内容到文件中时以追加的形式写入到文件中【true追加】 FileOutputStream(String name)
创建一个文件输出流,用指定的名称写入文件。FileOutputStream(String name, boolean append)
创建一个文件输出流,用指定的名称写入文件。
-
-
-
Modifier and Type Method and Description void
close()
关闭此文件输出流并释放与此流关联的任何系统资源。
-
-
-
void
write(byte[] b)
会将byte数组中的内容写入到文件中void
write(byte[] b, int off, int len)
会将byte数组中数据从数组的指定位置off,开始写,写出的长度是len【一般len是实际读取到文件的长度,这样可以保证不写出多于数据】
-
-
-
void
write(int b)
一次写出一个字节到文件中
-
-
-
void
flush()
刷新输出流,使缓存数据被写出来。 【加快流的流速】PS:在本地读取【就没有网络】时写与不写效果是一样,但是在网络流中必须要写
-
案例:
package com.qfedu.IO.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* 文件字节输出流
*/
public class FileOutputStreamDemo {
public static void main(String[] args) {
//1.先明确流的流向【写出数据,此时需要使用输出流(FileOutputStream)】
//创建流的对象
OutputStream os = null;
//参数要么是一个File对象,要么是一个字符串的文件路径【这个文件可以不存在,会自动创建(建议存在文件)】
try {
//如果流没有关闭,那么内容是一直往中写入
//os = new FileOutputStream("dir/File1.txt");
//追加内容到文件中 流关闭之后在次写时,需要追加写入,需要设置为true
os = new FileOutputStream("dir/File1.txt",true);
/*
写出数据的三个方法:
1.write(int b) 把一个字节写入到文件中
2.write(byte[] b) 把b数组中的数据写入到文件中
3.write(byte[] b,int off ,int len)
将存在b数组中数据写入到文件中,off是指定从数组的什么位置开始写【注意是下标】 len是写多长的数据【数据的长度】
*/
// os.write(65);
//Java输出流写出数据文件时,他是覆盖写,不会保留有文件中数据,而是直接覆盖原有数中内容
//如果是追加写入内容,那么就需要使用当前 boolean类型参数即构造方法中的boolean类型为true
// os.write("BCDEFG".getBytes());//getBytes将字符串转换为字节数组
os.write("HIJKLMN".getBytes(),2,4);
os.flush();//效果不明显在本地读取,只有在网络流效果明显,就是在写出大文件的时候建议可以添加flush
System.out.println("老子写完了");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
综合案例:循环读写,图片
package com.qfedu.IO.InAndOut;
import java.io.*;
/**
* 需求:经dir文件夹下图片拷贝到desc文件夹下
*/
public class FileInputStreamAndOutputStream {
public static void main(String[] args) {
//完成图片读写
//1.先创建字节输入输出流对象
InputStream is = null;
OutputStream os = null;
try {
//使用文件字节输入流读取图片
is = new FileInputStream(new File("dir/gamersky_01origin_01_2017128201572D.jpg"));
//使用文件字节输出流将图片写出
os = new FileOutputStream(new File("desc/gamersky_01origin_01_2017128201572D.jpg"));
//2.进行数据的循环读取
byte[] bs = new byte[1024];
//在创建一个变量,这个变量接收实际读取文件内容的长度
int len;
while((len = is.read(bs))!=-1){ //只要不读取到-1,就循环读取数据
//在循环中将数据写到对应的文件夹中
os.write(bs,0,len);
}
os.flush();
System.out.println("老子写完了!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
字节流总结:
字节流中数据是字节,所以字节流基本所有的文件都可以进行读取和写出(文本文件、音频、视频、图片、压缩包等等),但是如果文本文件中是汉字,就不建议使用字节流来完成,因为使用字节流读取汉字容易出现乱码
PS:一次读取一个字节的内容,但是内容是一个汉字,这时就会出现问题
作业小需求:自己预习字符流Reader和Writer这两个流,并且使用FileReader和FileWriter,循环读写图片,然后查看图片,寻找问什么?【参考字节输入输出流做就可以】
预习: OutputStreamWriter和InputStreamReader
BufferedReader类
对象序列化