Java学习路线(18)——File、递归、字符集和IO流(上)

一、文件
(1)概述

  • File类在java.io.File下,代表操作系统的文件对象(文件、文件夹)
  • File类提供诸如:定位文件,获取文件信息,删除文件,创建文件等操作

(2)构造器创建File对象

构造器 说明
File(File parent,String filed) 获取已有的文件对象中的某一文件对象
File(String pathname) 通过文件路径名查找文件对象
File(String parent,String filed) 根据父路径查找文件对象后,通过子文件名查找文件对象
File(String Uri) 通过网络路径查找文件对象

使用示例

public class FileDemo {
    public static void main(String[] args) {
        /*创建File对象的方式(绝对路径)
        * 1、使用分隔符为 \\ 形式的地址
        * 2、使用分隔符为 / 形式的地址
        * 3、File.separator 自动获取系统分隔符,跨平台时推荐(mac、linux、windows等系统)
        * */

        /*绝对路径创建*/
        File file = new File("E:\\360MoveData\\Users\\zain\\Desktop\\text.txt");
        File file1 = new File("E:/360MoveData/Users/zain/Desktop/text.txt");
        File file2 = new File("E:"+File.separator+"360MoveData"+File.separator+"Users"+File.separator+"zain"+File.separator+"Desktop"+File.separator+"text.txt");
        /*返回字节数*/
        System.out.println(file.length());
//        file.delete();  //删除磁盘中的文件

        /*相对路径创建:主要定位于模块中的文件*/
        File file3 = new File("JavaSEpro/src/demo.txt");
        System.out.println(file3.length());
    }
}

(3)常用API

获取类、判断类API

方法 说明
boolean isDirectory() 判断File是否为文件夹
boolean isFile() 判断File是否为文件
boolean exists() 判断File是否存在
String getAbsolutePath() 获取File对象的绝对路径
String getPath() 获取自定义的路径
String getName() 获取File对象的文件名
long lastModified() 返回文件最后修改的时间戳

创建类、删除类API

方法 说明
boolean createNewFile() 创建新的空文件夹
boolean mkdir() 只创建一级文件夹
boolean mkdirs() 创建多级文件夹
boolean delete() 删除文件或空文件夹

注意:多级指的是 a/b/c/d 这样类型的多级文件夹

遍历文件夹API

方法 说明
String[] list() 获取当前目录下的一级文件名称
File[] listFiles() 获取当前目录下的所有的一级文件对象

遍历文件夹示例

public class FileDemo {
   public static void main(String[] args) {
       //1、定位目录
       File dir = new File("D:\\JavaBase\\JavaSEpro");
       //2.1、获取目录下的一级文件名称
       String[] names = dir.list();
       System.out.println(Arrays.toString(names));	
   	   /*2.2、获取目录下的一级文件对象*/
   	   File[] files = dir.listFiles();
   }
}

/*打印结果*/
[.idea, JavaSEpro.iml, lib, log, out, src]

listFiles 方法注意事项

  • 当调用者不存在时,返回null
  • 当调用者为文件时,返回null
  • 当调用者为空文件夹时,返回长度为0的数组
  • 当调用者为有内容的文件夹时,将里面所有的文件和文件夹的路径放在File数组中返回
  • 当调用者为隐藏文件夹时,将里面所有文件和文件夹的路径放在File数组中返回,包括隐藏内容
  • 当文件夹需要权限进入时,返回null

二、递归

(1)概念: 方法直接调用自己或间接调用自己的形式称为方法递归(recursion),作为一种算法在程序设计语言中广泛使用。

(2)形式:

  • 直接递归: 方法调用自身。
  • 间接递归: 方法调用其他方法,其它方法中调用该方法。

(3)方法递归存在的问题: 递归若没有控制终止,会出现递归死循环,导致栈内存溢出现象。

(4)递归解决问题的思路: 复杂问题拆解为相似的规模较小的问题求解。

(5)递归的步骤

  • 总结通用公式
  • 递归跳出点
  • 递归方向

(6)递归的示例

/**
 需求:使用递归,计算1-n的阶乘
 通用公式:f(n) = f(n-1)*n;
 跳出点:f(1)
 递归方向,自上而下
*/
public class FileDemo {
    public static void main(String[] args) {
        for (int i = 1; i <= 5; i++) {
            System.out.println(factorial(i));
        }
    }

