Java基础之反射详解

对于任意一个运行时的类(Runtime),都可以知道类的属性与方法;对于任意一个对象,都可以调用它的任意的方法与属性。动态获取类中的信息和动态调用对象的属性与方法的功能就是反射(直接操作属性和方法)。主要运用在框架中,其实框架不仅仅只有反射,另外还包括设计模式和注解,三者结合运用就是框架。

获取Class实例的方式

对于class类就是java中通过javac进行编译生成的.class文件(字节码文件),一个class类就代表一个运行时的实例,加载到内存当中,会缓存一段时间,在这时间段中我们可以通过不同的方式获取此运行时的类,下面介绍如何获取Class实例。

获取之前准备一个Person类,具体属性,方法我已经在类中说明

/**
 * @author jektong 
 * @Date 2020-7-27 10:31:01
 */
public class Person {
    
    
    //人物名
	private String name;
    //年龄
	private int age;
    
    //四个不同范围的变量
	public String a;
	protected String b;
	String c;
	private String d;   
	//无参构造
	public Person() {
    
    
	}
    //私有有参构造
	private Person(String name, int age) {
    
    
		super();
		this.name = name;
		this.age = age;
	}
    //setter与getter方法
	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;
	}
    //自定义printStr方法
	public void printStr(String s) {
    
    
		System.out.println(s);
	}
    //自定义默认的方法
	String getString(String s){
    
    
		return s+"hello";
	}	
    //自定义show方法
	private void show() {
    
    
		System.out.println("show");
	}
    //重写toString
	@Override
	public String toString() {
    
    
		return "Person [name=" + name + ", age=" + age + "]";
	}
}

①. 方式1:通过类.class的方式获取

Class<Person> c1 = Person.class;

②. 方式2:对象的getClass获取

Person person = new Person();
Class<? extends Person> c2 = person.getClass();

③. 方式3:forName(className)静态的获取

Class<?> c3 = Class.forName("com.jektong.reflect.Person");

④. 方式4:通过类加载器进行获取

ClassLoader classLoader = ReflectDemo01.class.getClassLoader();
Class<?> c4 = classLoader.loadClass("com.jektong.reflect.Person")
  • 获取的类是全路径名,路径必须写全
  • Class<?> 是泛型,里面的问号为何不可写Person,因为加载的时候还不确定Person是否存在

接下来介绍另一种获取Class的方式,可以将类名写入properties文件中使用Properties类进行流读取

类路径下创建person.Properties文件(此文件需要在类路径下),配置全类名

ClassName=com.jektong.reflect.Person

代码实现:

    Properties prop = new Properties();
    //类加载
    ClassLoader classLoader = ReflectDemo01.class.getClassLoader();
    //读取proerties文件
    InputStream is = classLoader.getResourceAsStream("person.properties");
    //加载文件
    prop.load(is);
    //获取配置名
    String className = prop.getProperty("ClassName");
    Class<?> c4 = Class.forName(className);
    System.out.println(forName);

以上的c1 ,c2,c3,c4输出的结果都是class com.jektong.reflect.Person

获取类的结构

通过反射我们可以获取到一个运行时的类的具体结构,比如属性,方法构造器,父类,接口,父类的泛型,包,注解和异常等等

  1. 获取类的成员变量

    通过Field类的对象来获取,主要有以下方法:

    • Field[] getFields():获取类中由于public修饰的成员变量

    • Field[] getDeclaredFields():获取所有成员变量,不考虑修饰符

    • Field getField(String name) :获取指定名称的由public修饰的成员变量

    • Field getDeclaredField(String name) :获取指定的与变量,不考虑修饰符

          System.out.println("==========获取类中所有public属性==========");
          Class<?> forName = Class.forName("com.jektong.reflect.Person");
          //获取类中所有public属性
          Field[] fields = forName.getFields();
          for (Field field : fields) {
              
              
              System.out.println(field);
          }
          System.out.println("==========获取类中指定的public属性==========");
          //获取类中指定的public属性
          //Field field = forName.getField("name");//namejava.lang.NoSuchFieldException
          Field field = forName.getField("a");
          System.out.println(field);
          System.out.println("==========获取类中所有属性,不考虑修饰符==========");
          //获取类中所有属性,不考虑修饰符
          Field[] declaredFields = forName.getDeclaredFields();
          for (Field field2 : declaredFields) {
              
              
              System.out.println(field2);
          }
      

在这里插入图片描述

从输出的结果来看,输出了变量的修饰符,数据类型,和定义的变量

