Home Tags Posts tagged with "反射"

反射

0 25

反射是 Java 语言中一个相当重要的特性,它允许正在运行的 Java 程序观测,甚至是修改程序的动态行为。举例来说,我们可以通过 Class 对象枚举该类中的所有方法,我们还可以通过 Method.setAccessible(位于 java.lang.reflect 包,该方法继承AccessibleObject)绕过 Java 语言的访问权限,在私有方法所在类之外的地方调用该方法。

反射在 Java 中的应用十分广泛。开发人员日常接触到的 Java 集成开发环境(IDE)便运用了这一功能:每当我们敲入点号时,IDE 便会根据点号前的内容,动态展示可以访问的字段或者方法。

另一个日常应用则是 Java 调试器,它能够在调试过程中枚举某一对象所有字段的值。

反射Reflect的实现

默认情况下,方法的反射调用为委派实现,委派给本地实现来进行方法调用;在调用超过15次之后,委派实现便会委派对象切换至动态实现。这个动态实现的字节码是自动生成的,它将直接使用invoke指令来调用目标方法。

反射Reflect的开销

原因有三:

1.变长参数方法导致的Object数组

2.基本类型的自动装箱,拆箱

3.方法内联

反射Reflect的API

 

框架:类里面定义大量的注解,然后通过反射框架去读取注解生成相应信息

假设数据库中有db——student这样的表,我们会通过注解去定义一些数据库的语言,通过注解去执行增删改查

 

 

package com.yang.reflection;


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

//练习反射操作注解
public class ReflectionOpsAnno {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {

        //通过反射获得注解
        Class c1 = Class.forName("com.yang.reflection.Student");
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        
        //获取注解的vlaue的值
        TableYang tableYang = (TableYang)c1.getAnnotation(TableYang.class);
        String value = tableYang.value();
        System.out.println(value);

        //获得类指定的注解(Declared才可以获取私有的)
        Field field = c1.getDeclaredField("name");
        FieldYang annotation = field.getAnnotation(FieldYang.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}

//对应表名
@TableYang("db_student")
class Student{

    //对应表字段名
    @FieldYang(columnName = "db_id",type = "int" ,length =10)
    private int id;
    @FieldYang(columnName = "db_age",type = "int" ,length =10)
    private int age;
    @FieldYang(columnName = "db_name",type = "varchar" ,length =3)
    private String name;

    public Student(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public Student() {
    }

    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 getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

//类名的注解
@Target(ElementType.TYPE)//作用域
@Retention(RetentionPolicy.RUNTIME)//获取范围
@interface TableYang{
    String value();
}

//属性的注解
@Target(ElementType.FIELD)//作用域
@Retention(RetentionPolicy.RUNTIME)//获取范围
@interface FieldYang{
    String columnName();
    String type();
    int length();
}

在我们的框架使用中大量运用到反射机制,我们以后的开发也必然会使用到反射,那么反射的性能分析就很有必要了,这篇文章将通过代码实例的方式为您对比反射和new之间调用方法及创建对象的性能,以及小小的优化(代码地址:Desktop\个人学习代码\Reflection)

 

package com.yang.reflection;


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

/**
 * 测试New调用对象方法和反射调用对象方法的性能
 * test01:New
 * test02:Reflection
 * test03:关闭安全检查的反射
 * 注:如不关闭安全检查则无法修改私有属性
 */
public class ReflectionVsNew_Method {

    public static void test01(){
        long startTime = System.currentTimeMillis();
        User user = new User();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("New方法所消耗的时间为:"+(endTime-startTime)+"ms");
    }

    /**
     * @throws ClassNotFoundException
     * @throws NoSuchMethodException
     *
     * getDeclaredMethod(name,parameterTypes)
     * @param name 方法名
     * @param parameterTypes 方法所需要传入的参数
     *
     * method.invoke(object,args)
     * @param object 执行方法的对象
     * @param args 方法所需要传入的参数
     */
    public static void test02() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        long startTime = System.currentTimeMillis();
        User user = new User();
        Class c1= Class.forName("com.yang.reflection.User");
        Method getName = c1.getDeclaredMethod("getName", null);
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射方法所消耗的时间为:"+(endTime-startTime)+"ms");
    }

    //反射方式调用,关闭检测
    public static void test03() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        long startTime = System.currentTimeMillis();
        User user = new User();
        Class c1= Class.forName("com.yang.reflection.User");
        Method getName = c1.getDeclaredMethod("getName", null);
        getName.setAccessible(true);
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("关闭安全检查后,反射方法所消耗的时间为:"+(endTime-startTime)+"ms");
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test01();
        System.out.println("===========================");
        test02();
        System.out.println("===========================");
        test03();
    }

}

总结:
反射比new大概慢了400倍
关闭安全检查后,大概慢了200倍

反射调用方法四部曲
    1.传入需要调用方法的所属对象
    2.获取该对象对应的Class对象
    3.通过Class对象得到需要调用的方法
    4.执行方法名.invoke()

 

通过Class创建实例

创建实例的五种方式链接:http://yangbili.co/class%e7%b1%bb%e7%9a%84%e8%8e%b7%e5%8f%96%e6%96%b9%e5%bc%8f%ef%bc%88%e9%87%8d%e7%82%b9%ef%bc%89/

老规矩,上代码:

package com.yang.reflection;


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

/**
 * 创建对象的方式
 * 1.new
 * 2.默认无参构造器:Class.forname(),.newInstance()
 * 3.指定构造器
 */
//动态的创建对象,通过反射
public class GetNewObject {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //获得Class对象
        Class c1 =Class.forName("com.yang.reflection.User");

