Skip to content

day10【多线程和 File 类】 ​

今日内容介绍

java
Lock接口
线程通信,等待与唤醒
线程池思想【重要】
JDK提供的线程池
Callable和Future接口
File类
递归【重要】

第一章 Lock 接口 ​

1.1 JDK5 的特性 Lock 接口 ​

java
jdk1.5提供了Lock接口
Lock接口在juc包(并发包: java.util.concurrent)里面
    java.util.concurrent.locks.Lock接口: 比同步方法/同步代码块使用更灵活
        抽象方法:
            public abstract void lock(): 获取锁。
            public void unlock(): 释放锁。  释放资源的动作必须要执行

    常用实现类:
        java.util.concurrent.locks.ReentrantLock类:

        空参构造方法: 直接创建对象
            pubic ReentrantLock():  是一个锁对象,在使用的时候,务必要保证锁对象的唯一性

    Lock接口替换了同步代码块和同步方法
        使用格式:
            上锁: lock.lock();
            try {
                操作共享数据的代码;
            } catch(...){
                ...
            } finally {
                //释放锁的代码
                lock.unlock();
            }

1.2 Lock 接口替换同步 ​

java
package com.itheima.lock;

import sun.awt.windows.ThemeReader;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicketTask implements Runnable {

    private int ticket = 100;
    private Lock lock = new ReentrantLock();
//    private Object lock = new Object();
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        while(true){
//            synchronized (lock){//获取到锁 进入代码区域执行
            lock.lock();
                if(ticket>0){
                    try {
                        Thread.sleep(100);
                        System.out.println(name+"正在出第"+ticket+"张");

                        ticket--;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        lock.unlock();
                    }


                }
//            }//释放锁  多个线程重新抢夺锁


        }
    }
}

package com.itheima.lock;

