10-JavaSE【Lambda,Stream流,File类,递归】

回顾多线程

  • 线程安全

    • 影响线程安全的原因

      • //明确1:单个线程不存在线程安全问题
        //明确2:多个线程在对同一个资源进行读操作时,也不存在线程安全问题
        多个线程在对同一个资源进行写操作时,造成数据的不同步(错乱数据),此时会出现线程安全问题
        
    • 解决线程安全

      • 同步代码块

        • 语法:

          • //对象锁:可以任意类型的对象
            synchronized(对象锁){
                //对共享资源进行操作的代码
            }
            
      • 同步方法

        • 语法:

          • //非静态的方法
            public synchronized 返回值类型 方法名(参数列表){  //锁:this
                //对共享资源进行操作的代码
            }
            
            //静态方法
            public synchronized static 返回值类型 方法名(参数列表){  //锁:类.class
                //对共享资源进行操作的代码
            }
            
      • Lock锁

        • 完成可以代替同步代码块和同步方法

        • 在开发中Lock锁比同步代码块、同步方法更灵活(好用) 【 Condition 】

        • 使用:

          • //创建Lock锁对象
            Lock l = new ReenTrantLock();
            
            void run(){
                ....
                l.lock(); //获取锁
                try{
                    //对共享资源进行操作的代码  
                }finally{
                    l.unlock();//释放锁
                }
                ,...  
            }
            
  • 死锁

    • 产生死锁的情况:
      • 多个线程中使用了嵌套的同步代码块,且在同步代码块中进行对象锁的循环交替,此时会有概率出现死锁
    • 避免死锁:
      • 代码中不要书写嵌套同步代码块
  • 线程状态

    • 6种:新建、可运行、锁阻塞、无限等待、计时等待、终止
  • 线程通讯

    • 概念:A线程去唤醒B线程
    • 线程通讯要使用的方法:
      • wait()、wait(long time)
        • wait():无限等待
        • wait(long) : 计时等待
      • notify()、notifyAll()
        • notify(): 唤醒同一个对象锁下的处于等待状态的任意一个线程
        • notifyAll():唤醒同一个对象锁下的处于等待状态的所有线程
  • 线程池

    • 概念:在一个容器中,预先创建了一些Thread,供程序中使用

    • 原理:在线程池中创建一些Thread对象,把线程任务提交给线程池,由线程池分配Thread来执行任务,当任务执行结束之后,Thread对象会回归到线程池中(Thread可以反复使用)

    • 使用:

      • //1、创建线程池
        ExecutorService es = Executors.newFixedThreadPool(线程数量);
        //2、定义线程任务
        Task task = new Task();//Task类实现Runnable接口 或 实现Callable接口
        //3、提交任务给线程池
        Future f = es.submit(task);
        
        //4、对于 Callable接口 有返回值需要接收任务返回的结果
        Object result = f.get();
        

一、Lambda表达式

1、 Lambda的介绍

Lambda表达式是JAVA8中提供的一种新的特性,是一个匿名函数(方法)。可以把 Lambda表达式 理解为是一段可以传递的代码 (将代码像数据一样进行传递),可以写出更简洁、更灵活的代码。

2 、Lambda表达式使用前提条件

Lambda的语法非常简洁,完全没有面向对象复杂的束缚。但是使用时有几个问题需要特别注意:

  1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。无论是JDK内置的RunnableComparator接口还是自定义的接口,只有当接口中的**抽象方法存在且唯一时,**才可以使用Lambda。
  2. 使用Lambda必须具有上下文推断。也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

备注:有且仅有一个抽象方法的接口,称为“函数式接口”。

Lambda表达式可以认为是对匿名内部类的一种简化,但不是所有的匿名内部类都可以简化为Lambda表达式。只有函数式接口的匿名内部类才可以使用Lambda表达式来进行简化。函数式接口是一种比较特殊的接口,接口中只有一个抽象方法是需要我们去实现的。而Lambda表达式体现的就是这个抽象方法的实现。

// 使用Lambda表达式有什么条件?
Lambda表达式的定义需要有函数式接口

// 什么是函数式接口?
只有一个抽象方法需要实现的接口我们称为函数式接口

