文章目录
- 1. 创建运行时类的对象
- 2. 体会反射的动态性
- 3. 通过反射获取运行时类的结构
- 3.1 用于测试的类的准备
- 3.2 获取运行时类的属性
- 3.2.1 getFields()
- 3.2.2 getDeclaredField()
- 3.2.3 获取属性的结构
- 3.3 获取运行时类的方法
- 3.3.1 getMethods()
- 3.3.2 getDeclaredMethods()
- 3.3.3 获取方法的结构
- 3.4 获取运行时类的构造器
- 3.4.1 getConstructors()
- 3.4.2 getDeclaredConstructors()
- 3.5 获取运行时类的父类及其泛型
- 3.5.1 获取运行时类的父类
- 3.5.2 获取运行时类的带泛型的父类
- 3.5.3 获取运行时类的带泛型的父类的泛型
- 3.6 获取运行时类实现的接口
- 3.6.1 获取运行时类实现的接口
- 3.6.2 获取运行时类的父类实现的接口
- 3.7 获取运行时类所在的包
- 3.8 获取运行时类的注解
1. 创建运行时类的对象
通过反射的方式创建运行时类的对象。
以Person类为例,先通过反射获取Person类的运行时类,再通过获取到的Person类的运行时类来实例化对象。
通过反射的方式创建运行时类的对象,使用获取到的运行时类中newInstance()
方法创建运行时类的对象。
调用运行时类的
newInstance()
方法创建对应类的对象,其实还是使用对应的类的构造方法进行对象的创建,只是调用构造器创建对象封装在newInstance()
方法了。
@Testpublic void test7() throws Exception{
// 通过反射的方式创建运行时类对应的对象
// 获取Person的运行时类
// 获取运行时类的时候使用泛型,在创建实例对象的时候,可以得到对应类型的对象
// 返回的对象不用进行强制转换
// 也可以不使用泛型,newInstance()得到的对象为Object类型Class<Person> personClass = Person.class;
// 使用获取到的Person运行时类中的newInstance()方法创建对应类的对象Person person = personClass.newInstance();System.out.println(person);}
使用
newInstance()
方法创建运行时类的对象,如果对应的构造器(无参或使用其他类型构造器)不存在,会抛出InstantiationException
异常;如果构造器的权限不够大,会抛出IllegalAccessException
异常。
要newInstance()
方法正常创建运行时类的对象,需要满足:
- 运行时类必须存在对应的构造器
- 对应的构造器的访问权限要够,通常设置为public
通过反射创建运行时类的对象,更喜欢使用无参的构造器创建对象,即使用
newInstance()
来创建运行时类的对象,因为这样子可以写出通用的代码,提高代码的复用性。如果使用newInstance(String.class, int.class, ...)
创建运行时类的对象,不同的类有不同的有参构造器,该写法代码复用性不强。
2. 体会反射的动态性
//体会反射的动态性@Testpublic void test8(){
// 循环运行10次for(int i = 0;i < 10;i++){
// 每次随机产生一个整数,0-2,int num = new Random().nextInt(3);//0,1,2
// 类的全类名String classPath = "";
// 根据生成的随机数指定对应的全类名switch(num){case 0:classPath = "java.util.Date";break;case 1:classPath = "java.lang.Object";break;case 2:classPath = "Person";break;}try {
// 通过指定的全类名创建对应的类的对象Object obj = getInstance(classPath);System.out.println(obj);} catch (Exception e) {e.printStackTrace();}}}/*创建一个指定类的对象。classPath:指定类的全类名*/public Object getInstance(String classPath) throws Exception {
// 根据传递的全类名,获取对应类的运行时类Class clazz = Class.forName(classPath);
// 使用指定类的运行时类创建实例对象return clazz.newInstance();}
3. 通过反射获取运行时类的结构
通过反射我们可以获取实现的全部接口、获取所继承的父类、获取全部的构造器、获取全部的方法、获取全部的Field。
3.1 用于测试的类的准备
Creature.java:
import java.io.Serializable;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.java:
public interface MyInterface {void info();
}
MyAnnotation.java:
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)
public @interface MyAnnotation {String value() default "hello";
}
Person.java:
@MyAnnotation(value="hi")
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;}@MyAnnotationprivate String show(String nation){System.out.println("我的国籍是:" + nation);return nation;}public String display(String interests,int age) throws NullPointerException,ClassCastException{return interests + age;}@Overridepublic void info() {System.out.println("我是一个人");}@Overridepublic int compareTo(String o) {return 0;}private static void showDesc(){System.out.println("我是一个可爱的人");}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", id=" + id +'}';}
}
3.2 获取运行时类的属性
3.2.1 getFields()
获取运行时类及其父类中声明为public访问权限的属性。
@Testpublic void test1() {Class personClass = Person.class;
// 使用getFields()获取运行时类及其父类中声明为public访问权限的属性。
// 返回值为一个Field类型的数组Field[] fields = personClass.getFields();for (Field field: fields) {System.out.println(field);}}
3.2.2 getDeclaredField()
获取运行时类中声明的所有属性,不包含父类中声明的属性。
@Testpublic void test2() {Class personClass = Person.class;
// getDeclaredField()获取运行时类中声明的所有属性,不包含父类中声明的属性。
// 返回值为一个Field类型的数组Field[] fields = personClass.getDeclaredFields();for (Field field: fields) {System.out.println(field);}}
3.2.3 获取属性的结构
属性的权限修饰符、数据类型、变量名等也可以通过反射进行获取。
@Testpublic void test3() {Class personClass = Person.class;Field[] fields = personClass.getDeclaredFields();for (Field field: fields) {
// 1. 获取属性的权限修饰符
// field.getModifiers()得到的为权限修饰符对应的数字
// 默认权限为0
// 可以使用Modifier.toString(modifiers)将权限修饰符对应数字转换为对应的权限修饰符字符串int modifiers = field.getModifiers();System.out.println(Modifier.toString(modifiers));// 2. 获取属性的数据类型
// 返回值类型为ClassClass type = field.getType();
// type.getName()会返回数据类型的全类名System.out.println(type.getName());// 3. 获取属性的变量名String fieldName = field.getName();System.out.println(fieldName);System.out.println("---------------");}}
3.3 获取运行时类的方法
3.3.1 getMethods()
获取运行时类及其所有父类中声明为public访问权限的方法。
@Testpublic void test1() {Class personClass = Person.class;
// getMethods()获取运行时类及其所有父类中声明为public访问权限的方法。
// 返回值为Method类型的数组Method[] methods = personClass.getMethods();for (Method method: methods) {System.out.println(method);}}
3.3.2 getDeclaredMethods()
获取运行时类中声明的所有方法,不包含父类中声明的方法
@Testpublic void test2() {Class personClass = Person.class;
// getDeclaredMethods()获取运行时类中声明的所有方法,不包含父类中声明的方法
// 返回值为Method类型的数组Method[] methods = personClass.getDeclaredMethods();for (Method method: methods) {System.out.println(method);}}
3.3.3 获取方法的结构
方法的结构:
@Xxxx
权限修饰符 返回值类型 方法名(参数类型1 参数名1, ...) throws Exception {}
@Testpublic void test4() {Class clazz = Person.class;Method[] methods = clazz.getDeclaredMethods();for (Method m : methods) {//1.获取方法声明的注解//返回值为Annotation类型的数组,因为一个方法上可以加多个注解Annotation[] annos = m.getAnnotations();for (Annotation a : annos) {System.out.println(a);}//2.权限修饰符//m.getModifiers()得到的为权限修饰符对应的数字//Modifier.toString()将权限修饰符数字转换为对应的字符串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.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() + ",");}}System.out.println("{}");}}
3.4 获取运行时类的构造器
3.4.1 getConstructors()
获取运行时类中声明为public访问权限的构造器。
@Testpublic void test1() {Class personClass = Person.class;
// getConstructors()获取运行时类中声明为public访问权限的构造器。
// 返回值为Constructor类型的数组,构造器可能有多个Constructor[] constructors = personClass.getConstructors();for (Constructor constructor : constructors) {System.out.println(constructor);}}
3.4.2 getDeclaredConstructors()
获取运行时类中声明的所有构造器
@Testpublic void test2() {Class personClass = Person.class;
// getDeclaredConstructors()获取运行时类中声明的所有构造器
// 返回值为Constructor类型的数组,构造器可能有多个Constructor[] constructors = personClass.getDeclaredConstructors();for (Constructor constructor : constructors) {System.out.println(constructor);}}
通过反射也可以获取构造器的结构,获取方法与获取方法的结构类似。
3.5 获取运行时类的父类及其泛型
3.5.1 获取运行时类的父类
@Testpublic void test() {Class personClass = Person.class;
// 获取运行时类的父类
// 运行时类的父类的类型也为ClassClass superClass = personClass.getSuperclass();System.out.println(superClass);}
3.5.2 获取运行时类的带泛型的父类
@Testpublic void test() {Class personClass = Person.class;Type genericSuperclass = personClass.getGenericSuperclass();System.out.println(genericSuperclass);}
3.5.3 获取运行时类的带泛型的父类的泛型
@Testpublic void test() {Class personClass = Person.class;Type genericSuperclass = personClass.getGenericSuperclass();System.out.println(genericSuperclass);//已经确定genericSuperclass带有泛型,将其强转为带有参数类型ParameterizedType类型ParameterizedType paramType = (ParameterizedType) genericSuperclass;//获取泛型类型//返回的为数组,是由于有时候泛型有多个,如Map<K, V>就有两个泛型Type[] actualTypeArguments = paramType.getActualTypeArguments();System.out.println(actualTypeArguments[0].getTypeName());}
3.6 获取运行时类实现的接口
3.6.1 获取运行时类实现的接口
@Testpublic void test() {Class clazz = Person.class;// 获取运行时类实现的接口// 由于实现的接口可能有多个,所以返回的为数组Class[] interfaces = clazz.getInterfaces();for(Class c : interfaces){System.out.println(c);}}
3.6.2 获取运行时类的父类实现的接口
@Testpublic void test() {Class clazz = Person.class;//获取运行时类的父类实现的接口//先获取运行时类的父类//然后获取运行时类的父类实现的接口Class[] interfaces1 = clazz.getSuperclass().getInterfaces();for(Class c : interfaces1){System.out.println(c);}}
3.7 获取运行时类所在的包
@Testpublic void test() {Class clazz = Person.class;Package pack = clazz.getPackage();System.out.println(pack);}
3.8 获取运行时类的注解
@Testpublic void test() {Class clazz = Person.class;Annotation[] annotations = clazz.getAnnotations();for(Annotation annos : annotations){System.out.println(annos);}}