目录
一、File文件操作
1、File类概述
public static void main(String[] args) {
//1、创建file对象(指定文件路径)
//多于字符删去
//两个斜杠表示 第一个斜杠告诉后一个斜杠你只是一个斜杠,你不能做转义
// 也可以使用正斜杠 / D:/2ProgramTool/JavaTest/filetest/2(2).jpg
File f = new File("D:\\2ProgramTool\\JavaTest\\filetest\\2(2).jpg");
//调用API File.separator作为分隔符,可以实现跨平台操作,在不同平台匹配不同分隔符
File f1 = new File("D:" + File.separator + "2ProgramTool" + File.separator + "JavaTest" + File.separator + "filetest" + File.separator + "(2).jpg");
//取文件的字节大小
long length = f.length();
System.out.println(length);
//2、File创建对象,支持绝对路径,也支持相对路径(重点)
//D:\2ProgramTool\JavaTest\filetest\2(2).jpg 从盘符开始 绝对路径
//相对路径 一般定位模块中的文件,相对在工程下
//有些文件不能放在电脑的盘符中,会创建在项目中,因为一旦脱离电脑文件就无法使用
//而创建在模块中的文件可以跟着项目走,使用相对路径引用
File f2 = new File("file-io-app/src/data.txt");
System.out.println(f1.length());
//3、File创建对象,可以是文件也可以是文件夹,但是文件夹不能拿大小
File f3 = new File("D:\\2ProgramTool\\JavaTest\\filetest");
//判断这个路径(文件夹)是否存在
System.out.println(f3.exists());
}
2、File类常用操作
public static void main(String[] args) {
//1、相对路径创建一个文件对象
File f = new File("file-io-app/src/data.txt");
//a、获取它的绝对路径
System.out.println(f.getAbsolutePath());
//b、获取文件定义时使用的路径
System.out.println(f.getPath());
//c、获取文件名称:带后缀
System.out.println(f.getName());
//d、获取文件的大小:字节个数
System.out.println(f.length());
//e、获取文件的最后修改时间(时间毫秒值)
System.out.println(f.lastModified());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
System.out.println(sdf.format(f.lastModified()));
//f、判断是文件还是文件夹
System.out.println(f.isFile());
System.out.println(f.isDirectory());
}
3、File类创建和删除文件
public static void main(String[] args) throws IOException {
//创建文件对象
File f = new File("file-io-app/src/data2.txt");
//a、创建新文件成功返回true
System.out.println(f.createNewFile()); //(几乎不用)
//b、mkdir 创建一级目录
File f1 = new File("D:\\2ProgramTool\\JavaTest\\filetest\\aa");
System.out.println(f1.mkdir());
//c、mkdirs 创建多级目录
File f2 = new File("D:/2ProgramTool/JavaTest/filetest/cc/ss/eee");
System.out.println(f2.mkdirs());
//d、删除文件或空文件夹,不能删除非空文件夹
System.out.println(f.delete());
File f3 = new File("D:\\2ProgramTool\\JavaTest\\filetest\\aa");
//即使文件被占用仍然强制删除,不走回收站
System.out.println(f3.delete());
}
4、遍历文件夹
public static void main(String[] args) {
//遍历文件夹中的一级目录
File f = new File("D:\\2ProgramTool\\JavaTest\\filetest");
//只能拿一级文件
String[] list = f.list();
for (String s : list) {
System.out.println(s);
}
//遍历文件夹中的一级文件对象,到一个文件对象数组中去
//调用listFiles()的必须是一个文件夹
//包括文件夹中的隐藏文件都会被遍历出来
File[] files = f.listFiles();
for (File file : files) {
System.out.println(file.getName());
System.out.println(file);
//对文件夹遍历删除
//file.delete();
}
}
二、方法递归
1、递归形式
public static void main(String[] args) {
//test();
test2();
}
public static void test(){
System.out.println("被执行");
//直接递归
//递归死循环:递归方法无限调用自己,无法终止,出现栈内存溢出
test();
}
public static void test2(){
System.out.println("test2被执行");
//间接递归
test3();
}
private static void test3() {
System.out.println("test3被执行");
test2();
}
2、递归的算法流程、核心要素
public static void main(String[] args) {
System.out.println(factorial(30));
}
public static int factorial(int n){
if (n == 1) {
return 1;
}else {
return factorial(n-1) * n;
}
}
3、猴子吃桃问题
public static void main(String[] args) {
//猴子吃桃问题
//第一天摘下若干个桃子,当即吃了一半又多吃一个,每天如此,到第十天发现桃子只有一个,问摘了多少桃子
/**
* f = n/2 - 1
* 一:n/2 - 1 f1
* 二:f1/2 - 1 f2
* 三:f2/2 - 1 f3
* 。。。
* 九:f8/2 - 1 f9
* 十:f9/2 - 1 1
* n=10 f10 = 1
* fx - fx/2 -1 = f(x+1)
*/
System.out.println(eat(1));
}
public static int eat(int n){
if (n == 10){
return 1;
}else {
return (eat(n+1)+1)*2;
}
}
4、非规律化递归:文件搜索功能
public static void main(String[] args) {
LocalTime d = LocalTime.now();
//在文件夹中搜索文件
searchFile(new File("D:\\1Amusement"), "QQ.exe");
LocalTime d2 = LocalTime.now();
System.out.println("共用时:" + (d2.getSecond() - d.getSecond()));
}
private static void searchFile(File dir, String filename) {
//1、查看是否是在目录中查找文件
if (dir != null && dir.isDirectory()) {
//2、对文件夹进行遍历,将遍历到的文件放进文件数组中
File[] files = dir.listFiles();
//3、查看遍历到的文件是否是文件夹,或者查找文件是否在一级目录中
if (files != null && files.length > 0) {
//5、遍历文件夹
for (File file : files) {
//6、判断遍历到的是文件还是文件夹
if (file.isFile()) {
//8、判断此文件是不是要查找的文件
if (file.getName().contains(filename)) {
System.out.println("文件在:" + file.getAbsolutePath());
//启动软件
Runtime r = Runtime.getRuntime();
try {
r.exec(file.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
} else {
//7、是文件夹就继续遍历
searchFile(file, filename);
}
}
}
} else {
System.out.println("对不起,请在文件夹中进行搜索");
}
}
5、非规律化递归:啤酒问题
通过将瓶子和盖子分别换算成金额然后相加,将换算的金额再进行递归,直到金额小于2。
public class RecusionDemo3 {
//啤酒总数
private static int totalBar;
//剩余瓶子总数
private static int lastBottle;
//剩余盖子总数
private static int lastLid;
public static void main(String[] args) {
buy(10); //10 6 6
System.out.println("啤酒总数:" + totalBar);
System.out.println("剩余瓶子总数:" + lastBottle);
System.out.println("剩余盖子总数:" + lastLid);
}
public static void buy(int money){
//啤酒净购买量
int buyNummber = money / 2; //5 3 3
totalBar += buyNummber; //5 + 3 + 3
//瓶子和盖子能换的钱
int changeMoney = 0;
//购买的盖子数量
int lidNumber = lastLid + buyNummber; //5 4 3
//购买的瓶子数量
int bottleNumber = lastBottle + buyNummber; //5 4 3
//盖子能换多少钱
if (lidNumber >= 4){
changeMoney += (lidNumber / 4) * 2; //2 2
}
lastLid = lidNumber % 4; // 1 0
//瓶子
if (bottleNumber >= 2) {
changeMoney += (bottleNumber / 2) * 2; //4 4
}
lastBottle = bottleNumber % 2; //1 0
if (changeMoney >= 2){
buy(changeMoney);
}
}
}
三、字符集
字符串编码与解码
public static void main(String[] args) throws UnsupportedEncodingException {
//1、编码:把文字转换成字节(使用指定的编码)
String name = "abc我爱你中国";
//获取字符串的编码
// byte[] bytes = name.getBytes(); //以当前代码默认字符集进行编码(UTF-8)
//用指定字符集进行编码
//中文字符占两个字节
byte[] bytes = name.getBytes("GBK");
System.out.println(bytes.length);
//打印数组内容
//字节为负数就是中文,三个字节为一个字符 -26, -120, -111 表示我
System.out.println(Arrays.toString(bytes));
//2、解码:把字节转换成对应的中文形式(编码 和 解码使用的字符集必须一致,否则乱码)
// String rs = new String(bytes); //以当前代码默认字符集进行编码(UTF-8)
//指定字符集解码
String rs = new String(bytes,"GBK");
System.out.println(rs);
}
四、IO流
1、IO流 的概述
2、IO流的分类
3、IO流体系
3.1、字节流的使用
理解成管道,读取一个字节相当于获取一滴水
-
文件字节输入流:每次读取一个字节
一次获取一滴水
public static void main(String[] args) throws IOException {
//File f = new File("file-io-app/data.txt");
//System.out.println(f.getAbsolutePath());
//1、创建一个文件字节输入流管道与源文件接通
//直接放字符串地址实际仍然是通过内部new一个文件对象
InputStream is = new FileInputStream("D:\\2ProgramTool\\JavaTest\\file-io-app\\src\\data.txt");
//读取一个字节返回,读取一滴水
int b = is.read();
System.out.println(b);
//查看字符内容
System.out.println((char) b);
//is.read();
//读取完毕后返回-1
System.out.println(is.read());
//可以使用循环读取水滴,但是一次只能读取一个字节,遇到中文三个字节就会乱码,性能很差
//定义一个变量记录每次读取的字节
/*int c;
while ((c = is.read()) != -1){
System.out.print((char) c);
}*/
}
-
文件字节输入流:每次读取一个字节数组
一次获取一桶水
public static void main(String[] args) throws Exception {
InputStream is1 = new FileInputStream("D:\\2ProgramTool\\JavaTest\\file-io-app\\src\\data2.txt");
//2、一次读取一个字节数组返回,如果字节以及没有可读的返回-1
//相当于用一个桶来接管道中的水滴
//定义一个桶,桶可以重复使用
//byte[] buffer = new byte[1024]; 一次读1024个字节 也就是1KB的数据
byte[] buffer = new byte[3];
//先接第一桶 每次读取的字节数
int read = is1.read(buffer);
System.out.println(read);
//对字节数组进行解码
String rs = new String(buffer);
System.out.println(rs); //abc
//接第二桶 每次读取的字节数
//接第二桶时会倒出第一桶中的水
int read2 = is1.read(buffer);
System.out.println(read2);
//对字节数组进行解码
String rs2 = new String(buffer);
//read=[a,b,c] //read2=[1,2,c],只有两滴水,第三滴水还在桶内,所以输出三个字节
//多输出了一个字节c是因为上一桶的水没有倒干净
System.out.println(rs2); //12c
//读多少水就倒多少水
String rs3 = new String(buffer,0,read2); //0表示从桶底开始装
System.out.println(rs3);
//使用循环每次读取一个字节数组
int len;//记录每次读取的字节数
while ((len = is1.read(buffer)) != -1){
//读多少倒多少
System.out.println(new String(buffer, 0, len));
}
}
这两种方式都无法解决读入中文字符乱码的问题
-
文件字节输入流:一次读完全部字节
文件相当于池塘,一次定义另一个池塘,直接将文件所有字节装进池塘。
public static void main(String[] args) throws Exception {
//1、创建一个文件字节输入流管道与源文件接通
//创建一个文件对象,获取池塘大小
File f = new File("file-io-app\\src\\data3.txt");
InputStream fs = new FileInputStream(f);
//2、定义一个字节数组与文件大小相同
//定义一个池塘,大小与文件池塘相同
//强转原因:由于文件大小随机,防止定义的桶过大,将内存挤爆
byte[] buffer = new byte[(int) f.length()]; //直接强转为int型
System.out.print(fs.read(buffer)); //返回读取的字节数
//3、对数据进行解码
//读取后的字节就会存放在buffer中
String s = new String(buffer);
System.out.println(s);
//一次读取全部字节 官方API
byte[] buffer1 = fs.readAllBytes();
System.out.println(new String(buffer1));
}
但是一次读完全部字节也存在问题,如果文件过大,字节数组可能引起内存溢出,所以使用字节输入流不适合读文本内容。字符流可以专门用于读取文本内容。
-
文件字节输出流:写文件数据到文件
public static void main(String[] args) throws Exception {
//创建一个文件字节输出流管道与源文件接通
OutputStream os = new FileOutputStream("file-io-app\\src\\data4.txt",true);
//通过管道向文件写入内容
//1、一次写入一个字符
os.write(34);
os.write('a');
//将字符串转成字节数组,输出换行 windows中\n是换行但其它系统不一定,使用\r\n更保险
os.write("\r\n".getBytes());
os.write(99);
//2、一次写入一个字节数组(桶)
byte[] buffer = {'b',34,97,78,34};
os.write(buffer);
//写入汉字
byte[] buffer2 = "年初鲜花卡".getBytes(); //以当前字符集默认格式编码
os.write(buffer2);
//3、写入字节数组的一部分
byte[] buffer3 = {'b',34,97,78,34};
os.write(buffer3,0,3);
//写完数据要刷新数据,关闭管道同时也可以刷新数据
//每次写入数据时,管道都会将内容存放在一个自带的内存缓冲区中,然后再从缓冲区向文件流数据
//os.flush(); 刷新相当于将所有数据从缓冲区抖出去,不刷新有可能有的数据写不进去
//管道使用完后一定要关闭管道,否则影响系统性能,相当于系统后台
os.close();
}
每次向文件中写入数据,管道都会对文件原本内容进行情况,假如文件中原本有数据,再向文件写入数据时,会将原有内容清空
FileOutputStream管道被创建后会先清空文件的数据再向文件中写入数据,因此也叫追加管道,如果不希望每次写入都清空,则:
//追加一个true OutputStream os = new FileOutputStream("file-io-app\\src\\data4.txt",true);
-
文件拷贝
public static void main(String[] args) throws Exception {
//文件复制,无论是文件还是视频都可以
//创建文件字节输入流与源文件接通
InputStream is = new FileInputStream("C:\\Users\\misGood\\Desktop\\1.jpg");
//用一个池塘将源文件一次打包
byte[] buffer = is.readAllBytes();
//创建文件字节输出流与源文件接通
OutputStream os = new FileOutputStream("D:\\2ProgramTool\\JavaTest\\file-io-app\\1.jpg");
//将池塘内的文件全部倒入源文件
os.write(buffer);
//关闭管道资源
os.close();
}
3.2、资源释放的方式 try-catch-finally
基本做法
如果IO流操作中出现异常,则可能导致管道资源无法关闭,无法执行到关闭语句。
public static void main(String[] args) {
byte[] buffer = new byte[0];
OutputStream os = null;
InputStream is = null;
try {
//创建文件字节输入流与源文件接通
is = new FileInputStream("file-io-app/src/data3.txt");
//用一个池塘将源文件一次打包
buffer = is.readAllBytes();
//创建文件字节输出流与源文件接通
os = new FileOutputStream("file-io-app/src/data4.txt", true);
//将池塘内的文件全部倒入源文件
os.write(buffer);
//操作中间抛异常,无法执行到关闭语句
int a = 10 / 0;
//即使代码中有return干掉方法,也要等finally执行完毕
return;
} catch (IOException e) {
e.printStackTrace();
//开发中不建议在finally中添加return,因为他会使程序直接返回return的内容
} finally {
//关闭管道资源
try {
if (os != null) os.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (is != null) is.close();
} catch (IOException e) {
e.printStackTrace();
}
//使用finally即使中间抛异常仍然可以关闭管道资源
//必须等finally执行完才会结束语句
System.out.println("Finally被执行");
}
}
JDK7、JDK9改进方法
public static void main(String[] args) {
//JDK7改进方法
try(InputStream is = new FileInputStream("file-io-app/src/data3.txt"))
{
//创建文件字节输入流与源文件接通
//用一个池塘将源文件一次打包
byte[] buffer = is.readAllBytes();
//操作中间抛异常,无法执行到关闭语句
int a = 10 / 0;
//即使代码中有return干掉方法,也要等finally执行完毕
return;
} catch (IOException e) {
e.printStackTrace();
}
}
3.3、字符流的使用
-
文件字符输入流:一次读取一个字符
public static void main(String[] args) throws Exception {
//目标:每次读取一个字符
//1、创建一个字符输入流管道与源文件接通
Reader fr = new java.io.FileReader("file-io-app/src/data.txt");
//2、读取一个字符并返回编号,没有返回-1
/*int code = fr.read();
System.out.print((char)code);
int code2 = fr.read();
System.out.print((char)code2);
*/
//3、使用循环读取字符
int code;
while ((code = fr.read()) != -1){
//文本内容自带换行,读取字符时不需要换行
System.out.print((char) code);
}
}
字节流读取数据返回的结果是 读取到的字节数; 字符流读取数据返回的结果是 字符对应的编码ASCILL码。
-
文件字符输入流:一次读取一个字符数组
public static void main(String[] args) throws Exception {
//目标:每次读取一个字符数组
//1、创建一个字符输入流管道与源文件接通
Reader fr = new java.io.FileReader("file-io-app/src/data.txt");
//2、每次读取一个字符数组的数据
//char[] buffer = new char[1024]; //每次读取1K个字符
char[] buffer = new char[30]; //每次读取10个字符
//3、使用循环读取字符
int len;
//len 表示一次读取的字符个数
while ((len = fr.read(buffer)) != -1){
//对读取的内容进行解码
String s = new String(buffer,0,len);
//查看一次读取的字符个数
System.out.print(len);
//30303030303030303030303025
//文本内容自带换行,读取字符时不需要换行
System.out.print(s);
}
}
-
文件字符输出流
public static void main(String[] args) throws Exception {
//1、创建一个字符输出流管道与源文件接通
//不需要创建文件,自动生成,覆盖管道,每次启动会清空之前的数据
Writer fw = new FileWriter("file-io-app/src/data5.txt",true);
//2、一次写一个字符出去 从内存写入磁盘
fw.write(80);
fw.write('w');
fw.write("\r\n");
//3、一次写一个字符串出去
fw.write("wuc");
fw.write("大家比较阿布");
fw.write("\r\n");
//4、一次写一个字符数组出去
char[] c = "asnjca五年快餐".toCharArray();
fw.write(c);
fw.write("\r\n");
//5、一次写一个字符数组的一部分
fw.write(c,6,3);
//6、一次写一个字符串的一部分
fw.write("caca馅儿饼",0,5);
//fw.flush();
fw.close();
}