资讯专栏INFORMATION COLUMN

Java动态性(2) - 之反射机制(Reflection)

妤锋シ / 3296人阅读

摘要:的动态性反射机制动态编译动态执行代码动态字节码操作动态语言程序运行时可以改变程序得结构或变量类型典型语言等如下代码不是动态语言但有一定的动态性我们可以利用反射机制字节码操作获得类似动态语言的特性的动态性让编程的时候更加灵活反射机制反射机制指

1.Java的动态性

反射机制

动态编译

动态执行JavaScript代码

动态字节码操作

2.动态语言
程序运行时,可以改变程序得结构或变量类型.典型语言:

Python,Ruby,JavaScript等.

如下JavaScript代码

function test(){
    var s = "var a=3;var b=5;alert(a+b);";
    eval(s);
}

C,C++,Java不是动态语言,但Java有一定的动态性,我们可以利用反射机制,字节码操作获得类似动态语言的特性

Java的动态性让编程的时候更加灵活

3.反射机制

反射机制指的是可以在运行期间加载一些知道名字的类
对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意一个方法或属性

Class c = Class.forName("com.test.User");

类加载完之后,在堆内存中会产生一个Class类的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息,我们可以通过这个对象看到类的结果

4.Class类介绍

java.lang.Class类十分特殊,用来表示java中类型(class/interface/enum/annotation/primitive type/void)本身

Class类的对象包含了某个被加载类的结构,一个被加载的类对应一个Class对象
当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM便会自动产生一个Class对象

Class类是Reflection的根源

针对任何你想动态加载,运行的类,只有先获得相应的Class对象

User bean:

package com.lorinda.bean;

public class User {
    
    private int id;
    private int age;
    private String uname;
    
    public User(int id, int age, String uname) {
        super();
        this.id = id;
        this.age = age;
        this.uname = uname;
    }
    
    public User() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

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

    public String getUname() {
        return uname;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }
     
}

Demo01 测试各种类型对应Class对象的获取方式:

/**
 * 测试各种类型对应Class对象的获取方式
 * @author Matrix42
 *
 */
public class ReflectionDemo01 {