// Lambda有什么作用?
可以用来简化函数式接口的匿名内部类

代码比较

// 匿名内部类方式创建线程
Thread t1 = new Thread(new Runnable() {  
@Override
public void run() {
System.out.println("匿名内部类方式——启动线程");
	}
});

t1.start();
Lambda表达式去创建线程
    
------------------------------------------------------------   
    
// Lambda表达式去实现
Thread t2 = new Thread(()> System.out.println("Lambda方式创建线程"));
t2.start();
3、Lambda表达式语法格式

Lambd表达式就是对函数式接口中抽象方法的实现,

是对其匿名内部类的一个简写**,只保留了方法的参数列表和方法体**,其他的成分可以省略。

因此Lambda的格式非常简洁,只有三部分组成:

1. 参数列表

2. 箭头 ->

3. 方法体

(参数列表)->{方法体}

4、Lambda的省略格式

1 省略规则说明

整个Lambda格式只有三个部分组成,参数列表,箭头,方法体。

  1. 箭头不能省(格式的组成)

  2. 能省只有两个:参数类别,方法体

参数列表的省略规则

  1. 如果参数列表有参数,参数类型可以省略一省全省。

  2. 如果参数列表只有一个参数,类型和小括号都可以一起省略,如果括号省略,类型一定要省略

方法体的省略规则

如果方法体中只存在一条语句,可以将方法体的大括号,当前语句的分号,如果有返回值对应的return 都可以省略,一省全省。

5、含有参数和返回值的Lambda表达式介绍

我们之前学习过java.util.Comparator接口,就是一个函数式接口,我们需要实现里面的compare抽象方法,就是有参数和返回值的如下:public abstract int compare(T o1, T o2) ; Lambda的格式只有三部分:参数列表,箭头,方法体。返回值类型的表示不管用什么都不写,如果有返回值,只要正常在方法体返回对应数据就可,参数列表保持一致,因此没有有没有返回值,参数列表,变化不大。

代码演示:

// 匿名内部类排序
Collections.sort(list, new Comparator<Person>() {
    @Override
    public int compare(Person p1, Person p2) {
        return p1.getAge() - p2.getAge();
    }
});

---------------------------------------------------------
    
//Lambda省略模式
Collections.sort(list, (p1, p2) -> p2.getAge() - p1.getAge() );
6、函数式接口

只有一个抽象方法需要实现的接口,函数式接口

函数式接口是允许有其他的非抽象方法的存在例如静态方法,默认方法,私有方法。

为了标识接口是一个函数式接口,可以在接口之上加上一个注解: @FunctionalInterface

JDK的 java.util.function 包中的所有接口都是函数式接口。之前学习线程时的Runnable也是函数式接口。

@FunctionalInterface

public interface Runnable {

	public abstract void run();

}

// 我们自己也可以定义函数式接口,比如定义一个游泳的函数式接口:

@FunctionalInterface
    
public interface Swim {

	public abstract void swimming();

}

二、Stream流

1、 Stream流的介绍

Stream流是Java1.8才出现的。处理极大的简化了对于集合、数组等的操作,只要是可以转换成流,

我们都可以借助流式处理。

一个流式操作可以分为三个部分:获取流、中间操作、终端操作。

我们先去将要使用流处理的数组或者集合转换为流,然后再进行中间方法的调用,最后使用终端方法

2、Stream流对象获取

**1)Collection获取流 ** *

自从JDK8开始,Collection存在一个默认方法stream去获取流对象。

所有Collection的实现类都具有该方法,能够直接使用集合对象调用该方法。

default Stream<E> stream() // 返回以此集合作为源的顺序 Stream 。
    
Stream s = 集合.stream();

2) Map集合获取流

Map中没有专属的获取流的默认方法。可以先将Map集合转换为Collection集合,再转换Stream就可。如果要使用Stream处理键值对对象数据,可以借助entrySet。如果要专门处理值及,可以借助values方法。

//获取键值对对象对应的流
Stream<Map.Entry<String, String>> s1 = map.entrySet().stream();
//获取键对应的流
Stream<String> s2 = map.keySet().stream();
//获取值对应的流
Stream<String> s3 = map.values().stream();

