JDK8新特性----lambda表达式

一.Lambda表达式

1.Lambda表达式

注意: 函数式接口:接口中只有一个抽象方法。

(参数1,参数2): 抽象方法的参数

->: 分隔符

{}:表示抽象方法的实现

(1) lambda基本用法 

package com.wt.practice.lx01;

/**
 * @Author wt
 * @Date 2022/7/19 19:00
 * @PackageName:com.wt.practice.lx01
 * @ClassName: Demo01
 * @Description: TODO
 * @Version 1.0
 */
public class Demo01 {
    public static void main(String[] args) {
        //引入自定义接口类
        My my = new My();
        Thread thread = new Thread(my);
        thread.start();

        //创建匿名对象
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("这是匿名内部类的任务对象");
            }
        };
        Thread thread1 = new Thread(runnable);
        thread1.start();

        //lambda表达式
        Runnable runnable1 = ()-> System.out.println("这是lambda类型的任务对象");
        Thread thread2 = new Thread(runnable1);
        thread2.start();
    }
}

class My implements Runnable{
    @Override
    public void run() {
        System.out.println("这是自定义任务型接口");
    }
}
  • Thread 类需要 Runnable 接口作为参数,其中的抽象 run 方法是用来指定线程任务内容的核心

  • 为了指定 run 的方法体,不得不需要 Runnable 接口的实现类

  • 为了省去定义一个 Runnable 实现类的麻烦,不得不使用匿名内部类

  • 必须覆盖重写抽象 run 方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错

  • 而实际上,似乎只有方法体才是关键所在。

这时可以使用lambda表示完成上面的要求  

//lambda表达式

//Runnable task03 =  ()-> {
//    System.out.println("这是使用Lambda表达式完成的");
//};
简写(省略了{})
Runnable runnable1 = ()-> System.out.println("这是lambda类型的任务对象");
Thread thread2 = new Thread(runnable1);
thread2.start();

(2) 无参无返回值的lambda表达式 

package com.wt.practice.lx01;

/**
 * @Author wt
 * @Date 2022/7/19 19:06
 * @PackageName:com.wt.practice.lx01
 * @ClassName: Demo02
 * @Description: 无参无返回值
 * @Version 1.0
 */
public class Demo02 {
    public static void main(String[] args) {
        Swrimable swrimable1 = new Swrimable() {
            @Override
            public void Swrimming() {
                System.out.println("这是使用匿名内部类的方式实现");
            }
        };
        fun(swrimable1);


        Swrimable swrimable = ()-> System.out.println("这是使用lambda表达式");
        fun(swrimable);
    }
    public static void  fun(Swrimable swrimable){
        swrimable.Swrimming();
    }

}

//匿名式接口
interface Swrimable{
    public void Swrimming();
}

(3) 练习有参数有返回值的Lambda

下面举例演示 java.util.Comparator 接口的使用场景代码,其中的抽象方法定义为:

  • public abstract int compare(T o1, T o2);

当需要对一个对象集合进行排序时, Collections.sort 方法需要一个 Comparator 接口实例来指定排序的规则。

package com.wt.practice.lx01;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * @Author wt
 * @Date 2022/7/19 19:13
 * @PackageName:com.wt.practice.lx01
 * @ClassName: Demo03
 * @Description: 有参有返回值
 * @Version 1.0
 */
public class Demo03 {
    public static void main(String[] args) {
        List<Person> list = new ArrayList<>();
        list.add(new Person("阮籍",42,178));
        list.add(new Person("阮咸",32,190));
        list.add(new Person("向秀",25,175));
        list.add(new Person("刘伶",19,187));

        //对集合中的元素进行排序,

        //传统做法,定义匿名接口
        Comparator<Person> comparator = new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge()-o2.getAge();
            }
        };



        Collections.sort(list,comparator);
        for (Person xx:list){
            System.out.println(xx);
        }
        System.out.println("===============");
        //使用lambda表达式进行排序
        Comparator<Person> comparator1 = (Person o1,Person o2)->{
            return o1.getAge()- o2.getAge();
        };
        Collections.sort(list,comparator1);
        for (Person xx:list){
            System.out.println(xx);
        }
        System.out.println("================");

        //使用更简洁的lambda表达式排序
        Comparator<Person> comparator2 = (o1, o2) -> o2.getAge()- o1.getAge();
        Collections.sort(list,comparator2);
        for (Person xx:list){
            System.out.println(xx);
        }

    }
}