public class Demo {
    public static void main(String[] args) {
        //卖票任务
        SellTicketTask task = new SellTicketTask();

        //怎么搞线程
        Thread t1 = new Thread(task,"窗口1");
        Thread t2 = new Thread(task,"窗口2");
        Thread t3 = new Thread(task,"窗口3");
        //启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}

第二章 生产者与消费者【重点理解】 ​

2.1 线程间的通信 ​

java
多个线程在处理同一资源的时候,任务不同,这个时候就需要线程间的通信来完成了。

1641464380098

线程间存在通信,也就是多个线程在操作同一份数据的时候,会争夺共享变量,但是他们的任务是不一样的。

我们得保证 线程间的通信是正常的正确的。

怎么保证 正确正常呢?

吃货 和 厨师加同样的锁,就可以保证线程间的通信正常执行。

吃货 是 消费者,厨师是 生产者。

我们的最终目的是 保证 线程间的通信,是可以有效的利用起来!!

如何保证 我们线程间的通信 有效的利用起来呢

我们推出了一个 等待唤醒的概念。

2.2 线程通信等待唤醒概念 ​

java
等待唤醒 是解决 生产者与消费者 线程间通信问题的 一种机制。
用来保证,我们可以有效的利用资源。

今天只讲解 单消费者 单生产者问题。

1628041940082

1641523937981

线程等待和唤醒的方法定义在java.lang.Object类中。

等待唤醒的方法只能被锁对象调用!!因为只有在加锁状态下,才能去唤醒,才能去进入等待。

方法声明方法含义
public final void wait()当前线程等待,释放掉当前线程对象所拥有的锁对象。
public final void notify()唤醒在此对象监视器(锁对象)上等待的单个线程。(唤醒等待该锁对象的一个线程)
public final void notifyAll()唤醒在此对象监视器上等待的所有线程。(唤醒等待该锁对象的所有个线程)

分析

桌子类

​ 定义一个成员 boolean isHaveHamburger = false;

​ public void eat(){}

​ public void make(){}

吃货线程和吃货任务写到一起---继承方式创建线程

java
创建  吃货线程 --传递桌子

  写一个带参构造
  private Table table;

  public Foodie(Table table){
    this.table=table;
  }

   public void run(){
       //可以使用table了

   }

厨师线程和厨师任务写到一起---继承方式创建线程

java
  private Table table;

  public Cooker(Table table){
    this.table=table;
  }

   public void run(){
       //可以使用table了

   }

在测试类测试

​ 创建桌子对象

​ 创建 厨师线程 --传递桌子

​ 创建 吃货线程 --传递桌子

​ 开启厨师

​ 开启吃货

2.3 案例实现 ​

java
package com.itheima.waitnotify;

public class Table {
    //桌子  桌子上有 汉堡包  表示汉堡包状态
    boolean isHaveHamburger = false;


    public void eat(){
        System.out.println("吃货在桌子上吃汉堡");
    }

    public void make(){

        System.out.println("厨师把做好的汉堡放到桌子。");
    }
}
java
package com.itheima.waitnotify;
//吃货 吃的任务是自己的 所以 使用继承的方式
public class Foodie extends Thread{

    //吃货需要  桌子的 因为有了桌子 才能看有没有汉堡
    private  Table table;

    public Foodie(Table table){
        this.table=table;
    }
    //写带参构造的目的 是为了能够在run中使用到 传递的桌子

    @Override
    public void run() {
       while(true){
           //吃货吃的时候  厨师不能做 厨师做的时候 吃货不能吃  他们要加同步代码块
           synchronized (table){
                //判断
               if(table.isHaveHamburger==false){//没有汉堡 等待
                   try {
                       table.wait();//将当前的 吃货线程进入等待区域
                       // 当当前线程进入到wait下 就代表着 当前线程释放锁
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }

               //如果 吃货没等待  有汉堡
               //吃货吃
               table.eat();
               //吃货吃完   桌子上没有
               table.isHaveHamburger = false;//改变汉堡状态

               //唤醒厨师
               table.notify();
           }
       }
    }
}
java
package com.itheima.waitnotify;
//吃货 吃的任务是自己的 所以 使用继承的方式
public class Cooker extends Thread{

    //吃货需要  桌子的 因为有了桌子 才能看有没有汉堡
    private  Table table;

    public Cooker(Table table){
        this.table=table;
    }
    //写带参构造的目的 是为了能够在run中使用到 传递的桌子

    @Override
    public void run() {
       while(true){
           //吃货吃的时候  厨师不能做 厨师做的时候 吃货不能吃  他们要加同步代码块
           synchronized (table){
                //判断
               if(table.isHaveHamburger==true){//有汉堡 等待

                   try {
                       table.wait();//将当前的 厨师线程进入等待区域
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }

               //如果 没有汉堡
               //厨师做
               table.make();

               //厨师做完 有
               table.isHaveHamburger = true;//改变汉堡状态

               //唤醒吃货
               table.notify();
           }
       }
    }
}
java
package com.itheima.waitnotify;

public class Demo {
    public static void main(String[] args) {
        //我要模拟线程间的通信 并且要保证线程间通信 资源可以被有效的使用
        // 有几个线程? 2  吃货(消费者)线程  厨师(生产者)线程
        // 两个线程做的任务一样吗?  吃货 吃桌子上的汉堡包   初始 产出桌子上的汉堡包
         //        任务不一样,但是   桌子上的汉堡包是同一个!!!  两个线程得自己写自己的任务
        // 1:实现的第一步 创建两个线程类
        // 创建 共享的桌子 因为桌子上放包子
         Table table = new Table();
        // 厨师线程  生产者
         Cooker cooker = new Cooker(table);
         //吃货线程  消费者
         Foodie foodie = new Foodie(table);

         //吃货开吃
        foodie.start();
        //厨师 开做
        cooker.start();
    }
}

2.4 wait()和 sleep(毫秒值)的区别 ​

java
wait()方法来自Object,sleep方法来自Thread.
   sleep方法不会释放锁,在当前线程休眠时间段内霸占这Cpu,其他的同步线程只能阻塞。
   wait方法会释放锁,其他的同步线程可以去抢夺锁资源。
   wait方法只能用在同步(锁)控制中,而sleep想用在哪里用在哪里,跟同步没关系。

第三章 线程池 Thread Pool(概念思路重要) ​

创建线程每次都要和操作系统交互,线程执行任务后就默认销毁,销毁也会消耗资源。

如果我们在程序中频繁进行创建和销毁,会降低程序的性能,拖垮程序。

于是我们就响了一个办法,创建一个池子出来,在使用线程之前,先创建出来一些线程对象,放到池子里面,

但我们需要线程执行任务的时候,就从池子里面获取出来一个,帮我们去执行,

当执行完了,就把线程对象归还给线程池,这样就提高了线程的利用率,并且没有进行频繁的创建和销毁,

从而不会拖垮程序,相对的就提高了程序的性能。

3.1 线程池原理 ​

java
线程属于系统的宝贵资源,频繁的创建和销毁线程,会降低效率
所以需要使用线程池

1601987457740

3.2 JDK 中内置的线程池 了解 ​

java
JDK中内置的线程池

    java.util.concurrent.Executor接口: 与线程池相关的接口,所有线程池必须实现这个接口
        抽象方法:
             public abstract void execute(Runnable command):
				执行方法参数指定的Runnable接口类型的任务

    Executor接口规定的功能比较少,使用常用子接口:
        java.util.concurrent.ExecutorService接口: 规定了线程池的很多功能

    要使用ExecutorService接口中规定的方法,必然要获取到ExecutorService接口的实现类对象
    实现类:
        java.util.concurrent.ThreadPoolExecutor类:
        可以创建对象,发现构造方法参数太多,不方便

        解决方案:
            使用工具类java.util.concurrent.Executors调用静态方法创建线程池对象
            静态方法:
                public static ExecutorService newFixedThreadPool(int nThreads)
                    创建一个可重用固定线程数的线程池
                    返回值:
                        ExecutorService接口: 方法内部必然返回实现类对象

3.3 JDK 中内置的线程池介绍及 submit 方法演示 ​

java
通过 Executors 静态方法
static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池,

有了线程池,执行任务
java
package com.itheima.threadpool;

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            System.out.println("该 "+Thread.currentThread().getName()+" 线程正在执行我的线程任务"+i);
        }
    }
}


public class Demo {

