阅读 1109

Java反射机制

反射机制概述

反射视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作认一对象的内部属性及方法。 加载完类之后在,在堆内存的方法区就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。 在这里插入图片描述 反射相关的主要API  获取Class类的四种结构:

  1. 调用运行时类的属性

Class clazz1 = Person.class; System.out.prinln(clazz1);
2. 通过运行时类的对象,调用getClass() Person p1 = new Person(); Class clazz2 = p1.getClass(); System.out.println(clazz2); 3. 调用Class的静态方法:forName(String classPath) Class clazz3 = Class.forName("java.lang.String"); System.out.println(clazz3); 4. 使用类的加载器:ClassLoader ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class clazz4 = classLoader.loadClass("com.atguigu.java.Person"); System.out.println(clazz4); 在这里插入图片描述

理解Class类并获取Class实例

在反射之前,在一个类的外部,不可以通过Person类的对象调用其内部私有结构。(如:name、showNation()以及私有的构造器)。 在这里插入图片描述 反射之后对于Person的常规操作: 在这里插入图片描述 通过反射,可以调用Person类的私有构造器,私有属性和私有方法。

  1. 调用私有构造器

Constructor cons1 = clazzz.getDeclaredConstructor(String.class); cons1.setAccessible(true); // 私有的都需要执行这一步 Person p1 = (Person) cons1.newInstance("jerry"); System.out.println(p1); 2. 调用私有属性 Field name = clazz.getDeclaredField("name"); name.setAccessible(true); name.set(p1,"HanMeimei"); System.out.prinln(p1); 3. 调用私有方法 Method showNation = clazz.getDeclaredMethod("showNation",String.class) ; showNamtion.setAccessible(true); showNation.invoke(p1,"中国"); // 执行私有方法 String nation = (String) showNation.invoke(p1,”中国"); //
System.out.prinln(nation); 相关疑惑?

  1. 通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用哪个?

建议:使用new的方式 什么时候使用反射: 当我们编译阶段不确定要造哪个类的对象,那我们这时需要用到反射 2. 反射与封装是否相矛盾

类的加载与ClassLoader的理解

  1. 类的加载过程:

程序经过java.exec命令以后,会生成一个或多个字节码文件(.class结尾)。 接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程成为类的加载。加载到内存中的类,我们称为运行时类,此运行时的类,就作为Class的一个实例。 2. Class的实例对应着一个运行时类 3. 加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类 在这里插入图片描述 在这里插入图片描述

什么是类的加载器

在这里插入图片描述 三种类加载器: 在这里插入图片描述 普通读取配置文件的方式: 在这里插入图片描述 使用系统类加载器来读取配置文件 在这里插入图片描述

通过反射创建运行时类的对象

Class clazz = Person.class; Person obj = (Person)clazz.newInstance(); 在这里插入图片描述

获取运行时类的完整结构

获取类的属性:

Class clazz = Person.class;
Field[] fields =  clazz.getFields();    // 可以获取当前类以及父类中定义的public属性
Field[] declaredFields = clazz.getDeclaredFields();  // 可以获取当前类中所有定义的属性
获取类属性的内部结构:
for(Field f: declaredFields){
 获取类属性的权限修饰符
 int modifier = f.getModifiers();
 System.out.print(Modifier.toString(modifier));
获取类属性的数据类型
Class type = f.getType();
System.out.println(type.getName());
获取类属性的变量名   
String fName = f.getName();
System.out.print(fName);
}
## 获取类的方法
Class clazz = Person.class;
Method[] methods = clazz.getMethods(); // 可以获取当前类以及父类中定义的public方法
Method[] methods = clazz.getDeclaredMethods(); // 可以获取当前类中所有定义的方法
获取类方法的内部结构:
获取方法的注解:
 Annotation[] annos = m.getAnnotations();
 for(){sout(a));
 获取权限修饰符:
 System.out.print(Modifier.toString(m.getModifiers()));
 获取返回值类型:
 System.out.print(m.getReturnType().getName());
获取形参列表:
 Class[] parameterTypes = m.getParameterTypes();
 if(!(parameterTypes == null || parameterTyupes.length ==0)){
     for(Class p:parameterTypes){
         System.out.print(p.getName)
         }
     }
获取抛出的异常:
Class[] exceptionTypes = m.getExceptionTypes();
  if(!(exceptionTypes == null || exceptionTypes.length ==0)){
     for(Class p:exception  Types){
         System.out.print(p.getName)
         }
     }
复制代码

获取运行时类构造器结构

Class clazz = Person.class;
Constructor[] construcotrs = clazz.getConstructors();      // 获取当前运行时类中声明为public的构造器
for(Constructor c : constructors){
 System.out.println(c);
 }
Constructor[] construcotrs = clazz.getDeclaredConstructors();      // 获取当前运行时类中所有构造器
for(Constructor c : constructors){
 System.out.println(c);
 }
复制代码

获取运行时类父类和父类的类型(功能性代码vs逻辑性代码)

// 获取运行时类的父类
Class clazz = Person.class;
Class superclass = clazz.getSuperclass();
System.out.println(superclass);
// 获取运行时类带泛型的父类
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(superclass);
// 获取运行时类带泛型父类的泛型
 Class clazz = Person.class;
 Type genericSuperclass = clazz.getGenericSuperclass();
 ParameterizedType paramType = (ParameterizedType) genericSuperclass;
 Type[] actualTypeArguments = paramType.getActualTypeArguments(); // 为什么是数组呢,因为像Map类型的泛型会有两个参数:Map<k,v>
 System.out.println(genericSuperclassj[0]o.getTypeName());