class Person{
    private String name;
    private int age;
    private int height;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public Person(String name, int age, int height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
}

(4) 详细介绍lambda表达式  

2.函数式接口

**内置函数式接口的由来**  

public class Test03 {

    public static void main(String[] args) {
        Operater o=arr -> {
             int sum=0;
             for(int n:arr){
                 sum+=n;
             }
            System.out.println("数组的和为:"+sum);
        };

        fun(o);
    }

    public static void fun(Operater operater){
         int[] arr={2,3,4,5,6,7,11};
         operater.getSum(arr);
    }

}
@FunctionalInterface
interface Operater{
    //求数组的和
    public abstract void getSum(int[] arr);
}

分析

我们知道使用Lambda表达式的前提是需要有函数式接口。而Lambda使用时不关心接口名,抽象方法名,只关心抽 象方法的参数列表和返回值类型。因此为了让我们使用Lambda方便,JDK提供了大量常用的函数式接口。

常见的函数式接口 

 (1) Consumer<T>消费型接口

方法:accept()

有参数,无返回值

package com.wt.practice.lx02;

import java.util.function.Consumer;

/**
 * @Author wt
 * @Date 2022/7/19 19:45
 * @PackageName:com.wt.practice.lx02
 * @ClassName: ConsumerImpl
 * @Description: Consumer 有参数无返回值
 * @Version 1.0
 */
public class ConsumerImpl {
    public static void main(String[] args) {
        //t指向调用的方法中的参数money
        Consumer<Double> consumer = t-> System.out.println("吃饭花费了"+t);
        fun(consumer,200.0);
    }
    //调用某个方法时,该方法需要的参数为接口类型,
    //参数money的数据类型需要与接口类型的泛型相同
    public static void fun(Consumer<Double> consumer,Double money){
        consumer.accept(money);
    }
}

(2) Supplier<T>供给型函数式接口

T:表示返回结果的泛型

方法 :get();

无参,想有返回结果的函数式接口时

package com.wt.practice.lx02;

import java.util.Random;
import java.util.function.Supplier;

/**
 * @Author wt
 * @Date 2022/7/19 19:50
 * @PackageName:com.wt.practice.lx02
 * @ClassName: SupplierImpl
 * @Description: 无参可以指定返回值(四类八种及引用类型)
 * @Version 1.0
 */
public class SupplierImpl {
    public static void main(String[] args) {
        //可以返回四类八种基本类型,及引用类型
        Supplier supplier = ()-> "dfdf";
        fun(supplier);
        System.out.println("==============");
        //返回[0-10)的随机数
        Supplier supplier1 = ()->new Random().nextInt(10);
        fun(supplier1);
    }
    public static void fun(Supplier supplier){
        Object o = supplier.get();
        System.out.println("指定返回的数据是:"+o);
    }
}

 (3) Function<T,R> 函数型函数式接口

T: 参数类型的泛型

R: 函数返回结果的泛型

方法:apply()

 有参,有返回值

package com.wt.practice.lx02;

import java.util.Locale;
import java.util.function.Function;

/**
 * @Author wt
 * @Date 2022/7/19 20:04
 * @PackageName:com.wt.practice.lx02
 * @ClassName: FunctionImpl
 * @Description: Function 有参有返回值
 * @Version 1.0
 */
public class FunctionImpl {
    public static void main(String[] args) {
        //将输入的参数转换为大写
        Function<String,String> function = t->t.toUpperCase(Locale.ROOT);
        //计算输入参数的长度(包含空格)
        Function<String,Integer> function1 = t2-> t2.length();

        fun(function,"Hello word");
        fun1(function1,"Hello word");


        //直接将lambda表达式写入方法中
        fun2(t->t.toUpperCase(Locale.ROOT),"hgji");
    }
    public static void fun(Function<String,String> function,String prin){
        String apply = function.apply(prin);
        System.out.println(apply);
    }
    public static void fun1(Function<String,Integer> function,String prin){
        Integer apply = function.apply(prin);
        System.out.println(apply);
    }
    public static void fun2(Function<String,String> function,String prin){
        String apply = function.apply(prin);
        System.out.println(apply);
    }
}

(4) Predicated<T>

T: 参数的泛型

方法: test()

当传入一个参数时,需要对该参数进行判断时,则需要这种函数  

package com.wt.practice.lx02;

import java.util.function.Predicate;

/**
 * @Author wt
 * @Date 2022/7/19 20:12
 * @PackageName:com.wt.practice.lx02
 * @ClassName: PredicateImpl
 * @Description: Predicate 当传入一个参数时,需要对该参数进行判断时,则需要这种函数
 * @Version 1.0
 */
public class PredicateImpl {
    public static void main(String[] args) {
        //返回类型为boolean
        fun(t->t>3?true:false,5);
    }
    public static void fun(Predicate<Integer> predicate,Integer num){
        boolean test = predicate.test(num);
        System.out.println(test);
    }
}

3.方法引用

