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<String> 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<User> 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<String>
  • 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<String, User> map,List<User> list){
        System.out.println("ReflectionDemo05.test02");
    }
    
    public Map<Integer, User>test02(){
        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<java.lang.String, com.lorinda.bean.User>
                                            泛型类型: class java.lang.String
                                            泛型类型: class com.lorinda.bean.User
               #java.util.List<com.lorinda.bean.User>
                                            泛型类型: 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();
        }

    }

}

Matrix42
1 声望0 粉丝