反射

首先要了解什么是“动态类型语言”:类型的检查是在运行时检查的,程序在运行时可以改变程序的结构和类型。常见的语言:javascript、Python、Ruby,而静态性语言是在程序编译的阶段,对程序进行检查。相对的来说有"动态的类型语言",就会有"静态性语言"的存在的,常见的"静态性语言"有 java、c、c++等,这些静态性语言都是在程序编译的时候检查程序。

静态类型的语言的特点:

优点:结构非常的规范,便于调试,类型安全。

缺点:为此需要写更多的类型相关代码,导致不便于阅读、不清晰明了。

动态类型的语言的特点:

优点:方便阅读,不需要写非常多的类型相关的代码。

缺点:自然就是不方便调试,命名不规范时会造成读不懂,不利于理解等。

静态语言的开发效率可由IDE工具提高,而动态语言对程序员个人要求更高。

反射可以更好的帮助我们理解java中所说的"一切皆对象"的论述,正因为java中含有反射的技术,所以可以认为java语言是"准动态性的语言",我们可以利用反射机制、字节码操作获得类似动态语言的特性

从而让编程的时候更加灵活!

反射机制:

   原先我们想调用一个类中的方法的时候,那么我们最初的做法是,先创建一个对象,然后使用对象.方法名()来调用方法,当然如果方法是静态的方法的话,那么我们可以使用类名.方法名来调用;我们知道,java源代码经过javac变成.class的字节码,然后被类加载器加载,加载完成后在堆内存的方法区产生一个Class的对象(一个类中对应一个对象),这个对象对应类中的完整的结构,这个对象就像一面镜子,我们通过这个镜子可以看见这个类的完整的结构,这就是反射。

反射是在程序运行的时候,通过获取某个类的Class类的对象,进而可以操作此类的所有的属性和方法。那么问题就转换成如何获得Class的对象。

获取Class的对象的方式:

1.类名.class :前提是知道该类的类名。

2.对象.getClass:前提是知道该类的对象。

3.Class对象.forName("类的全路径 = 包名 + 类名");一般用于加载配置文件,来获取相应的class的对象(比如说好多框架的原理)

4.使用类的加载器:ClassLoader.loadClass("类的全类名");

我们知道类的加载器将字节码的文件加载进来,一个类只对应一个class,那么这个类中的成员也有相对应的class,这也就对应了,万物皆对象的概念,正如 jdkApi所说:

Class的实例表示正在运行的 Java 应用程序中的类和接口。

枚举是一种类,enum

注释(指的是注解,是给计算机看的)是一种接口。annocation

每个数组([])属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。

基本的 Java 类型(primitive type)(booleanbytecharshortintlongfloatdouble)和关键字 void 也表示为 Class 对象。

Class 没有公共构造方法。

Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

那么可以将各个类型 的Class的对象,打印一下,看看都是什么,代码如下

基本数据类型的Class的对象仍然是他们本身(void的class对象也是他本身):

public static void main(String[] args) {
        //基本数据类型:
        Class bytes = byte.class;
        System.out.println(bytes);
        
        Class shorts = short.class;
        System.out.println(shorts);
        
        Class ints = int.class;
        System.out.println(ints);
        
        Class longs = long.class;
        System.out.println(longs);
        
        Class floats = float.class;
        System.out.println(floats);
        
        Class doubles = double.class;
        System.out.println(doubles);
        
        Class bolleans = boolean.class;
        System.out.println(bolleans);
        
        Class chars = char.class;
        System.out.println(chars);
    }

结果:

数组:只要是数据类型相同,维度相同,那么他们是存在同一个数据的class中的。

class中对应的情况:

java.lang.Class:代表一个类

java.lang.reflect.Method:代表类的方法

java.lang.reflect.Field:代表类的成员变量

java.lang.reflect.Constructor:代表类的构造方法

Class.getPackage()获取包

getName():全限定名

动态的构造对象分为两种情况:

1.含有无参的构造函数:可以直接使用Class的对象来调用newInstance();创建对象。

/*
     * 动态的获取对象的属性:
     */
    @Test
    public void getObj() throws Exception {
        //1.构造方法非私有的。
        Class clazz = Student.class;
        Object instance = clazz.newInstance();
        System.out.println(instance);//Student [name=null, age=0]
        //2.给属性赋值:
        //获取某个属性在字节码文件 中Field类型的对象
        Field name = clazz.getDeclaredField("name");
        //由于属性被私有化了,那么需要打开访问的权限才可以访问
        name.setAccessible(true);
        //设置属性name的值为"张三"
        name.set(instance, "张三");
        System.out.println(name.get(instance));//获取哪个对象的实例
        //3.设置id的值:与上面的设置属性是同理的。
        Field fieldId = clazz.getDeclaredField("age");
        fieldId.setAccessible(true);
        fieldId.set(instance, 1);
        System.out.println(fieldId.get(instance));
    }

实体类:

package com.atguigu.reflect.bean;

public class Student {

    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
    
    /**
     * 获取学生的信息
     * @return
     */
    public String getInfo() {
        return "名称:" + name + ",年龄:" + age;
    }
    

}