        //构造一个对象
//        User user1 = (User) c1.newInstance();//本质上调用了类的无参构造器
//        System.out.println(user);

        //通过指造器创建对象
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        User user2 = (User) constructor.newInstance("咩咩咩", 001, 19);
        System.out.println(user2);

        //通过反射调用普通方法
        User user3 = (User) c1.newInstance();
        //通过反射获取一个方法
        Method setName = c1.getDeclaredMethod("setName",String.class);
        /**
         * @param user3 调用方法的对象(这里体现了反字)
         * @param "大黑狗" 给方法传参
         * */
        setName.invoke(user3,"大黑狗");
        System.out.println(user3.getName());

        //通过反射操作属性
        User user4 = (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");

        //不能直接操作私有属性,需要关闭程序的安全检查,setAccessible(true)
        name.setAccessible(true);
        name.set(user4,"白羊");
        System.out.println(user4.getName());
    }
}

废话不多说,直接上代码

(代码地址:Desktop\个人学习代码\Reflection)

package com.yang.reflection;

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

//获得类的信息
public class GetClassinfomation{
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {

        Class<?> c1 = Class.forName("com.yang.reflection.User");

//        User user = new User();
//        c1 = user.getClass();

        //获得类的名字
        System.out.println(c1.getName());//包名+类名
        System.out.println(c1.getSimpleName());//获得类名
        System.out.println("======================");

        //获得类的属性
        Field[] fields = c1.getFields();//只能找到public属性

        fields = c1.getDeclaredFields();//找到全部的属性
        for (Field field : fields) {
            System.out.println(field);
        }

        Field name = c1.getDeclaredField("name");//通过属性名获得属性
        System.out.println(name);

        System.out.println("======================");

        //获取类的方法
        Method[] methods = c1.getMethods();//获得本类及父类的全部public方法
        for (Method method : methods) {
            System.out.println("正常的:"+method);
        }

        Method[] declaredMethods = c1.getDeclaredMethods();//获得本类的全部方法,包括私有的方法
        for (Method declaredMethod : declaredMethods) {
            System.out.println("不正常的:"+declaredMethod);
        }

        System.out.println(c1.getMethod("getName", null));//获得指定方法
        /**
         * 为什么这里需要传入String.class?
         * JAVA的重载,如果方法名一样,但是无参数判断的话,它无法判断要得到哪个方法
         */
        System.out.println(c1.getMethod("setName", String.class));
        System.out.println("======================");

        //获得类构造器
        System.out.println("======================");
        Constructor[] constructors = c1.getConstructors();//获得public构造器
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        Constructor[] constructors2 = c1.getDeclaredConstructors();//获得本类的全部构造器
        for (Constructor constructor : constructors2) {
            System.out.println("#"+constructor);
        }

        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class,int.class);
        System.out.println("指定构造器:"+declaredConstructor);
    }
}




总结:
一般来说,通过Class对象获得类的运行时的完整结构分三类:
  1. 获得public方法 getxxx()
  2.获得全部方法(包括private)getDeclaredxxx()
  3.按指定参数获得指定方法

用处:可通过Class对象获得构造器之后创建相应的实例

九大类型,直接上代码:
package com.yang.reflection;

import java.lang.annotation.ElementType;

//所有类型的class
public class AllClassTest04 {
    public static void main(String[] args) {

        //类的Class
        Class c1 = Object.class;
        //接口的Class
        Class c2 = Comparable.class;
        //一维数组的Class
        Class c3 = String[].class;
        //二维数组的Class
        Class c4 = int[][].class;
        //注解类型的Class
        Class c5 = Override.class;
        //枚举类型的Class
        Class c6 = ElementType.class;
        //Class类型的Class
        Class c7 = Integer.class;
        //void类型的Class
        Class c8 = void.class;
        //Class类型的Class
        Class c9 = Class.class;

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);