Field:成员变量

  • void set(Object obj, Object value) : 设置值
  • get(Object obj) : 获取值
  • setAccessible(true): 忽略访问权限修饰符的安全检查
  • int getModifiers(): 获取修饰符
  • Class getType() :获取类型
  • String getName :获取字段名
	//获取任意一个成员变量
    Field declaredField = forName.getDeclaredField("name");//name为私有属性
    //获取修饰符
    int modifiers = declaredField.getModifiers();
    String modName = Modifier.toString(modifiers);	
    System.out.println(modName);
    //获取字段名
    String name = declaredField.getName();
    System.out.println(name);
  1. 获取类中的构造方法

    • Constructor<?>[] getConstructors() :获取公共的构造
    • Constructor<?>[] getDeclaredConstructors() :获取私有的构造
    • Constructor getConstructor(类<?>… parameterTypes) :获取带有参数的公共构造
    • Constructor getDeclaredConstructor(类<?>… parameterTypes) 获取带有参数的私有构造
    • Constructor:构造方法
      T newInstance(Object… initargs) 创建对象
      如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
        System.out.println("==========获取公共的构造==========");
        Class<?> c = Class.forName("com.jektong.reflect.Person");
        Constructor<?>[] constructors = c.getConstructors();
        for (Constructor<?> constructor : constructors) {
          
          
            System.out.println(constructor);
        }
    
        System.out.println("==========获取所有的构造==========");
        Constructor<?>[] declaredConstructors = c.getDeclaredConstructors();
        for (Constructor<?> constructor : declaredConstructors) {
          
          
            System.out.println(constructor);
        }
    
        System.out.println("==========获取无参构造==========");
        Constructor<?> constructor = c.getConstructor();
        System.out.println(constructor);
    
        System.out.println("==========获取有参构造==========");
        Constructor<?> declaredConstructor = c.getDeclaredConstructor(String.class,int.class);
        System.out.println(declaredConstructor);
    
        System.out.println("==========无参构造创建对象==========");
        Person p1 = (Person) c.newInstance();
        p1.setName("张三");
        p1.setAge(12);
        System.out.println(p1);
        //强制访问私有属性
        declaredConstructor.setAccessible(true);
    
        System.out.println("==========私有构造创建对象==========");
        Person p2 = (Person) declaredConstructor.newInstance("李四", 13);
        System.out.println(p2);
        Field declaredName = c.getDeclaredField("name");
        Field declaredAge = c.getDeclaredField("age");
    
        System.out.println("==========获取私有的对象名==========");
        declaredName.setAccessible(true);
        System.out.println(declaredName.get(p2));
    

在这里插入图片描述

不要看这么长其实道理都是一样的。

上面已经对类中的属性和构造方法进行了访问和操作,下面讲解类中的成员方法

  1. 获取类中的成员方法
    • Method[] getMethods() 获取当前运行时类及其所父类中声明为public权限的方法
    • Method[] getDeclaredMethods() 获取当前运行时类中声明的所方法。(不包含父类中声明的方法)
    • Method getMethod(String name, 类<?>… parameterTypes)
    • Method getDeclaredMethod(String name, 类<?>… parameterTypes)
    • Method:方法对象
      • Object invoke(Object obj, Object… args) 获取方法名称
      • String getName 获取方法名
      • Annotation[] getAnnotations() 获取注解释
      • int getModifiers 获取修饰符
      • Modifier.toString(int modifier)
      • Class getReturnType() 获取返回值类型
      • Class[] getParameterTypes() 获取形参类型
      • Class[] getExceptionTypes() 获取异常类型

方法的使用与上面的是大同小异,这里说一下获取方法上的注解和参数类型

准备一个注解类:MyAnno.class,注解只是这里不说明

    @Target({
    
    ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnno {
    
    
    }

将这个注解使用到Person类中的printStr方法上

    @MyAnno
	@SuppressWarnings("all")
	public void printStr(String s) {
    
    
		System.out.println(s);
	}

获取此方法上的注解以及参数类型

    Method psMethod = c.getMethod("printStr", String.class);
    System.out.println(psMethod);
    //获取类上的注解
    Annotation[] annotations = psMethod.getAnnotations();
    for (Annotation annotation : annotations) {
    
    
        System.out.println(annotation);
    }
    //获取参数类型
    Class<?>[] parameterTypes = psMethod.getParameterTypes();
    for (Class<?> class1 : parameterTypes) {
    
    
        System.out.println(class1);
    }

获取父类

定义一个Student.class来继承Person.java不做任何实现

public class Student extends Person {
    
    
}
  • Class getSuperclass() 获取运行时类的父类
  • Type getGenericSuperclass() 获取运行时类的带泛型的父类

主要就两个方法的实现:

    Class<?> c = Class.forName("com.jektong.reflect.Student");
    Class<?> superclass = c.getSuperclass();
    System.out.println(superclass);

如果父类是泛型的话,只需要改为:

    Type type = c.getGenericSuperclass();
    System.out.println(type);

invoke()方法的使用

只要获取类中的方法,我们就可以通过反射invoke()方法去调用此方法

   //获取printStr()方法
    Method psMethod = c.getMethod("printStr", String.class);
   //创建对象,调用方法
    Person per = (Person) c.newInstance();
    psMethod.invoke(per, "hello");
    System.out.println(psMethod);
  • invoke方法中提供了两个参数,第一个是调用方法的对象,第二个是方法的参数,如果是多个参数使用数组即可
  • 如果调用的是静态的方法,第一个参数写成null

关于反射还有两个重要的概念

静态代理与动态代理

猜你喜欢

转载自blog.csdn.net/qq_41857955/article/details/107643801