复制代码

获取运行时类实现的接口

Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
Class[] interfaces1 = clazz.getSuperclass.getInterfaces(); // 获取父类的接口
for(Class c: interfaces){
System.out.println(c);
} 
复制代码

获取运行时类所在的包

Class clazz = Person.class;
Package pack = clazz.getPackage();
System.out.prinln(pack);
复制代码

获取运行时类声明的注解

Class clazz = Person.class;
Annotation[] annotaions = clazz.getAnnotations();
for(Annotation annos: annotations){
 System.out.println(annos);
 }
复制代码

调用运行时类的指定结构(重要)

获取和操作运行时类指定的属性

Class clazz = Person.class; // 首先创建运行时类的对象 Person p = clazz.newInstance(); Field id = clazz.getField("id"); // 要求运行时类属性声明为public,通常不用此方法 Field id = clazz.getDeclaredField("id"); id.setAccessible(true); // public以下的权限需要设置为true // 设置当前属性的值 id.set(p,1001); // 获取当前属性的值 int pid = (int)id.get(p);

获取和操作运行时类中指定的方法(掌握)

Class clazz = Person.class; Person p = (Person) clazz.newInstance(); Method show = clazz.getDecalaredMethod("show",String.class); //参数1: 获取指定方法的名称,参数2:指明获取方法的形参列表 show.setAccessible(true); show.invoke(p,"CHN") // 参数1:方法的调用者 参数2:给方法形参赋值的实参 String nation = (String) show.invoke(p,"CHN") // 有返回值的方法的调用 // 调用静态方法 调用静态方法不需要实例化 Method showStatic = clazz.getDeclaredMethod("showStatic"); showStatic.setAccessible(true); Object returnVal = showStatic.invoke(Person.class); // 括号里面为null也可以,因为每个对象的static方法都是一样的 System.out.prinln(returnVal); //null 如果是void方法的话返回值为null

获取和操作运行时类中指定的构造器(不常用,常用newInstance)

Class clazz = Peson.class; Constructor cons = clazz.getDeclaredConstructor(String.class); cons.setAccessible(true); Person per = (Person) cons.newInstance("Tom"); System.out.println(per);

反射的应用:动态代理AOP(aspect orient proxy)(Sprint里面会再讲)

静态代理

// 工厂接口:代理类需要代理做的事情
interface ClothFactory{
    void produceCloth();
}
// 代理类
class ProxyClothFactory implements ClothFactory{
    private ClothFactory factory;
    public ProxyClothFactory(ClothFactory factory){
        this.factory = factory;    // 创建被代理类的对象
    }
    @Override
    public void produceCloth() {
        System.out.println("代理工厂的准备工作");
        // 被代理类做的事情
        factory.produceCloth();
        System.out.println("代理工厂做的后续工作");
    }
}
// 被代理类
class NikeClothFactory implements ClothFactory{
    public void produceCloth(){
        System.out.println("Nike生产一批运动服");
    }
}
public class StaticProxyTest {
    public static void main(String[] argv){
        // 创建被代理类的对象
        ClothFactory nike = new NikeClothFactory();
        // 创建代理类对象
        ClothFactory proxyFactory = new ProxyClothFactory(nike);
        proxyFactory.produceCloth();
    }
}
复制代码

动态代理

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
// 被代理类需要让代理类需要做的事情
interface Human{
    String getBelief();
    void eat(String food);
}
// 被代理类
class SuperMan implements Human{
    @Override
    public String getBelief(){
        return "I believe I can fly";
    }
    @Override
    public void eat(String food){
        System.out.println("我喜欢吃" + food);
    }
}
// 问题:
// 1. 如何根据发加载到内存中的被代理类,动态的创建一个代理类及其对象?
// 2. 当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法?
//创建代理类对象
class ProxyFactory{
    // obj为被代理类
    public static Object getProxyInstance(Object obj){
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.bind(obj);
        // 创建一个代理类对象:参数1:获取obj类的类加载器,参数2:获取obj实现的接口,参数3:获取obj中调用的方法
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }
}
class MyInvocationHandler implements InvocationHandler{
    private Object obj;
    // 绑定被代理类
    public void bind(Object obj){
        this.obj = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 调用被代理类中的方法:method是反射的
        HumanUti1 util1 = new HumanUti1();
        HumanUtil2 util2 = new HumanUtil2();
        util1.method1();
        Object returnValue = method.invoke(obj,args);
        util2.method2();
        return returnValue;
    }
}
// 调用通用方法:
class HumanUti1{
    public void method1(){
        System.out.println("-------通用方法1-------");
    }
}
class HumanUtil2{
    public void method2(){
        System.out.println("-----通用方法2---------");
    }
}
public class ProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        // 实例化动态代理类
        Human proxyInstance = (Human)ProxyFactory.getProxyInstance(superMan);
        // 通过代理类对象调用方法时调用的都是被代理类中的方法
        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.eat("鸡腿");
        // 代理类动态性的体现:
        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ClothFactory proxyClothFactory = (ClothFactory)ProxyFactory.getProxyInstance(nikeClothFactory);
        proxyClothFactory.produceCloth();
    }
}
复制代码

动态代理复习: 在这里插入图片描述 静态代理的举例说明: 在这里插入图片描述 静态代理的缺点: 在这里插入图片描述 动态代理的特点: 在这里插入图片描述 动态代理的实现:

  1. 如何根据加载到内存中的被代理类,动态创建一个代理类及其对象:通过Proxy.newProxyInstance()实现
  2. 当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a:通过InvocationHandler接口的实现类及其方法invoke()
文章分类
后端
文章标签