尚硅谷JavaSE笔记(五)

JavaSE(硅谷视频学习笔记 硅谷30天Java笔记,包括JavaSE基础和高级部分 阅读详情

系列文章目录

尚硅谷JavaSE笔记(一)

尚硅谷JavaSE笔记(二)

尚硅谷JavaSE笔记(三)

尚硅谷JavaSE笔记(四)


文章目录

十八、反射机制

1、反射的概念

反射的概述

Java 反射机制研究及应用

反射相关的主要 API

反射的优缺点

2、理解 Class 类并获取 Class 实例

理解 Class

获取 Class 类的实例(四种方法)

哪些类型可以有 Class 对象

Class 类的常用方法

3、类的加载与 ClassLoader 的理解

类的生命周期

类加载器(classloader)

4、反射的基本应用

应用 1:创建运行时类的对象

应用 2:获取运行时类的完整结构

应用 3:调用运行时类的指定结构

应用4:动态代理

十九、Java8新特性

1、Lambda 表达式

Lambda 表达式及其语法

2、函数式(Functional)接口

什么是函数式接口

Java 内置函数式接口

3、方法引用与构造器引用

方法引用

构造器引用

数组构造引用

4、强大的 Stream API

Stream API 说明

Stream 的操作三个步骤

optional类


十八、反射机制

1、反射的概念

反射的概述

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在 运行期间 借助于 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个 Class 类型的对象(一个类 只有一个 Class 对象),这个对象就包含了完整的类的结构信息。我们可以通 过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构, 所以,我们形象的称之为:反射。

从内存加载上看反射:

Java 反射机制研究及应用

Java 反射机制提供的功能:
• 在运行时判断任意一个对象所属的类
• 在运行时构造任意一个类的对象
• 在运行时判断任意一个类所具有的成员变量和方法
• 在运行时获取泛型信息
• 在运行时调用任意一个对象的成员变量和方法
• 在运行时处理注解
• 生成动态代理

反射相关的主要 API

java.lang.Class :代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类 的构造器
… …

反射的优缺点

优点:
• 提高了 Java 程序的灵活性和扩展性,降低了耦合性 ,提高 自适应 能力
• 允许程序创建和控制任何类的对象,无需提前硬编码 目标类
缺点:
• 反射的性能较低
        – 反射机制主要应用在对灵活性和扩展性要求很高的系统框架上
• 反射会模糊 程序内部逻辑, 可读性较差

2、理解 Class 类并获取 Class 实例

要想 解剖 一个类,必须先要获取到该类的 Class 对象。而剖析一个类或用反射解决具体的问题就是使用相关 API:
• java.lang.Class
• java.lang.reflect.*
所以,Class 对象是反射的根源。

理解 Class

理论上
在 Object 类中定义了以下的方法,此方法将被所有子类继承:
public final Class getClass ()
以上的方法返回值的类型是一个 Class 类,此类是 Java 反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。

对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个结构 (class/interface/enum/annotation/primitive type/void/[])的有关信息。
        • Class 本身也是一个类
        • Class 对象只能由系统建立对象
        • 一个加载的类在 JVM 中只会有一个 Class 实例
        • 一个 Class 对象对应的是一个加载到 JVM 中的一个.class 文件
        • 每个类的实例都会记得自己是由哪个 Class 实例所生成
        • 通过 Class 可以完整地得到一个类中的所有被加载的结构
        • Class 类是 Reflection 的根源,针对任何你想动态加载、运行的类,唯有先获得相应的 Class 对象

内存结构上

说明:上图中字符串常量池在 JDK6 中存储在方法区;JDK7 及以后,存储在堆空间。

1.类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。

2.换句话说,CLass的实例就对应着一个运行时类。

3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。

package Test_2.reflectiontest;

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

    public Person() {
    }

    private Person(String name){
        this.name = name;
    }

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

    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;
    }

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

    public void show(){
        System.out.println("你好,我是一个人");
    }

    private String showNation(String nation){
        System.out.println("我的国籍是:" + nation);
        return nation;
    }

}
 //反射之前,对Person的操作
    @Test
    public void test1(){
        //1、创建Person类的对象
        Person p1 = new Person("Tom", 12);
        //通过对象,调用其内部的属性方法
        p1.age = 10;
        System.out.println(p1.toString());
        p1.show();
        //在Person类外部,不可以通过Person类的对象调用其内部私有结构;
        //比如:name,showNation()以及私有的构造器
    }

    //反射之后,对Person的操作
    @Test
    public void test2() throws Exception {
        Class clazz = Person.class;
        //通过反射,创建Person类的对象
        Constructor cons = clazz.getConstructor(String.class,int.class);
        Object obj = cons.newInstance("Tom", 12);
        Person p = (Person) obj;
        System.out.println(p.toString());
        //通过反射,调用对象指定的属性、方法
        //调用属性
        Field age = clazz.getDeclaredField("age");
        age.set(p,10);
        System.out.println(p.toString());
        //调用方法
        Method show = clazz.getDeclaredMethod("show");
        show.invoke(p);
        //通过反射,可以调用Person类的私有结构;
        //调用私有构造器
        Constructor cons1 = clazz.getDeclaredConstructor(String.class);
        cons1.setAccessible(true);
        Person p1 = (Person) cons1.newInstance("Jerry");
        System.out.println(p1);
        //调用私有属性
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p1, "lilei");
        System.out.println(p1);
        //调用私有方法
        Method showNation = clazz.getDeclaredMethod("showNation", String.class);
        showNation.setAccessible(true);
        String nation = (String) showNation.invoke(p1, "中国");
        System.out.println(nation);
    }

获取 Class 类的实例(四种方法)

方式 1:要求编译期间已知类型 前提:若已知具体的类,通过类的 class 属性获取,该方法最为安全可靠,程序性能最高
实例:
Class clazz = String . class ;
方式 2:获取对象的运行时类型
前提:已知某个类的实例,调用该实例的 getClass()方法获取 Class 对象
实例:
Class clazz = "www.atguigu.com" . getClass ();
方式 3:可以获取编译期间未知的类型
前提:已知一个类的全类名,且该类在类路径下,可通过 Class 类的静态方法 forName()获取,可能抛出 ClassNotFoundException
实例:
Class clazz = Class . forName ( "java.lang.String" );
方式 4:其他方式
前提:可以用系统类加载对象或自定义加载器对象加载指定路径下的类型
实例:
ClassLoader cl = this . getClass (). getClassLoader ();
Class clazz4 = cl . loadClass ( " 类的全类名 " );