 (1) lambda表达式的冗余

package com.wt.practice.lx03;

import java.util.function.Consumer;

/**
 * @Author wt
 * @Date 2022/7/19 20:20
 * @PackageName:com.wt.practice.lx03
 * @ClassName: Demo01
 * @Description: TODO
 * @Version 1.0
 */
public class Demo01 {
    public static void main(String[] args) {
        //第一种方法
        Consumer<Integer[]> consumer = (arr)->{
            int sum=0;
            for (Integer xx:arr){
                sum+=xx;
            }
            System.out.println(sum);
        };
        fun(consumer);


        //第二种:直接调用类中所拥有的方法,防止造成代码冗余
        fun(Demo01::sum);
    }
    public static void fun(Consumer<Integer[]> consumer){
        Integer[] arr = {1,2,3,4,5};
        consumer.accept(arr);
    }

    public static void sum(Integer[] arr){
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum+=arr[i];
        }
        System.out.println("总和是:"+sum);
    }
}

 请注意其中的双冒号 :: 写法,这被称为“方法引用”,是一种新的语法

(2) 什么是方法引用

 方法引用的分类

(2).实例方法的引用

实例方法引用,顾名思义就是调用已经存在的实例的方法,与静态方法引用不同的是类要先实例化,静态方法引用类无需实例化,直接用类名去调用。  

package com.wt.practice.lx03;

import java.util.function.Consumer;

/**
 * @Author wt
 * @Date 2022/7/19 21:40
 * @PackageName:com.wt.practice.lx03
 * @ClassName: Demo02
 * @Description: 实例方法引用
 * @Version 1.0
 */
public class Demo02 {
    public static void main(String[] args) {
        //先创建对象,再通过对象调用方法
        Computer computer = new Computer();

        fun(computer::sum);
    }
    public static void fun(Consumer<Integer[]> consumer){
        Integer[] arr = {1,2,3,4,5};
        consumer.accept(arr);
    }

}
//创建对象
class Computer{
    //定义求和方法
    public void sum(Integer[] arr){
        int sum = 0;
        for (Integer xx:arr){
            sum+=xx;
        }
        System.out.println("总和是"+sum);
    };
}

(3) 对象方法引用

public class Demo03 {
    public static void main(String[] args) {
        /**
         * 对象方法引用 类名::实例方法  (参数1,参数2)->参数1.实例方法(参数2)
         */
        //Function<String,Integer> function = t->t.length();
        Function<String,Integer> function = String::length;
        Integer hello = function.apply("hello");
        System.out.println(hello);

        //比较两个字符串是否一致
        //BiFunction<String,String,Boolean> biFunction = (t1,t2)->t1.equals(t2);
        BiFunction<String,String,Boolean> biFunction = String::equals;
        Boolean apply = biFunction.apply("hello", "hello");
        System.out.println(apply);
    }
}

(4) 构建方法引用

package com.wt.pratice.lx01;

import java.util.function.Function;

/**
 * @Author wt
 * @Date 2022/7/21 9:59
 * @PackageName:com.wt.pratice.lx01
 * @ClassName: Demo04
 * @Description: 构造方法引用 类名::new
 * @Version 1.0
 */
