Java-反射

一、反射的概述

1. 反射的简介

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

2. 反射机制提供的功能

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时获取泛型信息;
  • 在运行时调用任意一个对象的成员变量和方法;
  • 在运行时处理注解;
  • 生成动态代理;

3. 主要API

  • java.lang.Class:代表一个类;描述类的一个类;
  • java.lang.reflect.Method:代表类的方法;
  • java.lang.reflect.Field:代表类的成员变量;
  • java.lang.reflect.Constructor:代表类的构造器;

 

二、Class类

一个Class对象对应的是一个加载到JVM中的一个.class文件;通过Class可以完整地得到一个类中的所有被加载的结构;

1. 类的加载过程

  • 程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例;
  • 换句话说,Class的实例就对应着一个运行时类
  • 加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类,并且都是同一个;

2. 获取Class实例的常用方式

【代码示例】

@Test
public void test2() throws Exception {
    //方式一:调用运行时类的属性:.class
    Class<Person> clazz1 = Person.class;
    System.out.println(clazz1);//class com.chx.reflectionTest.Person

    //方式二:通过运行时类的对象,调用getClass()
    Person p1 = new Person();
    Class<? extends Person> clazz2 = p1.getClass();
    System.out.println(clazz2);//class com.chx.reflectionTest.Person

    //方式三:调用Class的静态方法:forName(String classPath)
    Class<?> clazz3 = Class.forName("cn.bruce.java.Person");
    System.out.println(clazz3);//class com.chx.reflectionTest.Person

    System.out.println(clazz1 == clazz2);//true
    System.out.println(clazz1 == clazz3);//true
   

还有一种使用类加载器:ClassLoader;

3. Class实例可以代表的结构

(1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类;

(2)interface:接口;

(3)[]:数组; 只要数组的元素类型与维度一样,就是同一个Class;

(4)enum:枚举;

(5)annotation:注解@interface;

(6)primitive type:基本数据类型;

(7)void;

 

三、反射的简单使用

1. 获取运行时类的对象

【代码示例】

先获取一个运行时类对应的Class对象(上述多种方式),在通过该对象创建运行时类的对象;这里的运行时类就是 Person;

Class<Person> clazz = Person.class;
Person person1 = clazz.newInstance();

【注意】

调用newInstance()方法,会创建对应的运行时类的对象。内部调用了运行时类的空参的构造器;

要想此方法正常的创建运行时类的对象,要求:

  1. 运行时类必须提供空参的构造器;
  2. 空参的构造器的访问权限得够。通常,设置为public;

【说明】

在javabean中要求提供一个public的空参构造器。原因:

  1. 便于通过反射,创建运行时类的对象;
  2. 便于子类继承此运行时类时,默认调用super()时,保证父类此构造器;

 

2. 获取运行时类的完整结构

我们可以通过反射,获取对应的运行时类中所有的属性、方法、构造器、父类、接口、父类的泛型、包、注解、异常等;

【代码示例】

以获取属性为例

Class<Person> clazz = Person.class;
Person person1 = clazz.newInstance();

//getFields():获取当前运行时类及其父类中声明为public访问权限的属性;
Field[] fields = clazz.getFields();
//getDeclaredFields():获取当前运行类中声明的所有属性。(不包含父类声明的属性)
Field[] declaredFields =clazz.getDeclaredFields();

其余的结构获取都是类似的;

例如获取方法 :getMethods(); getDeclaredMethods(); 返回值则是 Method[] 数组;还能获取方法中的修饰符等等;

【获取泛型示例】

//获取运行时类的带泛型的父类的泛型;generic就是泛型的意思
@Test
public void test4(){
    //获取Class对象;
    Class<Person> clazz = Person.class;
    //获取父类(带泛型);
    Type genericSuperclass = clazz.getGenericSuperclass();
    //向下转型为ParameterizedType类型;
    ParameterizedType paramType = (ParameterizedType) genericSuperclass;
    //获取泛型类型参数;getActualTypeArguments();
    Type[] actualTypeArguments = paramType.getActualTypeArguments();
    
    }

 

3. 调用运行时类的指定结构

与上述不同,上述都是获取全部的结构,如全部的属性;现在我们要获取指定的结构;

3.1 调用指定的属性

在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作;

  1. getDeclaredField(String fieldName)获取运行时类中指定变量名的属性;返回Field类型;

  2. 调用Field的方法:

    • set():参数1;指明设置哪个对象的属性 ;参数2:将此属性值设置为多少;
    • get():参数1:获取哪个对象的当前属性值;
Class<Person> clazz = Person.class;
//创建运行时类的对象;
Person person = clazz.newInstance();

//1、getDeclaredField(String fieldName)获取运行时类中指定变量名的属性;
Field name = clazz.getDeclaredField("name");

//2、保证当前属性是可访问的;私有也能访问;
name.setAccessible(true);
//3、设置、获取当前属性的值;
name.set(person, "张三");

System.out.println(name.get(person));
3.2 调用指定的方法

通过getDeclaredMethod获取Method实例,通过Method实例调用运行时类的方法;

【代码示例】

Class<Person> clazz = Person.class;
//创建运行时类的对象
Person person = clazz.newInstance();

/*
1、获取指定的某个方法
getDeclaredMethod()
参数1:指明获取的方法的名称;
参数2:指明获取的方法的形参列表;
*/
Method show = clazz.getDeclaredMethod("show", String.class);

//2、保证当前方法是可访问的;
show.setAccessible(true);

/*
3、调用方法的invoke():
参数1:方法的调用者;参数2:给方法形参赋值的实参;
invoke()的返回值即为对应类中调用的方法的返回值;
*/
Object returnValue = show.invoke(person, "China");
//等同于:String nation = person.show("China");
System.out.println(returnValue);

System.out.println("静态方法示例:");

//在Person类中添加了private static void showDesc()

//获取方法;
Method showDesc = clazz.getDeclaredMethod("showDesc");
//强制访问;私有也能访问;
showDesc.setAccessible(true);
//如果调用的运行时类中的方法没有返回值,则此invoke()返回null;
//传入Person.class参数调用静态方法;		
Object returnVal = showDesc.invoke(Person.class);
//不需要运行时类也能访问静态方法;
Object returnVal = showDesc.invoke(null);
System.out.println(returnVal);//null
3.3 调用指定的构造器

【代码示例】

@Test
public void test4() throws Exception{
    Class<Person> clazz = Person.class;
    //1.获取指定的构造器,指明构造器的参数列表
    Constructor<Person> declaredConstructor = clazz.getDeclaredConstructor(String.class);
    //2.保证此构造器是可访问的
    declaredConstructor.setAccessible(true);
    //3.调用此构造器创建运行时类的对象
    Person person = declaredConstructor.newInstance("张三");

    System.out.println(person);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值