反射:指在运行时去获取一个类的变量和方法信息,然后通过获取到的信息来创建对象,调用方法的一种机制,由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译器就完成确定,在运行期仍然可以扩展。
简而言之就是:反射通过拿到类的构造方法来创建对象
获取class类的对象:我们要想通过反射去使用一个类,首先我们要获取到该类的字节码文件对象,也就是类型为class类型的对象
三种方法获取class类型的对象:
- 使用类的class属性来获取该类对应的class对象,比如:student.class将会返回student类对应的class对象
//使用类的class属性来获取该类对应的class对象
Class<Student> c1 = Student.class;
System.out.print(c1);
Class<Student> c2 = Student.class;
System.out.print(c1 == c2);
- 调用对象的getClass()方法,返回该对象所属类对应的class对象,该方法是object类中的方法,所有的java对象都可以调用该方法
//调用对象的getClass()方法,返回该对象所属类对应的class对象,该方法是object类中的方法,所有的java对象都可以调用该方法
Student s =new Student();
Class<?extends Student > c3 = s.getClass();
System.out.print(c1 == c3);
- 使用class类中的静态方法forName(string classname),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径
Class类中用于获取构造方法的方法
1. Constructor<?>[] getDeclaredConstructors():返回所有构造方法对象的数组
2. Constructor<?>[] getConstructors():返回所有公共构造方法对象的数组
3. Constructor<T> getConstructor(Class<?>... parameterTypes) :返回单个公共构造方法对象
4. Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) :返回单个构造方法对象
Constructor类中用于创建对象的方法
1. T newInstance():根据指定的构造方法创建对象
小练习:通过反射实现
Student s= new Student("小丁","40","西安");
System.out.print(s);
public static void main(String[] args)throws ClassNotFoundException,NoSuchMethodException,InstantiationException,InvocationTargetException,IllegalAccessException {
//获取Class对象
Class<?> c = Class.forName("Student");
//基本数据类型也可以通过.class得到对应的class类型
// public Student(String name,int age,String address)
Constructor<?> con = c.getConstructor(String.class, int.class, String.class);
Object obj = con.newInstance("zhuohai", 22, "xian");
System.out.println(obj);
}
练习2:
Student s= new Student("小丁");
System.out.print(s);
public static void main(String[] args)throws ClassNotFoundException,NoSuchMethodException,InstantiationException,InvocationTargetException,IllegalAccessException {
Class<?> c = Class.forName("Student");
//基本数据类型也可以通过.class得到对应的class类型
// private Student(String name,int age,String address)
Constructor<?> con = c.getDeclaredConstructor(String.class, int.class, String.class);
Object obj = con.newInstance("zhuohai", 22, "xian");
System.out.println(obj);
}
我们运行以上代码会发现代码抛以下异常了,这个是怎么回事呢?
私有的构造方法可以拿到,但是我们不可以通过私有的构造方法去构造,
Exception in thread "main" java.lang.IllegalAccessException: Class testcase can not access a member of class Student with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Constructor.newInstance(Constructor.java:413)
at testcase.main(testcase.java:15)
解决方案如下:
public void setAccessible(boolean flag) throws SecurityException
//将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
//值为 false 则指示反射的对象应该实施 Java 语言访问检查。
//首先,如果存在安全管理器,则在 ReflectPermission("suppressAccessChecks") 权限下调用 checkPermission 方法。
//如果 flag 为 true,并且不能更改此对象的可访问性(例如,如果此元素对象是 Class 类的 Constructor 对象),则会引发 SecurityException。
//如果此对象是 java.lang.Class 类的 Constructor 对象,并且 flag 为 true,则会引发 SecurityException。
我们可使用该方法进行暴力反射
public static void main(String[] args)throws ClassNotFoundException,NoSuchMethodException,InstantiationException,InvocationTargetException,IllegalAccessException {
Class<?> c = Class.forName("Student");
//基本数据类型也可以通过.class得到对应的class类型
// private Student(String name,int age,String address)
Constructor<?> con = c.getDeclaredConstructor(String.class, int.class, String.class);
//暴力反射
//public void setAccessible(boolean flag)//值为true,取消访问检查
con.setAccessible(true);
Object obj = con.newInstance("zhuohai", 22, "xian");
System.out.println(obj);
}
反射获取成员变量并使用
- Field[] getFields()
//返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。 - Field[] getDeclaredFields()
//返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。 - // Field getField(String name)
// 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。 - // Field getDeclaredField(String name)
// 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 - // void set(Object obj, Object value)//给obk对象的成员变量赋值为value
// 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
public static void main(String[] args)throws ClassNotFoundException,NoSuchMethodException,InstantiationException,InvocationTargetException,IllegalAccessException {
Class<?> c = Class.forName("Student");
//Field[] getFields()
//返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
Field[] fields = c.getFields();
for(Field field:fields){
System.out.println(field);
}
System.out.println("----------------------");
// Field[] getDeclaredFields()
//返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
Field[] allfields = c.getDeclaredFields();
for(Field field:allfields){
System.out.println(field);
}
}
如下图,是我们上段代码的返回结果
反射获取成员变量并使用的步骤:
- 获取class对象c
- 通过获取到的class对象c来获取成员变量
- 获取无参构造方法创建对象
- 给对象的成员变量进行赋值
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
Class<?> c = Class.forName("Student");
// Field getField(String name)
// 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
Field addressfileld = c.getField("address");
//获取无参构造方法创建对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
// void set(Object obj, Object value)
// 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
addressfileld.set(obj,"xian");//给obj的成员变量addressfileld赋值为xian
System.out.println(addressfileld);
//以上代码主要为了实现下方的代码
Student s =new Student();
s.address = "xian";
System.out.println(s);
}
下面是全部练习代码
public class Student {
//私有成员变量
private String name;
//默认成员变量
int age;
//公共成员变量
public String address;
//公共构造方法
public Student(String name){
this.name = name;
}
public Student(){
}
//默认构造方法
Student(String name,int age){
this.name = name;
this.age = age;
}
//私有构造方法
private Student(String name,int age,String address){
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString(){
return "Student{" +"name="+name+","+"age="+age+","+"address="+address+"}";
}
}
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
//反射获取构造方法并使用
public class Test {
public static void main(String[] args)throws ClassNotFoundException,NoSuchMethodException,InstantiationException,InvocationTargetException,IllegalAccessException{
//使用类的class属性来获取该类对应的class对象
Class<Student> c1 = Student.class;
System.out.print(c1);
Class<Student> c2 = Student.class;
System.out.print(c1 == c2);
//调用对象的getClass()方法,返回该对象所属类对应的class对象,该方法是object类中的方法,所有的java对象都可以调用该方法
Student s =new Student();
Class<?extends Student > c3 = s.getClass();
System.out.print(c1 == c3);
// 使用class类中的静态方法forName(string classname),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径
//<?>为范型
Class<?> c4 = Class.forName("Student");
//Constructor<?>[] getConstructors()返回一个包含Constructor对象的数组。Constructor对象反映了由该class对象表示的类的所有公共构造函数
Constructor<?>[] cons = c4.getConstructors();
for(Constructor con:cons){
System.out.println(con);
}
//Constructor<?>[] getDeclaredConstructors()返回反映由该Class对象表示的类声明的所有构造函数的Constructor对象的数组。
Constructor<?>[] cons1 = c4.getDeclaredConstructors();
for(Constructor con:cons1){
System.out.println(con);
}
//反射通过拿到类的构造方法来创建对象
//getDeclaredConstructor(类<?>... parameterTypes)
//返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定 类函数。
// getConstructor(Class<?>... parameterTypes)
//返回一个Constructor对象,该对象反映由该Class对象表示的类的指定公共构造函数
//参数:要获取的构造方法的参数的个数和数据类型对应的字节码文件对象
Constructor<?> con = c4.getConstructor();
//Constructor提供了一个类的单个构造函数的信息和访问权限
//public T newInstance()
//使用由此Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例
Object obj = con.newInstance();
System.out.println(obj);
Constructor<?> cons = c4.getConstructor(0);
}
}