File递归练习
练习1:统计该文件夹大小
-
从键盘接受一个文件夹路径
- 创建键盘录入对象
- 定义一个无限循环
- 将键盘录入的结果存储并封装成File对象
- 对File对象判断
- 路径是否存在
exists()
- 录入的是文件路径
isFile()
- 路径是否存在
- 将文件夹路径对象返回
-
统计该文件夹大小
- 获取该文件夹下所有的文件和文件夹
listFiles()
- 遍历数组
- 判断
- 文件累加
- 文件夹递归
public static void main(String[] args) { //File dir = new File("F:\\day06"); //System.out.println(dir.length()); //直接获取文件夹的结果是0 File dir = getDir(); System.out.println(getFileLength(dir)); } public static File getDir() { Scanner sc = new Scanner(System.in); System.out.println("请输入一个文件夹路径:"); while(true) { String line = sc.nextLine(); File dir = new File(line); if(!dir.exists()) { System.out.println("您录入的文件夹路径不存在,请输入一个文件夹路径:"); }else if(dir.isFile()) { System.out.println("您录入的是文件路径,请输入一个文件夹路径:"); }else { return dir; } } } public static long getFileLength(File dir) { //dir = F:\day06\day07 long len = 0; File[] subFiles = dir.listFiles(); for (File subFile : subFiles) { if(subFile.isFile()) { len = len + subFile.length(); }else { len = len + getFileLength(subFile); } } return len; }
- 获取该文件夹下所有的文件和文件夹
练习2:删除该文件
-
获取该文件夹下所有的文件和文件夹
-
遍历数组
- 文件删除
- 文件夹递归调用
- 循环结束后把空文件删除
public static void main(String[] args) { File dir = Test1.getDir(); deleteFile(dir); } public static void deleteFile(File dir) { File[] subFiles = dir.listFiles(); for (File subFile : subFiles) { if(subFile.isFile()) { subFile.delete(); }else { deleteFile(subFile); } } dir.delete(); }
练习3:拷贝
- 在目标文件夹中创建原文件夹
- 获取原文件夹中所有的文件和文件夹并存储
- 遍历数组
- 如果是文件就用io流读写
- 文件夹递归
public static void main(String[] args) throws IOException { File src = Test1.getDir(); File dest = Test1.getDir(); if(src.equals(dest)) { //不能在原文件下拷贝原文件 System.out.println("目标文件夹是源文件夹的子文件夹"); }else { copy(src,dest); } } /* * 把其中一个文件夹中(包含内容)拷贝到另一个文件夹中 * 1,返回值类型void * 2,参数列表File src,File dest */ public static void copy(File src, File dest) throws IOException { File newDir = new File(dest, src.getName()); newDir.mkdir(); File[] subFiles = src.listFiles(); for (File subFile : subFiles) { if(subFile.isFile()) { BufferedInputStream bis = new BufferedInputStream(new FileInputStream(subFile)); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(newDir,subFile.getName()))); int b; while((b = bis.read()) != -1) { bos.write(b); } bis.close(); bos.close(); }else { copy(subFile,newDir); } } }
练习4:求1000的阶乘中所有的0
-
非递归方法:
public static void main(String[] args) { /*int result = 1; for(int i = 1; i <= 1000; i++) { result = result * i; } System.out.println(result); //因为1000的阶乘远远超出了int的取值范围 */ //demo1(); demo2(); } public static void demo2() { //获取1000的阶乘尾部有多少个零 BigInteger bi1 = new BigInteger("1"); for(int i = 1; i <= 1000; i++) { BigInteger bi2 = new BigInteger(i+""); bi1 = bi1.multiply(bi2); //将bi1与bi2相乘的结果赋值给bi1 } String str = bi1.toString(); //获取字符串表现形式 StringBuilder sb = new StringBuilder(str); str = sb.reverse().toString(); //链式编程 int count = 0; //定义计数器 for(int i = 0; i < str.length(); i++) { if('0' != str.charAt(i)) { break; }else { count++; } } System.out.println(count); } public static void demo1() { //求1000的阶乘中所有的零 BigInteger bi1 = new BigInteger("1"); for(int i = 1; i <= 1000; i++) { BigInteger bi2 = new BigInteger(i+""); bi1 = bi1.multiply(bi2); //将bi1与bi2相乘的结果赋值给bi1 } String str = bi1.toString(); //获取字符串表现形式 int count = 0; for(int i = 0; i < str.length(); i++) { if('0' == str.charAt(i)) { //如果字符串中出现了0字符 count++; //计数器加1 } } System.out.println(count); }
-
递归
- 规律:
5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100…1000 1000 / 5 = 200
5 * 5 5 * 5 * 2 5 * 5 * 3 5 * 5 * 4 5 * 5 * 5 5 * 5 * 6 200 / 5 = 40
5 * 5 * 5 * 1 5 * 5 * 5 * 2 5 * 5 * 5 * 3 5 * 5 * 5 * 4 5 * 5 * 5 * 5 5 * 5 * 5 * 6 5 * 5 * 5 * 7 5 * 5 * 5 * 8
40 / 5 = 8
5 * 5 * 5 * 5 8 / 5 = 1
public static void main(String[] args) { System.out.println(fun(1000)); } public static int fun(int num) { if(num > 0 && num < 5) { return 0; }else { return num / 5 + fun(num / 5); } }
- 规律:
练习5:约瑟夫环
public static void main(String[] args) {
System.out.println(getLucklyNum(8));
}
/*
* 获取幸运数字
* 1,返回值类型int
* 2,参数列表int num
*/
public static int getLucklyNum(int num) {
ArrayList<Integer> list = new ArrayList<>(); //创建集合存储1到num的对象
for(int i = 1; i <= num; i++) {
list.add(i); //将1到num存储在集合中
}
int count = 1; //用来数数的,只要是3的倍数就杀人
for(int i = 0; list.size() != 1; i++) { //只要集合中人数超过1,就要不断的杀
if(i == list.size()) { //如果i增长到集合最大的索引+1时
i = 0; //重新归零
}
if(count % 3 == 0) { //如果是3的倍数
list.remove(i--); //就杀人
}
count++;
}
return list.get(0);
}
Thread
-
定义
- 线程是程序执行的一条路径, 一个进程中可以包含多条线程
- 多线程并发执行可以提高程序的效率, 可以同时完成多项工作
-
并行和并发
- 并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
- 并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。
-
实现方法
-
继承Thread
- 定义类继承Thread
- 重写run方法
- 把新线程要做的事写在run方法中
- 创建线程对象
- 开启新线程, 内部会自动执行run方法
public static void main(String[] args) { MyThread mt = new MyThread(); //4,创建自定义类的对象 mt.start(); //5,开启线程 for(int i = 0; i < 3000; i++) { System.out.println("bb"); } } } class MyThread extends Thread { //1,定义类继承Thread public void run() { //2,重写run方法 for(int i = 0; i < 3000; i++) { //3,将要执行的代码,写在run方法中 System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa"); } } }
-
实现Runnable
- 定义类实现Runnable接口
- 实现run方法
- 把新线程要做的事写在run方法中
- 创建自定义的Runnable的子类对象
- 创建Thread对象, 传入Runnable
- 调用start()开启新线程, 内部会自动调用Runnable的run()方法
public static void main(String[] args) { MyRunnable mr = new MyRunnable(); //4,创建自定义类对象 //Runnable target = new MyRunnable(); Thread t = new Thread(mr); //5,将其当作参数传递给Thread的构造函数 t.start(); //6,开启线程 for(int i = 0; i < 3000; i++) { System.out.println("bb"); } } } class MyRunnable implements Runnable { //1,自定义类实现Runnable接口 @Override public void run() { //2,重写run方法 for(int i = 0; i < 3000; i++) { //3,将要执行的代码,写在run方法中 System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa"); } } }
-
-
两种方式的区别
-
查看源码的区别:
- a.继承Thread : 由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法
- b.实现Runnable : 构造函数中传入了Runnable的引用, 成员变量记住了它, start()调用run()方法时内部判断成员变量Runnable的引用是否为空, 不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法
-
继承Thread
- 好处是:可以直接使用Thread类中的方法,代码简单
- 弊端是:如果已经有了父类,就不能用这种方法
-
实现Runnable接口
- 好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
- 弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂
-
-
匿名内部类实现
-
继承Thread类
new Thread() { //1,new 类(){}继承这个类 public void run() { //2,重写run方法 for(int i = 0; i < 3000; i++) { //3,将要执行的代码,写在run方法中 System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa"); } } }.start();
-
实现Runnable接口
new Thread(new Runnable(){ //1,new 接口(){}实现这个接口 public void run() { //2,重写run方法 for(int i = 0; i < 3000; i++) { //3,将要执行的代码,写在run方法中 System.out.println("bb"); } } }).start();
-
ThreadMethod
-
Thread.currentThread()
-
getName()
-
设置名字
- 通过构造函数直接设置
- setName()
-
sleep()
- sleep(毫秒,纳秒), 控制当前线程休眠若干毫秒1秒= 1000毫秒 1秒 = 1000 * 1000 * 1000纳秒 多设置毫秒
-
setDaemon()
-
设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出
Thread t1 = new Thread() { public void run() { for(int i = 0; i < 50; i++) { System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } }; Thread t2 = new Thread() { public void run() { for(int i = 0; i < 5; i++) { System.out.println(getName() + "...bb"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } }; t1.setDaemon(true); //将t1设置为守护线程 t1.start(); t2.start();
-
-
加入线程
- join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
- join(int), 可以等待指定的毫秒之后继续
final Thread t1 = new Thread() { public void run() { for(int i = 0; i < 50; i++) { System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } }; Thread t2 = new Thread() { public void run() { for(int i = 0; i < 50; i++) { if(i == 2) { try { //t1.join(); //插队,加入 t1.join(30); //加入,有固定的时间,过了固定时间,继续交替执行 Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(getName() + "...bb"); } } }; t1.start(); t2.start();
-
礼让线程
- yield让出cpu
-
设置线程的优先级
- setPriority()设置线程的优先级
- yield和setPriority()效果不明显仅做了解
同步代码块
- 作用点
- 当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.
- 如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码.
- synchronized
-
同步代码块需要使用同一个锁对象
- 锁对象没有限制任意设置
class Printer { Demo d = new Demo(); public static void print1() { synchronized(d){ //锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象 System.out.print("黑"); System.out.print("马"); System.out.print("程"); System.out.print("序"); System.out.print("员"); System.out.print("\r\n"); } } public static void print2() { synchronized(d){ System.out.print("传"); System.out.print("智"); System.out.print("播"); System.out.print("客"); System.out.print("\r\n"); } } }
-
同步方法
-
使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的
class Printer { public static void print1() { synchronized(Printer.class){ //锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象 System.out.print("黑"); System.out.print("马"); System.out.print("程"); System.out.print("序"); System.out.print("员"); System.out.print("\r\n"); } } /* * 非静态同步函数的锁是:this * 静态的同步函数的锁是:字节码对象 */ public static synchronized void print2() { System.out.print("传"); System.out.print("智"); System.out.print("播"); System.out.print("客"); System.out.print("\r\n"); } }
线程安全问题
-
多线程并发操作同一数据时, 就有可能出现线程安全问题
-
使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作
-
继承thread:
public class Demo2_Synchronized { /** * @param args * 需求:铁路售票,一共100张,通过四个窗口卖完. */ public static void main(String[] args) { TicketsSeller t1 = new TicketsSeller(); TicketsSeller t2 = new TicketsSeller(); TicketsSeller t3 = new TicketsSeller(); TicketsSeller t4 = new TicketsSeller(); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t4.setName("窗口4"); t1.start(); t2.start(); t3.start(); t4.start(); } } class TicketsSeller extends Thread { private static int tickets = 100; static Object obj = new Object(); public TicketsSeller() { super(); } public TicketsSeller(String name) { super(name); } public void run() { while(true) { synchronized(obj) { if(tickets <= 0) break; try { Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName() + "...这是第" + tickets-- + "号票"); } } } }
-
Runnnable:
/** * @param args * 火车站卖票的例子用实现Runnable接口 */ public static void main(String[] args) { MyTicket mt = new MyTicket(); new Thread(mt).start(); new Thread(mt).start(); new Thread(mt).start(); new Thread(mt).start(); /*Thread t1 = new Thread(mt); //多次启动一个线程是非法的 t1.start(); t1.start(); t1.start(); t1.start();*/ } class MyTicket implements Runnable { private int tickets = 100; @Override public void run() { while(true) { synchronized(this) { if(tickets <= 0) { break; } try { Thread.sleep(10); //线程1睡,线程2睡,线程3睡,线程4睡 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "...这是第" + tickets-- + "号票"); } } } }
死锁
- 多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁
-
尽量不要嵌套使用
private static String s1 = "筷子左"; private static String s2 = "筷子右"; public static void main(String[] args) { new Thread() { public void run() { while(true) { synchronized(s1) { System.out.println(getName() + "...拿到" + s1 + "等待" + s2); synchronized(s2) { System.out.println(getName() + "...拿到" + s2 + "开吃"); } } } } }.start(); new Thread() { public void run() { while(true) { synchronized(s2) { System.out.println(getName() + "...拿到" + s2 + "等待" + s1); synchronized(s1) { System.out.println(getName() + "...拿到" + s1 + "开吃"); } } } } }.start(); }
-
以前的线程安全类回顾
- 看源码:Vector,StringBuffer,Hashtable
- Vector是线程安全的,ArrayList是线程不安全的
- StringBuffer是线程安全的,StringBuilder是线程不安全的
- Hashtable是线程安全的,HashMap是线程不安全的
- Collections.synchroinzed(xxx)
- 将类设置成线程安全的