//获取class实例的方法
    @Test
    public void test3() throws ClassNotFoundException {
        //方式一:调用运行时类的属性:.class
        Class clazz1 = Person.class;
        System.out.println(clazz1);
        //方式二:通过运行时类的对象,调用getClass()
        Person p1 = new Person();
        Class clazz2 = p1.getClass();
        System.out.println(clazz2);
        //方式三:调用Class的静态方法:forName(String classPath)
        Class clazz3 = Class.forName("Test_2.reflectiontest.Person");
        System.out.println(clazz3);

        System.out.println(clazz1 == clazz2);
        System.out.println(clazz1 == clazz3);

        //方式四:使用类的加载器:ClassLoader
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("Test_2.reflectiontest.Person");
        System.out.println(clazz4);
        System.out.println(clazz1 == clazz4);

    }

哪些类型可以有 Class 对象

简言之,所有 Java 类型!
(1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部 类
(2)interface:接口
(3)[]:数组
(4)enum:枚举
(5)annotation: 注解@interface
(6)primitive type:基本数据类型
(7)void
举例:
Class c1 = Object.class;
Class c2 = Comparable.class;
Class c3 = String[].class;
Class c4 = int[][].class;
Class c5 = ElementType.class;
Class c6 = Override.class;
Class c7 = int.class;
Class c8 = void.class;
Class c9 = Class.class;
int[] a = new int[10];
int[] b = new int[100];
Class c10 = a.getClass();
Class c11 = b.getClass();
// 只要元素类型与维度一样,就是同一个 Class
System.out.println(c10 == c11);

Class 类的常用方法

方法名功能说明
static Class forName(String name)返回指定类名 name 的 Class 对象
Object newInstance()
调用缺省构造函数,返回该 Class 对象的一个实例
getName()
返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称
Class getSuperClass()返回当前 Class 对象的父类的 Class 对象
Class [] getInterfaces()
获取当前 Class 对象的接口
ClassLoader getClassLoader()
返回该类的类加载器
Class getSuperclass()返回表示此 Class 所表示的实体的超类的Class
Constructor[] getConstructors()
返回一个包含某些 Constructor 对象的数组
Field[] getDeclaredFields()返回 Field 对象的一个数组
Method getMethod(String
name,Class … paramTypes)
返回一个 Method 对象,此对象的形参类型为 paramType

3、类的加载与 ClassLoader 的理解

类的生命周期

类在内存中完整的生命周期:加载-->使用-->卸载。其中加载过程又分为:装载、链接、初始化三个阶段。

类的加载过程

当程序主动使用某个类时,如果该类还未被加载到内存中,系统会通过加载、 链接、初始化三个步骤来对该类进行初始化。如果没有意外,JVM 将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载。

类的加载又分为三个阶段:
(1)装载(Loading)
将类的 class 文件读入内存,并为之创建一个 java.lang.Class 对象。此过程由类 加载器完成
(2)链接(Linking)
        ①验证 Verify:确保加载的类信息符合 JVM 规范,例如:以 cafebabe 开头,没 有安全方面的问题。
        ②准备 Prepare:正式为类变量(static)分配内存并设置类变量默认初始值 的阶段,这些内存都将在方法区中进行分配。
        ③解析 Resolve:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。(3)初始化(Initialization)
        • 执行类构造器<clinit>() 方法 的过程。 类构造器 <clinit>() 方法 是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构 造类信息的,不是构造该类对象的构造器)。
        • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的 初始化。
        • 虚拟机会保证一个类的<clinit>() 方法 在多线程环境中被正确加锁和同步。

类加载器(classloader)

@Test
    public void test1(){
        //对于自定义类,使用系统类加载器进行加载
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);
        //调用系统类加载器的getParent():获取扩展类加载器
        ClassLoader classLoader1 = classLoader.getParent();
        System.out.println(classLoader1);
        //调用扩展类加载器的getParent():无法获取引导类加载器
        //引导类加载器 主要负责java的核心库类,无法加载自定义类
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2);
    }

查看某个类的类加载器对象 

(1)获取默认的系统类加载器
ClassLoader classloader = ClassLoader . getSystemClassLoader ();
(2)查看某个类是哪个类加载器加载的
ClassLoader classloader = Class . forName ( "exer2.ClassloaderDemo" ). getClassLoader ();
// 如果是根加载器加载的类,则会得到 null
ClassLoader classloader1 = Class . forName ( "java.lang.Object" ). getClassLoader ();
(3)获取某个类加载器的父加载器
ClassLoader parentClassloader = classloader . getParent ();

使用 ClassLoader 获取流(加载配置文件)
@Test
    public void test2() throws Exception {
        Properties pros = new Properties();
        //此时的文件默认在当前的module下。
        //读取配置文件的方式一:
        //FileInputStream fis = new FileInputStream("jdbc.properties");
        //pros.load(fis);

        //读取配置文件的方式二:使用ClassLoader
        //配置文件默认识别为:当前module的src下
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
        pros.load(is);

        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        System.out.println("user = " + user + ", password = " + password);
    }

4、反射的基本应用

应用 1:创建运行时类的对象

创建运行时类的对象有两种方式:
方式 1:直接调用 Class 对象的 newInstance()方法
要 求: 1)类必须有一个无参数的构造器。
             2)类的构造器的访问权限需要足够。
方式 2:通过获取构造器对象来进行实例化
方式一的步骤:
1)获取该类型的 Class 对象
2)调用 Class 对象的 newInstance()方法创建对象
方式二的步骤:
1)通过 Class 类的 getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器
2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
3)通过 Constructor 实例化对象。
如果构造器的权限修饰符修饰的范围不可见,也可以调用 setAccessible(true)

@Test
    public void test1() throws Exception {
        Class<Person> clazz = Person.class;
        Object obj = clazz.newInstance();
        System.out.println(obj);

        Class<?> clazz1 = Class.forName("Test_2.reflectiontest.Person");
        Object obj1 = clazz1.newInstance();
        System.out.println(obj1);

        Class<?> clazz2 = Class.forName("Test_2.reflectiontest.Person");
        Constructor<?> constructor = clazz2.getConstructor(String.class,int.class);
        Object obj2 = constructor.newInstance("王五",12);
        System.out.println(obj2);
    }

