Java 反射机制
一、反射简介
1.java.lang.Class:是反射的源头
创建一个类,通过编译(javac.exe),生成对应的.class文件。之后我们使用java.exe加载(JVM类加载器)
次.class 文件加载到内存以后,就是一个运行时类,存放在缓冲区。那么这个运行时类本身就是一个Class实例
- 一个运行时类只加载一次。
- 有了这个Class实例才可以进行如下操作:
- 创建对应的运行时类的对象
- 获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注解。。。。)
- 调用对应的运行时类的指定结构(属性、方法、构造器)
- 反射的应用 动态代理
二、反射机制
1.通过反射获取类对象
Class<Person> c = Person.class;
Person p = c.newInstance();//调用无参构造器
System.out.println(p);
创建c的运行时类Person类的对象时,默认调用无参的构造函数,所以Person必须有一个无参的构造器。
2.通过反射获取类的属性
getField(String fieldName):获取 public 属性,可直接赋值。
getDeclaredField(String fieldName)
- 可以获取所有的属性
- 当属性权限修饰符为 private 时,需要先设置可访问 setAccessible(true),才能赋值。
- 当属性权限修饰符为缺省时,可直接赋值。
获取属性:
@Test
public void test1(){
Class clazz = Person.class;
//1.getFields():获取运行时类和棋父类中的public属性
Field[] field1 = clazz.getFields();
for(int i = 0;i < field1.length;i++)
System.out.println(field1[i]);
//2.getDeclaredFields():只能获取运行时类本身的所有的属性
Field[] field2 = clazz.getDeclaredFields();
for(Field f:field2){
System.out.println(f.getName());
}
}
获取权限修饰符、变量类型、变量名:
public void test2(){
Class clazz = Person.class;
Field[] field2 = clazz.getDeclaredFields();
for(Field f:field2){
//1.权限修饰符
int m = f.getModifiers();
String modifiers = Modifier.toString(m);
System.out.print(modifiers + " ");
//2.属性的类型
Class type = f.getType();
System.out.print(type + " ");
//3.属性名
System.out.println(f.getName());
}
}
给运行时类的指定属性赋值:
public void test3() throws Exception{
Class clazz = Person.class;
//1.获取指定属性
Field name = clazz.getField("name");
//2.创建运行时类
Person p = (Person) clazz.newInstance();
System.out.println(p);
//3.给运行时类的指定属性赋值
name.set(p, "Tom");
Field age = clazz.getDeclaredField("age");//可以获取所有声明的属性
age.setAccessible(true);//当属性权限是 private 时,需要设置可访问
age.set(p, 18);
System.out.println(p);
//缺省修饰符属性
Field sex = clazz.getDeclaredField("sex");
sex.set(p, "女");
System.out.println(p);
}
当为私有属性赋值时,必须先设置其可访问!
3.通过反射获取类的方法
获取运行时类的方法:
public void test1(){
Class clazz = Person.class;
//1.getMethods():获取当前运行时类和其父类的所有public方法
Method[] methods = clazz.getMethods();
for(Method m:methods){
System.out.println(m);
}
System.out.println("----");
//2.getDeclaredMethods():获取当前运行时类申明的方法
Method[] methods2 = clazz.getDeclaredMethods();
for(Method m:methods2){
System.out.println(m);
}
}
获取注解、权限修饰符、方法类型、方法名称、参数列表、异常:
public void test2(){
Class clazz = Person.class;
Method[] methods = clazz.getMethods();
for(Method m:methods){
//1.获取注解
Annotation[] ann = m.getAnnotations();
for(Annotation an:ann){
System.out.println(an);
}
//2.获取权限修饰符
System.out.print(Modifier.toString(m.getModifiers()) + " ");
//3.获取方法类型
Class type = m.getReturnType();
System.out.print(type + " ");
//4.获取方法名称
System.out.print(m.getName() + " ");
//5.获取参数列表
Class[] par = m.getParameterTypes();
System.out.print("(");
for(Class p:par){
System.out.print(p + " ");
}
System.out.print(")");
//6.获取异常
Class[] exClasses = m.getExceptionTypes();
if(exClasses.length != 0){
System.out.print("throw");
}
for(Class e:exClasses){
System.out.print(e);
}
System.out.println();
}
}
调用运行时类中指定的方法:
public void test3() throws Exception{
Class clazz = Person.class;
//getMethod(String methodName,Class ... params):调用声明为public 的方法
Method m1 = clazz.getMethod("show");
Person p = (Person) clazz.newInstance();
Object object = m1.invoke(p);//第一个参数:调用运行时类对象;第二个参数:方法 m1 的形参
System.out.println(object);
System.out.println("--------");
Method m2 = clazz.getMethod("toString");
Object object2 = m2.invoke(p);
System.out.println(object2);
System.out.println("--------");
//对于静态方法可以这样调用
Method m3 = clazz.getMethod("info");
m3.invoke(Person.class);
System.out.println("--------");
Method m4 = clazz.getDeclaredMethod("testPrivate",String.class);
m4.setAccessible(true);//这里testPrivate是私有的方法需要先设置可访问
Object object3 = m4.invoke(p, "张超");
System.out.println(object3);
}
和私有属性一样,在调用 private 方法时先要setAccessible(true),设置其可访问才能成功调用。
获取运行时类的构造器:
public void test2(){
Class clazz = Person.class;
Constructor[] con = clazz.getConstructors();
for(Constructor c:con){
System.out.println(c);
}
System.out.println("---------");
Constructor[] con2 = clazz.getDeclaredConstructors();
for(Constructor c:con2){
System.out.println(c);
}
}
调用指定的构造器:
public void test4() throws Exception{
Class clazz = Person.class;
Constructor con = clazz.getDeclaredConstructor(String.class,int.class);
con.setAccessible(true);
Person p = (Person)con.newInstance("张超",21);
System.out.println(p);
}
其他:
//6.获取注解
@Test
public void test6(){
Class clazz = Person.class;
Annotation[] ann = clazz.getAnnotations();
for(Annotation an:ann){
System.out.println(an);
}
}
//5.获取所在的包
@Test
public void test5(){
Class clazz = Person.class;
Package package1 = clazz.getPackage();
System.out.println(package1.getName());
}
//4.获取实现的接口
@Test
public void test4(){
Class clazz = Person.class;
Class[] inter = clazz.getInterfaces();
for(Class c:inter){
System.out.println(c);
}
}
//3.获取父类的泛型
@Test
public void test3(){
Class clazz = Person.class;
Type type = clazz.getGenericSuperclass();
ParameterizedType param = (ParameterizedType)type;
Type[] types = param.getActualTypeArguments();
for(Type t:types){
System.out.println(((Class)t).getName());
}
}
//2.获取带泛型的运行时类的父类
@Test
public void test2(){
Class clazz = Person.class;
Type type = clazz.getGenericSuperclass();
System.out.println(type);
}
//1.获取运行时类的父类
@Test
public void test1(){
Class clazz = Person.class;
Class superClass = clazz.getSuperclass();
System.out.println(superClass);
}
三、代理设计模式–动态代理
反射是动态语言的关键
Q:什么是代理设计模式?
A:为其他对象提供一种代理以控制对这个对象的访问。
1.静态代理方式:
若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的。 通常情况下, 静态代理中的代理类和被代理类会实现同一接口或是派生自相同的父类。
//接口
interface ClothFactory{
void productCloth();
}
//被代理类
class NikeClothProduct implements ClothFactory{
@Override
public void productCloth() {
System.out.println("Nike 工厂生产一批衣服。");
}
}
//代理类
class ProxyFactory implements ClothFactory{
ClothFactory cf;
//创建代理类对象时,实际传入的是被代理类的对象
public ProxyFactory(ClothFactory cf) {
this.cf = cf;
}
@Override
public void productCloth() {
System.out.println("Nike 工厂开始收取代理费.");
cf.productCloth();
}
}
public class TestClothProduct {
public static void main(String[] args) {
NikeClothProduct nike = new NikeClothProduct();
ProxyFactory pf = new ProxyFactory(nike);
pf.productCloth();
}
}
2.动态代理模式:
代理类在程序运行时创建的代理方式被成为 动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
interface Subject{
public void action();
}
//被代理类
class RealSubject implements Subject{
@Override
public void action() {
System.out.println("我是被代理类,RealSubject.");
}
}
class MyInvocationHandler implements InvocationHandler{
Object object;//实现了接口的被代理类对象的声明
//①给被代理类对象实例化②返回一个代理类对象
public Object blind(Object object){
this.object = object;
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),this);
}
//当通过对代理类发起对重写函数的调用时,都会转化为如下的invoke方法调用。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//returnVal是method的返回值(重写方法的返回值)
Object returnVal = method.invoke(object, args);
return returnVal;
}
}
public class TestProxy {
public static void main(String[] args) {
//1.创建被代理类的对象
RealSubject real = new RealSubject();
//2.创建一个实现了InvocationHandler接口的对象
MyInvocationHandler handler = new MyInvocationHandler();
//3.调用blind方法,动态的返回一个同样实现了real被代理所在类的接口的代理类对象
Object object = handler.blind(real);
Subject subject = (Subject)object;//这里的subject就是代理类的对象
subject.action();//一执行就转到InvocationHandler接口的实现类的invoke方法
NikeClothProduct nike = new NikeClothProduct();
ClothFactory proxyCloth = (ClothFactory)handler.blind(nike);
proxyCloth.productCloth();
}
}