    public static int factorial(int num){
        if (num == 1) return 1;
        return factorial(num - 1) * num;
    }
}

/*打印结果*/
1
2
6
24
120

(7)常见案例(规律化递归)
(a)计算1-n的和

/**
	通用公式:f(n)=f(n-1) + n;
	终结点:f(1)
	递归方向:自上而下
*/
public static int sum(int num) {
	if (num == 1) return 1;
	return sum(num-1) + num;
}

(2)猴子吃桃

	/**
		问题:第一天摘下n个桃子,当即吃了一半多一个。
		第二天又吃了前天剩余数量的一半多一个。
		以后每天都是吃前天剩余数量的一半多一个。(递归条件)
		等到第10天的时候发现桃子只有1个了。(终点)
		现求第一天摘了几个桃子。
		递归分析: 当天的个数 = 前天个数/2 - 1; <=> f(n+1) = 2f(n) + 2;
	*/
    public static int calMount(int day){
        if (day == 10) return 1;
        return 2*calMount(day+1) + 2;
    }

(8)常见案例(非规律化递归)

(a)查找文件
查找的文件是detail下的Test.java文件,以下是目录图
Java学习路线(18)——File、递归、字符集和IO流(上)_第1张图片

	/**
	* 文件搜索
	* 需求:文件搜索,从项目中,搜索某个文件名称并输出绝对路径
	* 分析:
	* 1、定位项目位置
	* 2、遍历文件,判断是否为文件
	* 3、若是文件,则按照规则判定
	* 4、若是文件夹,递归文件夹的内容
	*/

	public static void main(String[] args) {
        searchFile(new File("D:\\JavaBase\\JavaSEpro\\src"));
    }

    public static void searchFile(File dir) {
        File[] files = dir.listFiles();
        for (File file : files != null ? files : new File[0]) {
            if (file.isFile()){
                if (file.getName().equals("Test.java")){
                    System.out.println(file.getAbsolutePath());
                    return;
                }
            }
            else searchFile(file);
        }
    }

(b)啤酒问题

/**
* 需求:啤酒2元1瓶,4个盖子换1瓶,2个空瓶换一瓶,问10元能喝多少瓶,剩余多少空瓶和盖子。
*/
public class FileDemo {

    public static int nowMount = 0; //现有啤酒数
    public static int emptyBottle = 0; //空瓶数
    public static int lid = 0; //盖子数

    public static void main(String[] args) {
        int money = 10;
        
        //初始化
        nowMount = buy(money);
        lid = nowMount;
        emptyBottle = nowMount;
        
        //兑换
        warp();
        
        System.out.println(nowMount+","+lid+","+emptyBottle);
        //打印结果
        1531
    }

    /*啤酒 2元-> 1瓶*/
    public static int buy(int money){
        return (int)Math.floor(money/2.0);
    }

    /*盖子 * 4 -> 1瓶,空瓶 * 2 -> 1瓶 */
    public static void warp(){
        if (lid >= 4) {
            int bottle = lid / 4;
            nowMount += bottle; //盖子换酒
            emptyBottle += bottle; //空酒瓶
            //剩余盖子
            lid %= 4;
            lid += bottle;
            warp();
        }

        if (emptyBottle >= 2){
            int bottle = emptyBottle / 2;
            nowMount += bottle;  //空瓶换酒
            lid += bottle; //盖子
            //剩余空酒瓶
            emptyBottle %= 2;
            emptyBottle += bottle;
            warp();
        }
    }
}

三、字符集

1、什么是字符集?

  • 字符集是用以表示计算机底层二进制(0/1)的编号规则

2、常用字符集

(1)ASCII字符集

  • 概述: 美国信息交换标准代码(American Standard Code for Information Interchange),包括了对数字、英文、符号进行编号。
  • 存储方式: 1个字节存储1个字符,1个字节是8bit,总共可以表示128个字符信息。

(2)GBK字符集

  • 概述: 汉字内码扩展规范(Chinese Internal Code Specification),兼容ASCII码表,包含数万个汉字,并支持繁体汉字以及部分日韩文字。
  • 存储方式: 一个中文以两个字节的形式存储,但不包含世界上所有国家的文字。

(3)Unicode码表

  • 概述: 统一码,现有计算机体系中一项业内字符编码标准,容纳了世界上大部分国家的所有常见文字和符号。
  • 存储方式: Unicode通过UTF-8,UTF-16以及UTF-32的编码成二进制后存储计算机。
  • 注意:
    • Unicode是万国码,以UTF-8编码后一个中文一般以三个字节进行存储。
    • UTF-8兼容ASCII。
    • 技术人员通常使用UTF-8作为字符集编码标准
    • 编码前后字符集要一致,否则会乱码。

