当前位置 博文首页 > A_art_xiang的博客:Java反射,从0开始

    A_art_xiang的博客:Java反射,从0开始

    作者:[db:作者] 时间:2021-07-14 13:14

    目录

    静态VS动态语言

    Java?Reflection(反射)介绍

    Java反射机制提供的功能

    Java反射优点和缺点

    反射相关的主要API

    获取运行时类的完整结构

    反射的使用

    有了Class对象,如何创建对象?

    使用反射调用对象的指定方法

    Object invoke(Object obj, Object[] args)

    setAccessible

    性能检测

    反射操作泛型

    反射操作注解


    静态VS动态语言

    动态语言:

    ? ? 是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗短说就是在运行时代码可以根据某些条件改变自身结构。

    ? ? 主要动态语言:Object-C、C#、JavaScript、PHP、Python等。

    静态语言:

    ? ? 与动态语言相对应的,运行时结构不可变的语言就是静态语言,如Java、C、C++。

    ? ? Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活。

    Java?Reflection(反射)介绍

    ? ? Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection?API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

    ? ? Class?c =?Class.forName("java.lang.String")

    ? ? 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

    Java反射机制提供的功能

    ? ? 在运行时判断任意一个对象所属的类。

    ? ? 在运行时构造任意一个类的对象。

    ? ? 在运行时判断任意一个类所具有的成员变量和方法。

    ? ? 在运行时获取泛型信息。

    ? ? 在运行时调用任意一个对象的成员变量和方法。

    ? ? 在运行时处理注解。

    ? ? 生成动态代理。

    ? ? ......

    Java反射优点和缺点

    ? ? 优点:可以实现动态创建对象和编译,体现出很大的灵活性。

    ? ? 缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。

    反射相关的主要API

    ? ? java.lang.Class:代表一个类

    ? ? java.lang.reflect.Method:代表类的方法。

    ? ? java.lang.reflect.Field:代表类的成员变量。

    ? ? java.lang.reflect.Constructor:代表类的构造器。

    ? ? ......

    获取运行时类的完整结构

    ? ? 通过反射获取运行时类的完整结构:Field、Method、Constructor、Superclass、Interface、Annotation。

    ? ? 实现的全部接口、所继承的父类、全部的构造器、全部的方法、全部的Field、注解等等。

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    //获取类的信息
    public class Test06 {
    
        public static void main(String[] args) throws Exception {
    
            Class c1 = Class.forName("com.myUtils.reflectUtils.learn.MyUser");
            MyUser user = new MyUser();
            c1 = user.getClass();
            //获得类的名字
            System.out.println(c1.getName());//包名 + 类名
            System.out.println(c1.getSimpleName());//获得类名
            //获得类的属性
            Field[] fields = c1.getFields();//获取所有(只能找到public的属性)属性
            fields = c1.getDeclaredFields();//获取所有(找到全部的属性)属性
            Field id = c1.getDeclaredField("id");//获取指定属性
            //获得类的方法
            Method[] methods = c1.getMethods();//获取所有(本类及其父类public的)方法
            methods = c1.getDeclaredMethods();//获取所有(本类全部的)方法
            Method getName = c1.getMethod("getName", null);
            Method setName = c1.getMethod("setName", String.class);//需要参数类型(重载问题)
            //获得构造器
            Constructor[] constructors = c1.getConstructors();
            Constructor constructor = c1.getConstructor(null);//也需要参数类型
        }
    }
    
    class MyUser{
        public Integer id;
        public String name;
        private int age;
    
        @Override
        public String toString() {
            return "MyUser{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        private int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }

    反射的使用

    有了Class对象,如何创建对象?

    创建类的对象:调用Class对象的newInstance()方法:

    ? ? 1.类必须有一个无参数的构造器。

    ? ? 2.类的构造器的访问权限需要足够。

    没有无参构造器、构造器权限不足的情况创建对象:

    ? ? 1.通过Class类的getDeclaredConstructor(Class ... parameterTypes)取得本类的指定形参类型的构造器。

    ? ? 2.向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。

    ? ? 3.通过Constructor实例化对象。

    使用反射调用对象的指定方法

    通过反射,调用类中的方法,通过Method类完成。

    ? ? 1.通过Class类的getMethod(String name,Class ... parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。

    ? ? 2.之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。

    Object invoke(Object obj, Object[] args)

    ? ? Object对应原方法的返回值,若原方法无返回值,此时返回null。

    ? ? 若原方法为静态方法,此时形参Object?obj可为null。

    ? ? 若原方法形参列表为空,则Object[] args为null。

    ? ? 若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法。将可访问private的方法。

    setAccessible

    ? ? Method和Field、Construetor对象都有setAccessible()方法。

    ? ? setAccessible作用是启动和禁用访问安全检查的开关。

    ? ? 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。

    ?? ?? ? 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true;

    ?? ?? ? 使得原本无法访问的私有成员也可以访问。

    ? ? 参数值为false则指示反射的对象应该实施Java语言访问检查。

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    //通过反射创建对象
    public class Test07 {
        public static void main(String[] args) throws Exception {
            Class c1 = Class.forName("com.myUtils.reflectUtils.learn.MyUser07");
            //构造一个对象
            MyUser07 user = (MyUser07) c1.newInstance();//实际上调用了无参构造器
            //通过构造器创建对象
            Constructor constructor = c1.getDeclaredConstructor(Integer.TYPE, String.class);
            user = (MyUser07) constructor.newInstance(5, "张三");
            //通过反射调用方法
            Method setName = c1.getDeclaredMethod("setName", String.class);
            setName.invoke(user, "李四");//(对象,参数)
            System.out.println(user.getName());
            //通过反射操作属性
            Field name = c1.getDeclaredField("name");
            name.setAccessible(true);//设置为true就可以获取到私有属性、方法(关闭程序的安全监测)
            name.set(user, "王五");
            System.out.println(name.get(user));
        }
    }
    class MyUser07{
        private Integer id;
        private String name;
    
        public MyUser07(Integer id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }

    ?

    性能检测

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class Test08 {
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    
            /**
             * 普通时17ms
             * 反射耗时3398ms
             * 关闭检测耗时1657ms
             */
            test1();
            test2();
            test3();
        }
    
        //普通方式
        public static void test1(){
            User08 user08 = new User08();
            long startTime = System.currentTimeMillis();
            for (int i = 0; i < 1000000000; i++) {
                user08.getName();
            }
            long endTime = System.currentTimeMillis();
            System.out.println("普通时" + (endTime - startTime) + "ms");
        }
        //反射方式
        public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            User08 user08 = new User08();
            Class c1 = user08.getClass();
            Method getName = c1.getDeclaredMethod("getName", null);
    
            long startTime = System.currentTimeMillis();
            for (int i = 0; i < 1000000000; i++) {
                getName.invoke(user08, null);
            }
            long endTime = System.currentTimeMillis();
            System.out.println("反射耗时" + (endTime - startTime) + "ms");
        }
        //反射方式,关闭检测
        public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            User08 user08 = new User08();
            Class c1 = user08.getClass();
            Method getName = c1.getDeclaredMethod("getName", null);
            getName.setAccessible(true);
            long startTime = System.currentTimeMillis();
            for (int i = 0; i < 1000000000; i++) {
                getName.invoke(user08, null);
            }
            long endTime = System.currentTimeMillis();
            System.out.println("关闭检测耗时" + (endTime - startTime) + "ms");
        }
    }
    class User08{
    
        private Integer id;
        private String name;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }

    反射操作泛型

    ? ? Java采用泛型擦除的机制来引入泛型,Java的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除。

    ? ? 为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType集中类型来代表不能被归一到class类中的类型但是又和原始类型齐名的类型。

    ParameterizedType:表示一种参数化类型,比如Collection<String>。

    GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型。

    TypeVariable:是各种类型变量的公共父接口。

    WildcardType:代表一种通配符类型表达式。

    import java.lang.reflect.Method;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.List;
    import java.util.Map;
    
    //通过反射获取泛型
    public class Test09 {
        public void test01(Map<String, User> map, List<Integer> list){
            System.out.println("test01");
        }
        public Map<String, User> test02(){
            System.out.println("test02");
            return null;
        }
    
        public static void main(String[] args) throws NoSuchMethodException {
            //获取方法参数泛型
            Method method = Test09.class.getMethod("test01", Map.class, List.class);
            Type[] genericParameterTypes = method.getGenericParameterTypes();
            for (Type genericParameterType : genericParameterTypes) {
                System.out.println("#"+genericParameterType);
                if(genericParameterType instanceof ParameterizedType){
                    Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                    for (Type actualTypeArgument : actualTypeArguments) {
                        System.out.println(actualTypeArgument);
                    }
    
    
                }
            }
            //获取返回值泛型
            Method method2 = Test09.class.getMethod("test02", null);
            Type genericReturnType = method2.getGenericReturnType();//获取返回类型
                System.out.println("#"+genericReturnType);
                if(genericReturnType instanceof ParameterizedType){
                    Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
                    for (Type actualTypeArgument : actualTypeArguments) {
                        System.out.println(actualTypeArgument);
                    }
            }
        }
    }

    反射操作注解

    import java.lang.annotation.*;
    import java.lang.reflect.Field;
    
    
    public class Test10 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
            Class c1 = Class.forName("com.myUtils.reflectUtils.learn.Student10");
            //通过反射获取注解
            Annotation[] annotations = c1.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            }
            //获得注解的value的值
            TableTest tableTest = (TableTest)c1.getAnnotation(TableTest.class);
            String value = tableTest.value();
            System.out.println(value);//db_student
            //获得类指定的注解
            Field f = c1.getField("name");
            FieldTest annotation = f.getAnnotation(FieldTest.class);
            System.out.println(annotation.columnName());
            System.out.println(annotation.type());
            System.out.println(annotation.length());
        }
    }
    
    
    @TableTest("db_student")
    class Student10{
        @FieldTest(columnName = "db_id", type = "int", length = 10)
        private Integer id;
        @FieldTest(columnName = "db_name", type = "varchar", length = 20)
        private String name;
    
    
        public Student10() {
        }
        public Student10(Integer id, String name) {
            this.id = id;
            this.name = name;
        }
    
    
        public Integer getId() {
            return id;
        }
    
    
        public void setId(Integer id) {
            this.id = id;
        }
    
    
        public String getName() {
            return name;
        }
    
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
    
    //类名的注解
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface TableTest{
        String value();
    }
    //属性的注解
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface FieldTest{
        String columnName();
        String type();
        int length();
    }

    ?

    cs