应用 2:获取运行时类的完整结构

可以获取:包、修饰符、类型名、父类(包括泛型父类)、父接口(包括泛型父接口)、成员(属性、构造器、方法)、注解(类上的、方法上的、属性上的)。

相关 API
//1. 实现的全部接口
public Class <?>[] getInterfaces ()
// 确定此对象所表示的类或接口实现的接口。
//2. 所继承的父类
public Class <? Super T > getSuperclass ()
// 返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class
//3. 全部的构造器
public Constructor < T >[] getConstructors ()
// 返回此 Class 对象所表示的类的所有 public 构造方法。
public Constructor < T >[] getDeclaredConstructors ()
// 返回此 Class 对象表示的类声明的所有构造方法。
//Constructor 类中:
// 取得修饰符 :
public int getModifiers ();
// 取得方法名称 :
public String getName ();
// 取得参数的类型:
public Class <?>[] getParameterTypes ();
//4. 全部的方法
public Method [] getDeclaredMethods ()
// 返回此 Class 对象所表示的类或接口的全部方法
public Method [] getMethods ()
// 返回此 Class 对象所表示的类或接口的 public 的方法
//Method 类中:
public Class <?> getReturnType ()
// 取得全部的返回值
public Class <?>[] getParameterTypes ()
// 取得全部的参数
public int getModifiers ()
// 取得修饰符
public Class <?>[] getExceptionTypes ()
// 取得异常信息
//5. 全部的 Field
public Field [] getFields ()
// 返回此 Class 对象所表示的类或接口的 public Field
public Field [] getDeclaredFields ()
// 返回此 Class 对象所表示的类或接口的全部 Field
//Field 方法中:
public int getModifiers ()
// 以整数形式返回此 Field 的修饰符
public Class <?> getType ()
// 得到 Field 的属性类型
public String getName ()
// 返回 Field 的名称。
//6. Annotation 相关
get Annotation ( Class < T > annotationClass )
getDeclaredAnnotations ()
//7. 泛型相关
// 获取父类泛型类型:
Type getGenericSuperclass ()
// 泛型类型: ParameterizedType
// 获取实际的泛型类型参数数组:
getActualTypeArguments ()
//8. 类所在的包
Package getPackage ()

获取运行时类的属性结构及其内部结构

public class FieldTest {
    @Test
    public void test1(){
        Class clazz = Person.class;
        //获取属性结构
        //getFields():获取当前运行时类及其父类中声明为public访问权限的属性
        Field[] fields = clazz.getFields();
        for(Field f : fields){
            System.out.println(f);
        }
        System.out.println("-----------");

        //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f1 : declaredFields){
            System.out.println(f1);
        }

    }

    //权限修饰符   数据类型  变量名
    @Test
    public void test2(){
        Class clazz = Person.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f : declaredFields){
            //System.out.println(f);
            //1.权限修饰符
            int modifiers = f.getModifiers();
            //System.out.println(modifiers);
            System.out.print(Modifier.toString(modifiers) + "\t");

            //2.数据类型
            Class type = f.getType();
            System.out.print(type.getName() + "\t");

            //3.变量名
            String fName = f.getName();
            System.out.println(fName);
            System.out.println();

        }

    }

}
 

获取运行时类的方法结构及其内部结构

public class MethodTest {
    //获取运行时类的方法结构
    @Test
    public void test1(){
        Class clazz = Person.class;
        //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
        Method[] methods = clazz.getMethods();
        for(Method m : methods){
            System.out.println(m);
        }
        System.out.println();
        //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for(Method m : declaredMethods){
            System.out.println(m);
        }
    }

    //获取运行时类的方法的内部结构
    @Test
    public void test2(){
        Class clazz = Person.class;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for(Method m : declaredMethods){
            //1.获取方法声明的注解
            Annotation[] annotations = m.getAnnotations();
            for(Annotation a : annotations){
                System.out.println(a);
            }

            //2.权限修饰符
            System.out.print(Modifier.toString(m.getModifiers()) + "\t");

            //3.返回值类型
            System.out.print(m.getReturnType().getName() + "\t");

            //4.方法名
            System.out.print(m.getName());

            //5.形参列表
            System.out.print("(");
            Class[] parameterTypes = m.getParameterTypes();
            if(!(parameterTypes == null && parameterTypes.length == 0)){
                for(int i = 0; i < parameterTypes.length; i++){
                    if(i == parameterTypes.length -1){
                        System.out.print(parameterTypes[i].getName() + "arg_" + i);
                        break;
                    }
                    System.out.print(parameterTypes[i].getName() + "arg_" + i + ", ");
                }
            }
            System.out.print(")");

            //6.抛出的异常
            Class[] exceptionTypes = m.getExceptionTypes();
            if(exceptionTypes.length > 0){
                System.out.print("throws");
                for(int i = 0; i < exceptionTypes.length; i++){
                    if( i == exceptionTypes.length -1){
                        System.out.print(exceptionTypes[i].getName());
                        break;
                    }
                    System.out.print(exceptionTypes[i].getName() + ", ");
                }
            }


            System.out.println();

        }
    }
}

获取其他结构(构造器、父类、接口、包、注解等)
public class OtherTest {
    //获取构造器结构
    @Test
    public void test1(){
        Class clazz = Person.class;
        //getConstructors():获取当前运行时类中声明为public的构造器
        Constructor[] constructors = clazz.getConstructors();
        for(Constructor c : constructors){
            System.out.println(c);
        }

        System.out.println();
        //getDeclaredConstructors():获取当前运行时类中声明的所以构造器
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for(Constructor c : declaredConstructors){
            System.out.println(c);
        }
    }

    //获取运行时类的父类及父类的泛型
    @Test
    public void test2(){
        Class clazz = Person.class;
        //获取运行时类的父类
        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);
        //获取运行时类的泛型父类
        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);
        //获取父类的泛型类型
        ParameterizedType paramType = (ParameterizedType) genericSuperclass;
        Type[] actualTypeArguments = paramType.getActualTypeArguments();
        for(Type t : actualTypeArguments){
            System.out.println(t.getTypeName());
        }
    }

    //获取运行时类实现的接口
    @Test
    public void test3(){
        Class clazz = Person.class;
        Class[] interfaces = clazz.getInterfaces();
        for(Class c : interfaces){
            System.out.println(c);
        }
        System.out.println();
        //获取运行时类的父类实现的接口
        Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
        for(Class c :interfaces1){
            System.out.println(c);
        }
    }

    //获取运行时类所在的包
    @Test
    public void test4(){
        Class clazz = Person.class;
        Package aPackage = clazz.getPackage();
        System.out.println(aPackage);
    }

    //获取运行时类声明的注解
    @Test
    public void test5(){
        Class clazz = Person.class;
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation a : annotations){
            System.out.println(a);
        }
    }

}

