java反射机制

最近终于大概了解了以下java反射机制。本文将从以下7个方面简单介绍自己在学习反射机制的收获
  1. 反射的简单介绍
  2. Class对象的简单介绍(反射机制获取Class对象的引用)
  3. 反射机制创建类的实例对象(无参构造器和有参构造器)
  4. 反射机制操作类的方法
  5. 反射如何获取类的属性,被private修饰时怎么获取?
  6. 反射中的invoke方法介绍
  7. 动态代理的demo

一、反射的简单介绍:

运行时类型信息使得我们可以在程序运行时发现和使用类型信息。主要实现方式有两种:

  • RTTI:它要求我们在编译时就已经知道了所有类型;
  • 反射:它允许我们在运行时发现和使用类的信息。

所有的类都是在对其第一次使用的时候,动态的加载到JVM中去的,当程序创建第一个类的静态成员的引用时,就会加载这个类。构造器也是类的静态方法,使用new操作符创建类的新对象也会被当做对类的静态成员的引用。java程序在它开始运行的之前并没有完全加载,各个部分是在需要用到的时候才加载。

  • 为什么要使用反射?

很多情况下,我们在程序已经完成编译后需要更新或者新增一些功能,按照传统的功能,需要把整个程序重新编译一次才可以实现功能的修改,但是如果采用了反射机制,就可以在需要的时候动态的创建和编译,实现修改的功能。总而言之,使用反射的目的是:得到类的信息,并能调用该类对象的方法和属性。

二、Class对象的简单介绍:

每一个类都有一个Class对象,它包含了与类有关的信息。保存在.class文件中,它就是用来创建类的所有“常规”对象的。每一个Class对象都属于Class类。所以想在运行时得到类的信息,我们需要先获取该类的Class对象信息。在java中有以下三种获取Class对象的方法

1.使用类字面常量,即.class形式

要求在编译时就已经对类信息做检查。

Class <?> c1 = User.class;

2.使用Object类中的getClass()方法

要求类的实例已经得到,使用Class   c = 对象名 . getClass()。

 User  user = new User();    
 Class <?> c2 = user.getClass(); 

3.使用Class类的静态方法Class . forName("类的全限定名称");

Class对象类似于其他普通的对象,我们是通过forName()方法来获取Class对象的引用。java反射机制就是使用该方法来获取类的Class对象。

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

三、反射机制创建类的实例对象(无参构造器和有参构造器):

我们现在仅仅获取到类的Class对象引用,在编译期不具备任何类的信息,所以需要创建类的实例对象。通过调用Class类中的newInstance()方法来实现。

  • 使用newInstance()方法创建的类必须要有默认的构造器。

如下实例:

package text1;
class Person {  
    
    String personName = "旺仔牛奶";
    
    public Person() {  
        System.out.println("运行了Person类的无参默认构造方法");  
    } 
    public Person( String personName) {  
        this.personName = personName;
        System.out.println("运行了Person类的自定义构造方法");  
    } 
    @Override  
    public String toString() {  
        System.out.println( "Person类的personName:"+personName); 
        return  personName;
    }
}  
public class TextReflctDemo {  
  
    public static void main(String[] args) throws Exception {  
        Class<?> c = Class.forName("text1.Person");  
        // 相当于关键字实例化对象,Object obj = new Person();  
        Object obj = c.newInstance();  
        System.out.println(obj);  
    }  
}  

输出结果如下:

运行了Person类的无参默认构造方法
Person类的personName:旺仔牛奶
旺仔牛奶

去掉默认的无参构造方法运行结果报错:

Exception in thread "main" java.lang.InstantiationException: text1.Person
	at java.lang.Class.newInstance(Class.java:427)
	at text1.TextReflctDemo.main(TextReflctDemo.java:22)
Caused by: java.lang.NoSuchMethodException: text1.Person.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.newInstance(Class.java:412)
	... 1 more
因此,从上个例子可以看出调用newInstace()方法时,程序会默认调用Person的无参构造方法,并且获取到了Person的实例化对象,可以调用Person类的方法和属性。

  • 那如果想通过指定的构造方法来实例化对象,应该怎么办呢?

还是之前的Class类,Class类中有两个方法去获取类中的构造方法

1.获取所有构造方法对象的数组

Class<?> c = Class.forName("text1.Person");  
Constructor<?>[] cons = c.getConstructors();

2.获取指定参数的构造方法

Class<?> c = Class.forName("text1.Person");  
Constructor<?> con = c.getConstructor(String.class);  
Object obj = con.newInstance("旺仔牛奶"); 

如果你没有提供默认无参构造方法,那就必须通过以上两种方式去获取类的实例化对象。