3)数组转换为流对象

Stream接口本身存在一个静态方法:of

static Stream< T > of (T… values) 可以指定该类型的数组或者单个的多个数据也可以转换为流

//数组
String[] arr = {"A", "B"};
Stream<String> s1 = Stream.of(arr);
//单个存在的多个数据
Stream<String> s2 = Stream.of("A", "B", "C");
3、Stream流常用方法

1 、流的终端操作

long count();						 // 统计流中数据的个数
void forEach(Consumer<? super T> action); // 遍历流中的数据

collect(Collectors.toList()); 		// 将流中数据收集到List集合中
collect(Collectors.toSet ()); 		// 将流中数据收集到Set集合中
Object[] toArray(); 				// 将流中数据收集到数组中
    
// forEach方法参数中的函数式接口
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
//省略其他
}

2 、流的中间操作方法:过滤,去重,排序

Stream<T> filter (Predicate<? super T> predicate); // 根据条件过滤不需要的数据
Stream<T> limit (long n); 			// 获取流中前n个数据,不足n个获取全部
Stream<T> skip (long n); 			// 跳过流中前n个数据,不足n个则没有数据
Stream<T> distinct ();       		// 去重(equals比较为true的元素会被去重)
Stream<T> sorted ();				// 自然排序
Stream<T> sorted (Comparator<? super T> comparator); // 自定义比较器排序

// filter方法参数类型
@FunctionalInterface
public interface Predicate<T> {
	boolean test(T t);
		//省略......
}

3 、流的中间操作方法:映射

// 将流中数据转换为另外一种想要的数据类型
<R> Stream<R> map(Function<? super T, ? extends R> mapper); 

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
	//省略...
}

4、 流的中间操作方法:拼接

// 将两个流合并为一个流
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
代码演示

映射与拼接

Stream<String> stream1 = one 
    .stream()
    .filter(name -> name.length() == 3)
    .limit(3);

Stream<String> stream2 = two
    .stream()
    .filter(name -> name.startsWith("张"))
    .skip(2);


Stream<String> newStream = Stream.concat(stream1, stream2);
List<Person> personList = newStream
    .map(name -> new Person(name))  // String --> 映射成 Object类型
    .collect(Collectors.toList());  // 将流中数据收集到List集合中

获取流 + 流的中间操作方法(过滤,去重,排序) + 流终端操作

public class StreamDemo {
    public static void main(String[] args) {
        //List集合
        List<Student> students = new ArrayList<>();

        students.add(new Student("小明", 18, 100));
 		.............
        .............    
		.............
            
        // 1. 筛选出所有及格的成员 并打印
        students
                .stream()
                .filter(student -> student.getScore() >= 60)
                .forEach(stu -> System.out.println(stu));

		// 2. 对学员信息进行去重 并打印
        students
                .stream()
                .distinct() 
             // distinct() 使用对象的equals方法比较两个对象是否相同 
            // 当比较对象内容时,需要:对象中重写equals方法
                .forEach(stu -> System.out.println(stu));

		// 3. 获取流中前3名学员 并打印
        students
                .stream()
                .limit(3)
                .forEach(stu-> System.out.println(stu));
       
		// 4. 对学生按照分数升序排序 并打印
        students
                .stream()
                .sorted((s1,s2) -> s1.getScore() - s2.getScore())
                .forEach(stu -> System.out.println(stu));

        // 5.
        students
                .stream()
                .distinct()
                .filter( stu -> stu.getScore()>=60 )
                .limit(3)  // 取前三个
                .sorted((s1,s2) -> s2.getScore() - s1.getScore())
                .forEach(stu -> System.out.println(stu));
    }
}

三、File文件类

1 、File类介绍

java.io.File 类是文件和目录路径名的抽象表示,

主要用于文件和目录的创建、查找和删除等操作。

 文件和目录都使用File类来表示

 文件和目录都使用路径来表示

例如:

code目录用路径表示 D:\abc\code

123.jpg文件用路径表示 D:\abc\123.jpg

**注意:**路径是唯一的,同一台计算机中不可能存在有两个不同的文件但路径又相同。

目录和文件名 是不区分大小写的

2、 File对象定义