2.如果没有无参数的构造函数:就不能直接使用字节码的对象来调用newInstance();来直接创建对象;可以创建Constructor的对象,然后再去newInstance()

/*
     * 动态的获取成员的方法。
     * 在没有无参数的构造函数
     */
    @Test
    public void getObj_3() throws Exception {
        //1.同样也是先获取该类的Class的对象
        Class clazz = Student.class;
        //2.实例化相应的对象(要求对象有默认的无参构造)
        //Object instance = clazz.newInstance();
        //如果没有无参数构造函数的话,那么:需要使用有参构造
        Constructor constructor = clazz.getConstructor(String.class,int.class);
        Object instance = constructor.newInstance("张三丰",12);
        //3.使用Class的对象去获取相应的Method
        //想要确定一个方法:1.方法名;2.参数列表
        Method method = clazz.getDeclaredMethod("getInfo");
        //方法被某个对象调用。
        Object object = method.invoke(instance);
        //因为设计者并不知道,具体的什么方法会被执行,所以设置的是任意的类型,
        //所以想使用具体的那个方法的话,那么只有是将其向下转型处理
        String s = (String) object;
        System.out.println(s);
    }

实体类:

package com.atguigu.reflect.bean;

public class Student {

    private String name;
    private int age;

    /*public Student() {
    }*/

    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
    
    /**
     * 获取学生的信息
     * @return
     */
    public String getInfo() {
        return "名称:" + name + ",年龄:" + age;
    }
    

}

通过反射来获取泛型

Java采用泛型擦除的机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有和泛型有关的类型全部擦除。所以原先直接获取不到泛型信息。为了通过反射操作这些类型以迎合实际开发的需要,Java5.0就新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class中的类型但是又和原始类型齐名的类型

  • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型,比如:T[],ArrayList<T>[]
  • TypeVariable:是各种类型变量的公共父接口。比如:T,E

  • Type[] getBounds()可以继续获取上限,如果类型变量指定了上限例如T extends Person就返回上限Person,如果未指定就是Object
  • ParameterizedType:表示一种参数化的类型,比如:Comparable<Cat>,ArrayList<?>,ArrayList<? extends/super  T/Cat>

  • Type[]  getActualTypeArguments()返回表示此类型实际类型参数的 Type 对象的数组。

     WildcardType:代表一种通配符类型表达式,(Wildcard通配符)比如?,? extends/super  Person/T

    Type[]  getUpperBounds()上限,如果有的话,没有就默认是Object

    Type[]  getLowerBounds()下限,如果有的话,没有就返回长度为0的数组

泛型类和泛型接口:

Type getGenericSuperclass():如果父类有泛型,那么返回ParameterizedType参数化类型,即extends后面的父类写法

例如:com.reflection.type.DAOImpl<T, P>保留

      com.reflection.type.DAOImpl<User, Integer>

      com.reflection.type.DAOImpl 擦除

 如果此 Class 表示 Object 类、接口、基本类型或 void,则返回 null。如果此对象表示一个数组类,则返回表示 Object 类的 Class 对象。

2)Type[] getGenericInterfaces():同上

3)TypeVariable[] clazz.getTypeParameters():返回本Class对象对应类型的泛型的类型变量

  例如:ArrayList类的E,如果有E extends 父类,可以通过TypeVariable的getBounds()方法得到上限。如果没有指定上限,默认上限是Object

通过反射读取注解配置参数的值:

package com.atguigu.reflect.annocation;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;

import org.junit.Test;

/**
 * 所用的知识: a:自定义注解 b:使用反射读取注解中的值
 * 
 * @author DHB
 *
 */
public class AnnocationTest {
    public static void main(String[] args) {

    }

    /**
     * 获取方法上的注解
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws SecurityException 
     * @throws NoSuchMethodException 
     */
    @Test
    public void test_2() throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException {
        // 获取一个类上的注解:
        Class clazz = UseAnnocation.class;
        Method method = clazz.getMethod("test");
        // 获取相应的注解的对象:
        Annotation annotation = method.getDeclaredAnnotation(MyAnnocation.class);
        //向下转型:有风险
        MyAnnocation my = (MyAnnocation) annotation;
        System.out.println(my.value());
    }
    
    
    /**
     * 获取类上的注解
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    @Test
    public void test() throws InstantiationException, IllegalAccessException {
        // 获取一个类上的注解:
        Class clazz = UseAnnocation.class;
        // 获取相应的注解的对象:
        Annotation annotation = clazz.getAnnotation(MyAnnocation.class);
        // 获取的对象的时候,一般需要对其进行向下转型。
        MyAnnocation m = (MyAnnocation) annotation;
        System.out.println(m.value());
    }

}

// 自定义一个注解:定义其作用的位置(类上、方法上)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME) // 指定runtime的生命周期,反射访问的到
@interface MyAnnocation {
    // 声明配置参数
    String value() default "atguigu";
}

// 定义一个类去使用注解:
@MyAnnocation("生命在于静止....")
class UseAnnocation {
    @MyAnnocation
    public void test() {
        System.out.println("hello world!!!");
    }
}

反射的应用之一:动态的代理。

动态代理的演示将会在后续的码出。

  

原文地址:https://www.cnblogs.com/donghaibin/p/9159383.html