应用 3:调用运行时类的指定结构

调用指定的属性
 //调用指定的属性(方法1,不推荐
    @Test
    public void testField() throws Exception{
        Class clazz = Person.class;
        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();
        //获取指定的属性:要求运行时类中的属性声明为public
        //通常不采用此方法
        Field id = clazz.getField("id");
        //设置当前属性值
        id.set(p,1001);
        //获取当前属性值
        int pId = (int) id.get(p);
        System.out.println(pId);
    }

    //调用指定的属性方法2
    @Test
    public void testField1() throws Exception{
        Class clazz = Person.class;
        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();
        //1.getDeclaredField():获取运行时类中指定变量名的属性
        Field name = clazz.getDeclaredField("name");
        //2.保证当前属性是可访问的
        name.setAccessible(true);
        //3.获取、设置指定对象的此属性值
        name.set(p,"Tom");
        System.out.println(name.get(p));
    }
调用指定的方法
 //调用指定的方法
    @Test
    public void testMethod() throws Exception{
        Class clazz = Person.class;
        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();
        //getDeclaredMethod():参数1:方法名称;参数2:方法形参列表
        Method show = clazz.getDeclaredMethod("show", String.class);
        //保证当前属性是可访问的
        show.setAccessible(true);
        //invoke():参数1:方法的调用者 ; 参数2:给方法形参赋值的实参
        Object returnValue = show.invoke(p, "China");
        System.out.println(returnValue);

        //调用静态方法
        Method showDesc = clazz.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        //以下三个都行
        //Object returnValue1 = showDesc.invoke(p);
        //Object returnValue1 = showDesc.invoke(Person.class);
        Object returnValue1 = showDesc.invoke(null);

        System.out.println(returnValue1);

    }

调用指定的构造器

 //调用指定的构造器
    @Test
    public void testConstructor() throws Exception{
        Class clazz = Person.class;
        //1.获取指定构造器
        Constructor constructor = clazz.getDeclaredConstructor(String.class);
        //2.保证此构造器是可访问的
        constructor.setAccessible(true);
        //3.调用此构造器创建运行时类的对象
        Person p = (Person) constructor.newInstance("Tom");
        System.out.println(p.toString());
    }

关于 setAccessible 方法的使用:
• Method 和 Field、Constructor 对象都有 setAccessible()方法。
• setAccessible 启动和禁用访问安全检查的开关。
• 参数值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
        – 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调 用,那么请设置为 true。
        – 使得原本无法访问的私有成员也可以访问
• 参数值为 false 则指示反射的对象应该实施 Java 语言访问检查。

应用4:动态代理

代理设计模式的原理:
使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
 

静态代理


interface ClothFactory{

    void produceCloth();

}

//代理类
class ProxyClothFactory implements ClothFactory{

    private ClothFactory factory;  //用被代理类对象进行实例化

    public ProxyClothFactory(ClothFactory factory){
        this.factory = factory;
    }

    @Override
    public void produceCloth() {
        System.out.println("代理工厂做一些准备工作");
        factory.produceCloth();
        System.out.println("代理工厂做一些后续的收尾工作");
    }
}

//被代理类
class NikeClothFactory implements ClothFactory{

    @Override
    public void produceCloth() {
        System.out.println("耐克工厂生产一批运动服");
    }
}

public class StaticProxyTest {
    public static void main(String[] args) {
        //创建被代理类的对象
        NikeClothFactory nike = new NikeClothFactory();
        //创建代理类的对象
        ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike);
        proxyClothFactory.produceCloth();
    }

}

动态代理


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Human{

    String getBrief();

    void eat(String food);

}



//被代理类
class SuperMan implements Human{

    @Override
    public String getBrief() {
        return "I can fly!";
    }

    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃 " + food);
    }
}



class HumanUtil{

    public void method1(){
        System.out.println("通用方法一");
    }

    public void method2(){
        System.out.println("通用方法二");
    }

}



class ProxyFactory{
    //调用此方法,返回一个代理类的对象
    public static Object getProxyInstance(Object obj){
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.bind(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }
}



class MyInvocationHandler implements InvocationHandler{

    private Object obj; //需要使用被代理类的对象进行赋值

    public void bind(Object obj){
        this.obj = obj;
    }

    //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
    //将被代理类要执行的方法a的功能就声明在invoke()中

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        HumanUtil util = new HumanUtil();
        util.method1();

        //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        //obj:被代理类对象
        Object returnValue =  method.invoke(obj, args);

        util.method2();

        return returnValue;
    }
}



public class ProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        //proxyInstance:代理类的对象
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        String brief = proxyInstance.getBrief();
        System.out.println(brief);
        proxyInstance.eat("汉堡");

        System.out.println("----------------------------");
        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
        proxyClothFactory.produceCloth();

    }

}

十九、Java8新特性

1、Lambda 表达式

Lambda 表达式及其语法

Lambda 是一个 匿名函数 ,我们可以把 Lambda 表达式理解为是 一段可以传递 的代码 (将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使 Java 的语言表达能力得到了提升。

Lambda表达式的本质:作为接口的实例

Lambda表达式语法

Lambda 表达式:在 Java 8 语言中引入的一种新的语法元素和操作符。这个操
作符为 “ -> ” , 该操作符被称为 Lambda 操作符 箭头操作符 。它将 Lambda 分为两个部分:
• 左侧:指定了 Lambda 表达式需要的参数列表
• 右侧:指定了 Lambda 体,是抽象方法的实现逻辑,也即 Lambda 表达式要执行的 功能。

语法格式一:无参,无返回值
//语法格式一:无参,无返回值
    @Test
    public void test1(){
        //未使用 Lambda 表达式
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("我爱北京天安门");
            }
        };
        r1.run();
        System.out.println("***********************");
        //使用 Lambda 表达式
        Runnable r2 = () -> {
            System.out.println("我爱北京故宫");
        };
        r2.run();
    }