在IO流学习中,会读取文件的信息或者写信息到文件,我们需要构建文件对象来定位计算机中的真实文件。File构造方法如下:

public File(String pathname)
    // 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。

public File(String parent, String child) 
    // 从父路径名字符串和子路径名字符串创建新的 File实例。

public File(File parent, String child) 
    // 从父抽象路径名和子路径名字符串创建新的 File实例

了解相对路径与绝对路径

 **绝对路径:**从盘符开始的路径,这是一个完整的路径。

 **相对路径:**相对于项目目录的路径,这是一个便捷的路径,开发中经常使用

3、File类常用功能
1) 获取的方法
public String getAbsolutePath() // 返回此File的绝对路径名字符串。

public String getPath() // 将此File转换为路径名字符串。 【构造路径】
    
public String getName() // 返回由此File表示的文件或目录的名称。 【如果有后缀,包含】

public long length() // 返回由此File表示的文件的长度(文件的大小,单位是字节)。 不能获取目录的长度。File类常用功能
2 )判断的方法
public boolean exists() // 此File表示的文件或目录是否实际存在。

public boolean isDirectory() // 此File表示的是否为目录。

public boolean isFile() // 此File表示的是否为文件。
3 )创建删除的方法
public boolean createNewFile() // 当且仅当具有该名称的文件尚不存在时,创建一个新的空件。

public boolean mkdir() // 创建由此File表示的目录。(单个目录)

public boolean mkdirs() // 创建由此File表示的目录,包括必需但不存在的父目录。(多级目录)

public boolean delete() // 删除由此File表示的文件或目录。
4 )目录遍历的方法
public String[] list() // 返回一个String数组,表示该File目录中的所有子文件或目录。

public File[] listFiles() // 返回一个File数组,表示该File目录中的所有的子文件或目录

四、递归

什么是递归?

递归就是方法自身调用自身的结果

递归通常是由三个部分组成,分别是什么?

递归前进段、边界条件、递归返回段。

当边界条件不满足时,递归前进

当边界条件满足时,递归返回

递归有什么隐患?

每次递归时方法都会入栈,从而占用栈内存资源,当递归次数太多,

栈内存用完时会造成内存溢出错误。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-whcfWK3l-1617099115506)(C:/Users/16096/Desktop/java127/javaSE127学习讲义/JavaSE16全天版本(v2.0)/JavaSE16天全版/day10【递归,字节流,字符流,IO异常处理,Properties】/imgs/day08_01_递归累和.jpg)]

tip:递归一定要有条件限定,保证递归能够停止下来,次数不要太多,否则会发生栈内存溢出。

(递归实现)n的阶乘:n! = n * (n-1) 3 * 2 * 1

public class DiGuiDemo {
    public static void main(String[] args) {
        int n = 3;
      	// 调用求阶乘的方法
        int value = getValue(n);
      	// 输出结果
        System.out.println("阶乘为:"+ value);
    }

    public static int getValue(int n) {       
      	// 1的阶乘为1  , 直到n == 1时,递归结束,开始回归返回值
        if (n == 1) {
            return 1;
        }

        return n * getValue(n - 1);  
    }
}

(递归实现)文件搜索

目的:输出d:\aaa目录中的所有.java文件的绝对路径。

分析

  1. 目录搜索,无法判断多少级目录,所以使用递归,遍历所有目录。
  2. 遍历目录时,获取的子文件,通过文件名称,判断是否符合条件。

代码实现

public class DiGuiDemo3 {
    public static void main(String[] args) {
        // 创建File对象
        File dir  = new File("D:\\aaa");
      	// 调用打印目录方法
        printDir(dir);
    }

    public static void printDir(File dir) {
      	// 获取子文件和目录
        File[] files = dir.listFiles();
      	
      	// 循环打印
        for (File file : files) {
            if (file.isFile()) {
              	// 是文件,判断文件名并输出文件绝对路径
                if (file.getName().endsWith(".java")) {
                    System.out.println("文件名:" + file.getAbsolutePath());
                }
            } else {
                // 是目录,继续遍历,形成递归
                printDir(file);
            }
        }
    }
}

