反射枚举及lambda的使用

本文详细介绍了Java中的反射机制,包括反射的基本概念、用途、Class类、Field类、Method类、Constructor类以及注解的相关方法。此外,还探讨了枚举的定义、使用场景、优缺点以及与反射的交互。最后,讲解了Lambda表达式,包括其背景、语法、使用示例以及在集合操作中的应用,展示了Lambda如何简化代码和提升效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、反射

1.定义

2.用途

3.反射基本信息

4.反射相关的类

(1)Class类(反射机制的起源)

(2)Field类的相关方法

(3)Method类的相关方法

(4)Constructor类的相关方法

(5)获得类中注解相关的方法

(6)获得Class对象的三种方式

(7)反射的使用

五、反射优点和缺点

二、枚举

1.背景及定义

2.枚举的使用

3.枚举的优缺点

4.枚举和反射

5.一条重要的题

三、lambda表达式

1.背景

2.基本语法

3.基本使用

4.变量捕获

5.lambda在集合当中的使用

(1)Collection接口

(2)List接口

 (3)Map接口

 (6)lambda的优缺点


一、反射

1.定义

Java 的反射(reflection)机制是在 运行 状态中,对于任意一个类,都能够知道这个类的 所有属性和方法 ;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息;这种动态获取信息以及动态调用对象方法的功能称java 语言的反射(reflection)机制。

2.用途

(1)在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java 的反射机制通过反射来获取所需的私有成员或是方法 。
(2)反射最重要的用途就是开发各种通用框架,比如在 spring 中,我们将所有的类 Bean 交给 spring 容器管理,无论是XML 配置 Bean 还是注解配置,当我们从容器中获取 Bean 来依赖注入时,容器会读取配置,而配置中给的就是类的信息,spring 根据这些信息,需要创建那些 Bean spring 就动态的创建这些类。

3.反射基本信息

Java 程序中许多对象在运行时会出现两种类型: 运行时类型 (RTTI) 和编译时类型 ,例如 Person p = new Student();这句代码中 p 在编译时类型为 Person ,运行时类型为 Student 。程序需要在运行时发现对象和类的真实信心。而通过用反射程序就能判断出该对象和类属于哪些类。

4.反射相关的类

类名
用途
Class
代表类的实体,在运行的 Java 应用程序中表示类和接口
Field
代表类的成员变量 / 类的属性
Method类
代表类的方法
Constructor
代表类的构造方法

(1)Class类(反射机制的起源)

①Class类代表类的实体,在运行的 Java 应用程序中表示类和接口  。Java文件被编译后,生成了 .class 文件, JVM 此时就要去解读 .class 文件 , 被编译后的 Java 文件 .class 也被 JVM 解析为一个对象,这个对象就是 java.lang.Class . 这样当程序在运行时,每个类 文件就最终变成了 Class 类对象的一个实例。我们通过Java 的反射机制应用到这个实例,就可以去 获得甚至去添加改变这个类的属性和动作 ,使得这个类成为一个动态的类 。
②Class类的相关方法
方法
用途
getClassLoader()
获得类的加载器
getDeclaredClasses()
返回一个数组,数组中包含该类中所有类和接口类的对象 ( 包括私有的 )
forName(String className)
根据类名返回类的对象
newInstance()
创建类的实例
getName()
获得类的完整路径名字

(2)Field类的相关方法

方法用途
getField(String name)
获得某个公有的属性对象
getFields()
获得所有公有的属性对象
getDeclaredField(String name)
获得某个属性对象
getDeclaredFields()
获得所有属性对象

(3)Method类的相关方法

方法用途
getMethod(String name, Class...<?> parameterTypes)
获得该类某个公有的方法
getMethods()
获得该类所有公有的方法
getDeclaredMethod(String name, Class...<?> parameterTypes)
获得该类某个方法
getDeclaredMethods()
获得该类所有方法

(4)Constructor类的相关方法

方法用途
getConstructor(Class...<?> parameterTypes)
获得该类中与参数类型匹配的公有构造方法
getConstructors()
获得该类的所有公有构造方法
getDeclaredConstructor(Class...<?> parameterTypes)
获得该类中与参数类型匹配的构造方法
getDeclaredConstructors()
获得该类所有构造方法

