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.