语法格式二:Lambda 需要一个参数,但是没有返回值。
 @Test
    public void test2(){
        //未使用 Lambda 表达式
        Consumer<String> con = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        con.accept("谎言和誓言的区别是什么?");

        System.out.println("*******************");
        //使用 Lambda 表达式
        Consumer<String> con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("一个是听得人当真了,一个是说的人当真了");
    }
语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
//语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
    @Test
    public void test3(){
        //语法格式三使用前
        Consumer<String> con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("一个是听得人当真了,一个是说的人当真了");
        System.out.println("*******************");
        //语法格式三使用后
        Consumer<String> con2 = (s) -> {
            System.out.println(s);
        };
        con2.accept("一个是听得人当真了,一个是说的人当真了");
    }
语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略
 //语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略
    @Test
    public void test4(){
        //语法格式四使用前
        Consumer<String> con1 = (s) -> {
            System.out.println(s);
        };
        con1.accept("一个是听得人当真了,一个是说的人当真了");
        System.out.println("*******************");
        //语法格式四使用后
        Consumer<String> con2 = s -> {
            System.out.println(s);
        };
        con2.accept("一个是听得人当真了,一个是说的人当真了");
    }
语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
//语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
    @Test
    public void test5(){
        //语法格式五使用前
        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                System.out.println(o1);
                System.out.println(o2);
                return o1.compareTo(o2);
            }
        };
        System.out.println(com1.compare(12,21));
        System.out.println("*****************************");
        //语法格式五使用后
        Comparator<Integer> com2 = (o1,o2) -> {
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        };
        System.out.println(com2.compare(12,6));
    }
语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
//语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
    @Test
    public void test6(){
        //语法格式六使用前
        Comparator<Integer> com1 = (o1,o2) -> {
            return o1.compareTo(o2);
        };
        System.out.println(com1.compare(12,6));
        System.out.println("*****************************");
        //语法格式六使用后
        Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
        System.out.println(com2.compare(12,21));
    }
    @Test
    public void test7(){
        //语法格式六使用前
        Consumer<String> con1 = s -> {
            System.out.println(s);
        };
        con1.accept("一个是听得人当真了,一个是说的人当真了");
        System.out.println("*****************************");
        //语法格式六使用后
        Consumer<String> con2 = s -> System.out.println(s);
        con2.accept("一个是听得人当真了,一个是说的人当真了");
    }
关于类型推断
在语法格式三 Lambda 表达式中的参数类型都是由编译器推断得出的。
Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断 ”。

举例:

@Test
public void test() {
 //类型推断 1
// ArrayList<String> list = new ArrayList<String>();
 ArrayList<String> list = new ArrayList<>();
 //类型推断 2
//int[] arr = new int{1, 2, 3};
 int[] arr = {1, 2, 3};
}

         

2、函数式(Functional)接口

什么是函数式接口

• 只包含一个抽象方法 (Single Abstract Method,简称 SAM)的接口,称为函数式接 口。当然该接口可以包含其他非抽象方法。
• 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。
• 我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
• 在 java.util.function 包下定义了 Java 8 的丰富的函数式接口

作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收 Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。

Java 内置函数式接口

之前的函数式接口
之前学过的接口,有些就是函数式接口,比如:
• java.lang.Runnable
        – public void run()
• java.lang.Iterable
        – public Iterator iterate()
• java.lang.Comparable
        – public int compareTo(T t)
• java.util.Comparator
        – public int compare(T t1, T t2)

四大核心函数式接口

其他接口

public class LambdaTest2 {
    @Test
    public void test1(){
        happyTime(500, new Consumer<Double>() {
            @Override
            public void accept(Double aDouble) {
                System.out.println("学习太累了,去天上人间买了瓶矿泉水,价格为:" + aDouble);
            }
        });
        System.out.println("---------------");
        happyTime(400,money ->  System.out.println("学习太累了,去天上人间喝了口水,价格为:" + money));

    }

    public void happyTime(double money, Consumer<Double> con){
        con.accept(money);
    }



    @Test
    public void test2(){
        List<String> list = Arrays.asList("北京", "南京", "天津", "东京", "西经", "普京");
        List<String> newList = filterString(list, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.contains("京");
            }
        });
        System.out.println(newList);

        System.out.println("-----------------");
        List<String> newList1 = filterString(list, s -> s.contains("京"));
        System.out.println(newList1);

    }


    public List<String> filterString(List<String> list , Predicate<String> pre){
        ArrayList<String> filterList = new ArrayList<>();
        for(String s : list){
            if(pre.test(s)){
                filterList.add(s);
            }
        }
        return filterList;
    }

}

3、方法引用与构造器引用

Lambda 表达式是可以简化函数式接口的变量或形参赋值的语法。而方法引用和构造器引用是为了简化 Lambda 表达式的。 

方法引用

当要传递给 Lambda 体的操作,已经有实现的方法了,可以使用方法引用。
方法引用可以看做是 Lambda 表达式深层次的表达。换句话说,方法引用就是Lambda 表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是 Lambda 表达式的一个语法糖。

方法引用格式 
• 格式:使用方法引用操作符 “:: ” 将类(或对象) 与 方法名分隔开来。
        – 两个:中间不能有空格,而且必须英文状态下半角输入
• 如下三种主要使用情况:
        – 情况 1:对象 :: 实例方法名
        – 情况 2:类 :: 静态方法名
        – 情况 3:类 :: 实例方法名

方法引用使用前提
要求 1: Lambda 体只有一句语句,并且是通过调用一个对象的/类现有的方法来完成的
例如:
System.out 对象,调用 println()方法来完成 Lambda 体
Math 类,调用 random()静态方法来完成 Lambda 体
要求 2:
针对情况 1:函数式接口中的抽象方法 a 在被重写时使用了某一个对象的方法b。如果方法 a 的形参列表、返回值类型与方法 b 的形参列表、返回值类型都相同,则我们可以使用方法 b 实现对方法 a 的重写、替换。
针对情况 2:函数式接口中的抽象方法 a 在被重写时使用了某一个类的静态方法 b。如果方法 a 的形参列表、返回值类型与方法 b 的形参列表、返回值类型都相同,则我们可以使用方法 b 实现对方法 a 的重写、替换。
针对情况 3:函数式接口中的抽象方法 a 在被重写时使用了某一个对象的方法b。如果方法 a 的返回值类型与方法 b 的返回值类型相同,同时方法 a 的形参列表中有 n 个参数,方法 b 的形参列表有 n-1 个参数,且方法 a 的第 1 个参数作为方法 b 的调用者,且方法 a 的后 n-1 参数与方法 b 的 n-1 参数匹配(类型相同或满足多态场景也可以)
例如:
t->System.out.println(t)
() -> Math.random()        都是无参

