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
多个线程在处理同一资源的时候,任务不同,这个时候就需要线程间的通信来完成了。
线程间存在通信,也就是多个线程在操作同一份数据的时候,会争夺共享变量,但是他们的任务是不一样的。
我们得保证 线程间的通信是正常的正确的。
怎么保证 正确正常呢?
吃货 和 厨师加同样的锁,就可以保证线程间的通信正常执行。
吃货 是 消费者,厨师是 生产者。
我们的最终目的是 保证 线程间的通信,是可以有效的利用起来!!
如何保证 我们线程间的通信 有效的利用起来呢
我们推出了一个 等待唤醒的概念。
2.2 线程通信等待唤醒概念
java
等待唤醒 是解决 生产者与消费者 线程间通信问题的 一种机制。
用来保证,我们可以有效的利用资源。
今天只讲解 单消费者 单生产者问题。
线程等待和唤醒的方法定义在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
线程属于系统的宝贵资源,频繁的创建和销毁线程,会降低效率
所以需要使用线程池
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 类的判断方法
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);
}
}
}
}