反射概念:
反射是啥?Java的反射机制其实就是在程序运行时去获取一个类的成员变量和方法,通过获得的这些信息去创建对象,然后我们就可以访问类的成员变量和方法。
这个和正常创建对象的方式有什么不一样嘛?我不太理解,PPT只是说反射的这种方式可以极大的增强程序的灵活性,程序不用在编译器就能够确定,在运行期仍然可以扩展。
希望在后面的视频中能加深这种理解。
获取Class类的对象
1、概念
接下来敲代码演示这几种方法的使用过程。
2、案例演示
public class ReflexDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
// 第一种:使用类的class属性来获取该类对应的class对象
Class<Student> sc1 = Student.class;
System.out.println(sc1);
// 第二种:调用对象的getClass()方法
Student student = new Student();
Class<? extends Student> sc2 = student.getClass();
if (sc1==sc2)
System.out.println(sc2);
// 第三种:使用Class类的静态方法:forName()
Class<?> sc3 = Class.forName("Basic.Reflex.Student");
if (sc3==sc1)
System.out.println(sc3);
}
}
如何使用反射获取类的构造方法并使用呢?
使用到两个方法:
-
-
Constructor<T>
getConstructor(Class<?>... parameterTypes)
返回一个
Constructor
对象,该对象反映此Class
对象所表示的类的指定公共构造函数。
-
-
-
Constructor<?>[]
getDeclaredConstructors()
返回
Constructor
对象的数组,Constructor
对象反映由此Class
对象表示的类声明的所有构造函数。
-
getConstructor()该方法返回是公共的构造器函数数组。
getConstructors()该方法返回的是所有的构造器函数数组。
案例代码:
import java.lang.reflect.Constructor;
public class ReflexDemo2 {
public static void main(String[] args) {
// 获取class对象
Class<Student> c = Student.class;
// getConstructors()
Constructor<?>[] cons = c.getConstructors();
for (Constructor con : cons) {
System.out.println(con);
}
System.out.println("---------------------");
// getDeclareConstructors()
Constructor<?>[] conss = c.getDeclaredConstructors();
for (Constructor constructor : conss) {
System.out.println(constructor);
}
}
}
上面的这两种方法其实得到的都是数组,获得的都是数组,那反射能不能获取单个的或者指定的构造器函数呢?
答案当然是可以的!
-
-
Constructor<T>
getConstructor(Class<?>... parameterTypes)
返回一个
Constructor
对象,该对象反映此Class
对象所表示的类的指定公共构造函数。
-
-
-
Constructor<T>
getDeclaredConstructor(Class<?>... parameterTypes)
返回
Constructor
对象,该对象反映此Class
对象表示的类或接口的指定构造函数。
-
这两个方法可以获得单个的或者指定的构造函数,此外获得构造函数后还需要创建对象才得,需要用到Constructor类中的这个方法:
-
-
T
newInstance(Object... initargs)
使用此
Constructor
对象表示的构造方法,使用指定的初始化参数创建和初始化构造函数声明类的新实例。
-
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflexDemo3 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 创建字节码对象
Class<Student> c = Student.class;
/*
// 获得无参构造函数
Constructor<Student> con = c.getConstructor();
// 创建对象
Student student = con.newInstance();
*/
// 获得有参构造函数
Constructor<Student> con = c.getConstructor(String.class, int.class);
// 创建对象
Student student = con.newInstance("奥特曼", 18);
System.out.println(student);
}
}
上面这个是公共的,接下来我们玩个私有的。
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflexTest1 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 创建反射对象
Class<Student> c = Student.class;
// 获得一个参数的构造函数(我们创建的Student类中有一个private Student(String name))
Constructor<Student> con = c.getDeclaredConstructor(String.class);
// 使用构造函数创建对象
Student student = con.newInstance("林青霞");
System.out.println(student);
}
}
虽然我们使用了getDeclaredConstructor(),但是我们依然收到了错误提示:
这是为什么呢?因为getDeclaredConstructor()仅仅是帮助我们获得了私有的构造方法,但是我们不能够拿这个私有构造去创建对象,所以编译器报错了。
那有木有什么方法能解决这个问题呢?
有的,只要Constructor类中的setAccessible()方法即可解决这个问题。
-
-
void
setAccessible(boolean flag)
将此反射对象的
accessible
标志设置为指示的布尔值。设置为true就可以取消检查机制。
-
来吧Come on,看看效果如何:
public class ReflexTest1 { public static void main(String[] args) { // 创建反射对象 Class<Student> c = Student.class; // 获得一个参数的构造函数(我们创建的Student类中有一个private Student(String name)) Constructor<Student> con = c.getDeclaredConstructor(String.class); // 暴力反射 con.setAccessible(true); // 使用构造函数创建对象 Student student = con.newInstance("林青霞"); System.out.println(student); } }
程序完美的运行起来了,没有错误再出现。
反射获得成员变量并使用
毫无疑问,肯定要使用方法来获取类中的成员变量。
来吧,代码展示:
package Basic.Reflex;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflexDemo4 {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 反射对象
Class<Student> c = Student.class;
// 获得公共的所有成员变量
Field[] fields = c.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("-------------------");
// 获得所有的成员变量,包括私有
Field[] declaredFields = c.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("-------------------");
// 获得指定的单个的成员变量
Field age = c.getDeclaredField("age");
// 使用构造器创建对象,并为成员变量赋值
Constructor<Student> con = c.getConstructor();
Student student = con.newInstance();
// 为student中的age赋值(感觉student.age = 18)就可以了,这样子的反射是不是有些多余了?
age.set(student, 18);
System.out.println(student);
}
}
最后,我们来一个反射获取成员变量的练习题:
来吧,上代码:
package Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflexTest2 {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 反射对象
Class<Student> c = Student.class;
// 构造方法
Constructor<Student> con = c.getConstructor();
// 创建对象
Student student = con.newInstance();
// 成员变量
Field name = c.getDeclaredField("name");
Field age = c.getDeclaredField("age");
Field address = c.getField("address");
// 取消检查
name.setAccessible(true);
age.setAccessible(true);
address.setAccessible(true);
// 变量赋值
name.set(student,"林青霞");
age.set(student,18);
address.set(student,"广西");
System.out.println(student);
}
}
反射获得成员方法并使用
毫无疑问,肯定还是得上方法:
-
-
Method
getMethod(String name, Class<?>... parameterTypes)
返回公共的单个或者指定的成员方法。
Method[]
getMethods()
返回公共的所有成员方法(数组)
-
-
-
Mehtod
getDeclaredMethod(String name, Class<?>... parameterTypes)
返回指定的单个的或者指定的成员方法(包括私有)
Method[]
getDeclaredMethods()
返回指定的单个的或者指定的所有成员方法(包括私有)
-
哦,对了!还有一个方法需要学习的:
来吧,看代码学会如何使用:
首先,在Student类中创建四个方法:一私三公
public class Student { private String name; int age; public String address; private void function(){ System.out.println("我是private私有的"); } public void show1(){ System.out.println("show1"); } public void show2(String s){ System.out.println("show2:"+s); } public String show3(String s,int i){ return s+","+i; } }
然后创建我们的测试类:
package Basic.Reflex;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflexDemo5 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 反射对象
Class<Student> c = Student.class;
// 所有公共方法(包括自己类中的方法、以及继承自父类的方法)
Method[] methods = c.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("-----------------------");
// 所有方法,包括私有(只会包含自身类中的方法,不包含继承的方法)
Method[] declaredMethods = c.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println("-----------------------");
// 指定的当个公共方法
Method show1 = c.getMethod("show1");
// 有了公共方法,那么就需要创建对象使用了
Constructor<Student> con = c.getConstructor();
Student student = con.newInstance();
// Object invoke(Object obj, Object... args):在具有指定参数的指定对象上调用此方法对象表示的基础方法。
// Object: 返回值
// obj: 调用对象
// ... args: 方法需要的参数
// 调用成员方法
show1.invoke(student);
}
}
总结:
好,学完之后让我们做一道练习题巩固一下所学知识:
来吧,代码演示:
package Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflexTest3 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 反射对象
Class<Student> c = Student.class;
// 构造函数
Constructor<Student> con = c.getConstructor();
// 创建对象
Student student = con.newInstance();
// 成员方法
Method method1 = c.getMethod("method1");
Method method2 = c.getMethod("method2", String.class);
Method method3 = c.getMethod("method3", String.class, int.class);
Method function = c.getDeclaredMethod("function");
// 设置取消检查
function.setAccessible(true);
// 调用方法
method1.invoke(student);
method2.invoke(student,"奥特曼");
Object result = method3.invoke(student, "波塞冬", 20);
System.out.println(result);
function.invoke(student);
}
}