手撕Java反射机制

Class类


1、在面向对象的世界里,万事万物皆对象


java语言中,静态的成员、普通数据类型类不是对象

对象,类是java。lang。Class类的示例对象

任何一个类都是java.lang.Class类的的实例对象,三种获得该示例对象的方法:

//1.直接通过类名获得这个实例对象
Class s1 = Student.class;
//2.通过类的对象获得该类的实例对象
Student student = new Student();
Class s2 =  student.getClass();
//3.使用forName("全类名")
Class s3 = Class.forName("Student");

2.动态加载类


2.1 编译时刻加载类是静态加载类,运行时刻加载类是动态加载类


public class ClassDemo {
    public static void main(String[] args) {
        try {
            //使用Class.forName()动态加载
            Class c1 = Class.forName(args[0]);
            Able w = (Able) c1.newInstance();
            w.print();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}
//使用接口化编程
public interface Able {
    void print();
}
public class Work implements Able{
    public void print() {
        System.out.println("I 'm work");
    }
}
public class Excel implements Able{
    public void print() {
        System.out.println("I 'm excel");
    }
}

使用动态加载类,可避免在编译期的错误,只在运行时运行需要的类即可,当需要加入其它类时,只编译对应的类。

2.2 获得类的方法信息


public class ClassDemo {
    public static void printClassMessage(Object object){
        Class class1 = object.getClass();
        System.out.println("类的名称:" + class1.getName());
        //getMethods()获得类的所有public的方法,包括继承父类的
        //getDeclaredMethods() //获得类的所有自己声明的方法
        Method[] methods = class1.getMethods();
        for(int i=0;i<methods.length;i++){
            System.out.println("方法的名称是:" + methods[i].getName()  + " ");
            //得到方法的返回值类型
            Class returnType = methods[i].getReturnType();
            System.out.println("方法的返回值类型:" + returnType);
            //得到方法的参数类型
            Class[] paramType = methods[i].getParameterTypes();
            System.out.print("参数类型:");
            for(Class c : paramType){
                System.out.print(c.getName() + "  ");
            }
            System.out.println("\n");
        }
    }
    public static void main(String[] args) {
        String a = "abc";
        ClassDemo1.printClassMessage(a);
    }
}

2.3 获得成员变量的信息


成员变量也是一个对象,是java.lang.reflect.Field的实例

public static void printClassFieldMessage(Object object){
        Class c = object.getClass();
        Field[] fields = c.getDeclaredFields();
        for(int i=0;i<fields.length;i++){
            System.out.println("成员变量的名称:" + fields[i].getName());
            //得到成员变量的类型的类类型
            Class fieldType = fields[i].getType();
            String typeName = fieldType.getName();
            System.out.println("成员变量的类类型:" + typeName);
            System.out.println("\n");
        }
    }
2.4 获得构造函数的信息
构造函数也是一个对象
public static void printConMessage(Object object){
        Class c = object.getClass();
        Constructor[] constructors = c.getDeclaredConstructors();
        for(Constructor constructor : constructors){
            System.out.print(constructor.getName());
            Class[] paramTypes = constructor.getParameterTypes();
            System.out.print("(");
            for(Class class1 : paramTypes){
                System.out.print(class1.getName() + ",");
            }
            System.out.println(")");
        }
    }

由上可知,当需要获得一个类的信息时,首先需要获得该类的类类型

3.方法的反射


public class ClassDemo {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        A a = new A();
        //获得A类的类类型
        Class c1 = a.getClass();
        //获得方法信息  参数是方法的名称和方法的参数
//        Method method = c1.getDeclaredMethod("print",int.class,int.class);
        Method method = c1.getDeclaredMethod("print",new Class[]{int.class,int.class});
        //方法的反射操作
        Object o = method.invoke(a,10,10);
        System.out.println("返回值:" + o);
        Method method1 = c1.getDeclaredMethod("print");
        Object o1 = method1.invoke(a);
        System.out.println("返回值:" + o1);
    }
}
class A{
    public int print(){
        return 2;
    }
    public void print(int a,int b){
        System.out.println("a * b = " + a*b);
    }
    public void print(String a,String b){
        System.out.println("a + b = " + a+b);
    }
}

方法的反射是通过方法对象来反射操作:

  1. 首先获得类的类类型

  2. 通过类的类类型获得方法对象

  3. 通过方法对象用invoke来进行反射操作

  4. 集合泛型的反射操作

    public class ClassDemo {
     public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
         ArrayList list = new ArrayList();
         ArrayList<String> list1 = new ArrayList<>();
         /**
          * 打印结果为true,由此可见编译后集合的泛型是去泛型化的
          * Java中集合的泛型是为了防止错误输入的,只在编译期有效
          * 绕过编译就无效了
          */
         System.out.println(list.getClass() == list1.getClass());
         list1.add("hello");
    //        list1.add(10); //这是错误的
         //通过反射操作,绕过编译
         //获得list的类类型
         Class c = list1.getClass();
         Method method = c.getMethod("add",Object.class);
         Object o = method.invoke(list1, 10);
         System.out.println(list1);
     }
    }
    

    输出:

    true[hello, 10]
    

    Java中集合的泛型是为了防止错误的输入,是在编译阶段有效的,通过上述打印出的true可知在运行阶段是去泛型化的,于是可以通过反射操作绕过编译。

5.反射的优缺点

优点
反射提高了程序的灵活性和扩展性,在底层框架中用的比较多,业务层面的开发过程中尽量少用。
缺点

  • 性能不好
    反射是一种解释操作,用于字段和方法接入时要远慢于直接代码
  • 程序逻辑有影响
    使用反射操作会模糊化程序的内部逻辑,从代码的维护角度来讲,我们更希望在源码中看到程序的逻辑,反射相当于绕过了源码的方式,因此会带来维护难度比较大的问题。

6.用反射实现ORM框架

什么是ORM框架?
提供持久化类与表的映射关系,ORM框架在运行时就能参照映射文件的信息,把对象持久化到数据库中。当前ORM框架主要有五种种:Hibernate,iBATIS,mybatis,EclipseLink,JFinal。

如何使用反射实现ORM框架

下面以一条简单的保存数据到数据库为例:

//Student类
public class Student {
    private int id;
    private String name;
    //省略get、set
}
public class Save {
    public static void save(Object data, Class<?> entityClass) throws Exception {
        String sql = "insert into {0}({1}) values({2})";
        String tableName = entityClass.getSimpleName();
        List<String> names = new ArrayList<>();
        List<String> fs = new ArrayList<>();
        List<Object> values = new ArrayList<>();
        Field[] fields = entityClass.getDeclaredFields();
        for(Field field : fields){
            names.add(field.getName());
            fs.add("?");
            //允许访问私有变量
            field.setAccessible(true);
            values.add(field.get(data));
        }
        //将list中的数分成多个流,并用逗号连接成一个字符串
        String fieldStr = names.stream().collect(Collectors.joining(","));
        String valueStr = fs.stream().collect(Collectors.joining(","));
        System.err.println(MessageFormat.format(sql, tableName, fieldStr, valueStr));
        values.forEach(System.out::println);
    }
    public static void main(String[] args) {
        try {
            Student student = new Student();
            student.setId(1);
            student.setName("xiaolin");
            save(student, Student.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

输出结果如下:

insert into Student(id,name) values(?,?)
1
xiaolin
posted @ 2020-12-11 19:30  林無敌  阅读(106)  评论(0)    收藏  举报