反射-JAVA成长之路

java反射主要从以下几个方面理解

理解 Class 类

理解 Java 的类加载机制

学会使用 ClassLoader 进行类加载

理解反射的机制

掌握 Constructor、Method、Field 类的用法

理解并掌握动态代理

1、理解Class类

Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识,即所谓的RTTI。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。

简单解释上面一段话

1、Class类也是类的一种,只是名字和class关键字高度相似。Java是大小写敏感的语言。

2、Class类的对象内容是你创建的类的类型信息,比如你创建一个shapes类,那么,Java会生成一个内容是shapes的Class类的对象。

3、Class类的对象不能像普通类一样,以 new shapes() 的方式创建,它的对象只能由JVM创建,因为这个类没有public构造函数。

4、Class类的作用是运行时提供或获得某个对象的类型信息。

Class原理

所有java类都是继承了object这个类,在object这个类中有一个方法:getclass().这个方法是用来取得该类已经被实例化了的对象的该类的引用,这个引用指向的是Class类的对象。

怎么获取Class对象

1、Class类的forName方法

Class aClass = Class.forName("com.sl.reflect.Student");

2、对象的getClass()方法

Student student = new Student();

Class

3、使用类名加.class

Class classes = Student.class.getClass();

4、通过ClassLoader对象的loadClass()方法

Class aClass2 = ClassLoader.getSystemClassLoader().loadClass("com.sl.reflect.Student");

Class类的常用方法

image.png

敲黑板调用newInstance()方法时,调用的是无参构造器,所以每个类中一定要声明一个无参构造器

ClassLoader加载

类装载器是用来把类(class)装载进 JVM 的。JVM 规范定义了两种类型的类装载器:启动类装载器(bootstrap)和用户自定义装载器(user-defined class loader)。JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:

image.png

//1、获取系统类的加载器

ClassLoader classLoader = ClassLoader.getSystemClassLoader();

System.out.println(classLoader);

//2. 获取系统类加载器的父类加载器(扩展类加载器,可以获取).

classLoader = classLoader.getParent();

System.out.println(classLoader);

//3. 获取扩展类加载器的父类加载器(引导类加载器,不可获取).

classLoader = classLoader.getParent();

System.out.println(classLoader);

注意系统类加载器可以加载当前项目src目录下面的所有类,如果文件也放在src下面,也可以用类加载器来加载调用 getResourceAsStream 获取类路径下的文件对应的输入流。

//文件夹在src下

InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("text1.txt");

//文件夹在包名下

InputStream resourceAsStream1 = ClassLoader.getSystemClassLoader().getResourceAsStream("com/sl/reflect/text2.txt");

反射

Java反射机制主要提供了以下功能

在运行时构造任意一个类的对象

在运行时获取任意一个类所具有的成员变量和方法

在运行时调用任意一个对象的方法(属性)

生成动态代理

Student测试类

/**

* @author shuliangzhao

* @Title: Student

* @ProjectName design-parent

* @Description: TODO

* @date 2019/6/15 23:08

*/