    public static void main(String[] args) {
        // 通过 jdk提供的静态方法 完成一个线程池的创建
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        //创建一个 有两个线程的线程池对象
        MyRunnable m1 = new MyRunnable();
        MyRunnable m2 = new MyRunnable();
        MyRunnable m3 = new MyRunnable();

        //线程池 帮我们 处理任务的  其中线程对象由线程池自动匹配
        // 只需要我们进行任务提交
        threadPool.submit(m1);//线程1 处理m1
        threadPool.submit(m2);//线程2 处理m2
                        //线程1  线程2 被占用 m3 等待
                          //当线程1  或者线程2 执行完上一个任务后
        threadPool.submit(m3);//随机给 m3任务执行

//       threadPool.shutdown();
    }
}

3.4 获取线程执行的结果 ​

java
执行线程任务:
ExecutorService接口中规定的方法
    public abstract <T> Future<T> submit(Callable<T> task) :
        执行方法参数指定的Callable类型的线程任务
        参数:
            java.util.concurrent.Callable<V>接口: 调用方法必然传递实现类对象
            抽象方法:
                public abstract V call() throws Exception: 指定线程任务的
                    特点:
                        1.有返回值
                        2.方法上有声明抛出异常,重写后的方法,如果有异常,
                        	可以throws,可以try-catch
                        3.Runnable接口中run方法: 没有返回值,没有声明抛出异常
                            重写后的方法,如果有异常,只能内部try-catch
        返回值:
            java.util.concurrent.Future<V>接口: 方法内部必然返回该接口的实现类对象
            作用:
                封装结果数据的,因为Callable接口中的call方法有返回值,
				Future实现类对象内部封装call方法的返回值
                获取结果数据:
                    Future<V>接口定义方法:
                    public abstract V get(): 获取Future对象中封装的结果数据的
java
package com.itheima.threadpool;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {
    /**
     * 线程任务方嘎
     *     与  runnable  不同的是 这个call方法是 带有返回值
     * @return
     * @throws Exception
     */
    @Override
    public String call() throws Exception {
        for (int i = 0; i <10 ; i++) {
            System.out.println("当前执行的线程是:"+Thread.currentThread().getName()+"第"+i+"次");
        }
        return "执行成功";
    }
}
package com.itheima.threadpool;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Demo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //线程池 里面有很多线程  线程是用来执行 线程任务的
       // 创建一个线程池  里面包含 两个线程
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        //写两个任务
//        MyRunnable mr1 = new MyRunnable();
//        MyRunnable mr2 = new MyRunnable();
//        MyRunnable mr3 = new MyRunnable();
//        executorService.submit(mr1);//提交了两个任务
//        executorService.submit(mr2);
//        executorService.submit(mr3);