四、利用反射操作类的方法:

获取父类中的方法

Class<?> c = Class.forName("text1.Person");
//获取父类中定义的所有方法
Method[] methodall = c.getMethods();
//获取父类中定义的指定参数的方法
Method method = c.getMethod("方法的名称",String.class);
获取本类自身的方法

Class<?> c = Class.forName("text1.Person");
//获取本类中定义的所有方法  
Method[] methodall = c.getDeclaredMethods(); 
//获取本类中定义的指定的方法 
Method method = c.getDeclaredMethod("方法的名称",String.class);

五、利用反射获取类的属性:

Class<?> c = Class.forName("text1.Person");
//实例化  
Object obj = c.newInstance();  
//获取属性  
Field nameField = c.getDeclaredField("personCode");  
//取消访问检查,针对被private修饰的属性  
nameField.setAccessible(true);  
 //给属性赋值  
nameField.set(obj, "0136");  
 //获取属性值并输出  
System.out.println(nameField.get(obj));  
注意:对于Person类中的私有成员private修饰的属性或者方法,通过反射机制能到达或者调用吗?答案是可以的。

Person类中的personCode属性是private属性的,不对外开放,如果Field类直接访问该属性,会报权限错误。
在Construction,Method,Field三个类中有一个共同的父类AccessibleObject,定义了取消封装的操作:setAccessible(Boolean flag);
该方法默认的是参数是false,表示反射的对象应该实施 Java 语言访问检查。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。

六、反射中invoke()方法的使用:

该方法在Method类中,Method类本身就代表一个方法,当Method类中的对象调用invoke方法时,就相当于调用了Method对象所代表的方法,方法里面传入对应的参数,实现动态调用方法。

//获取本类中定义的指定的方法 
 Method method = c.getDeclaredMethod("方法的名称",String.class);
 //调用获取的方法,相当于 对象.方法名("参数");  
method.invoke(obj, "传入的参数"); 
例子:还是之前Person的例子,我们获取Person的setPersonCode()和getPersonCode()方法,并调用

package text1;

import java.lang.reflect.Method;

class Person {  
    
     String personName = "旺仔牛奶";
     
    public String getPersonCode() {
        return personCode;
    }
    
    public void setPersonCode(String personCode) {
        this.personCode = personCode;
    }
    private String personCode;
    
    public Person() {  } 
}  
public class TextReflctDemo {  
  
    public static void main(String[] args) throws Exception {  
        Class<?> c = Class.forName("text1.Person");
        //实例化  
        Object obj = c.newInstance();  
        //获取setPersonCode方法 
        Method setPersonCodeM = c.getDeclaredMethod("setPersonCode",String.class);
       //获取getPersonCode方法 
        Method getPersonCodeM = c.getDeclaredMethod("getPersonCode");
        //调用获取的方法,相当于 对象.方法名("参数");  
        setPersonCodeM.invoke(obj,"013674"); 
        System.out.println(getPersonCodeM.invoke(obj));  
      
    }  
        
    }  

七、反射内容的应用:动态代理的小demo:

思路:调用Proxy.newProxyInstance()创建动态代理,这个步骤需要得到代理对象的类加载器;代理对象实现的接口列表;InvocationHandler的一个实现类;

使用Method.invoke()方法将请求转发给代理对象,并传入必须的参数。

例子如下:

1.代理对象类需要实现的接口

package text1;

//PersonImple类需要实现的接口
public interface PersonInterface {
    public void talk();
}
2.代理对象对应的类

package text2;
import text1.PersonInterface;
//实现类PersonImple,代理对象对应的类
public class PersonImple implements PersonInterface {

    @Override
    public void talk() {
       System.out.println("person can talk");
    }
}

3.InvocationHandler的一个实现类

package text2;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//InvocationHandler的一个实现类
public class PersonHandler implements InvocationHandler{
    private Object personObj;
    public PersonHandler(Object obj){  
        this.personObj=obj;  
    }  

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");  
        Object result = method.invoke(personObj, args);  
        System.out.println("after");  
        return result;  
    }
    
}
测试demo

package text2;
import java.lang.reflect.Proxy;
import text1.PersonInterface;
//测试demo
public class PersonText {

    public static void main(String[] args) {
            PersonInterface personInterface = new PersonImple();  
            PersonHandler handler = new PersonHandler(personInterface);  
            PersonInterface proxy = (PersonInterface)Proxy.newProxyInstance(personInterface.getClass().getClassLoader(), personInterface.getClass().getInterfaces(), handler);  
            proxy.talk();  
        }  

    }

输出结果:

before
person can talk
after


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值