1.1 反射概念
反射是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。
由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展
1.2 获取类对象的三种方式
1)使用类的class属性来获取类对象
2)调用对象的getclass方法,返回该对象所属类对应的Class对象
3)使用Class类中的静态方法forname(String forname)
下面我们通过代码进行演示
public class test01_method3GetClass {
public static void main(String[] args) throws ClassNotFoundException {
//1.使用类的class属性来获取类对象
Class<Student> c1 = Student.class;
System.out.println(c1);//控制台输出:class ex1.Student
//2.调用对象的getclass方法,返回该对象所属类对应的Class对象
Student s = new Student();
Class<? extends Student> c2 = s.getClass();
System.out.println(c2);//控制台输出:class ex1.Student
System.out.println(c1==c2);//true,说明c1和c2是同一个对象
//3.使用Class类中的静态方法forname(String forname),参数为类地址
Class<?> c3 = Class.forName("ex1.Student");
System.out.println(c1==c3);//true,c1和c3也是同一个对象
}
}
通过控制台输出我们可以看出,三种方法获得的类对象都是一致的。
1.3 通过反射获得类对象的构造方法
首先我们定义一个学生类,里面包含公共,默认,私有的成员变量,构造方法,以及成员方法,接下来我们演示通过反射来获得类的构造方法。
学生类:
public class Student {
//成员变量:一个私有,一个默认,一个公共
private String name;
int age;
public String address;
//构造方法:一个私有,一个默认,两个公共
public Student() {
}
private Student(String name) {
this.name = name;
}
Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
//成员方法:一个私有,四个公共
private void function() {
System.out.println("好好学习,天天向上");
}
public void method1() {
System.out.println("method");
}
public void method2(String s) {
System.out.println("method:" + s);
}
public String method3(String s, int i) {
return s + "," + i;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
接下来我们介绍几种方法来获得对象类的构造器
使用代码进行演示:
public class test02_getConstructors {
public static void main(String[] args) throws Exception {
//获取类对象
Class<?> c = Class.forName("ex1.Student");
System.out.println("-----获取多个构造方法-------");
//getConstructors()
//返回一个数组,其中包含反映此对象所表示的类的所有公共构造函数的对象。ConstructorClass
Constructor<?>[] cons = c.getConstructors();
for (Constructor<?> con : cons) {
System.out.println(con);//public ex1.Student(),只能获取公共方法
// public ex1.Student(java.lang.String,int,java.lang.String)
}
System.out.println("-------------------");
//Constructor<?>[]
//getDeclaredConstructors()
//返回一个对象数组,该数组反映此对象所表示的类所声明的所有构造函数。ConstructorClass
Constructor<?>[] dcons = c.getDeclaredConstructors();
for (Constructor<?> dcon : dcons) {
System.out.println(dcon);//获得所有的构造方法,公共默认以及私有方法
}
System.out.println("-----获取单个个构造方法-------");
//Constructor<T>
//getConstructor(Class<?>... parameterTypes)
//返回一个对象,该对象反映此对象所表示的类的指定公共构造函数。ConstructorClass
//---------------------------------------------
//Constructor<T>
//getDeclaredConstructor(Class<?>... parameterTypes)
//返回一个对象,该对象反映此对象所表示的类或接口的指定构造函数。ConstructorClass
/*Student s = new Student();
System.out.println(s);*/
//使用反射来调用Student的无参构造
Constructor<?> con = c.getConstructor();
//Constructor提供有关类的单个构造函数的信息和对该类的访问
//T
//newInstance(Object... initargs)
//使用此对象表示的构造函数创建和初始化构造函数的声明类的新实例,并具有指定的初始化参数。Constructor
Object obj = con.newInstance();
System.out.println(obj);//Student{name='null', age=0, address='null'}
//即调用了Student的无参构造进行打印输出
}
}
上述代码还使用到了一个newInstance(Object… initargs)方法,使用此对象表示的构造函数创建和初始化构造函数的声明类的新实例,并具有指定的初始化参数。
上述代码在控制台运行结果:
最后,我们通过一个小练习来巩固一下通过反射获得类构造器方法。
要求 1.通过调用三个参数的构造器的方法实现姓名,年龄和地址的输出打印
2.调用student类中一个参数的构造器对姓名进行赋值并打印。
ok,接下来我们实现第一个要求:
public class test03_practice {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("ex1.Student");
//getConstructor(Class<?>... parameterTypes)
//返回一个对象,该对象反映此对象所表示的类的指定公共构造函数。ConstructorClass
Constructor<?> con = c.getConstructor(String.class, int.class, String.class);
Object obj = con.newInstance("Adrian",18,"厦门");
System.out.println(obj);
}
}
分析:首先我们调用forname来获得Student类对象,然后通过getConstructor方法并输入对应参数的字符类型的字节码对象来获取对应的构造方法,最后使用newInstance方法进行实例化,输出。
接下来完成第二个需求
public class test03_practice {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("ex1.Student");
Constructor<?> con1 = c.getDeclaredConstructor(String.class);
Object obj2 = con1.newInstance("Young");
System.out.println(obj2);
}
}
通过运行结果我们可以看出,出现了错误。接下来我们分析一下:首先我们调用forname来获得Student类对象,然后通过getDeclaredConstructor方法并输入对应参数的字符类型的字节码对象来获取对应的构造方法,最后输出,看似没问题,实则,我们应该注意,Student类中只含有名字的构造器是private,私有化的,我们在外面是没有办法进行访问的,如何解决呢?这时候,我们可以使用暴力反射通过**setAccessible(boolean flag)**将此反射对象的标志设置为指示的布尔值。ok,接下来我们来改一下代码:
public class test03_practice {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("ex1.Student");
Constructor<?> con1 = c.getDeclaredConstructor(String.class);
//暴力反射,将里面参数设置为true便能对私有构造器进行访问
con1.setAccessible(true);
Object obj2 = con1.newInstance("Young");
System.out.println(obj2);
}
}
ok,重新启动一下
可以看到,name已经赋值了。
1.4 通过反射获得类对象的成员变量
实际上,通过反射来获得类对象的成员变量的方法跟通过反射来获得类对象的构造器的方法大同小异,我们接下来就简单介绍一下如何获得成员变量的方法。
我们通过一个练习来巩固一下通过反射获取类对象的成员变量并对其进行赋值
public class test05_practice2 {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("ex1.Student");
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
Field name = c.getDeclaredField("name");
name.setAccessible(true);
name.set(obj,"Adrian");
System.out.println(obj);
Field age = c.getDeclaredField("age");
age.setAccessible(true);
age.set(obj,18);
System.out.println(obj);
Field address = c.getDeclaredField("address");
address.set(obj,"厦门");
System.out.println(obj);
}
}
运行结果
1.5 通过反射获得类对象的成员方法
public class test06_getMemberMethods {
public static void main(String[] args) throws Exception {
//获取Class对象
Class<?> c = Class.forName("ex1.Student");
//获取公共成员方法
/*Method[] methods = c.getMethods();
for (Method method : methods) {
System.out.println(method);
}*/
Method[] methods1 = c.getDeclaredMethods();
for (Method method : methods1) {
System.out.println(method);
}
System.out.println("------------------------");
Method method = c.getMethod("method1");
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//Object invoke(Object obj, Object... args)
//在具有指定参数的指定对象上调用此对象所表示的基础方法。Method
//Object :返回值类型,obj:调用方法的对象 args:方法所需要的参数
method.invoke(obj);
}
}
这里,我们需要着重介绍invoke方法:
Object invoke(Object obj, Object… args)
在具有指定参数的指定对象上调用此对象所表示的基础方法。
Object :返回值类型,obj:调用方法的对象 args:方法所需要的参数
在此例中, Method method = c.getMethod(“method1”);调用Student中的method1方法,
method1是一个没有返回值,且不含参数的一个方法,因此,通过method.invoke(obj)便可调用该方法,在控制台输出:
ok,至此,通过反射调用类对象的成员变量,构造方法,成员方法,以及如何给成员变量赋值,如何调用相对应的构造方法给成员变量赋值,如何调用成员方法我们已经解释完毕。