一、先创建需要测试的Studnet类
package cn.sk.reflection;
/**本类用于测试反射,先准备物料类*/
public class Student {
//1.定义成员变量
public String name;
public int age;
//2.生成构造方法
//右键空白处->Gennerate->Constryctor->Select None--无参构造/全选属性可以生成全参构造
/**默认存在无参构造,创建对象时触发,若提供了含参构造
* 默认的无参构造会被覆盖,注意手动添加无参构造*/
public Student() {
System.out.println("我是Student类无参构造");
}
public Student(String name, int age) {
System.out.println("我是Student类的全参构造");
this.name = name;
this.age = age;
}
//3.创建普通成员方法
public void study(){
System.out.println("第一阶段马上要结束了,皮皮霞会更美");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
二、反射测试类
package cn.sk.reflection;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 本类用于测试反射
*/
public class Test1Reflect {
//1.创建程序的入口函数main()--不用
/**单元测试方法:是Java测试的最小范围,使用灵活,推荐程度:5颗星
* 语法要求:@Test + void + 没有参数 + public
* 注意:使用时需要导包:Add JUnit4 to ClassPath
* */
/**
* 单元测试一:用来测试获取指定类的字节码对象
*/
/*@Test*/
public void getClazz() throws ClassNotFoundException {
//1.方式1:通过类的全路径名[包名+类名]获取对应的字节码对象
Class<?> student1 = Class.forName("cn.tedu.reflection.Student");
//2.方式2:直接类名.Class获取对应的字节码对象
Class<?> student2 = Student.class;
//3.方式3:通过类的对象来获取,注意,此处创建的是匿名对象,匿名对象没有名字
Class<?> student3 = new Student().getClass();
System.out.println(student1);
System.out.println(student2.getName());//cn.tedu.reflection.Student全路径
System.out.println(student3.getSimpleName());//Student,获取类名
System.out.println(student3.getPackage().getName());//cn.tedu.reflection获取包名
}
/**
* 单元测试2:获取构造方法
*/
// @Test
public void getConstruct() {
//1.get Class Object获取Student类的字节码对象
Class<?> clazz = Student.class;
//2.get Constructors and put it in array 通过字节码对象获取所有公开的构造方法
Constructor<?>[] cs = clazz.getConstructors();
//3.get Constructor by powerUp-for 通过增强for循环遍历存放所有构造函数的数组
//for(1 2 :3){}3 表示的是要遍历的构造函数数组 1 表示当前遍历到的类型 2参数名
for (Constructor<?> c : cs) {
//通过本轮循环遍历到的构造函数对象,获取构造函数名
System.out.println(c.getName());//get Constructor's name
//通过本轮循环遍历到的构造函数对象,获取这个构造函数的参数类型
Class<?>[] cp = c.getParameterTypes();//get Constructor's parameter type
System.out.println(Arrays.toString(cp));
}
}
/**
* 单元测试3:获取普通方法
*/
//@Test
public void getFunction() throws ClassNotFoundException {
//获取字节码对象
Class<?> clazz = Class.forName("cn.tedu.reflection.Student");
//获取指定类中的所有普通方法
Method[] ms = clazz.getMethods();
//遍历存放所有方法的数组
for (Method m : ms) {
//通过当前遍历到的方法对象获取当前方法的名字
System.out.println(m.getName());
//通过当前遍历到的方法对象获取当前方法的参数类型
Class<?>[] pt = m.getParameterTypes();
System.out.println(Arrays.toString(pt));
}
}
/**
* 单元测试方法4:获取成员变量
*/
//@Test
public void getFields() throws ClassNotFoundException {
/**注意!!!目前所有成员变量的修饰符必须是public才能获取到
* 如果属性采用的是默认的修饰符,是反射不到的*/
//1.获取字节码对象
/**Class<?>中的"?"是泛型的约束通配符,类似"*"*/
Class<?> clazz = Class.forName("cn.tedu.reflection.Student");
//2.通过字节码队形获取所有的成员变量
Field[] fields = clazz.getFields();
//3.遍历
for (Field field : fields) {
System.out.println(field.getName());
System.out.println(field.getType().getName());
}
}
/**
* 单元测试5:通过单元测试来反射创建对象
* 方式一:通过字节码对象直接调用newInstance(),触发无参构造来创建对象
* 方式二:先获取指定的构造函数,再通过构造函数对象调用newInstance,触发对应类型的构造函数来创建对象
*/
@Test
public void makeObject() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//1.获取class字节码对象
Class<?> clazz = Class.forName("cn.tedu.reflection.Student");
//2.创建对象
Object o = clazz.newInstance();
System.out.println(o);
//3通过全参构造创建对象
//3.1通过指定的构造函数创建对象
/**注意参数类型的指定,是对应类型的字节码对象,不能直接光写类型*/
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
//3.2触发刚刚获取到的全参构造
Object z = constructor.newInstance("张三", 18);/**此处是多态的向上造型*/
System.out.println(z);
/**查看对象具体的属性值,或者调用方法,需要把Object强转成指定的子类对象
* 为什么要把Object转成子类类型呢?因为想要使用子类的属性或者是特有功能
* 父类是无法使用子类的特有属性和功能,上面的obj2就是Object类型
* Object类中没有Student中的属性和特有功能
* 这种把之前看着是父类类型的子对象,重新转回成子类对象的现象,叫做向下造型
* */
Student s = (Student) z;
System.out.println(s.name);
System.out.println(s.age);
s.study();
}
}