(5)获得类中注解相关的方法

方法用途
getAnnotation(Class annotationClass)
返回该类中与参数类型匹配的公有注解对象
getAnnotations()
返回该类所有的公有注解对象
getDeclaredAnnotation(Class annotationClass)
返回该类中与参数类型匹配的所有注解对象
getDeclaredAnnotations()
返回该类所有的注解对象

(6)获得Class对象的三种方式

在反射之前,我们需要做的第一步就是先拿到当前需要反射的类的 Class 对象,然后通过 Class 对象的核心方法,达到反射的目的,即:在运行 状态中,对于任意一个类,都能够知道这个类的 所有属性和方法 ;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息。
第一种 ,使用 Class.forName(" 类的全路径名 "); 静态方法。 前提:已明确类的全路径名。
第二种 ,使用 .class 方法。 说明:仅适合在编译前就已经明确要操作的 Class
第三种 ,使用类对象的 getClass() 方法
package reflect;

class Student{
    //私有属性name
    private String name = "bit";
    //公有属性age
    public int age = 18;
    //不带参数的构造方法
    public Student(){
        System.out.println("Student()");
    }
    private Student(String name,int age) {
        this.name = name;
        this.age = age;
        System.out.println("Student(String,name)");
    }
    private void eat(){
        System.out.println("i am eat");
    }
    public void sleep(){
        System.out.println("i am a pig");
    }
    private void function(String str) {
        System.out.println(str);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class TestDemo2 {
    public static void main(String[] args) throws ClassNotFoundException{
        Class<?> c1 = Class.forName("reflect.Student");

        Class<?> c2 = Student.class;

        Student student = new Student();
        Class<?> c3 = student.getClass();

        System.out.println(c1 == c2);
        System.out.println(c1 == c3);
        System.out.println(c2 == c3);
        //全部都是true,说明不管使用哪种方式获取Class对象,该对象只有一个
    }
}

(7)反射的使用

(以下代码与上面的代码在同一个包底下)

package reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectClassDemo {
    //通过Class类的newInstance方法获取学生的一个实例
    public static void reflectNewInstance() {
        try{
            Class<?> c1 = Class.forName("reflect.Student");
            Student student = (Student) c1.newInstance();//newInstance()的返回值是T类型
            System.out.println(student);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    // 反射私有或公有的构造方法 屏蔽内容为获得公有的构造方法
    public static void reflectPrivateConstructor() {
        try{
            Class<?> c1 = Class.forName("reflect.Student");
            Constructor<?> constructor = c1.getDeclaredConstructor(String.class, int.class);
            constructor.setAccessible(true);
            Student student = (Student)constructor.newInstance("abc",18);
            System.out.println(student);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    //反射私有属性(获取私有或公开的)
    public static void reflectPrivateField() {
        try {
            Class<?> c1 = Class.forName("reflect.Student");
            Student student = (Student) c1.newInstance();
            Field field = c1.getDeclaredField("name");
            field.setAccessible(true);
            field.set(student,"zhangssan");
            System.out.println(student);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
    //反射私有的方法
    public static void reflectPrivateMethod() {
        try {
            Class<?> c1 = Class.forName("reflect.Student");
            Student student = (Student) c1.newInstance();
            Method method = c1.getDeclaredMethod("function", String.class);
            method.setAccessible(true);
            method.invoke(student,"我是私有方法的参数");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        //reflectNewInstance();
        //reflectPrivateConstructor();
        //reflectPrivateField();
        reflectPrivateMethod();
    }
}

五、反射优点和缺点

优点:
1. 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法
2. 增加程序的灵活性和扩展性,降低耦合性,提高自适应能力
3. 反射已经运用在了很多流行框架如: Struts Hibernate Spring 等等。
缺点:
1. 使用反射会有效率问题。会导致程序效率降低。
2. 反射技术绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂 。

二、枚举

1.背景及定义

枚举是在 JDK1.5 以后引入的。主要用途是:将一组常量组织起来,在这之前表示一组常量通常使用定义常量的方式:
public static int final RED = 1;
public static int final GREEN = 2; 
public static int final BLACK = 3;
但是常量举例有不好的地方,例如:可能碰巧有个数字 1 ,但是他有可能误会为是 RED ,现在我们可以直接用枚举来进行组织,这样一来,就拥有了类型,枚举类型。而不是普通的整形1.
public enum TestEnum { 
    RED,BLACK,GREEN; 
}
优点:将常量组织起来统一进行管理
场景:错误状态码,消息类型,颜色的划分,状态机等等 ....
本质:是 java.lang.Enum 的子类,也就是说,自己写的枚举类,就算没有显示的继承 Enum ,但是其默认继承了 这个类

2.枚举的使用

(1)switch语句里面:

public enum TestEnum {
    RED,BLACK,GREEN,WHITE;
    public static void main(String[] args) {
        TestEnum testEnum2 = TestEnum.BLACK;
        switch (testEnum2) {
            case RED:
                System.out.println("red");
                break;
            case BLACK:
                System.out.println("black");
                break;
            case WHITE:
                System.out.println("WHITE");
                break;
            case GREEN:
                System.out.println("black");
                break;
            default:
                break;
        }
    }
}
(2)常用方法
方法名称描述
values()
以数组形式返回枚举类型的所有成员
ordinal()
获取枚举成员的索引位置
valueOf()
将普通字符串转换为枚举实例
compareTo()
比较两个枚举成员在定义时的顺序
public enum TestEnum {
    RED,BLACK,GREEN,WHITE;

    public static void main(String[] args) {
        TestEnum[] testEnums = TestEnum.values();
        for (int i = 0; i < testEnums.length; i++) {
            System.out.println(testEnums[i] + "-->" + testEnums[i].ordinal());
        }
    }
}
public enum TestEnum {
    RED,BLACK,GREEN,WHITE;

    public static void main(String[] args) {
        TestEnum testEnum = TestEnum.valueOf("RED");//把字符串变成对应的枚举对象
        System.out.println(testEnum);//RED
        System.out.println(RED.compareTo(GREEN));//-2
        System.out.println(BLACK.compareTo(RED));//1
    }
}

 (3)枚举的构造方法默认是私有的

public enum TestEnum {
    RED("red",1),BLACK(),GREEN,WHITE;//它属于对象,当我们在它们后面加括号后相当于调用构造方法了
    
    public String color;
    public int ordinal;
    //构造方法默认是私有的
    TestEnum(String color,int ordinal){
        this.color = color;
        this.ordinal = ordinal;
    }
    TestEnum(){
        
    }
}

3.枚举的优缺点

优点:
1. 枚举常量更简单安全 。
2. 枚举具有内置方法 ,代码更优雅
缺点:
1. 不可继承,无法扩展

4.枚举和反射

既然枚举的构造方法是私有的,那么是否可以通过反射来获取到枚举对象的实例呢?

package enumdemo;

public enum TestEnum {
    RED("red",1),BLACK("black",3),GREEN("green",8), WHITE("white",2);

    public String color;
    public int ordinal;
    //构造方法默认是私有的
    TestEnum(String color,int ordinal){
        //super();
        this.color = color;
        this.ordinal = ordinal;
    }
}
package enumdemo;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class TestEnumReflect {
    public static void reflectPrivateConstructor(){
        try{
            Class<?> classEnum = Class.forName("enumdemo.TestEnum");
            Constructor<?> declaredConstructorStudent =
                    classEnum.getDeclaredConstructor(String.class,int.class);
            declaredConstructorStudent.setAccessible(true);
            Object objectStudent = declaredConstructorStudent.newInstance("green",666);
            TestEnum testEnum = (TestEnum) objectStudent;
            System.out.println("获得枚举的私有构造函数:"+testEnum);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
    public static void main(String[] args) {
        reflectPrivateConstructor();
    }
}

编译并运行该代码,输出如下:

java.lang.NoSuchMethodException: TestEnum.<init>(java.lang.String, int)的意思 就是没有对应的构造方法,但是我们提供的枚举的构造方法就是两个参数分别是String 和 int 啊!问题出现在哪里呢?我们说过的,我们所有的枚举类,都是默认继承与 java.lang.Enum , 继承了父类除构造函数外的所有东西,并且子类要帮助父类进行构造! 而我们写的类,并没有帮助父类构造!所以我们要在自己的枚举类里面,提供super 吗?不是的,枚举比较特殊,虽然我们写的是两个,但是默认他还添加了两个参数,哪两个参数呢?我们看一下Enum 类的源码:

 

 那么现在我们来修改一下它吧!

package enumdemo;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class TestEnumReflect {
    public static void reflectPrivateConstructor(){
        try{
            Class<?> classEnum = Class.forName("enumdemo.TestEnum");
            Constructor<?> declaredConstructorStudent =
                    classEnum.getDeclaredConstructor(String.class,int.class,String.class,int.class);
            declaredConstructorStudent.setAccessible(true);
            Object objectStudent = declaredConstructorStudent.newInstance("green",666,"abc",999);
            TestEnum testEnum = (TestEnum) objectStudent;
            System.out.println("获得枚举的私有构造函数:"+testEnum);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
    public static void main(String[] args) {
        reflectPrivateConstructor();
    }
}

编译并运行该代码,输出如下: 

 它还在报错,这是为什么呢?我们来看一下部分底层的源码:

 这说明:枚举十分安全,不能通过反射来获取枚举的实例对象

总结:

枚举本身就是一个类,其构造方法默认为私有的,且都是默认继承与 java.lang.Enum

②枚举可以避免反射和序列化问题

5.一条重要的题

写一个单例模式

(1)普通实现

public class Singleton { 
    private volatile static Singleton uniqueInstance; 
    private Singleton() {}
    public static Singleton getInstance() { 
        if (uniqueInstance == null) { 
            synchronized (Singleton.class){ 
                if(uniqueInstance == null){//进入区域后,再检查一次,如果仍是null,才创建实例                                                                                                                 
                    uniqueInstance = new Singleton(); 
                } 
            } 
        }  
        return uniqueInstance; 
    } 
}

(2)用静态内部类实现一个单例模式

class Singleton { /** 私有化构造器 */ 
    private Singleton() { }/** 对外提供公共的访问方法 */ 
    public static Singleton getInstance() { 
        return UserSingletonHolder.INSTANCE; 
    }/** 写一个静态内部类,里面实例化外部类 */ 
    private static class UserSingletonHolder { 
        private static final Singleton INSTANCE = new Singleton(); 
    } 
}
public class Main { 
    public static void main(String[] args) { 
        Singleton u1 = Singleton.getInstance(); 
        Singleton u2 = Singleton.getInstance(); 
        System.out.println("两个实例是否相同:"+ (u1==u2)); 
    } 
}

(3)用枚举实现一个单例模式

public enum TestEnum { 
    INSTANCE; public TestEnum getInstance(){ 
        return INSTANCE; 
    }
    public static void main(String[] args) { 
        TestEnum singleton1=TestEnum.INSTANCE; 
        TestEnum singleton2=TestEnum.INSTANCE; 
        System.out.println("两个实例是否相同:"+(singleton1==singleton2)); 
    } 
}

三、lambda表达式

1.背景

Lambda 表达式是 Java SE 8 中一个重要的新特性。 lambda 表达式允许你通过表达式来代替功能接口。 lambda 表达式就和方法一样, 它提供了一个正常的参数列表和一个使用这些参数的主体 (body, 可以是一个表达式或一个代码块) Lambda 表达式( Lambda expression )可以看作是一个匿名函数 ,基于数学中的 λ 演算得名,也可称为闭包(Closure

2.基本语法

(i)基本语法 : (parameters) -> expression (parameters) ->{ statements; }
Lambda 表达式由三部分组成:
(1)  paramaters :类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM 隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。
(2)->:可理解为 被用于 的意思
(3)方法体 :可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不返回,这里的代码块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不返回。
举几个例子:
不需要参数,返回值为 2                       () -> 2
接收一个参数(数字类型),返回其2倍的值                x -> 2 * x
接受2个参数(数字),并返回他们的和                   (x, y) -> x + y
接收2个int型整数,返回他们的乘积                  (int x, int y) -> x * y
接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)                  (String s) -> System.out.print(s)
(ii)函数式接口: 一个接口有且只有一个抽象方法
注意:
①如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口
②如果我们在某个接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解。加上就会自动进行检测的。
package lambdademo;

//函数式接口(只有一个抽象方法)
@FunctionalInterface//这个注释修饰这个接口,说明它是函数式接口
interface NoParameterNoReturn{
    void test();
}
public class TestDemo {
    public static void main(String[] args) {
        //简化的写法
        NoParameterNoReturn noParameterNoReturn = () -> System.out.println("重写方法!");
        noParameterNoReturn.test();
    }
    public static void main1(String[] args) {
        NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn(){
            @Override
            public void test(){
                System.out.println("重写方法!");
            }
        };
        noParameterNoReturn.test();
    }
}

3.基本使用

//没有返回值,没有参数
@FunctionalInterface//这个注释修饰这个接口,说明它是函数式接口
interface NoParameterNoReturn{
    void test();
}
//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {
    void test(int a);
}
//无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {
    void test(int a,int b);
}
//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {
    int test();
}
//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
    int test(int a);
}
//有返回值多参数
@FunctionalInterface
interface MoreParameterReturn {
    int test(int a,int b);
}

public class TestDemo {
    public static void main(String[] args) {
        OneParameterNoReturn oneParameterNoReturn = (a) ->{System.out.println(a);};
        oneParameterNoReturn.test(10);
        //优化
        OneParameterNoReturn oneParameterNoReturn2 = a ->System.out.println(a);
        oneParameterNoReturn.test(10);
        //继续优化
        OneParameterNoReturn oneParameterNoReturn3 = System.out::println;
        oneParameterNoReturn.test(10);

        MoreParameterNoReturn moreParameterNoReturn = (a,b) -> {//理论上来说要写(int a,int b)但是因为a,b同类型,所以两个int都可以省略
            System.out.println(a+b);
        };
        moreParameterNoReturn.test(10,20);

        NoParameterReturn noParameterReturn = () -> {return 10;};
        int ret = noParameterReturn.test();
        System.out.println(ret);
        //简写:
        NoParameterReturn noParameterReturn2 = () -> 10;
        int ret2 = noParameterReturn2.test();
        System.out.println(ret2);

        OneParameterReturn oneParameterReturn = a -> a + 11;
        System.out.println(oneParameterReturn.test(10));

        MoreParameterReturn moreParameterReturn = (a,b) -> a+b;
        System.out.println(moreParameterReturn.test(1,2));
    }
}
Lambda 表达式本质是一个匿名函数,函数的方法是:返回值 方法名 参数列表 方法体。在, Lambda表达式中我们只需要关心参数列表方法体。
语法精简:
①参数类型可以省略,如果需要省略,每个参数的类型都要省略。
②  参数的小括号里面只有一个参数,那么小括号可以省略
③  如果方法体当中只有一句代码,那么大括号可以省略
④  如果方法体中只有一条语句,其是 return 语句,那么大括号可以省略,且去掉 return 关键字。

4.变量捕获

package lambdademo;

class Test {
    public void func(){
        System.out.println("func()");
    }
}
public class TestDemo2 {
    public static void main(String[] args) {
        int a = 100;
        //a = 99;//error
        new Test() {
            @Override
            public void func() {
                System.out.println("我是内部类,且重写了func这个方法!");
                System.out.println("我是捕获到变量 a == " + a + " 我要么是一个常量,要么是一个没有改变过值的变量!");
            }
        }.func();
    }
}

5.lambda在集合当中的使用

为了能够让 Lambda Java 的集合类集更好的一起使用,集合当中,也新增了部分接口,以便与 Lambda 表达式对接。
对应的接
新增的方法
Collection
removeIf() spliterator() stream() parallelStream() forEach()
List
replaceAll() sort()
Map
getOrDefault() forEach() replaceAll() putIfAbsent() remove() replace() computeIfAbsent() computeIfPresent() compute() merge()

(1)Collection接口

forEach() 方法演示

该方法在接口 Iterable 当中,原型如下:

default void forEach(Consumer<? super T> action) { 
    Objects.requireNonNull(action); 
    for (T t : this) { 
        action.accept(t); 
    } 
}

该方法表示:对容器中的每个元素执行action指定的动作 。

演示如下: 

package lambdademo;

import java.util.ArrayList;
import java.util.function.Consumer;

public class TestDemo2 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("bit");
        list.add("hello");
        list.add("lambda");
        /*list.forEach(new Consumer<String>(){
            @Override
            public void accept(String str){ //简单遍历集合中的元素。
                System.out.print(str+" ");
            }
        });*/
        //等价于
        list.forEach(str -> System.out.print(str + " "));
    }
}

(2)List接口

sort() 方法的演示:
sort 方法源码:该方法根据 c 指定的比较规则对容器元素进行排序。
public void sort(Comparator<? super E> c) { 
    final int expectedModCount = modCount; 
    Arrays.sort((E[]) elementData, 0, size, c); 
    if (modCount != expectedModCount) { 
        throw new ConcurrentModificationException(); 
    }
    modCount++; 
}

演示如下:

package lambdademo;

import java.util.ArrayList;
import java.util.Comparator;

public class TestDemo2 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("zello");
        list.add("bit");
        list.add("hello");
        list.add("lambda");
        list.forEach(str -> System.out.print(str + " "));
        System.out.println();
        System.out.println("====================");
        //list.sort((o1,o2) -> o1.compareTo(o2));
        //等价于:
        list.sort(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        list.forEach(str -> System.out.print(str + " "));
    }
}

编译并运行该代码,输出如下:

 (3)Map接口

HashMap forEach(),该方法原型如下:

 演示如下:

package lambdademo;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.function.BiConsumer;

class Test {
    public void func(){
        System.out.println("func()");
    }
}
public class TestDemo2 {
    public static void main(String[] args) {
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1, "hello");
        map.put(2, "bit");
        map.put(3, "hello");
        map.put(4, "lambda");
        map.forEach(new BiConsumer<Integer, String>(){
            @Override
            public void accept(Integer key, String value){
                System.out.print(key + "=" + value + " ");
            }
        });
        //简化(使用lambda表达式)
        System.out.println();
        System.out.println("====================");
        map.forEach((k,v)-> System.out.print(k + "=" + v+" "));
    }
}
编译并运行该代码,输出如下:

 (6)lambda的优缺点

优点:
代码简洁,开发迅速
②方便函数式编程
③非常容易进行并行计算
④Java 引入 Lambda ,改善了集合操作
缺点:
①代码可读性变差
②在非并行计算中,很多计算未必有传统的 for 性能要高
③不容易进行调试

 

<think>好的,我现在需要回答用户关于RTTR反射库以及它如何与Qt结合使用的问题。首先,我得先理解RTTR反射库的基本概念和用途。反射库的主要作用是允许程序在运行时检查和操作类型信息,比如类、方法、属性等。这对于需要动态处理对象的情况非常有用,比如序列化、反序列化、脚本绑定等。 接下来,我需要确认RTTR的具体功能和特点。根据之前的了解,RTTR支持C++,并且不需要在代码中添加额外的宏或者使用特定的预处理工具,这是它的优势之一。用户可能想知道RTTR与Qt的结合使用,Qt本身有自己的元对象系统,尤其是在信号槽机制和属性系统方面,所以需要比较两者的异同,以及如何互补使用。 用户的问题可能还涉及到如何在Qt项目中集成RTTR,比如是否会有冲突,或者如何利用RTTR来增强Qt的功能。例如,Qt的元对象系统在编译时生成,而RTTR可以在运行时提供更多的灵活性。可能需要举一些具体的例子,比如如何在Qt中使用RTTR进行动态类型创建,或者在Qt的UI元素中动态绑定属性。 另外,需要注意RTTR和Qt结合时的潜在问题,比如内存管理、类型注册的步骤,以及可能的性能影响。需要确保提供的步骤是清晰的,让用户能够按照指导进行操作。 可能还需要检查是否有最新的RTTR版本和Qt版本之间的兼容性问题,确保提供的建议是当前有效的。同时,用户可能对实际应用场景感兴趣,比如游戏开发中的对象序列化,或者插件系统的动态加载,这些都可以作为例子来说明RTTR与Qt结合的实际好处。 最后,要确保回答结构清晰,分点说明RTTR的功能、与Qt的结合方法,以及具体的使用示例,让用户能够逐步理解和应用。</think>RTTR(Run Time Type Reflection)是一个开源的C++运行时类型反射库,允许在运行时动态获取和操作类型信息(如类、属性、方法等)。它弥补了C++原生缺乏反射支持的缺陷,常用于序列化、脚本绑定、数据检查、动态GUI生成等场景。 --- ### **RTTR 的核心功能** 1. **类型注册**:通过代码注册类、属性、方法、枚举等元信息。 2. **动态类型操作**:运行时创建对象、调用方法、访问属性。 3. **跨平台支持**:不依赖编译器特性(如RTTI),纯模板实现。 4. **无侵入性**:不需要修改已有类的代码(通过外部注册实现)。 --- ### **为何与 Qt 结合?** Qt 自身有一套基于宏(如`Q_OBJECT`)的元对象系统(Meta-Object System),支持信号槽、属性系统(`Q_PROPERTY`)和动态类型(`qobject_cast`)。但 Qt 的反射功能有限,无法直接获取所有类成员。 **结合 RTTR 的优势**: - **扩展反射能力**:动态访问非QObject类、标准库类型或第三方库的成员。 - **简化序列化**:结合 Qt 的 JSON/XML 模块,实现复杂对象的自动序列化。 - **动态UI绑定**:自动将对象属性映射到Qt控件(如动态生成属性编辑器)。 --- ### **RTTR 与 Qt 结合使用步骤** #### 1. **注册类型到 RTTR** ```cpp #include <rttr/registration> using namespace rttr; class MyClass { public: int value = 42; void print() { qDebug() << "Value:" << value; } }; // 注册类及其成员 RTTR_REGISTRATION { registration::class_<MyClass>("MyClass") .constructor<>() .property("value", &MyClass::value) .method("print", &MyClass::print); } ``` #### 2. **在 Qt 中动态使用 RTTR** ```cpp // 动态创建对象 variant obj = type::get_by_name("MyClass").create(); if (obj.is_valid()) { // 动态设置属性 property prop = obj.get_type().get_property("value"); prop.set_value(obj, 100); // 动态调用方法 method m = obj.get_type().get_method("print"); m.invoke(obj); } ``` #### 3. **与 Qt 信号槽结合** 若需将 RTTR 方法绑定到 Qt 信号,可通过 Lambda 或中间层适配: ```cpp QObject* adapter = new QObject; QPushButton* button = new QPushButton("Click"); QObject::connect(button, &QPushButton::clicked, [&obj]() { method m = obj.get_type().get_method("print"); m.invoke(obj); }); ``` #### 4. **序列化 Qt 对象** 将 Qt 对象属性通过 RTTR 转换为 JSON: ```cpp QJsonObject serialize(const QObject* obj) { QJsonObject json; const auto metaObj = obj->metaObject(); for (int i = 0; i < metaObj->propertyCount(); ++i) { QMetaProperty prop = metaObj->property(i); json[prop.name()] = QJsonValue::fromVariant(prop.read(obj)); } // 使用 RTTR 补充非 Q_PROPERTY 成员 type t = type::get_by_name(obj->metaObject()->className()); for (auto& prop : t.get_properties()) { if (!json.contains(prop.get_name().data())) { json[prop.get_name().data()] = prop.get_value(obj).to_int(); } } return json; } ``` --- ### **注意事项** - **QObject 派生类**:需确保先通过 Qt 宏(如`Q_OBJECT`)注册,再通过 RTTR 补充反射。 - **性能**:频繁的运行时反射可能影响性能,建议缓存 `type` 和 `method` 对象。 - **内存管理**:RTTR 的 `variant` 不管理对象生命周期,需手动控制或结合 `QSharedPointer`。 --- ### **典型应用场景** - **动态编辑器**:根据类的 RTTR 信息自动生成 Qt 属性面板。 - **游戏引擎**:结合 Qt 编辑器,动态修改 C++ 对象的属性。 - **插件系统**:通过 RTTR 动态加载插件类并调用方法。 通过结合 RTTR 和 Qt,可以显著增强 C++ 项目的动态性和灵活性,尤其适合需要高度可扩展架构的应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值