- 反射的简单介绍
- Class对象的简单介绍(反射机制获取Class对象的引用)
- 反射机制创建类的实例对象(无参构造器和有参构造器)
- 反射机制操作类的方法
- 反射如何获取类的属性,被private修饰时怎么获取?
- 反射中的invoke方法介绍
- 动态代理的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

1201

被折叠的 条评论
为什么被折叠?