public class MethodRefTest {
    // 情况一:对象 :: 实例方法
    //Consumer 中的 void accept(T t)
    //PrintStream 中的 void println(T t)
    @Test
    public void test1(){
        Consumer<String> con1 = str -> System.out.println(str);
        con1.accept("北京");
        System.out.println("**********************");
        PrintStream ps = System.out;
        Consumer<String> con2 = ps::println;
        con2.accept("beijing");
    }

    //Supplier 中的 T get()
    //Employee 中的 String getName()
    @Test
    public void test2(){
        Employee emp = new Employee("Tom", "海天大厦", 28);
        Supplier<String> sup1 = () -> emp.getName();
        System.out.println(sup1.get());
        System.out.println("*******************");
        Supplier<String> sup2 = emp::getName;
        System.out.println(sup2.get());
    }


    // 情况二:类 :: 静态方法
    //Comparator 中的 int compare(T t1,T t2)
    //Integer 中的 int compare(T t1,T t2)
    @Test
    public void test3(){
        Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2);
        System.out.println(com1.compare(12,231));
        System.out.println("----------------------");
        Comparator<Integer> com2 = Integer::compare;
        System.out.println(com2.compare(12,22));
    }


    //Function 中的 R apply(T t)
    //Math 中的 Long round(Double d)
    @Test
    public void test4(){
        Function<Double,Long> func = new Function<Double, Long>() {
            @Override
            public Long apply(Double aDouble) {
                return Math.round(aDouble);
            }
        };
        System.out.println(func.apply(10.5));
        Function<Double, Long> func1 = aDouble -> Math.round(aDouble);
        System.out.println(func1.apply(12.3));
        Function<Double,Long> func2 = Math::round;
        System.out.println(func2.apply(15.8));
    }


    // 情况三:类 :: 实例方法 (有难度)
    // Comparator 中的 int comapre(T t1,T t2)
    // String 中的 int t1.compareTo(t2)
    @Test
    public void test5(){
        Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2);
        System.out.println(com1.compare("abc","abd"));
        Comparator<String> com2 = String::compareTo;
        System.out.println(com2.compare("abc","abc"));
    }

    //BiPredicate 中的 boolean test(T t1, T t2);
    //String 中的 boolean t1.equals(t2)
    @Test
    public void test6(){
        BiPredicate<String, String> pre1 = (t1, t2) -> t1.equals(t2);
        System.out.println(pre1.test("abc","abd"));
        BiPredicate<String, String> pre2 = String::equals;
        System.out.println(pre2.test("abc","abc"));
    }


    // Function 中的 R apply(T t)
    // Employee 中的 String getName();
    @Test
    public void test7(){
        Employee employee = new Employee("Tom","北京",12);
        Function<Employee,String> func = e -> e.getName();
        System.out.println(func.apply(employee));
        Function<Employee,String> func1 = Employee::getName;
        System.out.println(func1.apply(employee));
    }

    
}

构造器引用

当 Lambda 表达式是创建一个对象,并且满足 Lambda 表达式形参,正好是给创建这个对象的构造器的实参列表,就可以使用构造器引用。
格式: 类名 ::new

public class ConstructorRefTest {
    //构造器引用
    //Supplier 中的 T get()
    //Employee 的空参构造器:Employee()
    @Test
    public void test1(){
        Supplier<Employee> sup = new Supplier<Employee>() {
            @Override
            public Employee get() {
                return new Employee();
            }
        };
        Supplier<Employee> sup1 = () -> new Employee();
        System.out.println(sup1.get());
        Supplier<Employee> sup2 = Employee::new;
        System.out.println(sup2.get());
    }


    //Function 中的 R apply(T t)
    @Test
    public void test2(){
        Function<Integer, Employee> func1 = age -> new Employee(age);
        System.out.println(func1.apply(12));
        Function<Integer,Employee> func2 = Employee::new;
        System.out.println(func2.apply(10));
    }

    //BiFunction 中的 R apply(T t,U u)
    @Test
    public void test3(){
        BiFunction<String,Integer,Employee> func1 = (name,age) -> new Employee(name,age);
        System.out.println(func1.apply("Tom",18));
        BiFunction<String,Integer,Employee> func2 = Employee::new;
        System.out.println(func2.apply("Jerry",22));
    }
}

数组构造引用

当 Lambda 表达式是创建一个数组对象,并且满足 Lambda 表达式形参,正好是给创建这个数组对象的长度,就可以数组构造引用。
格式: 数组类型名 ::new

//数组引用
    //Function 中的 R apply(T t)
    @Test
    public void test4(){
        Function<Integer, String[]> func1 = length -> new String[length];
        System.out.println(Arrays.toString(func1.apply(5)));
        Function<Integer,String[]> func2 = String[] :: new;
        System.out.println(Arrays.toString(func2.apply(10)));
    }
}

4、强大的 Stream API

Stream API 说明

• Java8 中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。
• Stream API ( java.util.stream) 把真正的函数式编程风格引入到 Java 中。这是目前为止对 Java 类库最好的补充 ,因为 Stream API 可以极大提供 Java 程序员的生产力,让程序员写出高效率、干净、简洁的代码。
• Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用 Stream API 对集合 数据进行操作,就类似于使用 SQL 执行的数据库查询。 也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

为什么要使用 Stream API
实际开发中,项目中多数数据源都来自于 MySQL、Oracle 等。但现在数据源可以更多了,有 MongDB,Radis 等,而这些 NoSQL 的数据就需要 Java 层面去处理。

什么是 Stream
Stream 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
Stream 和 Collection 集合的区别: Collection 是一种静态的内存数据结构, 讲的是数据,而 Stream 是有关计算的,讲的是计算。 前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过 CPU 实现计算。
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新 Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。即一旦执行终止操作,就执行中间操作链,并产生结果。
④ Stream 一旦执行了终止操作,就不能再调用其它中间操作或终止操作了。

Stream 的操作三个步骤

