[19/04/17-星期三] Java的动态性_反射(Reflection)机制

时间:2022-10-26 21:27:15

一、前言

动态语言:程序运行时,可以改变程序结构或变量类型。典型的代表:Python,ruby,JavaScript

如JavaScript代码:

function test(){

var s="var a=3;var b=5;alert(a+b)"

eval(s)

}

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

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

二、反射的概念

     指的是程序已经运行起来了但是依然可以加载、探知、使用编译时完全未知的类。 程序在运行状态时,可以动态加载一个只有名称的类,对于任意一个已加载的类,

都能知道这个类所有的属性和方法,对于任意一个对象,都能够调用它的属性和方法。

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

   加载完类("com.sxt.test.User")之后,在堆的内存中,就产生了一个Class类型的对象c(一个类只有一个Class对象),这个对象c就包含了完整的类的结构信息。

我们可以通过这个对象c看到类("com.sxt.test.User")的结构。这个对象c就是一面镜子,透过这个镜子可以看到类的结构,所以形象的称之为:反射

(通俗理解:把一个类映射成一个对象,以便于好操纵这个类)

【基本概念】

/***
 * 反射:把一个类映射成一个对象,以便于好操纵这个类
 */
package cn.sxt.jvm;

@SuppressWarnings("all")
public class Test_0417_Reflection {
    public static void main(String[] args) throws Exception {
        String  path="cn.sxt.jvm.Test_0417_User";
        
        Class clz=Class.forName(path);//对象表示或封装一些数据,一个类被加载之后,jVM会创建一个对应于该类的Class对象clz
        //类的整个结构信息会被加载到相应的Class对象(clz)中去。这个对象映射了这个类的全部信息。
        System.out.println(clz);
        System.out.println(clz.hashCode());
        
        Class clz2=Class.forName(path);
        System.out.println(clz2.hashCode());//输出结果是一样的,说明一个类只能对应一个反射对象(汽车图纸)。
        
        Class strClass=String.class;//获取常用的 String类的映射对象
        System.out.println(strClass);
        Class strClass2=path.getClass();//path是个字符串对象,通过对象.getClass()获取映射对象
        System.out.println(strClass2);    
        System.out.println(strClass==strClass2);//输出为真,说明获得的是同一个映射对象
        
        Class intClass=int.class;
        
        int arr01[]=new int[10];
        int arr02[]=new int[20];
        int arr03[][]=new int[5][3];
        double arr04[]=new double[10];
        System.out.println(arr01.getClass().hashCode());
        System.out.println(arr02.getClass().hashCode());//arr01[]和arr02[]输出结果是一样的,说明映射对象与数组大小无关
        System.out.println(arr03.getClass().hashCode());//输出结果不一样,而是与维数(一维or二维)相关
        System.out.println(arr04.getClass().hashCode());//输出结果不一样,映射对象与数据类型相关
        
    }
}

 【反射的作用】

/**
利用反射的API获取类的信息(名字、属性、方法、构造器)
 * 
 */
package cn.sxt.jvm;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@SuppressWarnings("all")
public class Test_0417_Reflection2 {
    public static void main(String[] args) throws Exception {
        Class clz=Class.forName("cn.sxt.jvm.Test_0417_User");
        
        //获得类信息
        System.out.println(clz.getName());//获取类的完整路径(包括包名+类名)
        System.out.println(clz.getSimpleName());//获取类的简单路径(仅给出类名)
        
        //获得属性信息
        Field[] fields=clz.getFields();//获取公开(public修饰的)的属性信息
        Field[] fields2=clz.getDeclaredFields();//获取所有属性信息(包括public和private修饰的) Declared:宣布的,定义的    
        System.out.println(fields.length);//输出0 ,表示数组中没东西
        System.out.println(fields2.length);//输出3 表示获得了3个私有的属性
        for (Field temp : fields2) {
            System.out.println("属性:"+temp);    
        }
        System.out.println("获得单个属性:"+clz.getDeclaredField("name"));//获得单个属性name
        
        //获得方法(不含构造方法)信息
        Method[] methods =clz.getDeclaredMethods();//获得所有方法(公开+私有的方法)
        System.out.println(methods.length);//输出结果为6 即6个get和set方法
        Method method=clz.getDeclaredMethod("setName", String.class);//参数为(方法的名字,方法中传进去参数的类型)
        System.out.println(method);
        
        //获得构造方法的信息
        Constructor[] constructors=clz.getDeclaredConstructors();
        System.out.println(constructors.length);//输出为2
        System.out.println(clz.getDeclaredConstructor(null));//获取所有的构造器中无参数的构造器
        System.out.println(clz.getDeclaredConstructor(int.class,String.class,int.class));//获取所有的构造器中有如下参数的构造器
        
    }
}
/***
 * 通过反射API调用构造方法 构造对象、普通方法、属性
 */
package cn.sxt.jvm;

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

@SuppressWarnings("all")
public class Test_0417_Reflection3 {
    public static void main(String[] args) throws Exception {
        Class clz=Class.forName("cn.sxt.jvm.Test_0417_User");
        
        //通过反射API调用构造方法 构造对象.
        Test_0417_User user=(Test_0417_User)clz.newInstance();//其实是调用Test_0417_User的无参构造方法
        
        Test_0417_User user2=(Test_0417_User)clz.getDeclaredConstructor(int.class,String.class,int.class).
                newInstance(1001,"小李",18);//通过调用有参数的构造方法实例化一个对象
        System.out.println(user2.getAge());
        
        //通过反射API动态调用普通方法
        Test_0417_User user3=(Test_0417_User)clz.newInstance();
        Method method=clz.getDeclaredMethod("setName", String.class);//"setName"这里可以灵活多变,需要什么传什么
        method.invoke(user3, "小王");//后2行代码等价于user3.setName("小王");  invoke:激活    
        System.out.println(user3.getName());
        
        //通过反射API操作属性
        Test_0417_User user4=(Test_0417_User)clz.newInstance();
        Field field=clz.getDeclaredField("name");
        field.setAccessible(true);//意思是访问name这个属性不用做安全检查,直接访问(否则name属性是私有的外边的类访问不了)
        field.set(user4, "小张");//通过反射直接写属性的值
        System.out.println(user4.getName());//通过反射直接读属性的值
        System.out.println(field.get(user4));//输出结果一样    
    }
}