获取和调用运行时类的结构
- 1,准备工作
- 2,获取运行时类的结构
- 2.1,通过反射获取运行时类的属性及其内部结构
- 2.2,通过反射获取运行时类的方法及其内部结构
- 2.3,通过反射获取运行时类的构造器及其内部结构
- 2.4,通过反射获取运行时类的带泛型的父类
- 2.5,通过反射获取运行时类实现的接口
- 2.6,通过反射获取运行时类所在的包
- 2.7,通过反射获取运行时类声明的注解
- 3,调用运行时类中指定的结构(重点)
- 3.1,调用运行时类中指定的属性
- 3.1,调用运行时类中指定的方法
- 3.3,调用运行时类中指定的构造器
1,准备工作
提供以下四个内容用于演示:
①先提供一个用于测试使用Creature类
import java.io.Serializable;
/**
* Person类的父类
*/
public class Creature<T> implements Serializable {
private char gender;
public double weight;
private void breath(){
System.out.println("生物呼吸");
}
public void eat(){
System.out.println("生物进食");
}
}
②再提供一个用于测试使用接口,名为MyInterface
public interface MyInterface {
void info(); //提供一个抽象方法
}
③提供一个注解类
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME) //想通过反射获取注解,生命周期应该是RUNTIME
public @interface MyAnnotation {
String value() default "hello";
}
④最后提供一个Person类用于演示
package com.atguigu.java1;
@MyAnnotation(value = "hi") //此处不指定value时,为默认的"hello"
public class Person extends Creature<String> implements Comparable<String>,MyInterface {
//提供三种不同权限的属性
private String name;
int age;
public int id;
//提供三种不同权限的构造器
public Person(){
}
@MyAnnotation(value = "abc")
private Person(String name){
this.name=name;
}
Person(String name,int age){ //此处传入两个参数,后面演示如何获取
this.name=name;
this.age=age;
}
//提供方法
@MyAnnotation //此处注解使用默认值
private String show(String nation){
System.out.println("我的国籍是:"+nation);
return nation;
}
public String display (String interests,int age)throws NullPointerException,ClassCastException{ //随便写两个异常,后面演示如何获取
return interests+age;
}
@Override
public void info() { //重写MyInterface接口中的方法
System.out.println("我是一个人");
}
@Override //重写Comparable接口中的方法
public int compareTo(String o) {
return 0;
}
}
2,获取运行时类的结构
2.1,通过反射获取运行时类的属性及其内部结构
package com.atguigu.java2;
import com.atguigu.java1.Person;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/**
* 获取当前运行时类的属性结构
*/
public class FiledTest {
//获取属性结构
@Test
public void test1(){
Class clazz= Person.class;
//getFields()返回Field数组,获取当前运行时类及其父类中声明为public访问权限的属性
Field[] fields = clazz.getFields();
for(Field f:fields){
System.out.println(f); //只会输出Person类及其父类中public权限的属性。
/*输出结果:
public int com.atguigu.java1.Person.id
public double com.atguigu.java1.Creature.weight
*/
}
System.out.println("***************************");
//getDeclaredFields()返回Field数组,获取当前运行时类中声明的所有属性(不包含父类中声明的属性)
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f:declaredFields){
System.out.println(f);
/*test1输出结果:
private java.lang.String com.atguigu.java1.Person.name
int com.atguigu.java1.Person.age
public int com.atguigu.java1.Person.id
*/
}
}
//通过反射也可以获取属性的内部结构,如权限修饰符、数据类型、变量名
@Test
public void test2(){
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f:declaredFields){
//1,权限修饰符,返回int型(),具体的数据代表具体的权限修饰符
int modifier = f.getModifiers();
System.out.print(Modifier.toString(modifier)+"\t"); //输出数字对应的权限修饰符
//2,数据类型
Class type = f.getType();
System.out.print(type.getName()+"\t"); //getName()获取当前Class的名字
//3,变量名
String fName = f.getName();
System.out.print(fName);
System.out.println(); //换行
/*test2运行结果:
private java.lang.String name
int age
public int id
*/
}
}
}
2.2,通过反射获取运行时类的方法及其内部结构
public class MethodTest {
@Test
public void test1(){
Class clazz = Person.class;
//getMethods()获取当前运行时类及其父类中声明为public权限的方法
Method[] methods = clazz.getMethods();
for (Method m:methods){
System.out.println(m);
}
System.out.println("********************************");
//getDeclaredMethods()获取当前运行时类中声明的所有方法,不包含父类中声明的结构
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m:declaredMethods){
System.out.println(m);
}
}
}
反射获取运行时类的方法的内部结构
@Test
public void test2(){
Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m:declaredMethods){
//1,获取方法声明的注解(元注解 @Retention(RetentionPolicy.RUNTIME)的可以获取到。@override不满足)
Annotation[] annos = m.getAnnotations(); //返回数组类型,可能有多个注解
for(Annotation a:annos){
System.out.println(a); //@com.atguigu.java1.MyAnnotation(value=hello)
}
//2,获取权限修饰符(getModifiers()返回int型)
System.out.print(Modifier.toString(m.getModifiers())+"\t");
//3,返回值类型
System.out.print(m.getReturnType().getName()+"\t");
//4,方法名
System.out.print(m.getName());
System.out.print("(");
//5,形参列表
Class[] parameterTypes = m.getParameterTypes();
if (!(parameterTypes==null || parameterTypes.length==0)){ //表示有形参
for(int i=0;i<parameterTypes.length;i++){
if (i==parameterTypes.length-1){
System.out.print(parameterTypes[i].getName()+" args_"+i);
break; //记得加
}
System.out.print(parameterTypes[i].getName()+" args_"+i+",");
}
}
System.out.print(") ");
//6,抛出的异常
Class[] exceptionTypes = m.getExceptionTypes();
if (!(exceptionTypes==null || exceptionTypes.length==0)){ //有异常
System.out.print("throws ");
for (int i = 0; i < exceptionTypes.length; i++) {
if (i==exceptionTypes.length-1){
System.out.print(exceptionTypes[i].getName());
break;
}
System.out.print(exceptionTypes[i].getName()+",");
}
}
//void本身也是Class的一个实例
System.out.println();
}
}
2.3,通过反射获取运行时类的构造器及其内部结构
@Test
public void test3(){
Class clazz = Person.class;
//getConstructors:获取 当前运行时类 中声明为public的结构。
Constructor[] constructors = clazz.getConstructors();
for (Constructor c:constructors){
System.out.println(c);
}
//getDeclaredConstructor获取当前运行时类中声明的所有构造器
System.out.println("***********");
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor c:declaredConstructors){
System.out.println(c);
}
}
2.4,通过反射获取运行时类的带泛型的父类
@Test
public void test4(){
Class clazz = Person.class;
Class superclass = clazz.getSuperclass(); //获取父类
System.out.println(superclass); //class com.atguigu.java1.Creature
Type genericSuperclass = clazz.getGenericSuperclass();//获取带泛型的父类
System.out.println(genericSuperclass); //com.atguigu.java1.Creature<java.lang.String>
System.out.println();
//获取运行时类的父类的泛型
ParameterizedType paramType = (ParameterizedType) genericSuperclass; //强转为ParameterizedType(带参数的一个类型)
Type[] actualTypeArguments = paramType.getActualTypeArguments(); //获取泛型类型
System.out.println(actualTypeArguments[0]);
}
2.5,通过反射获取运行时类实现的接口
(动态代理中常用,要掌握)
@Test
public void test5(){
Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
for (Class c:interfaces){
System.out.println(c);
}
System.out.println();
//获取运行时类父类实现的接口
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
for (Class c:interfaces1){
System.out.println(c);
}
}
2.6,通过反射获取运行时类所在的包
@Test
public void test6(){
Class clazz = Person.class;
Package pack = clazz.getPackage();
System.out.println(pack);
}
2.7,通过反射获取运行时类声明的注解
@Test
public void test7(){
Class clazz = Person.class;
Annotation[] annotations = clazz.getAnnotations();
for (Annotation a :annotations){
System.out.println(a);
}
}
3,调用运行时类中指定的结构(重点)
3.1,调用运行时类中指定的属性
public class ReflectionTest {
@Test
public void test() throws Exception {
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//获取指定的属性getField要求运行时类中的属性声明为public(通常不用)
Field id = clazz.getField("id"); //id可能没有,抛异常。
//设置当前属性值set参数一指明设置哪个对象的属性,参数二指明设置为多少
id.set(p,1001);
//获取当前属性值 get():参数是获取哪个对象的当前属性值
int pid = (int) id.get(p);
System.out.println(pid); //1001
}
@Test
//开发常用getDeclaredField(String fieldname)方法获取指定名字的属性(掌握)
public void test2() throws Exception {
Class clazz = Person.class;
Person p = (Person) clazz.newInstance();
Field name = clazz.getDeclaredField("name"); //name为private权限的
name.setAccessible(true); //保证当前属性可访问。一定不要忘,不然拿到属性也没有修改能力
name.set(p,"李白");
System.out.println(name.get(p)); //李白
}
}
3.1,调用运行时类中指定的方法
此处我们以获取Person类中声明的show方法和showDesc静态方法为例:
Person类增加声明的一个静态方法:
@Test
public void test3() throws Exception {
Class clazz = Person.class;
Person p = (Person) clazz.newInstance(); //非静态的方法必须有运行时类的对象。
//获取指定的某个方法getDeclaredMethod参数一指明获取方法的名称;参数二指明获取的方法的形参列表
Method show = clazz.getDeclaredMethod("show", String.class);
show.setAccessible(true); //一定不要忘!!!---保证方法可访问
//invoke是调用参数一指明方法的调用者;参数二是给方法形参赋值的实参。invoke方法的返回值即为对应类中调用的方法的返回值
Object returnValue = show.invoke(p, "CHN"); //我的国籍是:CHN
System.out.println(returnValue); //CHN
System.out.println("*****************如何调用静态方法*************************");
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
Object returnVal = showDesc.invoke(Person.class);//invoke参数一是调用者,静态方法调用者即为当前类
System.out.println(returnVal); //null。此方法没有返回值还非要接收以下,当然为null
}
3.3,调用运行时类中指定的构造器
(使用较少,一般通过 clazz.newInstance() 创建对象,此处了解即可)
此处以调用Person类带一个参数的构造器为例
Person中要记得重写toString(),下面代码中要用到。
@Test
public void test4() throws Exception {
Class clazz = Person.class;
//获取指定类的构造器,getDeclaredConstructor参数:指定构造器参数列表即可
Constructor constructor = clazz.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
//调用此构造器创建运行时类的对象
Person p = (Person) constructor.newInstance("小白");
System.out.println(p);
}