1- 创建 Stream 一个数据源(如:集合、数组),获取一个流
2- 中间操作 每次处理都会返回一个持有结果的新 Stream,即中间操作的方法返回值仍然是Stream 类型的对象。因此中间操作可以是个操作链 ,可对数据源的数据进行 n 次处理,但是在终结操作前,并不会真正执行。
3- 终止操作(终端操作) 终止操作的方法返回值类型就不再是 Stream 了,因此一旦执行终止操作,就结束整个 Stream 操作了。一旦执行终止操作,就执行中间操作链,最终产生结果并结束 Stream。

创建 Stream 实例

方式一:通过集合
Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:
• default Stream stream() : 返回一个顺序流
• default Stream parallelStream() : 返回一个并行流
 @Test
    public void test1(){
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        Stream<Integer> stream = list.stream();
    }

 

方式二:通过数组
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
• static Stream stream(T[] array): 返回一个流
• public static IntStream stream(int[] array)
• public static LongStream stream(long[] array)
• public static DoubleStream stream(double[] array)
 @Test
    public void test2(){
        String[] arr = {"hello", "world"};
        Stream<String> stream = Arrays.stream(arr);
    }

    @Test
    public void test3(){
        int[] arr = {1, 2, 3, 4, 5};
        IntStream stream = Arrays.stream(arr);
    }

方式三:通过 Stream 的 of()
可以调用 Stream 类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。
• public static Stream of(T... values) : 返回一个流

 @Test
    public void test4(){
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
        stream.forEach(System.out::println);
    }
方式四:创建无限流(了解)
可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
• 迭代 public static Stream iterate(final T seed, final UnaryOperator f)
• 生成 public static Stream generate(Supplier s)
 @Test
    public void test5(){
        // 迭代
        // public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
        Stream<Integer> stream = Stream.iterate(0, x -> x + 2);
        stream.limit(10).forEach(System.out::println);
        // 生成
        // public static<T> Stream<T> generate(Supplier<T> s)
        Stream<Double> stream1 = Stream.generate(Math::random);
        stream1.limit(10).forEach(System.out::println);
    }

一系列中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。

1-筛选与切片

@Test
    public void test1(){
        //1、创建 Stream
        Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
        //2、加工处理
        //过滤:filter(Predicate p)
        //把里面的偶数拿出来
        /*
         * filter(Predicate p)
         * Predicate 是函数式接口,抽象方法:boolean test(T t)
         */
        stream = stream.filter(t -> t % 2 == 0);
        //3、终结操作:例如:遍历
        stream.forEach(System.out::println);
    }

    @Test
    public void test2(){
        Stream.of(1, 2, 3, 4, 5, 6).filter(t -> t%2==0).forEach(System.out::println);
    }

    @Test
    public void test3(){
        Stream.of(1,2,2,3,4,4,5,5,5,6).distinct().forEach(System.out::println);
    }

    @Test
    public void test4(){
        Stream.of(1,2,2,3,4,4,5,5,5,6).limit(3).forEach(System.out::println);
    }

    @Test
    public void test5(){
        Stream.of(1,2,2,3,4,4,5,5,5,6).skip(3).forEach(System.out::println);
    }

2-映 射

 

@Test
    public void test6(){
        Stream.of(1, 2, 3, 4, 5, 6).map(t -> t += 1).forEach(System.out::println);
    }

    @Test
    public void test7(){
        String[] arr = {"hello", "world", "java"};
        Arrays.stream(arr).map(t -> t.toUpperCase()).forEach(System.out::println);
    }

    @Test
    public void test8(){
        String[] arr = {"hello", "world", "java"};
        Arrays.stream(arr).flatMap(t -> Stream.of(t.split("|"))).forEach(System.out::println);
    }

3-排序 

 @Test
    public void test9(){
        Stream.of(11, 2, 39, 4, 54, 6, 2, 22, 3, 3, 4, 54, 54).distinct().sorted((t1, t2) -> Integer.compare(t1, t2)).forEach(System.out::println);
    }

终止操作
• 终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、 Integer,甚至是 void 。
• 流进行了终止操作后,不能再次使用。

1-匹配与查找

 

 

 @Test
    public void test1(){
        Stream.of(1,2,3,4,5,6).forEach(System.out::println);
    }

    @Test
    public void test2(){
        long count = Stream.of(1,2,3,4,5).count();
        System.out.println("count:" + count);
    }

    @Test
    public void test3(){
        boolean result = Stream.of(2, 4, 6, 8, 9).allMatch(t -> t % 2 == 0);
        System.out.println(result);
    }

    @Test
    public void test4(){
        boolean result = Stream.of(2, 4, 6, 8, 9).anyMatch(t -> t % 2 == 0);
        System.out.println(result);
    }

    @Test
    public void test5(){
        Optional<Integer> opt = Stream.of(1,2,3,4,5,6).findFirst();
        System.out.println(opt);
    }

    @Test
    public void test6(){
        Optional<Integer> opt = Stream.of(1,2,3,4,5,6,7).filter(t->t%3==0).findFirst();
        System.out.println(opt);
    }

    @Test
    public void test07(){
        Optional<Integer> opt = Stream.of(1,2,4,5,7,8)
                .filter(t -> t%3==0)
                .findFirst();
        System.out.println(opt);
    }

    @Test
    public void test8(){
        Optional<Integer> max = Stream.of(1, 2, 3, 4, 5, 6).max((t1, t2) -> Integer.compare(t1, t2));
        System.out.println(max);
    }
2-归约

 @Test
    public void test9(){
        Integer reduce = Stream.of(1,2,3,4).reduce(5,(t1,t2)->t1+t2);
        System.out.println(reduce);
    }

    @Test
    public void test10(){
        Optional<Integer> max = Stream.of(2, 8, 4, 19, 12).reduce((t1, t2) -> t1 > t2 ? t1 : t2);
        System.out.println(max);
    }

3-收集 

 

 

 

 @Test
    public void test11(){
        List<Integer> list = Stream.of(1,2,4,5,7,8)
                .filter(t -> t%2==0)
                .collect(Collectors.toList());
        System.out.println(list);
    }

optional类

到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。

Optional<T>类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用null表示一个值不存在,现在 Optional可以更好的表达这个概念。并且可以避免空指针异常。

Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
 

EndNote20保姆级安装教程:从下载到汉化一步到位(附闪退解决方案) 本文提供EndNote20的详细安装与汉化教程,涵盖系统准备、分步安装、路径优化及闪退解决方案。针对科研人员的需求,特别优化了中文环境下的使用体验,确保文献管理工具的高效稳定运行。 阅读详情