        // 使用Callable的任务
        MyCallable myCallable = new MyCallable();

        Future<String> future = executorService.submit(myCallable);

        System.out.println("这次任务执行情况怎么样:"+future.get());

        executorService.shutdown();
    }
}

3.5 线程池的练习计算 1-100 的和 ​

java
/*
    线程池的练习计算1-100的和
    实现步骤:
        1.创建代表线程任务的Callable接口的实现类
        2.Callable接口的实现类覆盖重写抽象方法call,计算1到100的数字之和
        3.创建线程池对象,指定线程数量
        4.创建多个线程任务对象
        5.线程池对象调用submit方法执行线程任务对象获取结果Future对象
        6.打印结果Future对象中封装的具体结果
 */
public class Demo07ThreadPoolSum {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //3.创建线程池对象,指定线程数量
        ExecutorService pool = Executors.newFixedThreadPool(3);
        //4.创建多个线程任务对象
        MySumTask task100 = new MySumTask(1, 100);//计算1-100数字之和的线程任务
        MySumTask task200 = new MySumTask(1, 200);//计算1-200数字之和的线程任务
        MySumTask task300 = new MySumTask(1, 300);//计算1-300数字之和的线程任务
        //5.线程池对象调用submit方法执行线程任务对象获取结果Future对象
        Future<Integer> f = pool.submit(task100);
        //6.打印结果Future对象中封装的具体结果
        System.out.println(f.get());

        f = pool.submit(task200);
        //6.打印结果Future对象中封装的具体结果
        System.out.println(f.get());

        f = pool.submit(task300);
        //6.打印结果Future对象中封装的具体结果
        System.out.println(f.get());
    }
}


/*
    计算1-100的数字之和的线程任务
 */
public class MySumTask implements Callable<Integer> {
    private int start;
    private int end;
    //满参构造方法:指定求和范围
    public MySumTask(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        //计算指定范围内的所有数字之后
        //要求start必须<=end,但是这里代码上就不做判断
        for (int i = start; i <= end; i++) {
            sum += i;
        }
        return sum;
    }
}

第四章 File 类 ​

4.1 File 类的概述 ​

java
我们java程序运行在内存中,但是我们大量的数据其实不在内存,在硬盘上。而硬盘上数据都是以文件或者文件夹的形式出现的。
我们学习java其实也想要跟计算机硬盘上的数据进行交流。由于我们java是面向对象思想,
于是就把 计算机中文件/文件夹 用一个类来表示了 这个类就是java.io.File类。

4.2 File 类的构造方法 ​

java
File类的构造方法
	File(String pathname)
    	参数:String类型的路径
        	可以表示文件或者文件夹
        	所表示的文件或文件夹可以存在,也可以不存在

	File(String parent, String child)
    	参数:
        	parent:String类型的父路径
        	child:String类型的子路径
        	可以表示文件或者文件夹,子路径可以是文件或者文件夹
        	所表示的文件或文件夹可以存在也可以不存在
	File(File parent, String child)
    	参数:
        	parent:File类型的父路径
        	child:String类型的子路径
java
package com.itheima.file;

import java.io.File;

