反射基础

0、工厂模式分析

对于程序的开发模式之前一直强调:尽量减少耦合,而减少耦合的最好做法是使用接口,但是就算使用了接口也逃不出关键字new,所以实际上new是造成耦合的关键元凶。

没有使用反射

package CoreJavaColume.Chapter5.Factory.OldModel;

/**
 * @Author Zhou  jian
 * @Date 2020 ${month}  2020/1/17 0017  20:04
 */
public interface Fruit {

    public void eat();

}



package CoreJavaColume.Chapter5.Factory.OldModel;

/**
 * @Author Zhou  jian
 * @Date 2020 ${month}  2020/1/17 0017  20:04
 */
public class Apple implements Fruit {
    @Override
    public void eat() {
        System.out.println("吃苹果");
    }
}



/**
     * 旧的模式
     * @param className
     * @return
     */
    public  static  Fruit getInstance(String className){

        //这种工厂模式每当 添加一种水果都需要进行添加
        //if  -- else的判断
        if("apple".equals(className)){
            return new Apple();//new 对象
        }
        return null;
    }



使用反射

  /**
     * 使用到反射:Class
     * @param  className   类的全路径
     *            以上为之前所编写最简单的工厂设计模式,但是在这个工厂设计模式之中有一个最大的问题:如果现在接口的子类增加了,那么工厂类肯定需要修改,这是它所面临的最大问题,而这个最大问题造成的关键性的病因是new,
     *                     那么如果说现在不使用关键字new了,变为了反射机制呢?
     */

    public  static  Fruit getInstanceByReflect(String className){

        //使用反射的方法:这个类中的这种方法就不需要改变
        //根据传入类的全路径名称:即可构建对象
        try {
            Class clz = Class.forName(className);
            //根据类对象创建  对象
            Fruit fruit = (Fruit)clz.newInstance();
            System.out.println(fruit);
            return fruit;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }


使用反射的好处:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7IBq0u3e-1579340388213)(images/02.png)]

1、反射基础

1.1、什么是反射

反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法

Oracle的官方解释是:

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

通过反射,我们可以在运行是获得程序或程序集中每一个类型的成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而Java反射机制可以动态的创建对象并调用其属性,这样的对象的类型在编译期是未知的。我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的

反射的核心是JVM在运行时才动态的加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)直到运行对象是谁

Java反射主要提供以下功能:

  1. 运行时判断任意一个对象所属的类
  2. 运行时构造任意一个类的对象
  3. 运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法)
  4. 运行时调用任意一个对象的方法
  5. 运行时分析注解

1.2、为什么需要反射

可以思考工厂模式中的if-else判断问题以及直接使用全类路径名称然后再运行时创建对象的这样一种耦合性

1.3、反射作用

解决耦合的问题,动态扩展,解耦

在一些框架中大量使用:

Spring的配置:

XML的方式:在XML中的这种配置方式,框架会解析xml标签中的值,依据依赖关系,然后使用反射构造对象。

    <!-- 视图解析器 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>


注解的方式:在大量的代码中使用注解,在程序运行时,分析注解可以获取对象

1.4、反射原理

2、Java中的反射API

2.0、反射基本使用

  • 获取类的Class对象

    Class clz = Class.forName("com.zhenai.api.Apple");
    
  • 根据 Class 对象实例获取 Constructor 对象

Constructor appleConstructor = clz.getConstructor();

  • 使用 Constructor 对象的 newInstance 方法获取反射类对象
Object appleObj = appleConstructor.newInstance();

而如果要调用某一个方法,则需要经过下面的步骤:

  • 获取方法的 Method 对象
Method setPriceMethod = clz.getMethod("setPrice", int.class);

  • 利用 invoke 方法调用方法
setPriceMethod.invoke(appleObj, 14);

2.1、Class

在程序运行期间,Java运行时系统始终为所有对象维护一个被称为运行时的类型标志。这个信息跟踪着每个对象所属的类。虚拟机利用这些运行时类型信息选择相应额方法执行

可以通过专门的Java类访问这些信息,保存这些信息的类被称为Class

虚拟机为每个类型管理一个Class对象。因此,可以利用==运算符实现两个类对象的比较

2.1.0、class对象的源码

// reflection data that might get invalidated when JVM TI RedefineClasses() is called
    private static class ReflectionData<T> {
        volatile Field[] declaredFields;
        volatile Field[] publicFields;
        volatile Method[] declaredMethods;
        volatile Method[] publicMethods;
        volatile Constructor<T>[] declaredConstructors;
        volatile Constructor<T>[] publicConstructors;
        // Intermediate results for getFields and getMethods
        volatile Field[] declaredPublicFields;
        volatile Method[] declaredPublicMethods;
        volatile Class<?>[] interfaces;

        // Value of classRedefinedCount when we created this ReflectionData instance
        final int redefinedCount;

        ReflectionData(int redefinedCount) {
            this.redefinedCount = redefinedCount;
        }
    }
//在Class类中定义了一个静态内部类,在其类的内部
		//定义了一些成员变量,用来映射加载的类文件对应的一些成员属性:域,构造函数,方法

2.1.1、获取Class对象

三种方法:

  1. **使用 Class.forName 静态方法。**当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。
Class clz = Class.forName("java.lang.String");

  1. 使用 .class 方法。
Class clz = String.class;
  1. 使用类对象的 getClass() 方法(在Object类中的方法)。

String str = new String(“Hello”);
Class clz = str.getClass();
```

2.1.2、使用Class对象生成新对象

通过反射创建类的对象主要有两种方式:通过Class对象的newUnstance()方法;通过constructor对象的newInstance方法

  1. 通过Class对象的newInstacne方法:只能使用默认的无参数的构造方法-,若无默认构造器会抛出异常

    /**
    Creates a new instance of the class represented by this {@code Class}
         * object.  The class is instantiated as if by a {@code new}
         * expression with an empty argument list.  The class is initialized if it
         * has not already been initialized.*/
    
    Class clz = Apple.class;
    Apple apple = (Apple)clz.newInstance();
    
  2. 通过 Constructor 对象的 newInstance() 方法 :可以选择特定的构造方法

Class clz = Apple.class;
Constructor constructor = clz.getConstructor();
Apple apple = (Apple)constructor.newInstance();

///可以选择特定的构造方法
Class clz = Apple.class;
Constructor constructor = clz.getConstructor(String.class, int.class);
Apple apple = (Apple)constructor.newInstance("红富士", 15);


2.2、 Method

2.2.1、获取Method

  1. getDeclaredMethods 方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
public Method[] getDeclaredMethods() throws SecurityException
  1. ·getMethods 方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。
public Method[] getMethods() throws SecurityException

  1. getMethod 方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象。
public Method getMethod(String name, Class<?>... parameterTypes)

2.2.2、使用Method调用方法

当我们从类中获取了一个方法后,我们就可以用 invoke() 方法来调用这个方法。invoke 方法的原型为:

ublic Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException

2.3、Constructor

获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例:

public T newInstance(Object ... initargs)

2.4、Field

  1. 通过Class对象的getFields()方法可以获取Class类的属性,但无法获取私有属性。

    Class clz = Apple.class;
    Field[] fields = clz.getFields();
    for (Field field : fields) {
        System.out.println(field.getName());
    }
    
    
  2. 如果使用 Class 对象的 getDeclaredFields()方法则可以获取包括私有属性在内的所有属性

Class clz = Apple.class;
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
    System.out.println(field.getName());
}

2.5、Modifiers等其他Reflet包中的API

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值