反射的三种用途及应用

r到目前为止我们已经知道了当一个class文件被类加载器加载进内存时,会在JVM中将形成一份描述该class文件结构的元信息对象Class,通过该对象JVM就可以获知class文件的结构信息:如构造器,字段,方法等。

由面向对象引发的思考:

既然万物皆对象,那么类名、构造器、字段、方法等这些信息当然也需要封装成一个对象,这就是Class类、Constructor类、Field类、Method类。
而通过Class类、Constructor类、Method类、Field类等类的实例对象就可以得相应的信息,甚至可以不用new关键字就创建一个实例,并设置或获取字段的值,执行对象中的方法,这就是反射技术。

反射定义

反射就是在运行时期,动态的获取类中成员信息(构造器,字段,方法)的过程!

获取类的字节码对象(Class类型对象)的三种方式

要想获取和操作类中的内容,首先要获取类的字节码对象
获取字节码对象的方式:

1、对象名.getClass():获取到的是那个真正用来创建对象的子类的字节码对象。
2、类名.class:如果已经有了类名,可以通过.class的方式获取这个类的字节码对象。
3、通过Class.forName(String className):Class类中的一个静态方法,可以根据一个类的全类名,动态的加载某个类型。

Class的newInstance()方法创建对象

反射构造方法

获取多个
public Constructor<?>[] getConstructors():
该方法只能获取当前Class所表示类的public修饰的构造器
public Constructor<?>[] getDeclaredConstructors():
获取当前Class所表示类的所有的构造器,和访问权限无关

获取单个
public ConstructorgetConstructor(Class<?>…parameterTypes) :
获取当前Class所表示类中指定参数类型的public的构造器
如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。
public Constructor getDeclaredConstructor(Class<?>… parameterTypes) :
获取当前Class所表示类中指定参数类型的构造器
如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。

调用私有的构造器前需要设置开启暴力访问:constructor.setAccessible(true);

使用构造器创建对象

构造器对象的newinstance()创建对象

package demo0911;

/**
 * @creat 2020-09-10-20:35
 *
 */
public class Person {
    // 成员变量
    private Long id;
    public String name;
    public int age;

    // 构造方法
    public Person() {
        System.out.println("无参数public构造器被执行");
    }
    public Person(String name) {
        this.name = name;
        System.out.println("带有String的public构造器被执行");
    }
    // 私有的构造方法
    private Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("带有String, int的private构造器被执行");
    }
    public Person(Long id, String name, int age) {
        super();
        this.id = id;
        this.name = name;
        this.age = age;
        System.out.println("带有Long, String, int的public构造器被执行");
    }

    @Override
    public String toString() {
        return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
    }

}
package demo0911;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.util.Arrays;

/**
 * @creat 2020-09-10-20:36
 */
public class TestPerson {
    @Test
    public void test1() throws Exception {
        Class<Person> personClass = Person.class;
        //获取public的构造器
        Constructor<?>[] constructors = personClass.getConstructors();
        System.out.println(Arrays.toString(constructors));
        System.out.println();
        //获取全部构造器
        Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
        System.out.println(Arrays.toString(declaredConstructors));
        System.out.println();
        //获取一个无参的public构造器
        Constructor<Person> constructor = personClass.getConstructor();
        Person person = constructor.newInstance();
        System.out.println(person);
        //获取一个私有的
        Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class, int.class);
       //私有构造器不能直接访问,需要开启暴力访问
        declaredConstructor.setAccessible(true);
        Person tom = declaredConstructor.newInstance("tom", 12);
        System.out.println(tom);
    }
}

反射方法

获取多个:
public Method[] getMethods():获取本类和继承过来的所有的public方法
public Method[] getDeclaredMethods():获取本类中所有的方法(包括private,不包括继承的,和访问权限无关)

获取单个:
public Method getMethod(String methodName, Class<?>… parameterTypes):获取指定public的方法(包括继承的)
methodName: 表示方法名
parameterTypes:表示方法参数的Class类型如String.class

public Method getDeclaredMethod(String name,Class<?>… parameterTypes):获取指定方法(包括private,不包括继承的)
methodName: 表示方法名字
parameterTypes:表示方法参数的Class类型如String.class

调用方法

public Object invoke(Object obj,Object… args):表示调用当前Method所表示的方法
参数:
obj: 表示被调用方法所属对象
args:表示调用方法是传递的实际参数
返回:
方法的返回结果

在调用私有方法之前开启暴力访问:Method.setAccessible(true);

使用反射调用静态方法:

静态方法不属于任何对象,静态方法属于类本身.
此时把invoke方法的第一个参数设置为null即可.

使用反射调用数组参数方法(可变参数):

调用方法的时候把实际参数统统作为Object数组的元素即可.
Method对象.invoke(方法所属对象,new Object[]{所有实参 });

package demo09111;

import java.util.Arrays;

/**
 * @creat 2020-09-10-20:52
 */
public class Person {
    // 无参无返回的方法
    public void method1() {
        System.out.println("无参无返回的public方法被执行");
    }

    // 有参无返回的方法
    public void method2(String name) {
        System.out.println("有参无返回的public方法被执行 name= " + name);
    }

    // 无参有返回的方法
    public int method3() {
        System.out.println("无参有返回的public方法被执行");
        return 123;
    }