        System.out.println("===================");
        //长度不一样的数组,其Class也相同!!!同一个类型只有一份Class
        //只要元素类型与维度一样,就算同一个Class
        int[] a = new int[100];
        int[] b = new int[1000];
        System.out.println(a.getClass().hashCode());
        System.out.println(b.getClass().hashCode());
    }
}

其中getClass()与.class有如下区别:

两者最直接的区别就是,getClass()是一个类的实例所具备的方法,而class()方法是一个类的方法。
另外getClass()是在运行时才确定的,而class()方法是在编译时就确定了。

参考链接:http://yangbili.co/getclass%e5%92%8cclass%ef%bc%88%ef%bc%89%e7%9a%84%e5%8c%ba%e5%88%ab/

java中获取Class对象的五种方法总结:

1)运用getClass()
注:每个class 都有此函数
String str = “abc”;
Class c1 = str.getClass();

2)运用 Class.getSuperclass()
Button b = new Button();
Class c1 = b.getClass();
Class c2 = c1.getSuperclass();

3)Class.forName()
(最常被使用)
Class c1 = Class.forName(“java.lang.String”);
Class c2 = Class.forName(“java.awt.Button”);

4)运用 .class语法
Class c1 = String.class;

5)运用 primitivewrapper classes 的TYPE 语法
Class c1 = Boolean.TYPE;
Class c2 = Byte.TYPE;
Class c3 = Character.TYPE;
Class c4 = Short.TYPE;
Class c5 = Integer.TYPE;
Class c6 = Long.TYPE;
Class c7 = Float.TYPE;
Class c8 = Double.TYPE;
Class c9 = Void.TYPE;
————————————————

总结:

  1. 知道对象通过.getClass()
  2. 知道包名通过forname
  3. 知道类通过.class
  4. 知道子类通过.getSuperclass()

(代码:Reflection文件夹)

静态 VS 动态语言

区分标准:运行时能否改变其结构

 

什么叫反射:

Java反射机制提供的功能:

 

反射的优点和缺点:

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

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

性能:二者差了很多倍

 

反射相关的主要API

  • Java.lang.Class:代表一个类
  • Java.lang.reflect.Method:代表类的方法
  • Java.lang.reflect.Field:代表类的成员变量
  • Java.lang.reflect.Constructor:代表类的构造器

代码如下:

//什么叫反射
public class Test extends Object{
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//通过反射获取类的Class对象

Class<?> c1 = Class.forName(“com.yang.reflection.Test”);
System.out.println(c1);

Class<?> c2 = Class.forName(“com.yang.reflection.Test”);
Class<?> c3 = Class.forName(“com.yang.reflection.Test”);
Class<?> c4 = Class.forName(“com.yang.reflection.Test”);
//一个类在内存中只有一个Class对象
//一个类被加载后,类的整个结构都会被封装在Class对象中
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
System.out.println(c4.hashCode());
}

}
结果:
class com.yang.reflection.Test
1956725890
1956725890
1956725890

分析:
相同对象的Class都是同一个

Class类详解:
对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构的有关信息

  • Class本身也是一个类
  • Class对象只能由系统建立对象
  • 一个加载的类在JVM中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 每个类的实例都会记得自己是由哪个Class实例所生成(通过getClass()得到)
  • 通过Class可以完整地得到一个类中的所有被加载的结构
  • Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得对应的Class对象