java的反射机制理解

在学习一个项目时用到了Java的反射机制来实现dao层,发现java的反射机制很有趣,现在做一下笔记:
什么是反射机制:
根据网文,java中的反射机制可以如此定义:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

机制原理:
首先不得不提到的是java.lang.Class这个类。
有这么一段话:
Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。

也就是说,ClassLoader找到了需要调用的类时(java为了调控内存的调用消耗,类的加载都在需要时再进行,很抠但是很有效),就会加载它,然后根据.class文件内记载的类信息来产生一个与该类相联系的独一无二的Class对象。该Class对象记载了该类的字段,方法等等信息。以后jvm要产生该类的实例,就是根据内存中存在的该Class类所记载的信息(Class对象应该和我所了解的其他类一样会在堆内存内产生、消亡)来进行。

而java中的Class类对象是可以人工自然性的(也就是说开放的)得到的(虽然你无法像其他类一样运用构造器来得到它的实例,因为
Class对象都是jvm产生的。不过话说回来,客户产生的话也是无意义的),而且,更伟大的是,基于这个基础,java实现了反射机制。

获取Class对象有三种方式:

1.通过Object类的getClass()方法。例如:
Class c1 = new String(“”).getClass();
2.通过Class类的静态方法——forName()来实现:
Class c2 = Class.forName(“MyObject”);
3.如果T是一个已定义的类型的话,在java中,它的.class文件名:T.class就代表了与其匹配的Class对象,例如:
Class c3 = Manager.class;
Class c4 = int.class;
Class c5 = Double[].class;

这里需要解释一下3:请记住一句话,java中,一切皆对象。也就是说,基本类型int float 等也会在jvm的内存池像其他类型一样中生成
一个Class对象。而数组等组合型数据类型也是会生成一个Class对象的,而且更令人惊讶的是,java中数组的本来面目其实就是某个类,惊讶
中的惊讶是,含有相同元素的相同维数的数组还会共同享用同一个Class对象!其实根据我的臆想,数组的length性质应该就保存在这个Class对象里面。

Class类中存在以下几个重要的方法:

1.getName()
一个Class对象描述了一个特定类的特定属性,而这个方法就是返回String形式的该类的简要描述。由于历史原因,对数组的Class对象
调用该方法会产生奇怪的结果。

2.newInstance()
该方法可以根据某个Class对象产生其对应类的实例。需要强调的是,它调用的是此类的默认构造方法。例如:
MyObject x = new MyObject();
MyObject y = x.getClass().newInstance();

3.getClassLoader()
返回该Class对象对应的类的类加载器。
只有Class类才有getClassLoader()方法呀~ 可以这么想,我们平时讲述某某类,但是我们并没有说这个类怎么和虚拟机打交道,虚拟机怎么识别这个类.总不能全靠字符串吧. 所以呢java就设计了Class这个类.用于虚拟机对类的管理.当一个类被虚拟机装载完毕的时候,就会创建一个Class类的实例,对于类A就是A.class,对于类B就是B.class. Class类也提供了许多方法来获取类的信息. 要知道,类的装载器分为 “启动类装载器 “, “用户定义装载器 “.它不止一种 Class类需要保存这些信息. getClassLoader()是用来获取这个信息的

Test.class.getClassLoader()一般用在getResource,因为你想要获取某个资源文件的时候,这个资源文件的位置是相对固定的。

对于在项目中获取到文件的路径问题,我们又做了一个详细的介绍:
如下:

InputStream in = getClass().getResourceAsStream('/'+"spring-beans.dtd"); 表示从classs根目录下面的找文件,文件放在src下面就可以了.

InputStream in = getClass().getResourceAsStream("spring-beans.dtd"); 表示从当前classs下面的路径找文件

Class.getClassLoader.getResourceAsStream(String path) :默认则是从ClassPath根下获取,path不能以’/‘开头,最终是由
  ClassLoader获取资源。

ServletContext. getResourceAsStream(String path):默认从WebAPP根目录下取资源,Tomcat下path是否以’/‘开头无所谓,当然这和具体的容器实现有关。

在me.class目录的子目录下,例如:com.x.y 下有类me.class ,同时在 com.x.y.file 目录下有资源文件myfile.xml
  那么,应该有如下代码
  me.class.getResourceAsStream("file/myfile.xml");

4.getComponentType()
该方法针对数组对象的Class对象,可以得到该数组的组成元素所对应对象的Class对象。例如:
int[] ints = new int[]{1,2,3};
Class class1 = ints.getClass();
Class class2 = class1.getComponentType();
而这里得到的class2对象所对应的就应该是int这个基本类型的Class对象。

5.getSuperClass()
返回某子类所对应的直接父类所对应的Class对象。

6.isArray()
判定此Class对象所对应的是否是一个数组对象。

7. public static Class<?> forName(String className) :natice 方法,动态加载类。非常重要。
       如在sql中动态加载驱动程序:class.forName(sqlDriver);

8. public T newInstance() :根据对象的class新建一个对象,用于反射。非常重要。
       可用在反射中构建对象,调用对象方法:

       class doubleClass= class.forName("java.lang.Double");

       Object objDouble = doubleClass.newInstance();

       如在javaBean中就应用了这个方法,因为java默认要有一个无参构造函数。