    // 有参有返回方法
    public String method4(String name) {
        System.out.println("有参有返回方法的public方法被执行");
        return "哈哈" + name;
    }

    // 私有方法
    private void method5() {
        System.out.println("private私有方法被执行");
    }

    //静态方法
    public static void method6(String name) {
        System.out.println("静态方法被执行 name= " + name);
    }

    //参数是基本数据类型的数组
    public void method7(int... arr) {
        System.out.println("参数是基本数据类型的数组的方法被执行arr= " + Arrays.toString(arr));
    }

    //参数是引用数据类型的数组
    public void method8(String... arr) {
        System.out.println("参数是引用数据类型的数组的方法被执行arr= " + Arrays.toString(arr));
    }

}
package demo09111;


import org.junit.Test;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @creat 2020-09-10-20:53
 */
public class TestMethod {
    @Test
    public void test() throws Exception {
        Class<Person> personClass = Person.class;
        //获得所有公共方法
        Method[] methods = personClass.getMethods();
        System.out.println(Arrays.toString(methods));
        System.out.println();
        //获得所有方法
        Method[] methods1 = personClass.getDeclaredMethods();
        System.out.println(Arrays.toString(methods1));
        System.out.println();
        //获得无参无返回指公共方法
        Method method11 = personClass.getMethod("method1");
        Person person = personClass.newInstance();
        Object invoke = method11.invoke(person);
        System.out.println(invoke);
        //获得无参无返回值的私有方法
        Method method5 = personClass.getDeclaredMethod("method5");
        //开启暴力访问
        method5.setAccessible(true);
        method5.invoke(person);
        //获得有参无返回值的静态方法
        Method method6 = personClass.getDeclaredMethod("method6", java.lang.String.class);
        ///静态方法不属于任何对象,所以第一个参数给null也可以
        method6.invoke(null,"tom");
        //获得有参有返回值的公共方法
        Method method4 = personClass.getMethod("method4", String.class);
        Object lili = method4.invoke(person, "lili");
        System.out.println(lili);
        //获得参数是数组的无返回值方法
        Method method8 = personClass.getMethod("method8", String[].class);
        String[] str = {"1", "2", "a"};
        method8.invoke(person,new Object[]{str});//引用数据类型必须放在object数组中
        Method method7 = personClass.getMethod("method7", int[].class);
        method7.invoke(person,new int[]{1,2,3,4});

    }
}

反射属性

获取多个
public Field[] getFields() 获取所有public 修饰的变量
public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)

获取单个
public Field getField(String name) 获取指定的 public修饰的变量
public Field getDeclaredField(String name) 获取指定的任意变量(包含私有)

给指定对象的指定成员变量赋值或者获取值

public void set(Object obj, Object value)在指定对象obj中,将此Field设置为指定值
public Object get(Object obj)返回指定对象obj中,此 Field的值

私有成员变量,通过setAccessible(true)方法暴力访问

package demo09112;

/**
 * @creat 2020-09-10-21:29
 */
public class Person {
    // 成员变量
    private Long id;
    public String name;
    public int age;
}
package demo09112;

import org.junit.Test;

import java.lang.reflect.Field;
import java.util.Arrays;

/**
 * @creat 2020-09-10-21:30
 *  private Long id;
 *     public String name;
 *     public int age;
 */
public class TestPerson {
    @Test
    public void test() throws Exception {
        Class<Person> personClass = Person.class;
        Field[] fields = personClass.getFields();
        System.out.println(Arrays.toString(fields));
        Field[] declaredFields = personClass.getDeclaredFields();
        System.out.println(Arrays.toString(declaredFields));
        Field name = personClass.getField("name");
        Person person = personClass.newInstance();
        name.set(person,"tom");
        System.out.println(name.get(person));
        Field id = personClass.getDeclaredField("id");
        id.setAccessible(true);
        id.set(person,11l);
        System.out.println(id.get(person));
    }
}

反射应用—泛型擦除

//有如下集合
ArrayList list = new ArrayList<>();
list.add(666);
//设计代码,将字符串类型的"abc",添加到上述带有Integer泛型的集合list中

public class Demo11_反射练习_泛型擦除 {
  
	public static void main(String[] args) throws Exception{
		ArrayList<Integer> list = new ArrayList<Integer>();
		list.add(666);
		
		// 要把 "abc" 也加入到list中
		//list.add("abc");  // 编译期检查泛型——"abc" 不能加入到list中。可以在运行期加入
		/**
		 * 在编译阶段,检查add方法的实际参数,如果在编译阶段,不要调用add方法,
		 * 就会避免掉在编译阶段,对实际参数数据类型的检查
		 * 在运行阶段,调用add方法
		 * 使用反射的方式,调用某个方法,在写代码的阶段,根本不知道将来调用哪个方法
		 * 编译器也就没有办法在编译阶段对代码进行检查
		 * 
		 * 这种方式叫做“泛型擦除”
		 * 在java中,只会在编译阶段,对泛型进行检查,到了运行阶段,对泛型不检查
		 * 称这种泛型为:伪泛型
		 */
		Class clazz = list.getClass();
		
		Method method = clazz.getMethod("add",Object.class);
		method.invoke(list, "abc");		
		System.out.println(list);
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值