public class FileDemo01 {
    /*
     在java中使用File的对象来表示我们系统的文件或者文件夹

         E:\code\test\b.txt   实现使用一个File对象表示我们的 文件
         学习 构造
              File(String pathname)
                  //参数  pathname  文件或者文件夹在系统中的路径

             File(String parent,String child)
                //   父路径 + 子路径 确定一个文件位置

            File(File parent,String child)
              //  file形式的父路径 + 字符串形式的子路径  确定文件的位置
     */
    public static void main(String[] args) {
        // 根据  E:\code\test\b.txt 封装一个File对象
        // 第一种构造方式
        File file = new File("E:\\code\\test\\b.txt");
        System.out.println(file);

        //第二种构造方式  子路径父路径拼接一起
        File file2 = new File("E:\\code","test\\b.txt");
        System.out.println(file2);

        //目录能不能用file表示 E:\code\test
        File dir = new File("E:\\code\\test");
        System.out.println("目录是:"+dir);

        //第三种构造
        File file3 = new File(dir,"b.txt");
        System.out.println(file3);


    }
}

4.3 File 类的判断方法 ​

1641541060931

java
package com.itheima.file;

import java.io.File;

public class FileDemo02 {

    /*
    判断方法
      打假
         测试此路径表示file 是不是真实存在的
          boolean exists()

          //有没有方法判断是文件还是文件夹?
          boolean   isFile() 判断该file对象是不是文件
          boolean   isDirectory() 判断该file对象是不是文件夹

          不存在的文件或者文件夹 啥也不是
              调用结果是false
     */
    public static void main(String[] args) {
        File file1 = new File("E:\\code\\test\\b.txt");

        File file2 = new File("E:\\code\\test\\c.txt");

        System.out.println("file1存在吗?"+file1.exists());
        System.out.println("file2存在吗?"+file2.exists());

        File dir1 = new File("E:\\code\\test");
        File dir2 = new File("E:\\code\\hehetest");
        System.out.println("dir1存在吗?"+dir1.exists());
        System.out.println("dir2存在吗?"+dir2.exists());

        //判断  file1 是文件还是文件夹
        System.out.println("file1是文件吗?"+file1.isFile());
        System.out.println("file1是文件夹(目录)吗?"+file1.isDirectory());

        System.out.println("dir1是文件吗?"+dir1.isFile());
        System.out.println("dir1是文件夹(目录)吗?"+dir1.isDirectory());
    }

}

4.4 File 类的获取方法 ​

java
File类的获取方法
	public String getAbsolutePath() :返回此File的绝对路径名字符串。
	public String getPath() :将此File转换为路径名字符串(构造方法路径)。
	public String getName() :返回由此File表示的文件或目录的名称。
    	路径名中最后一个分隔符后面的内容
	public long length() :返回由此File表示的文件的长度。
    	文件中的字节数
        	文件为空: 返回0
        	文件不存在: 返回0
java
package com.itheima.file;

import java.io.File;
import java.util.Date;

public class FileDemo03 {
    /*
      File类的获取方法
          String getPath() 将File对象转换为字符串形式 (获取的是 封装路径)
          String  getAbsolutePath() 获取File对象的绝对路径(带盘符的路径)
          String getName() 获取文件或文件夹(目录)的名字

          length();文件的长度  文件的字节数
              没有0  有 具体是啥就是啥
        long lastModified()   获取最新修改文件的毫秒值
     */
    public static void main(String[] args) {
        //封装好了一个File对象
        File file1 = new File("E:\\code\\test\\b.txt");

        System.out.println("获取封装路径:"+file1.getPath());
        System.out.println("获取绝对路径:"+file1.getAbsolutePath());
        System.out.println("获取文件的名字:"+file1.getName());
        System.out.println("获取文件里面有多少个字节:"+file1.length());
        System.out.println("获取文件最新修改的毫秒值:"+new Date(file1.lastModified()));
    }
}

4.5 相对路径 绝对路径 ​

java
  File file = new File("E:\\code\\385\\day10\\a.txt");
  绝对路径: 带盘符的路径。
  相对路径:,是相对于 idea中的 project的路径!