3、字符集的编码与解码操作
(1)String编码

方法 说明
byte[] getBytes() 使用默认字符集将该String编码为一系列字节,结果存储到新的字节数组
byte[] getBytes(String charsetName) 使用指定字符集将该String编码为一系列字节,结果存储到新的字节数组

(2)String解码

构造器 说明
String(byte[] bytes) 使用默认字符集解码指定字节数组来构造新的String
String(byte[] bytes,String charsetName) 使用指定字符集解码指定字节数组来构造新的String

编码与解码的示例

public class StringDemo {
    public static void main(String[] args) {
        String str = "ASCII我不想用。";
        /*编码*/
        byte[] bytes = str.getBytes();
        System.out.println(Arrays.toString(bytes));

        /*解码*/
        String newString = new String(bytes);
        System.out.println(newString);
    }
}

/*打印结果*/
[65, 83, 67, 73, 73, -26, -120, -111, -28, -72, -115, -26, -125, -77, -25, -108, -88, -29, -128, -126]
ASCII我不想用。

四、IO流

(1)概念: 称为输入输出流。

符号 说明
I 表示input,是数据从硬盘文件读入内存的过程,称之为输入,负责
O 表示output,是内存程序的数据从内存到写出到硬盘文件的过程,称之输出,负责

(2)作用: 用来读写数据。
(3)IO流的分类方法

  • 按流的方向分类
    • 输入流
    • 输出流
  • 按流中的数据最小单位分类
    • 字节流
    • 字符流

(4)IO流体系

  • 字节流
    • 字节输入流——InputStream(抽象)——FileInputStream(实现)
    • 字节输出流——OutputStream(抽象)——FileoutputStream(实现)
  • 字符流
    • 字符输入流——Reader(抽象)——FileReader(实现)
    • 字符输入流——Writer(抽象)——FileWriter(实现)

(5)字节流

(a)文件字节流FileInputStream

  • 获取文件字节流
构造器 说明
FileInputStream(File file) 创建字节输入流管道与源文件对象接通
FileInputStream(String pathname) 创建字节输入流管道与源文件路径接通
  • 读取文件内容
方法 说明
int read() 每读取一个字节返回,无字节可读返回 -1
int read(byte[] buffer) 每读取一个字节数组返回,无字节可读返回 -1

使用示例

