java的反射

今天看了下java的反射机制,看后,恍然间对spring、annotation的工作机制有了进一步的了解。反射用在java上指的是我们可以运行时加载、探知、使用编译期间完全未知的classes,就是说,java程序可以加载一个运行时才得知名称的class,获得其完整的结构,并生成对象实例,读取改变fields,或调用其methods。

       谈到反射,要涉及到java.lang.class与java.lang.reflect。下面我就具体讲下反射的作用吧

1.通过反射查看类信息。

1.1获取class对象

每个类被加载之后,系统都会为该类生成一个对应的class对象,但是程序是如何获取这个class对象的呢?主要有三种方法,

(1)使用class类的forName方法,改方法需要传入字符串参数,该字符串参数的值是某个类的全限定类名(必须是完整包名)。我觉得spring用的就是这种反射机制

(2)调用class属性来获取class对象

(3)调用对象的getclass()方法

例如:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package Reflect;
class Demo{
     //other codes...
}
 
class hello{
     public static void main(String[] args) {
         Class<?> demo1= null ;
         Class<?> demo2= null ;
         Class<?> demo3= null ;
         try {
             //一般尽量采用这种形式
             demo1=Class.forName( "Reflect.Demo" );
         } catch (Exception e){
             e.printStackTrace();
         }
         demo2= new Demo().getClass();
         demo3=Demo. class ;
         
         System.out.println( "类名称   " +demo1.getName());
         System.out.println( "类名称   " +demo2.getName());
         System.out.println( "类名称   " +demo3.getName());
         
     }
}

【运行结果】:

类名称   Reflect.Demo

类名称   Reflect.Demo

类名称   Reflect.Demo

一旦获取了某个类对应的class对象,程序就可以调用class对象的方法了。

1.2.从class中获取信息

java.lang.class中提供了大量的实例方法来获取class对象所对应类的详细信息,如

(1)通过getConstructor等获取class对应类包含的构造器,共有4个方法,不再一一列举

(2)通过getMethod等获取class对应类包含的方法,共有4个方法,不再一一列举

(3)通过getField等获取class对应类包含的Field,共有4个方法,不再一一列举

(4)通过getAnnotation等获取class对应类包含的Annotation,共有3个方法,不再一一列举

(5)获得class对应类的内部类、外部类、父类、继承的接口

(6)获得class对应类的修饰符、所在包、类名等信息

………………

2.使用反射生成并操作对象

1中从class对象中获取类的方法(由Method对象表示)、构造器(由Constructor对象表示)、属性(由Feild对象表示)等,这三个类都位于java.lang.reflect包下,并实现了java.lang.reflect.member接口,程序可以通过method对象来执行对应的方法,通过Constructor对象来调用对应的构造器创建实例,能通过Field对象直接访问并修改对象的属性值。

2.1通过反射来生成对象有以下两种方式

(1)使用class对象的newInstance()方法来创建该class对象对应类的实例。要求class对象的对应类有默认的构造器。这种方式比较常见,因为目前绝大多数框架都是根据配置文件信息来创建java对象,从配置文件读取的只是某个类的字符串类名,要利用反射根据该字符串来创建对应的实例。spring对象就是采取的这种方式。

下面程序就实现了一个简单的对象池,该对象会根据配置文件读取key-value对,然后创建这些对象,并放入一个HashMap中

public class ObjectPoolFactory{
        //定义一个对象池,前面是对象名,后面是实际对象
        private Map<String,Object> objectPool = new HashMap<>();
        //定义一个创建对象的方法
        //该方法只要传入一个字符床类名,程序就可以根据该类名生成java对象
        private Object createObject(String clazzName) throws InstantiationException,
        IllegalAccessException,ClassNotFoundException {
            Class<?> clazz =Class.forName(clazzName);
            return clazz.newInstance();
        }
        
        //该方法根据指定文件来初始化对象池
        public void initPool(String fileName) throws InstantiationException,
        IllegalAccessException,ClassNotFoundException {
            try(FileInputStream fis = new FileInputStream(fileName)) {
                Properties props =new Properties();
                props.load(fis);
                for (SOtring name:props.stringPropertyNames()) {
                    //每取出一对key-valued,就根据value创建一个对象
                    objectPool.put(name, createObject(props.getProperty(name)));
                }
            }catch(IOException ex){
                System.out.println("读取"+fileName+"异常");
            }
            
        }
    }


(2)先使用class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对应类的实例

下面程序利用反射来创建一个JFrame对象,使用指定的构造器

public class CreateJFrame {
        public static void main(String[] args) throws Exception {
            Class<?> jframeClazz =Class.forName("javax.swing.JFrame");
            //获取JFrame中带一个字符串参数的构造器
            Constructor<T> ctor = new jframeClazz.getConstructor(String.class);
            //调用Constructor对象的newInstance方法创建对象
            object obj = ctor.newInstance("测试窗口");
            System.out.println(obj);
        }
    }

这种已知javax.swing.JFrame类的情形,通常没有必要使用反射来创建该对象,毕竟通过反射创建对象时性能要稍低一些。通常在开发通用性比较广的框架、基础平台时可能会大量使用反射。

2.2调用方法

获得class对象后,就可以通过前面讲的获取方法,获得Method对象后,就可以通过Method来调用它对应的方法,在Method里包含一个invoke()方法,该方法如下

Object invoke(Object obj,Object……args),obj是执行方法的主调,args是执行方法时的传入该方法的实参。

如:

Class<?>  targetClass = target.getClass();

Method mtd = targetClass.getMethod("setter方法名",String.class);

//通过Method的invoke方法执行setter方法

//讲“1234”值作为调用setter方法实参

mtd.invoke(target,"1234");

通过值Method对象的setAccessible(true),可以访问相应类的private方法,private构造器、private属性

2.3访问属性

获得class对象后,就可以通过前面讲的获取Fields,获得Fields对象后,就可以通过Fields来读取或设置Field值

,主要有两个方法

(1)getXxx(Object obj)

(2)setXxx(Object obj,Xxx val)

使用这两个方法可以随意的访问指定对象的所有属性,包括private属性

例子:

//创建一个Person对象

Person p =new Person;

//获取Person类对应的class对象

class<Person> personClazz = Person.class;

//获取person的名为name的field,使用getDeclaredField表明可以获取各种访问控制符的field

Field nameField = personClazz.getDeclaredField("name");

//可以访问private的field

nameField.setAccessible(true);

//为field设置值

nameFiled.set(p,"Moqian");

2.4操作数组

在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组,程序可以使用Array来动态的创建数组,操作数组元素

如:

//创建一个长度为10的string类型数组

Object arr = Array.newInstance(String.Class,10);

//为数组中index=5的元素赋值为“12345677”

Array.set(arr,5,"12345677");


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值