java
package com.itheima.file;

import java.io.File;

public class FileDemo04 {

    public static void main(String[] args) {
        //问题 :能否完成 将 day10 模块下 a.txt文件封装
        File file1 = new File("E:\\code\\385\\day10\\a.txt");
                   //封装的路径是 盘符开头的  这种封装方式采用的 绝对路径方式(带盘符的形式)
        System.out.println("file1是不是真实文件:"+file1.isFile());
        System.out.println("获取封装路径:"+file1.getPath());
        System.out.println("获取绝对路径:"+file1.getAbsolutePath());
        // 相对路径   相对 project而言 左上角  E:\code\385
        //  相对于 project而言 我们的路径是  day10\a.txt
        //使用相对路径封装 a.txt
        File file2  = new File("day10\\a.txt");
        System.out.println("file2是不是真实文件:"+file2.isFile());
        System.out.println("获取封装路径:"+file2.getPath());
        System.out.println("获取绝对路径:"+file2.getAbsolutePath());
       /*
         当操作的文件是 本模块中的文件的 时候 采用相对路径
         当操作的文件是 非本模块中文件的时候  采用绝对路径
        */
    }
}

4.6 File 类的创建和删除方法 ​

java
创建删除功能的方法
	public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
    	文件已经存在: 不创建     返回 false
    	文件不存在:
        	创建成功: true
        	创建失败: false

	public boolean mkdir() :创建由此File表示的目录。make
    	文件夹已经存在: 不创建     返回 false
    	文件夹不存在:
        	创建成功: true
        	创建失败: false
    	只能创建一级文件夹

	public boolean mkdirs() :创建由此File表示的目录,包括任何必需但不存在的父目录。
    	文件夹已经存在: 不创建     返回 false
    	文件夹不存在:
        	创建成功: true
        	创建失败: false
    	可以创建多级文件夹(包含一级)


	public boolean delete() :删除由此File表示的文件或目录,java中删除动作不走回收站。
    	可以删除文件:
        	存在:
            	成功: true
            	失败: false
        	不存在: false

    	可以删除文件夹:
        	不存在: false
        	文件夹为空:
             	成功: true
             	失败: false
        	文件夹不为空: false

	注意:
    	以下在演示后面代码的过程中,避免前面代码的影响
java
public class Demo04FileCreateDelete {
    public static void main(String[] args) throws IOException {
        File f1 = new File("day10\\io\\a.txt");
        System.out.println("文件是否存在? "+f1.exists());
        System.out.println("文件是否创建成功? "+f1.createNewFile());
        System.out.println("文件是否存在? "+f1.exists());
        System.out.println("------------------------");

        //创建一级文件夹
        File dir1 = new File("day10\\io\\abc");
        System.out.println("文件夹是否存在? "+dir1.exists());
        System.out.println("文件夹是否创建成功? "+dir1.mkdir());
        System.out.println("文件夹是否存在? "+dir1.exists());

        System.out.println("------------------------");

        //创建多级文件夹(因为mkdir只能创建一级文件夹)
        File dir2 = new File("day10\\io\\abcd\\ab\\cd");
        System.out.println("文件夹是否存在? "+dir2.exists());
        System.out.println("文件夹是否创建成功? "+dir2.mkdir());
        System.out.println("文件夹是否存在? "+dir2.exists());
        System.out.println("------------------------");

        //创建多级文件夹(因为mkdirs可以创建多级文件夹)
        File dir3 = new File("day10\\io\\12\\1\\2");
        System.out.println("文件夹是否存在? "+dir3.exists());
        System.out.println("文件夹是否创建成功? "+dir3.mkdirs());
        System.out.println("文件夹是否存在? "+dir3.exists());
        System.out.println("------------------------");

        File f3 = new File("day10\\io\\a.txt");
        System.out.println("文件是否存在? "+f3.exists());
        System.out.println("文件是否删除成功?"+f3.delete());
        System.out.println("文件是否存在? "+f3.exists());
        System.out.println("------------------------");

        //删除文件夹: 必须是空文件夹(里面啥都没有)
        File dir4 = new File("day10\\io\\12");
        System.out.println("文件夹是否存在? "+dir4.exists());
        System.out.println("文件夹是否删除成功? "+dir4.delete());
        System.out.println("文件夹是否存在? "+dir4.exists());
    }
}