public class Student {

private String name;

private Integer age;

public Student() {

}

public Student(String name,Integer age) {

this.name = name;

this.age = age;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Integer getAge() {

return age;

}

public void setAge(Integer age) {

this.age = age;

}

}

Method类

public static void testMethod() throws Exception {

Class aClass = (Class) Class.forName("com.sl.reflect.Student");

//1.获取方法

// 获取取clazz对应类中的所有方法--方法数组(一)

// 不能获取private方法,并且获取从父类继承来的所有方法

Method[] methods = aClass.getMethods();

for (Method method:methods) {

System.out.println(method);

}

System.out.println("================================");

//2.获取方法

// 获取取clazz对应类中的所有方法--方法数组(一)

// 不能获取private方法,不获取从父类继承来的所有方法

Method[] declaredMethods = aClass.getDeclaredMethods();

for (Method method:declaredMethods) {

System.out.println(method);

}

System.out.println("=================================");

// 1.3.获取指定的方法

// 需要参数名称和参数列表,无参则不需要写

// 对于方法public void setName(String name) { }

Method method = aClass.getDeclaredMethod("setName", String.class);

System.out.println(method);

// 而对于方法public void setAge(int age) { }

method = aClass.getDeclaredMethod("setAge", Integer.class);

System.out.println(method);

// 这样写是获取不到的,如果方法的参数类型是int型

// 如果方法用于反射,那么要么int类型写成Integer:public void setAge(Integer age) { }

// 要么获取方法的参数写成int.class

//2.执行方法

// invoke第一个参数表示执行哪个对象的方法,剩下的参数是执行方法时需要传入的参数

Object obje = aClass.newInstance();

method.invoke(obje,2);

//如果一个方法是私有方法,第三步是可以获取到的,但是这一步却不能执行

//私有方法的执行,必须在调用invoke之前加上一句method.setAccessible(true);

}

/**

* 把类对象和类方法名作为参数,执行方法

*

* 把全类名和方法名作为参数,执行方法

* 可变参数可以放数组

* @param obj: 方法执行的那个对象.

* @param methodName: 类的一个方法的方法名. 该方法也可能是私有方法.

* @param args: 调用该方法需要传入的参数

* @return: 调用方法后的返回值

*

*/

public Object invoke(Object obj, String methodName, Object ... args) throws Exception{

//1. 获取 Method 对象

// 因为getMethod的参数为Class列表类型,所以要把参数args转化为对应的Class类型。

Class [] parameterTypes = new Class[args.length];

for(int i = 0; i < args.length; i++){

parameterTypes[i] = args[i].getClass();

System.out.println(parameterTypes[i]);

}

Method method = obj.getClass().getDeclaredMethod(methodName, parameterTypes);

//如果使用getDeclaredMethod,就不能获取父类方法,如果使用getMethod,就不能获取私有方法

//

//2. 执行 Method 方法

//3. 返回方法的返回值

return method.invoke(obj, args);

}

Field类

public static void testField() throws Exception {

Class aClass = (Class) Class.forName("com.sl.reflect.Student");

//1.获取字段

// 1.1 获取所有字段 -- 字段数组

// 可以获取公用和私有的所有字段,但不能获取父类字段

Field[] declaredFields = aClass.getDeclaredFields();

for (Field field:declaredFields) {

System.out.println(field);

}

System.out.println("=============================");

// 1.2获取指定字段

Field field = aClass.getDeclaredField("name");

System.out.println(field.getName());

System.out.println("==============================");

Student student = new Student();

//如果字段是私有的,不管是读值还是写值,都必须先调用setAccessible(true)方法

field.setAccessible(true);

student.setAge(1);

student.setName("张三");

//2.使用字段

// 2.1获取指定对象的指定字段的值

Object o = field.get(student);

System.out.println(o);

System.out.println("==========================");

// 2.2设置指定对象的指定对象Field值

field.set(student, "DEF");

System.out.println(student.getName());

}

Constructor类

public static void testConstructor() throws Exception{

Class aClass = (Class) Class.forName("com.sl.reflect.Student");

//1. 获取 Constructor 对象

// 1.1 获取全部

Constructor[] constructors = aClass.getConstructors();

for (Constructor constructor:constructors) {

System.out.println(constructor);

}

System.out.println("============================");

// 1.2获取某一个,需要参数列表

Constructor constructor = aClass.getConstructor(String.class, Integer.class);

System.out.println(constructor);

System.out.println("============================");

//2. 调用构造器的 newInstance() 方法创建对象

Object obj = constructor.newInstance("zhagn", 1);

}

Annotation

@Retention(RetentionPolicy.RUNTIME)

@Target(value={ElementType.METHOD})

public @interface AgeValidator {

public int min();

public int max();

}

public static void testAnnotation() throws Exception{

Class aClass = Class.forName("com.sl.reflect.Student");

Object o = aClass.newInstance();

Method method = aClass.getDeclaredMethod("setAge", Integer.class);

int val = 6;

AgeValidator annotation = method.getAnnotation(AgeValidator.class);

if (annotation != null) {

if (annotation instanceof AgeValidator) {

AgeValidator ageValidator = annotation;

if (val < ageValidator.min() || val > ageValidator.max()) {

throw new RuntimeException("年龄非法");

}

}

}

method.invoke(o,20);

System.out.println(o);

}

小结

Class: 是一个类; 一个描述类的类.

封装了描述方法的 Method,

描述字段的 Filed,

描述构造器的 Constructor 等属性.

如何得到 Class 对象:

2.1 Person.class

2.2 person.getClass()

2.3 Class.forName("com.atguigu.javase.Person")

关于 Method:

3.1 如何获取 Method:

1). getDeclaredMethods: 得到 Method 的数组.

2). getDeclaredMethod(String methondName, Class ... parameterTypes)

3.2 如何调用 Method

1). 如果方法时 private 修饰的, 需要先调用 Method 的 setAccessible(true), 使其变为可访问

2). method.invoke(obj, Object ... args);

关于 Field:

4.1 如何获取 Field: getField(String fieldName)

4.2 如何获取 Field 的值:

1). setAccessible(true)

2). field.get(Object obj)

4.3 如何设置 Field 的值:

field.set(Obejct obj, Object val)

了解 Constructor 和 Annotation

反射和泛型.

6.1 getGenericSuperClass: 获取带泛型参数的父类

6.2 Type 的子接口: ParameterizedType

6.3 可以调用 ParameterizedType 的 Type[] getActualTypeArguments() 获取泛型参数的数组.

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200604A04F0600?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券