    public static void main(String[] args) {

        String path = "com.lorinda.bean.User";
        
        try {
            Class clazz = Class.forName(path);
            System.out.println(clazz);              //class com.lorinda.bean.User
            System.out.println(clazz.hashCode());   //366712642
            //同样的类只会被加载一次
            Class clazz2 = Class.forName(path);
            System.out.println(clazz2.hashCode());  //366712642
            
            Class strClazz = String.class;  //类名.class
            
            Class strClazz2 = path.getClass();   //对象.getClass();
            
            System.out.println(strClazz==strClazz2);//true
            
            Class intClazz = int.class;
            
            int[] arr01 = new int[10];
            int[] arr02 = new int [30];
            
            int[][] arr03 = new int[30][3];
            
            //数组的Class对象只与类型和维度有关
            System.out.println(arr01.getClass()==arr02.getClass()); //true
            
            System.out.println(arr01.getClass().hashCode());        //1829164700
            System.out.println(arr03.getClass().hashCode());        //2018699554
            
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
5.Class类的对象如何获取?

对于对象可以使用getClass()

使用Class.forName() (最常使用)

使用.class

6.反射机制的常见作用

动态加载类,动态获取类的信息(属性,方法,构造器)

动态构造对象

动态调用类和对象的任意方法,构造器

动态调用和处理属性

获取泛型信息

处理注解

Demo02 获取方法,属性,构造器等的信息:

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

/**
 * 获取方法,属性,构造器等的信息
 * @author Matrix42
 *
 */
public class ReflectionDemo02 {

    public static void main(String[] args) {
       
        String path = "com.lorinda.bean.User";
        
        try {
            Class clazz = Class.forName(path);

            //获取类的名字
            System.out.println(clazz.getName());//获得包名+类名:com.lorinda.bean.User
            System.out.println(clazz.getSimpleName());//获得类名:User
            
            //获取属性信息
            //Field[] fields = clazz.getFields();//只能获取public的field
            Field[] fields = clazz.getDeclaredFields();//获得所有的field
            Field field = clazz.getDeclaredField("uname");//根据名字获取field
            
            for(Field temp:fields){
                System.out.println("属性: "+temp);
            }
            
            //获取方法
            Method[] methods = clazz.getDeclaredMethods();
            Method method01 = clazz.getDeclaredMethod("getUname", null);
            //如果方法有参数,则必须传递参数类型对应的Class对象
            Method method02 = clazz.getDeclaredMethod("setUname", String.class);
            
            for(Method m:methods){
                System.out.println("方法: "+m);
            }
            
            //获得构造器信息
            Constructor[] constructors = clazz.getDeclaredConstructors();
            //多带带获取,无参
            Constructor c1 = clazz.getDeclaredConstructor(null);
            System.out.println("构造器: "+c1);
            //多带带获取,有参
            Constructor c2 = clazz.getDeclaredConstructor(int.class,int.class,String.class);
            System.out.println("构造器: "+c2);
            for(Constructor c:constructors){
                System.out.println("构造器: "+c);
            }   
            
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

    }

Demo03 通过反射动态操作构造器,方法,属性

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import com.lorinda.bean.User;

/**
 * 通过反射动态操作构造器,方法,属性
 * @auther Matrix42
 */
public class ReflectionDemo03 {
  
    public static void main(String[] args) {
        
        String path = "com.lorinda.bean.User";
        
        try {
            Class clazz = Class.forName(path);
            
            //动态操作构造器
            User u = (User) clazz.newInstance();    //调用了User的无参构造方法
            
            Constructor c = clazz.getConstructor(int.class,int.class,String.class);
            
            User u2 = c.newInstance(1000,20,"Matrix42");
            System.out.println(u2.getUname());
            
            //通过反射调用普通方法
            //好处:方法名,参数都可以是变量,可以从数据库读取
            User u3 = (User) clazz.newInstance();
            Method method = clazz.getDeclaredMethod("setUname", String.class);
            method.invoke(u3, "Matrix42");
            System.out.println(u3.getUname());
            
            //通过反射操作属性
            User u4 = (User) clazz.newInstance();
            Field f = clazz.getDeclaredField("uname");
            f.setAccessible(true);
            f.set(u4, "24xirtaM");  
            //默认会报错,添加f.setAccessible(true);关闭安全检查
            //can not access a member of class com.lorinda.bean.User with modifiers "private"
            System.out.println(u4.getUname());  //正常调用
            System.out.println(f.get(u4));      //通过反射调用  
            
        } catch (Exception e) {
            e.printStackTrace();
        } 
    
    }

}
7.反射机制性能问题
当你获得灵活性的时候也会牺牲你的性能

setAccessible

启用和禁用安全检查的开关,值为true则表示反射的对象在使用时应取消Java语言访问检查.值为fals则表示反射的对象应该实施Java语言访问检查.并不是为true就能访问,为false就不能访问

禁止安全检查,可以提高反射的运行速度

可以考虑使用:cglib/javasssist字节码操作

反射性能测试:

import java.lang.reflect.Method;
import com.lorinda.bean.User;

public class ReflectionDemo04 {
    
    public static void test01(){
        
        User user = new User();
        
        long startTime = System.currentTimeMillis();
        
        for(int i=0;i<1000000000L;i++){
            user.getUname();
        }
        
        long endTime = System.currentTimeMillis();
        
        //421ms
        System.out.println("普通方法调用,执行10亿次,耗时:"+(endTime-startTime)+"ms");
       
    }
    
    public static void test02() throws Exception{
        
        User user = new User();
        Class clazz = user.getClass();
        Method m = clazz.getDeclaredMethod("getUname", null);
        
        long startTime = System.currentTimeMillis();
        
        for(int i=0;i<1000000000L;i++){
            m.invoke(user, null);
        }
        
        long endTime = System.currentTimeMillis();
        
        //1650ms
        System.out.println("反射动态调用,执行10亿次,耗时:"+(endTime-startTime)+"ms");
       
    }
    
    public static void test03() throws Exception{
        
        User user = new User();
        Class clazz = user.getClass();
        Method m = clazz.getDeclaredMethod("getUname", null);
        m.setAccessible(true);
        
        long startTime = System.currentTimeMillis();
        
        for(int i=0;i<1000000000L;i++){
            m.invoke(user, null);
        }
        
        long endTime = System.currentTimeMillis();
        
        //1153ms
        System.out.println("反射动态调用,跳过安全检查,执行10亿次,耗时:"+(endTime-startTime)+"ms");
       
    }

    public static void main(String[] args) throws Exception {
        
        test01();
        test02();
        test03();
        
    }

}

可以看出在java8中使用安全检查的反射耗时大约是普通调用的4倍,不使用安全检查是普通调用的2.5倍

8.反射操作泛型(Generic)

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

为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型.

ParameterizedType:表示一种参数化类型,比如Collection

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

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

WildcardType:表示一种通配符类型表达式,比如?,? extends Number,? super Integer [wildcard就是通配符的意思]

Demo05 通过反射读取泛型

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import com.lorinda.bean.User;

/**
 * 通过反射读取泛型
 * @author Matrix42
 *
 */
public class ReflectionDemo05 {
    
    public void test01(Map map,List list){
        System.out.println("ReflectionDemo05.test02");
    }
    
    public Maptest02(){
        System.out.println("ReflectionDemo05.test2");
        return null;
    }

    public static void main(String[] args) {
        
        try {
            
            //获取指定方法参数泛型信息
            Method m = ReflectionDemo05.class.getMethod("test01", Map.class,List.class);
            Type[] t = m.getGenericParameterTypes();
            for(Type paramType:t){
                System.out.println("#"+paramType);
                if(paramType instanceof ParameterizedType){
                    Type[] genericTypes = ((ParameterizedType)paramType).getActualTypeArguments();
                    for(Type genericType:genericTypes){
                        System.out.println("泛型类型: "+genericType);
                    }
                }
            }
            /*
               #java.util.Map
                                            泛型类型: class java.lang.String
                                            泛型类型: class com.lorinda.bean.User
               #java.util.List
                                            泛型类型: class com.lorinda.bean.User
             */
            //获得指定方法返回值泛型信息
            Method m2 = ReflectionDemo05.class.getMethod("test02", null);
            Type returnType = m2.getGenericReturnType();
            if(returnType instanceof ParameterizedType){
                Type[] genericTypes = ((ParameterizedType)returnType).getActualTypeArguments();
                for(Type genericType:genericTypes){
                    System.out.println("返回值,泛型类型: "+genericType);
                }
            }
            /*
                                       返回值,泛型类型: class java.lang.Integer
                                       返回值,泛型类型: class com.lorinda.bean.User
           */
               
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}
9.反射操作注解

Student类:

package com.lorinda.bean;

import com.demo.util.MField;
import com.demo.util.MTable;

@MTable("tb_student")
public class MStudent {

    @MField(columnName="id",type="int",length=10)
    private int id;
    @MField(columnName="sname",type="varchar",length=10)
    private String studentName;
    @MField(columnName="age",type="int",length=3)
    private int age;
    
    public MStudent(int id, String studentName, int age) {
        super();
        this.id = id;
        this.studentName = studentName;
        this.age = age;
    }
    
    public MStudent() {
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getStudentName() {
        return studentName;
    }
    public void setStudentName(String studentName) {
        this.studentName = studentName;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
      
}

Table注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value={ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MTable {

    String value();
    
}

Field注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value={ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MField {

    String columnName();
    String type();
    int length();
    
}

Demo06 通过反射读取注解

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

public class ReflectionDemo06 {

    public static void main(String[] args) {

        try {
            Class clazz = Class.forName("com.lorinda.bean.MStudent");
            
            //获得类的所有有效注解
            Annotation[] annotations = clazz.getAnnotations();
            for(Annotation a:annotations){
                System.out.println(a);
            }
            
            //获得类的指定注解
            MTable table = (MTable) clazz.getAnnotation(MTable.class);
            System.out.println(table.value());
            
            //获得类的属性的注解
            Field f = clazz.getDeclaredField("studentName");
            MField field = f.getAnnotation(MField.class);
            System.out.println(field.columnName()+"--"+field.type()+"--"+field.length());
            
            //可以根据获得的表名,字段的信息,拼出DDL语句,然后使用JDBC执行这个SQL,在数据库中生成相关的表
            
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/71949.html

相关文章

  • ReflectionJava反射机制的应用场景

    近期在维护公司项目的时候遇到一个问题,因为实体类中的 set 方法涉及到了业务逻辑,因此在给对象赋值的过程中不能够使用 set 方法,为了实现功能,所以采用了反射的机制给对象属性赋值,借此机会也了解了反射的一些具体用法和使用场景,分以下两点对反射进行分析: 反射的优势和劣势 反射的应用场景 反射的优势和劣势   个人理解,反射机制实际上就是上帝模式,如果说方法的调用是 Java 正确的打开方式...

    浠ラ箍 评论0 收藏0
  • ReflectionJava反射机制基础

    摘要:反射机制是什么反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法对于任意一个对象,都能够调用它的任意一个方法和属性这种动态获取的信息以及动态调用对象的方法的功能称为语言的反射机制反射机制能做什么反射机制主要提供了以下功 反射机制是什么 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种...

    hizengzeng 评论0 收藏0
  • Java 反射(Reflection)

    摘要:效率运行效率使用反射减低程序的运行效率。开发效率由于反射运用到各个框架中,大大加快了开发的效率。 反射的核心就是Class对象,每一个类被jvm加载都会有一个对应的class对象,这个class对象包含了这个类的结构信息,反射就是会通过反射api反复操作这个class对象(属性,方法,注解,构造器,泛型),但是反射会降低程序的运行效率,比普通方法要慢30倍,通过setAccessble...

    shengguo 评论0 收藏0
  • 乐字节Java反射一:反射概念与获取反射源头class

    摘要:一反射机制概念程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言,如,是动态语言显然,,不是动态语言,但是有着一个非常突出的动态相关机制。相关的为二获取源头重点打开权限所有类的对象其实都是的实例。 一、Java反射机制概念 程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言,如Python, Ruby是动态语言;显然C++,Java,C#不是动态语言,但是JAVA有...

    caikeal 评论0 收藏0
  • 最最最常见的Java面试题总结——第二周

    摘要:与都继承自类,在中也是使用字符数组保存字符串,,这两种对象都是可变的。采用字节码的好处语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。 String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的? String和StringBuffer、StringBuilder的区别 可变性...

    yearsj 评论0 收藏0

发表评论

0条评论

妤锋シ

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<