相关推荐

【推荐100个unity插件之2】 DoTween动画插件的安装和使用整合(最全)

摘要:DOTween动画插件使用指南 DOTween是Unity中强大的动画插件,比原生动画更便捷。安装后需进行初始化设置,通过工具菜单生成程序集。主要提供通用方法(DOTween.To)和便捷方法(如transform.DOMove)两种动画实现方式。插件支持丰富的动画设置(循环、缓动等)和事件回调(OnComplete、OnKill)。还提供动画管理功能,包括清除(DOTween.Clear)、完成(DOTween.CompleteAll)和终止(DOTween.KillAll)动画。常用功能涵盖移动、

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 1万+

硅谷JAVA基础笔记吐血整理

硅谷java教程全程跟听,手动整理,从面向对象开始按章节按课时整理,适合对照视频作为笔试使用/java知识脉络梳理/八股理解背诵

LED_温控表目标检测数据集00.7z

LED温控表目标检测数据集,通过百度OCR深度学习后可以识别温控表位置检测

Java学习笔记----第一部分:java基础编程(硅谷宋红康老师)

了解java的基础知识,学习java中的基本语法,学习java的面向对象和异常知识

m0_52574098的博客 9441

硅谷JAVA笔记

硅谷JAVA笔记

wei的博客 1503

javaSE(变量与运算符、流程控制语句)

javaSE 变量与运算符、流程控制语句

char_China_man的博客 191

JavaSE目录(硅谷学习笔记)

跳转到总目录 01、版本一:视频教程笔记 这个版本是我跟着视频教程做的笔记 教程来源:java基础到高级_零基础自学Java硅谷–宋红康教程视频地址:https://www.bilibili.com/video/BV1ny4y1Y7CWMarkdown版笔记下载:h...

scy的技术博客 4245

硅谷JavaSE笔记

对第一个java程序进行总结java程序编写-编译-运行的过程编写: 编写java文件编译: javac命令编译java文件运行: java命令运行字节码文件在一个java源文件可以声明多个class,但是,只能最多有一个类声明为public。并且要求声明为public的类的类名必须与源文件名相同。程序的入口是main方法,格式是固定的先输出数据,然后换行只输出数据每一行执行语句都以“;”结束。编译的过程:编译以后,会生成一个或多个字节码文件。

qq_44038801的博客 714

硅谷JavaSE笔记(二)

笔记为B站硅谷Java入门视频教程(在线答疑+Java面试真题)笔记,是笔者对于已经学过的大二Java课程的查缺补漏及巩固。部分C语言学过的基本语法将跳过。异常:在Java语言中,将程序执行中发生的不正常情况称为“异常”(开发过程中的语法错误和逻辑错误不是异常)Java程序在执行过程中所发生的异常事件可分为两类:>Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM。一般不编写针对性的代码进行处理。

guanze1的博客 707

硅谷JavaSE笔记(一)

Java基础,到面向对象部分

guanze1的博客 865

硅谷JavaWEB学习笔记1(自整理,自用)

JavaWEB学习笔记 1、HTML 1.1、B/S软件的结构 Javase C/S结构 Client Server客户端----->服务器 B/S结构 Brower Server结构 浏览器----->服务器 1.2、前端的开发流程 美术实现---------------------------------------------->前端工程师------------------------------------------->java后台 网页设计师根据需求设计

qq_43130076的博客 6480

硅谷经典Java面试题(第1季)

01_硅谷_JavaSE面试题:自增变量 运行结果为i=4;j=1;k=11. 分析: 参考资料: ①Eclipse打开字节码指令:选择Text.class文件,点击Open With->Class File Viewer ②i++和++i:i++,先运算,后加一;++i,先加一,后运算。 ③https://mbd.baidu.com/ma/landingpage?t=smartapp...

qq_42466244的博客 8285

硅谷JavaSE笔记(四)

笔记为B站硅谷Java入门视频教程(在线答疑+Java面试真题)笔记,是笔者对于已经学过的大二Java课程的查缺补漏及巩固。部分C语言学过的基本语法将跳过。Java 提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在 Java 的本机安装系统里,由 JVM 进行控制。并且 Java 实现了一个跨平台的 网络库,程序员面对的是一个统一的网络编程环境。软件架构C/S 架构:全称为 Client/Server 结构,是指客户端和服务器结构。

guanze1的博客 619

笔记硅谷案例代码大全资源集锦

笔记硅谷案例代码大全资源集锦 去发现同类优质开源项目:https://gitcode.com/ 欢迎来到笔记硅谷案例代码大全资源集锦!这是一个全面的案例代码集合,涵盖了前端、后端以及全栈开发所需的多项技术。以下是本资源集锦的详细内容介绍: 资源包含 前端基础教程 前端html+css零基础教程:适合初学者的html和css入门教程,助您快速掌握前端基础。 2023最新前端开发html5+cs...

gitblog_06773的博客 941

java学习笔记硅谷

JAVA学习笔记

qq_41578884的博客 658

硅谷Java基础篇知识笔记

观看硅谷2019版Java知识视频所做笔记

weixin_73617818的博客 1412

JAVA知识点(硅谷宋红康老师的b站授课自我整理)

java基础知识

weixin_44289107的博客 1914

硅谷java课程笔记--语法

法1、e.g.int age = 19;法2、char gender;//变量声明gender = '女';//变量赋值数据类型。

Annie_____aaa的博客 2147

硅谷java基础详细笔记(附实例),持续更新中

java基础及实例,适合小白初学者

m0_52181584的博客 2570

硅谷JavaSE基础课堂笔记

面向对象的重点是类的设计->类的成员设计属性:该类事物的状态信息,对应成员变量也对应field行为:要做什么样的操作,对应成员方法也对应method面向对象完成具体功能的三步流程创建类,并设计类的内部成员(属性,方法)创建类的对象,比如,Phone p1 = new Phone();通过对象调用内部属性和方法,完成相关功能。虚拟机栈(栈):用于存放方法中声明的变量。堆:new出来的结构。比如:数组实体,对象实体,包括对象中的属性。方法区:存放类的模板,比如:Phone类的模板。

weixin_75181956的博客 657
上一篇: 尚硅谷JavaSE笔记(四)
guanze1
博客等级 码龄6年 17粉丝 11原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值