参考https://blog.csdn.net/xu__cg/article/details/52877573
RTTI,即 Run-Time Type Identification 运行时类型识别。
1.反射机制概念
在Java中的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制。
2.反射的应用场合
在Java程序中许多对象在运行是都会出现两种类型:编译时类型和运行时类型。
编译时的类型由声明对象时实用的类型来决定,运行时的类型由实际赋值给对象的类型决定
如:
Person p=new Student();
其中编译时类型为Person,运行时类型为Student。
除此之外,程序在运行时还可能接收到外部传入的对象,该对象的编译时类型为Object,但是程序有需要调用该对象的运行时类型的方法。为了解决这些问题,程序需要在运行时发现对象和类的真实信息。然而,如果编译时根本无法预知该对象和类属于哪些类,程序只能依靠运行时信息来发现该对象和类的真实信息,此时就必须使用到反射了。
反射API用来生成JVM中的类、接口或则对象的信息。
1.步骤
2.如何获取一个Class对象?
Person p=new Person();
返回的是一个泛型类
Class clazz=p.getClass();
//---------------Shape.java--------------//
package product.test;
public class Shape {
void showMsg(){
System.out.println("this is Shape class");
}
}
//---------------Circle.java--------------//
package product.test;
public class Circle extends Shape{
@Override
void showMsg() {
System.out.println("this is Circle class");
}
}
//---------------getClassName.java--------------//
package product.test;
public class getClassName {
public static void showName(Shape shape){
Class clazz = shape.getClass(); //获取shape实际所属的类
System.out.println(clazz.getName());//getName()获取类名
if (clazz.getName().contains("Shape"))
System.out.println("this is a shape object");
else if (clazz.getName().contains("Circle"))
System.out.println("this is a circle object");
}
public static void main(String[] args){
//调用showName方法,分别传入Circle对象和Shape对象
showName(new Circle());
showName(new Shape());
}
}
//---------------运行结果--------------//
product.test.Circle
this is a circle object
product.test.Shape
this is a shape object
Class clazz1 = Person.class;
Class clazz2 = int.class;
Class clazz3 = Double[].class;
//---------------getClassName.java--------------//
package product.test;
public class getClassName {
public static void showName(Shape shape){
Class clazz = shape.getClass();//获取shape类对象
if (clazz == Shape.class) //使用==号对类类型进行判断
System.out.println("this is a shape object");
else if (clazz == Circle.class)
System.out.println("this is a circle object");
}
public static void main(String[] args){
//调用showName方法,分别传入Circle对象和Shape对象
showName(new Circle());
showName(new Shape());
}
}
//---------------运行结果--------------//
this is a circle object
this is a shape object
返回的是一个Class类型
Class clazz=Class.forName(“类的全路径”); (最常用)
//加载数据库驱动
Class.forName(“com.mysql.jdbc.Driver”);
Class candy = Class.forName(“product.test.Candy”);
//---------------Candy.java--------------//
package product.test;
public class Candy {
static {
System.out.println("Loading Candy in static block");
}
public static void main(String[] args){
System.out.println("Loading Candy in main block");
}
}
//---------------loadClass.java--------------//
package product.test;
public class loadClass {
public static void main(String[] args){
System.out.println("Before loading Candy");
try {
Class.forName("product.test.Candy");//Class.forName加载Candy对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
//---------------运行结果--------------//
Before loading Candy
Loading Candy in static block
3.Class类的常用方法
1、getName()
一个Class对象描述了一个特定类的属性,Class类中最常用的方法getName以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
2、newInstance()
Class还有一个有用的方法可以为类创建一个实例,这个方法叫做newInstance()。例如:
x.getClass.newInstance(),创建了一个同x一样类型的新实例。newInstance()方法调用默认构造器(无参数构造器)初始化新建对象。
3、getClassLoader()
返回该类的类加载器。
4、getComponentType()
返回表示数组组件类型的 Class。
5、getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。
6、isArray()
判定此 Class 对象是否表示一个数组类。
7、instanceof()
判断一个对象真正所属类
objectName instanceof className
//---------------getClassName.java--------------//
注:本例中 showName(new Circle()); 传入的class对象通过 shape instanceof Circle 和 shape instanceof Shape 两个表达式均为true
package product.test;
public class getClassName {
public static void showName(Shape shape){
if (shape instanceof Circle) //使用instanceof判断所属类
System.out.println("this is a circle object");
else if (shape instanceof Shape)
System.out.println("this is a shape object");
}
public static void main(String[] args){
//调用showName方法,分别传入Circle对象和Shape对象
showName(new Circle());
showName(new Shape());
}
}
//---------------运行结果--------------//
this is a circle object
this is a shape object
7、isInstance()
判断一个对象所属类
clazz.isInstance(Object object)
//---------------getClassName.java--------------//
package product.test;
public class getClassName {
public static void showName(Shape shape){
Class clazz = shape.getClass();
if (clazz.isInstance(new Shape())) //使用isInstance判断所属类
System.out.println("this is a shape object");
else if (clazz.isInstance(new Circle()))
System.out.println("this is a circle object");
}
public static void main(String[] args){
//调用showName方法,分别传入Circle对象和Shape对象
showName(new Circle());
showName(new Shape());
}
}
//---------------运行结果--------------//
this is a circle object
this is a shape object
8、 getDeclaredMethods() //获取类成员方法
getParameterTypes() //获取参数类型
getDeclaredAnnotations() //获取注解
getAnnotations() //获取注解
getExceptionTypes() //获取异常
getDeclaredFields() //获取成员属性
getModifiers() //获取权限修饰符
getDeclaredConstructors() //获取构造方法
实体类
//---------------Person.java--------------//
package product.test;
public class Person {
private String name;
private String gender;
private int age;
//无参构造
public Person() {
}
//有参构造
public Person(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
//^getName()
@Target(name = "zhangsan")
public String getName() {
return name;
}
//^setName()
public void setName(String name) {
this.name = name;
}
//^getGender()
@Target(gender = "male")
public String getGender() {
return gender;
}
//^setGender()
public void setGender(String gender) {
this.gender = gender;
}
//^getAge()
public int getAge() {
return age;
}
//^setAge()
public void setAge(int age) {
this.age = age;
}
@Override
//^toString()
public String toString() throws NullPointerException{
return "人类{" +
"姓名='" + name + '\'' +
", 性别='" + gender + '\'' +
", 年龄=" + age +
'}';
}
}
自定义注解类
附:
注解的保留策略:Java定义了3种策略:SOURCE、CLASS、RUNTIME,它们封装在java.lang.annotation.RetentionPolicy枚举对象中。
SOURCE策略的注解只在源文件中保留,在编译期间删除。
CLASS策略的注解,在编译期间存储在.class文件中,在运行时不能通过JVM来获得。
RUNTIME策略的注解,在编译期间存储在.class文件中,在运行时可以通过JVM来获得。
因此RUNTIME保留策略提供了最长的注解持续期。
保留策略是通过使用一个Java内置注解@Retention来指定注解的,语法如下:@Retention(保留策略)
eg : @Retention(RetentionPolicy.RUNTIME)
//---------------Target.java--------------//
package product.test;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Target {
String name() default "";
String gender() default "male";
}
测试反射类
//---------------testPerson.java--------------//
package product.test;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
/**
* 通过用户输入类的全路径,来获取该类的成员方法和属性
* Declared获取全部不管是私有和公有
* 1.获取访问类的Class对象
* 2.调用Class对象的方法返回访问类的方法和属性信息
*/
public class testPerson {
public static void main(String[] args) {
try {
//获取Person类的Class对象
Class clazz = Class.forName("product.test.Person");
System.out.println("----------------------------成员方法信息----------------------------");
//获取Person类的所有方法信息
Method mt = null;
Class prmt[] = null;
Class exception[] = null;
Annotation annotations[] = null;
Annotation declaredAnnotations[] = null;
Method[] method = clazz.getDeclaredMethods();//获取成员方法
for (int i = 0; i < method.length; i++)
{
mt = method[i];
System.out.println("********第"+(i+1)+"个方法********");
//打印成员方法
System.out.println(mt.toString());
prmt = mt.getParameterTypes();//获取参数类型
declaredAnnotations = mt.getDeclaredAnnotations();//获取注解
annotations = mt.getAnnotations();//获取注解
exception = mt.getExceptionTypes();//获取异常
for (int j = 0; j < prmt.length; j++)
{
//打印成员方法所有的参数类型
System.out.println("param #" + j + " " + prmt[j]);
}
for (int k = 0; k < exception.length; k++)
{
//打印成员方法所有的异常
System.out.println("exception #" + k + " " + exception[k]);
}
for (Annotation d : declaredAnnotations)
{
//打印成员方法所有的注解
System.out.println("declaredAnnotations #" + d);
}
for (Annotation a : annotations)
{
//打印成员方法所有的注解
System.out.println("annotations #" + a);
}
}
System.out.println("----------------------------成员方法信息----------------------------");
System.out.println("----------------------------成员属性信息----------------------------");
//获取Person类的所有成员属性信息
Field[] field = clazz.getDeclaredFields();
for(Field f:field){
System.out.println(f.toString());
//获取成员属性的权限修饰符
int modifiers = f.getModifiers();
//打印成员属性的权限修饰符
System.out.println("modifiers # " + Modifier.toString(modifiers));
}
System.out.println("----------------------------成员属性信息----------------------------");
System.out.println("----------------------------构造方法信息----------------------------");
//获取Person类的所有构造方法信息
Constructor[] constructor = clazz.getDeclaredConstructors();
for(Constructor c:constructor){
System.out.println(c.toString());
}
System.out.println("----------------------------构造方法信息----------------------------");
} catch (Exception e) {
e.printStackTrace();
}
}
}
//---------------运行结果--------------//
----------------------------成员方法信息----------------------------
第1个方法
public java.lang.String product.test.Person.toString() throws java.lang.NullPointerException
exception #0 class java.lang.NullPointerException
第2个方法
public java.lang.String product.test.Person.getName()
declaredAnnotations #@product.test.Target(name=zhangsan, gender=male)
annotations #@product.test.Target(name=zhangsan, gender=male)
第3个方法
public void product.test.Person.setName(java.lang.String)
param #0 class java.lang.String
第4个方法
public void product.test.Person.setAge(int)
param #0 int
第5个方法
public int product.test.Person.getAge()
第6个方法
public java.lang.String product.test.Person.getGender()
declaredAnnotations #@product.test.Target(name=, gender=male)
annotations #@product.test.Target(name=, gender=male)
第7个方法
public void product.test.Person.setGender(java.lang.String)
param #0 class java.lang.String
----------------------------成员方法信息----------------------------
----------------------------成员属性信息----------------------------
private java.lang.String product.test.Person.name
modifiers # private
private java.lang.String product.test.Person.gender
modifiers # private
private int product.test.Person.age
modifiers # private
----------------------------成员属性信息----------------------------
----------------------------构造方法信息----------------------------
public product.test.Person()
public product.test.Person(java.lang.String,java.lang.String,int)
----------------------------构造方法信息----------------------------
4.创建Class对象
当我们获取到所需类的Class对象后,可以用它来创建对象,创建对象的方法有两种:
使用Class对象的newInstance()方法来创建该Class对象对应类的实例,但是这种方法要求该Class对象对应的类有默认的空构造器。
先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建 Class对象对应类的实例,通过这种方法可以选定构造方法创建实例。
使用method.invoke()调用成员方法赋值
测试反射类
//---------------testPerson.java--------------//
package product.test;
import java.lang.reflect.*;
public class createPerson {
public static void main(String[] args){
try {
//获取Person类的Class对象
Class clazz = Class.forName("product.test.Person");
/**
* 第一种方法创建对象
* newInstance方法
*/
//创建对象
Person p = (Person) clazz.newInstance();
//设置属性
p.setName("张三");
p.setAge(16);
p.setGender("男");
System.out.println(p.toString());
/**
* 第二种方法创建
* 构造方法
*/
//获取构造方法
Constructor c = clazz.getDeclaredConstructor(String.class,String.class,int.class);
//创建对象并设置属性
Person p1 = (Person) c.newInstance("李四","男",20);
System.out.println(p1.toString());
/**
* 第三种方法创建
*/
Method setMethod = null;
Method getMethod = null;
Object obj = clazz.newInstance(); //调用无参构造
Object personValue = null;
Class fieldClass = null;
Object sendRequestBody = null;
Field[] field = clazz.getDeclaredFields();
String person = "卜学习_男_10";
String[] personString = person.split("_");
for (int i = 0; i < field.length; i++) {
// field[i].setAccessible(true);
String fieldName = toUppCaseForFirstChar(field[i].getName());
Class fieldType = field[i].getType();
sendRequestBody = personString[i];
if(fieldType == String.class)
fieldClass = String.class;
else if (fieldType == int.class){
fieldClass = int.class;
sendRequestBody = Integer.valueOf(personString[i]).intValue();
}
else
fieldClass = String.class;
setMethod = clazz.getDeclaredMethod("set" + fieldName,fieldClass);
// getMethod = clazz.getDeclaredMethod("get"+ fieldName);
getMethod = clazz.getDeclaredMethod("toString");
setMethod.invoke(obj,sendRequestBody);
personValue = getMethod.invoke(obj);
}
System.out.println("invokePerson:"+ personValue);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
*把String的第一个字符变为大写
* @param string
*/
public static String toUppCaseForFirstChar(String string)
{
char c[] = string.toCharArray();
c[0] -= 32;
System.out.println(c);
return String.valueOf(c);
}
/**
*把String的第一个字符变为小写
* @param string
*/
public static String toLowerCaseForFirstChar(String string)
{
char c[] = string.toCharArray();
c[0] += 32;
System.out.println(c);
return String.valueOf(c);
}
}
//---------------运行结果--------------//
人类{姓名=‘张三’, 性别=‘男’, 年龄=16}
人类{姓名=‘李四’, 性别=‘男’, 年龄=20}
Name
Gender
Age
invokePerson:人类{姓名=‘卜学习’, 性别=‘男’, 年龄=10}