public class StreamDemo {
    public static void main(String[] args) {
        //1、创建文件字节流管道与源文件接通
        //1.1、创建File对象
        File file = new File("D:\\JavaBase\\JavaSEpro\\src\\com\\zengoo\\logback\\pacageDemo\\loco.txt");

        try {
            //1.2、文件字节流构造器创建流管道
            FileInputStream fis = new FileInputStream(file);
            FileInputStream fis1 = new FileInputStream("D:\\JavaBase\\JavaSEpro\\src\\com\\zengoo\\logback\\pacageDemo\\loco.txt");
            //两种读法
            //1.3.1 单字节读取
            StringBuilder sb = new StringBuilder();
            while(true){
                int byteByFile = fis.read();
                if (byteByFile == -1) break;
                System.out.println("读取了字节"+byteByFile+":" + (char)byteByFile);
            }
            System.out.println(sb.toString());

            //1.3.2 字节组读取
            byte[] buffer = new byte[5];
            while(true){
                int len = fis1.read(buffer);
                if (len == -1) break;
                System.out.println("读取了"+len+"个字节:"+new String(buffer,0,len));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}



/*打印结果*/
读取了字节97:a
读取了字节115:s
读取了字节100:d
读取了字节491
读取了字节502
读取了字节513

读取了5个字节:asd12
读取了1个字节:3

解决中文字符乱码问题

  • 方法1:自定义一个字节数组与文件的大小相同,通过读取字节数组的方式,一次性读取(很可能爆内存)
		//1、创建文件字节流管道与源文件接通
        File file = new File("D:\\JavaBase\\JavaSEpro\\src\\com\\zengoo\\logback\\pacageDemo\\loco.txt");
        //2、创建一个尺寸相同的字节数组
        byte[] bytes = new byte[Integer.parseInt(Long.toString(file.length()))];
        //3、文件字节流创建
        try {
            FileInputStream fis = new FileInputStream(file);
            //4、字节数组流读取
            fis.read(bytes);
            System.out.println(new String(bytes));
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        
/*打印结果*/
asd123我爱你asd123我爱你
asd123经典有机奶。asd123我爱你asd123我爱你
            asd123经典有机奶。
  • 方式2:官方API
//1、创建文件字节流管道与源文件接通
        File file = new File("D:\\JavaBase\\JavaSEpro\\src\\com\\zengoo\\logback\\pacageDemo\\loco.txt");
        
        try {
        //2、创建InputStream;
            InputStream is = new FileInputStream(file);
            //读取流中的字节
            byte[] buffer = is.readAllBytes();
            System.out.println(new String(buffer));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        
        /*打印结果*/
asd123我爱你asd123我爱你
asd123经典有机奶。asd123我爱你asd123我爱你
            asd123经典有机奶。

(b)文件字节输出流 FileOutputStream

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

构造器:

构造器 说明
FileOutputStream(File file) 创建字节输出流管道与源文件对象接通
FileOutputStream(File file,boolean append) 创建字节输出流管道与源文件对象接通,可追加数据
FileOutputStream(String filepath) 创建字节输出流管道与源文件路径接通
FileOutputStream(String filepath,boolean append) 创建字节输出流管道与源文件路径接通,可追加数据

写数据API

方法 说明
void write(int a) 写一个字节出去
void write(byte[] buffer) 写一个字节数组出去
void write(byte[] buffer, int pos , int len) 写一个部分字节数组出去

流的刷新与释放操作

方法 说明
void flush() 刷新流中的数据
void close() 关闭文件输出流

写入数据示例

public class StreamDemo {
    public static void main(String[] args) {
        try {
            //1、创建文件输出流
    		//默认覆盖文件数据,若想追加数据,在构造器中开启追加功能。
            OutputStream os = new FileOutputStream("D:\\JavaBase\\JavaSEpro\\src\\com\\zengoo\\logback\\OutStreamDemo.java");
            //2、写数据
            String str = "package com.zengoo.logback;";
            for (int i = 0; i < str.length(); i++) {
                // write(int a)
                os.write(str.charAt(i));
            }

            os.write("\r\n".getBytes());

            // write(byte[] buffer)
            os.write("public class OutStreamDemo{".getBytes());
            os.write("\r\n".getBytes());

            //write(byte[] buffer,int pos, int len)
            byte[] writeString = "\tpublic static void main(String[] args){\r\n\r\n\t}\r\n}".getBytes("UTF-8");
            os.write(writeString,0, writeString.length);
            os.flush(); //执行后,该流刷新数据并仍可操作。
            os.close(); //执行后,该流关闭不可再操作。
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}

OutStreamDemo.java

package com.zengoo.logback;
public class OutStreamDemo{
	public static void main(String[] args){

	}
}

(c)文件拷贝示例

/** 
* 需求:将某个文件复制到其它目录下
* 分析步骤:
* 1、读取文件
* 2、写入文件
*/

public class CopyFileDemo{
	public static void main(String[] args){
		String filepath = "D:\\JavaBase\\JavaSEpro\\src\\com\\zengoo\\logback\\loco.txt";
		String dicpath = "D:\\JavaBase\\JavaSEpro\\src\\com\\zengoo\\logback\\pacageDemo\\loco.txt";

		try {
			/*1、读写对象流*/
			FileInputStream input = getInputStream(filepath);
			FileOutputStream output = getOutputStream(dicpath);

			/*2、向目标对象写入数据*/
			output.write(input.readAllBytes());
			
			/*3、关闭流*/
			output.close();
			input.close();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	public static FileInputStream getInputStream(String path) throws FileNotFoundException {
		return new FileInputStream(path);
	}

	static FileOutputStream getOutputStream(String path) throws FileNotFoundException {
		return new FileOutputStream(path);
	}
}

(6)字符流

(a)文件字符输入流FileReader

  • 获取流对象
构造器 说明
FileReader(File file) 创建字符输入流管道与源文件对象接通
FileReader(String pathname) 创建字符输入流管道与源文件路径接通
  • 读取数据
方法 说明
int read() 每读取一个字符返回,若无字符可读,则返回-1
int read(char[] buffer) 每读取一个字符数组,返回读取字符个数,若无字符可读,则返回-1
public class ReaderDemo {
    public static void main(String[] args) {
        try(
                Reader reader = new FileReader("D:\\JavaBase\\JavaSEpro\\src\\com\\zengoo\\logback\\loco.txt");
                Reader reader2 = new FileReader("D:\\JavaBase\\JavaSEpro\\src\\com\\zengoo\\logback\\loco.txt");
                ) {
            /*方式1 读取1字符*/
            System.out.println("———————————————————方式1:读取1字符———————————————");
            int rs;
            while((rs = reader.read()) != -1){
                System.out.print((char) rs);
            }
            System.out.println();
            System.out.println("———————————————————方式2:读取字符数组———————————————");
            /*方式2 读取字符数组*/
            char[] chars = new char[20];
            while(reader2.read(chars) != -1){
                System.out.println(String.valueOf(chars));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            System.out.println("———————————————————读取完毕———————————————");
        }
    }
}

/*打印结果*/
———————————————————方式1:读取1字符———————————————
asd123我爱你asd123我爱你
asd123经典有机奶。asd123我爱你asd123我爱你
            asd123经典有机奶。
———————————————————方式2:读取字符数组———————————————
asd123我爱你asd123我爱你

asd123经典有机奶。asd123我爱
你asd123我爱你
        
    asd123经典有机奶。    
———————————————————读取完毕———————————————

(b)文件字符输出流FileWriter

  • 获取流对象
构造器 说明
FileWriter(File file) 创建字符输入流管道与源文件对象接通
FileWriter(File file,boolean append) 创建字符输入流管道与源文件对象接通,可追加数据
FileWriter(String pathname) 创建字符输入流管道与源文件路径接通
FileWriter(String pathname,boolean append) 创建字符输入流管道与源文件路径接通,可追加数据
  • 写入数据
方法 说明
int writer(intc ) 写入1个字符
int writer(char[] cbuf) 写入1个字符数组
int writer(char[] cbuf,int off,int len) 写入字符数组的一部分
int writer(String str) 写入1个字符串
int writer(String str,int off,int len) 写入字符数组的一部分
public class WriterDemo {
    public static void main(String[] args) {
        try(    //1、创建输出流
                FileWriter writer = new FileWriter("D:\\JavaBase\\JavaSEpro\\src\\com\\zengoo\\logback\\WriterTest.java");
        ) {
            /*写入字符串*/
            writer.write("package com.zengoo.logback;\n");

            writer.write("public class WriterTest{\n");

            /*写入字符数组*/
            writer.write("\tpublic static void main(String[] args) {\n\t\n\t}\n".toCharArray());

            /*写入单个字符*/
            writer.write('}');
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            System.out.println("———————————————————写入完毕———————————————");
        }
    }
}

WriterTest.java

package com.zengoo.logback;
public class WriterTest{
	public static void main(String[] args) {
	
	}
}

字节流与字符流适合的场景

  • 字节流: 适合做一切文件数据的拷贝(音频、视频、文本等)
  • 字符流: 适合做文本文件的操作(读、写)

五、资源释放

1、try…catch…finally
在原有处理异常基础上,增加默认执行代码finally

try{
	受监视的代码块
}catch(异常类型 变量){
	异常处理代码块
}final{
	/*3、关闭流*/
	output.close();
	input.close();
}

注意: finally代码即使在异常发生也会执行,所以finally不会使用return进行返回。

2、try(自定义流)…catch…
这种方法会使得流使用完毕后,自动关闭,达到简化的效果

/*JDK7(推荐使用)*/
try(
	InputStream is = new FileInputStream(...);
	OutputStream os = new FileOutputStream(...);
){
	监听代码块
}catch(Exception e){
	异常处理块
}finally{
	异常执行块
}

/*JDK9(不推荐)*/
InputStream is = new FileInputStream(...);//输入流
OutputStream os = new FileOutputStream(...);//输出流
try(is;os){
	监听代码块
}catch(Exception e){
	异常处理块
}finally{
	异常执行块
}

注意: JDK 7和JDK 9中( )只能放置资源对象

问:为什么资源对象
答:资源对象是实现了Closeable/AutoCloseable接口的类对象,在try()…catch…体系中,JVM会自动调用Closeable或是AutoCloseable的close()方法完成资源释放。

public abstract class InputStream implements Closeable{}
public abstract class OutputStream implements Closeable, Flushable{}

你可能感兴趣的:(java,学习,jvm)