4.7 File 类的遍历方法 ​

java
目录的遍历
    public String[] list() :获取当前目录下的所有的目录和文件的字符串的名字

    public File[] listFiles() :获取当前目录下的所有的目录和文件的File对象
java
package com.itheima.file;

import java.io.File;

/*
   文件夹的遍历

      String[] list() 获取文件夹下 的子目录和子文件的名字

      File[] listFiles() 获取文件夹下 的子目录和子文件的 file对象形式
 */
public class FileDemo06 {
    public static void main(String[] args) {
        //封装 day10\bbb
        File srcDir = new File("day10\\bbb");
        //获取文件夹下 的子目录和子文件的名字
        String[] list = srcDir.list();
        for (String s : list) {
            System.out.println(s);
        }
        System.out.println("=================================");
        //获取 文件夹下 子目录和子文件的 file对象形式
        File[] files = srcDir.listFiles();
        for (File file : files) {
            System.out.println(file.getAbsolutePath());
        }
    }
}

第五章 方法递归【重点】 ​

5.1 递归介绍及注意事项 ​

java
方法递归
	1:概念 就是自己调自己
	 直接递归
	   a(){
	     a()
	   }
	 间接递归
	   a(){
	      b()
	   }
	   b(){
	     c()
	   }
	   c(){
	     a()
	   }
	 2:死递归  永不休止自己调用自己。
       public static void main(String[] args) {
            method();
        }

        public static void method(){
            System.out.println("吊我呀");
            method();
        }
        Exception in thread "main" java.lang.StackOverflowError
        内存溢出错误~!
     3:正常的递归
           递归次数不宜过多,一定要有程序的出口

           因为递归调用是方法调用方法,上面的方法不结束,下面就出不来。
           没有出口 就一直被压着。越压越多

5.2 递归求和 ​

java
package com.itheima.digui;
/*
   1~n的和
        getSum(n-1)+n

  1~5的和
     1+2+3+4+5
  1~4
    1+2+3+4  getSum(3)+4
  1~3
   1+2+3  getSum(2)+3
  1~2
   1+2   getSum(1)+2
  1
   1
 */
public class QiuHe {
    public static void main(String[] args) {
        int n = 5;

        int sum = getSum(n);

        System.out.println(sum);
    }

    //设计一个方法  你传一个>0 的数字 我告诉你 相加的结果
    /**
     *  求和
     *     1~n的和
     *     三要素
     *         方法名  getSum
     *         参数 int n
     *         返回值  int 和
     */
    public static int getSum(int n){
//        int sum = 0;
//
//        for (int i = 1; i <=n ; i++) {
//            sum +=i;
//        }
        //递归操作
        if(n==1){//出口
            return 1;
        }

       return getSum(n-1)+n;
    }
}

5.4 递归获取文件夹下所有文件 ​

java
package com.itheima.digui;

import java.io.File;

public class Test {

    public static void main(String[] args) {
       File dir = new File("day10\\bbb");
       printDir(dir);
    }
    /**
     *  设计一个方法 可以遍历 某个文件夹下  所有的文件 包含子目录中的文件  并把文件的绝对路径打印出来
     *  三要素
     *      printDir
     *      返回值类型 void
     *      参数  文件夹对象
     */
    public static void printDir(File dir){//被传入的文件夹
        //获取 当前dir下所有的文件和文件夹
        File[] files = dir.listFiles();
        // 进行遍历  拿到每个文件和文件夹 对象file
        for (File file : files) {
            //file是文件还是文件夹 都有
            //做判断
            if(file.isFile()){//是文件
                System.out.println("文件的路径是:"+file.getAbsolutePath());
            }else{
                //file是一个文件夹  怎么办? 继续找
                printDir(file);
            }
        }
    }
}

Released under the MIT License.