总结

  • Lambda表达式

    • 应用点:代码书写的简化

    • Lambda表达式的作用:用来简化针对匿名内部类代码的书写

      • new Thread( new Runnable(){
            //重写run
            public void run(){
                ......
            }
        });
        //使用Lambda简化:    
        new Thread( () -> {
          .....  
        });  
            
        
    • 在Java要使用Lambda表达式,提前:针对函数式接口

      • 函数式接口:接口中有且仅有一个抽象方法
    • Lambda表达式的标准语法:

      • (参数列表) -> {
            
        }
        // 参数列表 
        // ->
        // 方法体
        
    • 可以对Lambda进行省略格式:

      • 省略参数列表

        •  //省略1:省略参数的数据类型
          (String str,int n) -> { .... }
          (str,n) -> {....}
          
          //省略2:参数列表只有一个参数时,可以省略数据类型、小括号 
          (int num) -> { ... }
          num -> { .... }
          
      • 省略方法体

        • //当方法体中仅有一行代码时,可以省略:大括号、return、分号
          (str) -> { return str.length(); }
          str -> str.length
          
  • Stream流

    • 应用点:针对集合、数组进行操作简化

    • 概念:流,就相当于生活中的流水线

    • Stream流的应用: 获取流、中间操作、终端操作

      • 获取流:

        • 集合转为Stream流:

          • //List集合
            Stream  s = list集合.stream()
            
            //Set集合
            Stream  s = set集合.stream()
            
            //Map集合
            Stream<K>  s = map集合.keySet().stream()   
            Stream<Map.Entry>  s = map集合.entrySet().stream()
            
        • 数组转为Stream流:

          • Stream  s = Stream.of(数组[])
            
      • 中间操作:

        • 常用API方法

          • //过滤
            filter()
            
            //获取前n个数据
            limit( n )
            
            //跑过前n个,获取后面的数据
            skip( n )
            
            //去重
            distinct()
            
            //排序
            sorted() //自然排序
            sorted(Comparator c) //比较器    
            
      • 终端操作:

        • 常用API方法

          • //遍历输出
            foeEach()
            
            //计算数据个数
            count()
            
            //收集数据,转为集合
            collect()
                
            //收集数据,转为数组
            toArray()    
            
  • File类

    File类:表示硬盘上的文件或目录

    //硬盘上的文件
    String path = "f:/files/hello.txt"; //绝对路径(有盘符)
    //实例化
    File file  = new File(path);
    
    //硬盘上的文件
    String parent ="f:/files" //父路径  //绝对路径
    String child ="demo/HelloWorld.java";//子路径     //相对路径(没有盘符)
    //父路径+子路径 = "f:/files/demo/HelloWorld"
    
    //实例化
    File file = new File(parent , child);
    
    
    //硬盘上的目录
    //原目录路径:f:/files/demo/test/123.txt
    //把原目录拆分:
    
    //父路径: f:/files
    File parent = new File("f:/files");
    
    //子路径:demo/test/123.txt
    String child = "demo/test/123.txt" //相对路径(没有盘符)
        
    //父路径+子路径
    File file = new File(parent , child);    
    
    String[]  list() //获取当前目录下所有文件及子目录的名称
    
    File[]   listFiles() //获取当前目录下所有文件及子目录的File对象
    

e类

File类:表示硬盘上的文件或目录

//硬盘上的文件
String path = "f:/files/hello.txt"; //绝对路径(有盘符)
//实例化
File file  = new File(path);
//硬盘上的文件
String parent ="f:/files" //父路径  //绝对路径
String child ="demo/HelloWorld.java";//子路径     //相对路径(没有盘符)
//父路径+子路径 = "f:/files/demo/HelloWorld"

//实例化
File file = new File(parent , child);

//硬盘上的目录
//原目录路径:f:/files/demo/test/123.txt
//把原目录拆分:

//父路径: f:/files
File parent = new File("f:/files");

//子路径:demo/test/123.txt
String child = "demo/test/123.txt" //相对路径(没有盘符)
    
//父路径+子路径
File file = new File(parent , child);    
String[]  list() //获取当前目录下所有文件及子目录的名称

File[]   listFiles() //获取当前目录下所有文件及子目录的File对象
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程小栈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值