9. public ClassLoader getClassLoader() :获得类的类加载器Bootstrap  ,Extension ,System or user custom      ClassLoader(一般为system classloader)。重要。

10. public String getName() :获取类或接口的名字。记住enum为类,annotation为接口。重要

11.public native Class getSuperclass():获取类的父类,继承了父类则返回父类,否则返回java.lang.Object。返回Object的父类为空-null。一般
12. public java.net.URL getResource(String name) :根据字符串获得资源。

13. 其他类 
 public boolean isEnum() :判断是否为枚举类型。
 public native boolean isArray() :判断是否为数组类型。
 public native boolean isPrimitive() :判断是否为基本类型。
 public boolean isAnnotation() :判断是否为注解类型。
public Package getPackage() :反射中获得package,如java.lang.Object 的package为java.lang。
public native int getModifiers() : 反射中获得修饰符,如public static void等 。
public Field getField(String name):反射中获得域成员。
public Field[] getFields() :获得域数组成员。    
public Method[] getMethods() :获得方法。
public Method getDeclaredMethod(String name, Class<?>... parameterTypes):加个Declared代表本类,继承,父类均不包括。

public Constructor<?>[] getConstructors() :获得所有的构造函数。

如此我们可以知道反射可以运行时动态获得类的所有信息,并新建对象(newInstance()方法)。

下面我们做一个小案例:

如我们定义一个类:

public class Test{

   //Constructor

   public Test(){this("");}

   public Test(String name){}

   //Field

   public int id;

   public String name;

   //Method

   public void testMethod(){
   }

}
我们可以:

Class c = Class.forName("Test");
Method m[] = c.getDeclaredMethods();//获取到类的方法
for (int i = 0; i < m.length; i++)
   System.out.println(m[i].toString());//输出testMethod
}

Constructor c[] = c.getDeclaredConstructors();
for (int i = 0; i < c.length; i++) {
   Constructor ct = c[i];
System.out.println("name = " + ct.getName());//输出两个构造函数信息

下面搜集了一些问题来做解答:
**1:
(ParameterizedType)getClass().getGenericSuperclass().getActualTypeArguments()[0]得到的是什么?**
例子:
这里写图片描述
这里写图片描述
运行SuperClass后控制台会打印:
class com.stos.test.generic.Cat
看到运行结果你可能就清楚了,就是获取实际的泛型类。
这一点在servlet中国定义baseDao(通用方法)时获取泛型实际类很好用;

2:4.如何通过反射来创建对象?getConstructor()和getDeclaredConstructor()区别?
详情:http://www.cnblogs.com/jiangyi-uestc/p/5686264.html
1. 通过类对象调用newInstance()方法,适用于无参构造方法:

例如:String.class.newInstance()
2. 通过类对象的getConstructor()或getDeclaredConstructor()方法获得构造器(Constructor)对象并调用其newInstance()方法创建对象,适用于无参和有参构造方法。

例如:

String.class.getConstructor(String.class).newInstance("Hello");

 Solution  solution2=solution.getClass().getDeclaredConstructor(String.class).newInstance("hello2");//有参的构造函数 

 Solution solution3 = (Solution) Class.forName("Solution").getConstructor().newInstance(); // 无参也可用getConstructor()
getConstructor()和getDeclaredConstructor()区别:

getDeclaredConstructor(Class<?>... parameterTypes) 
这个方法会返回制定参数类型的所有构造器,包括public的和非public的,当然也包括private的。
getDeclaredConstructors()的返回结果就没有参数类型的过滤了。

再来看getConstructor(Class<?>... parameterTypes)
这个方法返回的是上面那个方法返回结果的子集,只返回制定参数类型访问权限是public的构造器。
getConstructors()的返回结果同样也没有参数类型的过滤。

3:invoke方法怎么使用:
我们截取了一段代码:

    /**
     * 根据一个id 查找对象
     */
    public Entity findById(int id) throws Exception {
        Connection conn = DBUtils.createConn();
        String sql = " select * from  " + clazz.getSimpleName() +  " where id = " + id ;
        PreparedStatement ps = DBUtils.getPs(conn, sql);
        ResultSet rs = ps.executeQuery();
        Entity entity =  (Entity) clazz.newInstance();//clazz是实现获取到的该对象的Class
        if(rs.next()){
            Field[]  fs = clazz.getDeclaredFields();
            for (int i = 0; i < fs.length; i++) {               
                String methodName = "set" + Character.toUpperCase(fs[i].getName().charAt(0)) + fs[i].getName().substring(1);
                Method m = clazz.getDeclaredMethod(methodName, fs[i].getType());//后面参数需要指定类型
                m.invoke(entity, rs.getObject(fs[i].getName()));
                /*
                 * 这里用到invoke方法,上面的解释:在entity中调用m方法,参数为rs.getObject(fs[i].getName()),返回一个对象(invoke方法返回的就是一个对象)
                 * 这个对象就是entity;
                 * Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,  
                                                        如果参数为基本类型数据,必须转换为相应的包装类型的对象。invoke()方法的返回值总是对象,  
                                                         如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,  
                 */
            }
        }
        DBUtils.close(rs);
        DBUtils.close(ps);
        DBUtils.close(conn);
        return entity;
    }

以上就是对反射机制的初步了解,相信这些在一定阶段是够用了;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值