public class Demo04 {
    public static void main(String[] args) {
        //Function<String,People> function = t->new People(t);
        Function<String,People> function = People::new;
        People q = function.apply("千奈");
        System.out.println(q);

    }
}
class People{
    private String name;

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                '}';
    }

    public People(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

二.Stream流

Java8的两个重大改变,一个是Lambda表达式,另一个就是Stream API表达式。==Stream 是Java8中处理集合的关键抽象概念==,它可以对集合进行非常复杂的查找、过滤、筛选等操作.  

 

1.为什么使用Stream流

当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。我们来体验 集合操作数据的弊端,需求如下:

一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张强,张三丰,何线程
需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据

传统写法

public class My {
    public static void main(String[] args) {
// 一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张强,张三丰
// 需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");
// 1.拿到所有姓张的
        ArrayList<String> zhangList = new ArrayList<>(); // {"张无忌", "张强", "张三丰"}
        for (String name : list) {
            if (name.startsWith("张")) {
                zhangList.add(name);
            }
        }
// 2.拿到名字长度为3个字的
        ArrayList<String> threeList = new ArrayList<>(); // {"张无忌", "张三丰"}
        for (String name : zhangList) {
            if (name.length() == 3) {
                threeList.add(name);
            }
        }
// 3.打印这些数据
        for (String name : threeList) {
            System.out.println(name);
        }
    }
}

分析:

这段代码中含有三个循环,每一个作用不同:  

  1. 首先筛选所有姓张的人;

  2. 然后筛选名字有三个字的人;

  3. 最后进行对结果进行打印输出。

每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循环 是做事情的方式,而不是目的。每个需求都要循环一次,还要搞一个新集合来装数据,如果希望再次遍历,只能再使 用另一个循环从头开始。

那Stream能给我们带来怎样更加优雅的写法呢?

public class Demo01 {
    public static void main(String[] args) {
        // 需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰","何线程");

        list.stream()
                .filter(t->t.startsWith("张"))
                .filter(t->t.length()==3)
                .forEach(System.out::println);
    }
}

对集合的操作语法简洁:性能比传统快。  

2. Stream流的原理

注意:Stream和IO流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统IO流的固有印象!

Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种==数据结构==,==不保存数据==,而是对数据进行==加工 处理==。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。

 

Stream不存在数据,只对数据进行加工处理。  

3. 如何获取Stream流对象

 

public class Demo02 {
    public static void main(String[] args) {
        //串行流

        //通过集合对象调用stream()获取流
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"阮咸","向秀","刘伶","小谢");
        Stream<String> stream = list.stream();
        stream.forEach(System.out::println);
        System.out.println("=========");
        //通过数组工具类获取stream()对象
        int[] arr = {1,2,3,4,5};
        IntStream stream1 = Arrays.stream(arr);
        //使用Stream类中of方法调用
        Stream<String> hello = Stream.of("hello", "world");

        //通过Stream的方法调用
        LongStream range = LongStream.range(1, 10);
        //range.forEach(t-> System.out.println(t));

        //并行流--->如果流中的数据量足够大,并行流可以加快处速度
        Stream<String> stringStream = list.parallelStream();
        stringStream.forEach(t-> System.out.println(t));



    }
}

4. Stream流中常见的api

数据源

public class Demo03 {
    public static void main(String[] args) {

        List<Person> personList = new ArrayList<>();
        personList.add(new Person("欧阳雪",18,"中国",'F'));
        personList.add(new Person("Tom",24,"美国",'M'));
        personList.add(new Person("Harley",22,"英国",'F'));
        personList.add(new Person("向天笑",20,"中国",'M'));
        personList.add(new Person("李康",22,"中国",'M'));
        personList.add(new Person("小梅",20,"中国",'F'));
        personList.add(new Person("何雪",21,"中国",'F'));
        personList.add(new Person("李康",22,"中国",'M'));
        
    }

}
class Person {
    private String name;
    private Integer age;
    private String country;
    private char sex;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", country='" + country + '\'' +
                ", sex=" + sex +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public Person(String name, Integer age, String country, char sex) {
        this.name = name;
        this.age = age;
        this.country = country;
        this.sex = sex;
    }
}

(1)filter / foreach / count   

//找到年龄大于18且国家为中国的人
        personList.stream()
                .filter(t->t.getSex()>18)
                .filter(t->t.getCountry().equals("中国"))
                .forEach(System.out::println);

        System.out.println("============");
        //输出性别为F的人
        personList.stream()
                .filter(t->t.getSex()=='F')
                .forEach(System.out::println);

(2) map | sorted  

 map--接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

 

 //对流中元素排序 
        personList.stream()
                  .sorted((o1,o2)->o1.getAge()-o2.getAge())
                  .forEach(System.out::println);


        //集合中每个元素只要名.map--->原来流中每个元素转换为另一种格式。
//        personList.stream()
//                 .map(item->{
//                     Map<String,Object> m=new HashMap<>();
//                     m.put("name",item.getName());
//                     m.put("age",item.getAge());
//                     return m;
//                 })
//                  .forEach(System.out::println);

 (3) min max

//获取员工中年龄最大的人
        Optional<Person> max = personList.stream()
                .max(((o1, o2) -> o1.getAge() - o2.getAge()));
        System.out.println(max);
        //获取员工中年龄最小的人
        Optional<Person> min = personList.stream()
                .min(((o1, o2) -> o1.getAge() - o2.getAge()));
        System.out.println(min);

(4)规约reduce  

 

/**
         * 规约reduce
         * */
        //求所有人年龄的和
        Optional<Integer> reduce = personList.stream()
                .map(t -> t.getAge())
                .reduce((a, b) -> a + b);
        System.out.println(reduce.get());

        //给a设置初始值(默认为0)
        Integer reduce1 = personList.stream()
                .map(t -> t.getAge())
                .reduce(10, (a, b) -> a + b);
        System.out.println(reduce1);

 (5)collect搜集 match find

/**
         * collect 收集 match find
         */
        //收集方法 collect
        //年龄大于20且性别为M
        List<Person> collect = personList.stream()
                .filter(t -> t.getAge() > 20)
                .filter(t -> t.getSex() == 'M')
                .collect(Collectors.toList());
        System.out.println(collect);
        System.out.println("-------------");
        personList.stream()
                .filter(t->t.getAge()>20)
                .filter(t->t.getSex()=='M')
                .forEach(System.out::println);

 (6) 条件判断

//判断是否满足条件

        //查询所有性别为F的人的年龄是否存在大于20的,所有都满足则返回true,
        boolean b = personList.stream()
                .filter(t -> t.getSex() == 'F')
                .allMatch(t -> t.getAge()>= 20);
        System.out.println(b);

        //查询所有性别为F的人的年龄是否存在大于20的,存在一个即可返回true,
        boolean b1 = personList.stream()
                .filter(t -> t.getSex() == 'F')
                .anyMatch(t -> t.getAge() >= 20);
        System.out.println(b1);

        //都不满足则为true,否则为false
        boolean b2 = personList.stream()
                .filter(t -> t.getSex() == 'F')
                .noneMatch(t -> t.getAge() >= 24);
        System.out.println(b2);

5.新增了日期时间类

旧的日期时间的缺点:

  1. 设计比较乱: Date日期在java.util和java.sql也有,而且它的时间格式转换类在java.text包。

  2. 线程不安全。

 

新增加了哪些类?

LocalDate: 表示日期类。yyyy-MM-dd

LocalTime: 表示时间类。 HH:mm:ss

LocalDateTime: 表示日期时间类 yyyy-MM-dd t HH:mm:ss sss

DatetimeFormatter:日期时间格式转换类。

Instant: 时间戳类。

Duration: 用于计算两个日期类

public class Demo01 {
    public static void main(String[] args) {
        //获取当前日期
        LocalDate now = LocalDate.now();
        System.out.println(now);
        //指定日期
        LocalDate of = LocalDate.of(2022, 7, 20);
        System.out.println(of);
        //获取当前时间
        LocalTime nowtime = LocalTime.now();
        System.out.println(nowtime);
        //指定时间
        LocalTime oftime = LocalTime.of(21, 23, 23, 20);
        System.out.println(oftime);

        //获取当前日期时间
        LocalDateTime nowdatetime = LocalDateTime.now();
        System.out.println(nowdatetime);
        //指定日期时间
        LocalDateTime ofdatetime = LocalDateTime.of(2022, 6, 6, 22, 34, 34, 23);
        System.out.println(ofdatetime);
        //计算两个时间类的差
        Duration between = Duration.between(ofdatetime,nowdatetime);
        System.out.println("相差天数:"+between.toDays()+"天");
        System.out.println("相差小时:"+between.toHours()+"小时");

        //日期格式转换
        //1.定义日期格式
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        //2.将字符串转换为日期格式
        LocalDate parse = LocalDate.parse("2001-12-28", dateTimeFormatter);
        System.out.println("字符串转日期:"+parse);


    }
}

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值