原文链接:https://blog.csdn.net/sinat_38259539/article/details/71799078
一、反射的概述
java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能成为java的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象。
反射就是把java类中的各个成分映射成一个个的java对象
例如:一个类有:成员方法、构造方法、成员变量、包等信息。利用反射技术可以对一个类进行解剖,把一个个组成部分映射成一个个对象。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
二、查看Class类在java中的api详解(1.7的API)
Class类的实例表示正在运行的Java应用程序中的类和接口,也就是JVM中有N多的实例每个类都有该Class对象。(包括基本数据类型)
Class没有公共构造方法。Class对象实在加载类时由java虚拟机以及通过调用类加载器中的defineClass方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了
在这里插入图片描述
三、反射的使用
先写个Student类
1、获取Class对象的三种方式
1.1 Object -> getClass()
1.2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
1.3 通过Class类的静态方法:forName(String className) 常用
其中1.1是因为Object类中的getClass方法、因为所有类都继承Object类。从而调用Object类来获取
public class Main {
public static void main(String[] args) {
// 获取Class对象的三种方式
// 1、 Object -> getClass()
Student student = new Student();
Class c1 = student.getClass();
System.out.println(c1.getName());
// 2、 任何数据类型(包括基本数据类型)都有一个“静态"的class属性
Class c2 = Student.class;
System.out.println(c1 == c2);
// 3、通过Class类的静态方法:forName(String className) 常用
try {
Class c3 = Class.forName("reflect.Student");
System.out.println(c2 == c3);
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意:运行期间,一个类,只有一个Class对象产生。
三种方式常用第三种,第一种对象都已经有了,无需再反射。第二种,需要导入类的包,依赖太强,不导包就抛出编译错误。一般都第三种,一个字符串可以传入也可卸载配置文件中等多种方法。
2、通过反射获取构造方法并使用:
package reflect;
public class Student {
public String name;
private Integer age;
char sex;
protected String mobliePhone;
// 默认构造方法
Student(String str){
System.out.println("默认构造方法: " + str);
}
// 无参构造方法
public Student(){
System.out.println("调用了公有的无参构造方法");
}
// 有一个参数的构造方法
public Student(char name){
System.out.println("名字:" + name);
}
// 多个参数的构造方法
public Student(String name, Integer age){
this.name = name;
this.age = age;
System.out.println("名字:" + this.name + "年龄:" + this.age);
}
}
测试通过反射获取构造方法
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ConstructorTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 1、加载Class对象
Class aClass = Class.forName("reflect.Student");
// 获取所有公有的构造方法
System.out.println("---------所有公有的构造方法----------");
Constructor[] constructors = aClass.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
// 获取所有的构造方法
System.out.println("--------------所有的构造方法-------------");
Constructor[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
// 获取指定公有构造方法需要传参数类型,公有无参的构造方法的参数可以传null或者不传
System.out.println("-----获取指定的公有的构造方法------");
Constructor constructor = aClass.getDeclaredConstructor(String.class, Integer.class);
System.out.println(constructor);
// 调用构造方法,只能调用当前获取到的构造方法。
Object o = constructor.newInstance("陈陶然", 12);
System.out.println(o.toString());
/**
Constructor constructor1 = aClass.getConstructor();
Object o1 = constructor1.newInstance("陈陶然", "22"); // java.lang.IllegalArgumentException: 参数数目错误
System.out.println(o1.toString());
*/
System.out.println("----------获取指定任意修饰符构造方法,并调用;调用私有修饰符的构造方法需要setAccessible(true)--------------------");
Constructor declaredConstructor = aClass.getDeclaredConstructor(Integer.class);
System.out.println(declaredConstructor);
declaredConstructor.setAccessible(true); // 开启暴力反射
o = declaredConstructor.newInstance(22);
System.out.println(o.toString());
}
}
后台输出:
---------所有公有的构造方法----------
public reflect.Student(java.lang.String,java.lang.Integer)
public reflect.Student()
public reflect.Student(char)
--------------所有的构造方法-------------
private reflect.Student(java.lang.Integer)
protected reflect.Student(boolean)
public reflect.Student(java.lang.String,java.lang.Integer)
reflect.Student(java.lang.String)
public reflect.Student()
public reflect.Student(char)
-----获取指定的公有的构造方法------
public reflect.Student(java.lang.String,java.lang.Integer)
名字:陈陶然年龄:12
Student{name='陈陶然', age=12, sex= , mobliePhone='null'}
----------获取指定任意修饰符构造方法,并调用;调用私有修饰符的构造方法需要setAccessible(true)--------------------
private reflect.Student(java.lang.Integer)
私有构造方法, 年龄:22
Student{name='null', age=22, sex= , mobliePhone='null'}
调用方法:
1.获取构造方法:
1).批量的方法:
public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
2).获取单个的方法,并调用:
public Constructor getConstructor(Class… parameterTypes):获取单个的"公有的"构造方法:
public Constructor getDeclaredConstructor(Class… parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
调用构造方法:
Constructor–>newInstance(Object… initargs)
2、 newInstance是 Constructor类的方法(管理构造函数的类)
api的解释为:
newInstance(Object… initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象。并为之调用
3、获取成员变量并调用
Student类:
package reflect;
public class Student {
public String name;
private Integer age;
char sex;
protected String mobliePhone;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", mobliePhone='" + mobliePhone + '\'' +
'}';
}
}
测试通过反射获取Studnet的成员变量
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class Fields {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
// 1、获取Class对象
Class aClass = Class.forName("reflect.Student");
// 2、获取字段
System.out.println("----------获取所有公有的字段----------");
Field[] fields = aClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("-----------获取所有的字段-------------");
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("------------获取指定的公有字段----------------");
Field fieldname = aClass.getField("name");
System.out.println(fieldname);
// 获取一个对象
Object o = aClass.getConstructor().newInstance();
fieldname.set(o, "陈陶然"); //为Student对象中的name属性赋值-》student.name = "陈陶然"
Student student = (Student) o;
System.out.println("验证名字: " + student.name);
System.out.println("--------------获取指定私有字段--------------");
Field age = aClass.getDeclaredField("age");
System.out.println(age);
Object o1 = aClass.getDeclaredConstructor(String.class, Integer.class).newInstance("陈陶然", 22);;
age.setAccessible(true);// 暴力反射,解除限定
age.set(o1,18);
System.out.println("验证年龄: " + o1);
}
}
结果为:
---------所有公有的构造方法----------
public reflect.Student()
public reflect.Student(char)
public reflect.Student(java.lang.String,java.lang.Integer)
--------------所有的构造方法-------------
private reflect.Student(java.lang.Integer)
reflect.Student(java.lang.String)
public reflect.Student()
public reflect.Student(char)
public reflect.Student(java.lang.String,java.lang.Integer)
protected reflect.Student(boolean)
-----获取指定的公有的构造方法------
public reflect.Student(java.lang.String,java.lang.Integer)
名字:陈陶然年龄:12
Student{name='陈陶然', age=12, sex= , mobliePhone='null'}
----------获取指定任意修饰符构造方法,并调用;调用私有修饰符的构造方法需要setAccessible(true)--------------------
private reflect.Student(java.lang.Integer)
私有构造方法, 年龄:22
Student{name='null', age=22, sex= , mobliePhone='null'}
Process finished with exit code 0
//为字段设置值 :fieldname.set(o, “陈陶然”); //为Student对象中的name属性赋值-》student.name = “陈陶然”
4、获取成员方法并调用
Student:
package reflect;
public class Student {
public String name;
private Integer age;
char sex;
protected String mobliePhone;
// 默认构造方法
Student(String str){
System.out.println("默认构造方法: " + str);
}
// 无参构造方法
public Student(){
System.out.println("调用了公有的无参构造方法");
}
// 有一个参数的构造方法
public Student(char name){
System.out.println("名字:" + name);
}
// 多个参数的构造方法
public Student(String name, Integer age){
this.name = name;
this.age = age;
System.out.println("名字:" + this.name + "年龄:" + this.age);
}
// 受保护的构造方法
protected Student(boolean n){
System.out.println("受保护的方法: " + n);
}
// 私有构造方法
private Student(Integer age){
this.age = age;
System.out.println("私有构造方法, 年龄:" + this.age);
}
public void show1(String s1){
System.out.println("调用了公有的成员方法show1,参数为: " + s1);
}
protected void show2(){
System.out.println("调用了受保护的无参方法show2()");
}
void show3(){
System.out.println("调用了:默认的无参方法show3()");
}
private String show4(String s2){
System.out.println("调用了私有方法show4(),参数为:" + s2);
return s2;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", mobliePhone='" + mobliePhone + '\'' +
'}';
}
public static void main(String[] args) {
System.out.println("Student的Main方法执行了....");
}
}
测试通过反射获取Student的成员方法。
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MethodTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class aClass = Class.forName("reflect.Student");
// 获取所有公有方法
System.out.println("---------获取所有公有方法-----------");
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("---------获取所有的方法,包括私有方法------------");
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println("-------------获取指定公有方法---------");
// 不能获取公有方法以外的方法,执意获取报NoSuchMethodException
Method show1 = aClass.getMethod("show1", String.class);
System.out.println(show1);
// 实例化一个Student对象
Object o = aClass.getConstructor().newInstance();
show1.invoke(o, "陈陶然");
System.out.println("----------获取指定私有方法-----------");
Method show2 = aClass.getDeclaredMethod("show4", String.class);
show2.setAccessible(true);
Object res = show2.invoke(o, "陈陶然123");
System.out.println(res);
}
}
由此可见:
Method show2 = aClass.getDeclaredMethod(“show4”, String.class);//调用指定方法(所有包括私有的),需要传入两个参数,第一个是调用的方法名称,第二个是方法的形参类型,切记是类型。
show2.setAccessible(true);;//解除私有限定
Object res = show2.invoke(o, “陈陶然123”);//需要两个参数,一个是要调用的对象(反射获取),一个是实参
System.out.println(res);
结果为:
---------所有公有的构造方法----------
public reflect.Student(java.lang.String,java.lang.Integer)
public reflect.Student()
public reflect.Student(char)
--------------所有的构造方法-------------
private reflect.Student(java.lang.Integer)
protected reflect.Student(boolean)
public reflect.Student(java.lang.String,java.lang.Integer)
reflect.Student(java.lang.String)
public reflect.Student()
public reflect.Student(char)
-----获取指定的公有的构造方法------
public reflect.Student(java.lang.String,java.lang.Integer)
名字:陈陶然年龄:12
Student{name='陈陶然', age=12, sex= , mobliePhone='null'}
----------获取指定任意修饰符构造方法,并调用;调用私有修饰符的构造方法需要setAccessible(true)--------------------
private reflect.Student(java.lang.Integer)
私有构造方法, 年龄:22
Student{name='null', age=22, sex= , mobliePhone='null'}
5、反射main方法
Student类:
package reflect;
public class Student {
public static void main(String[] args) {
System.out.println("Student的Main方法执行了....");
}
}
测试通过反射获取main方法
public class Main {
public static void main(String[] args) {
// 3、通过Class类的静态方法:forName(String className) 常用
try {
Class c3 = Class.forName("reflect.Student");
Method main = c3.getMethod("main", String[].class);
//第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
//这里拆的时候将 new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
main.invoke(null, (Object)new String[]{"a", "b", "c"}); // 方式一
// main.invoke(null, new Object[]{new String[]{"a", "b", "c"}}); // 方式二
} catch (Exception e) {
e.printStackTrace();
}
}
}
控制台输出:Student的Main方法执行了…
6、反射方法的其它使用之—通过反射运行配置文件内容
Student类:
package reflect;
public class Student {
public String name;
private Integer age;
char sex;
protected String mobliePhone;
// 默认构造方法
Student(String str){
System.out.println("默认构造方法: " + str);
}
// 无参构造方法
public Student(){
System.out.println("调用了公有的无参构造方法");
}
// 有一个参数的构造方法
public Student(char name){
System.out.println("名字:" + name);
}
// 多个参数的构造方法
public Student(String name, Integer age){
this.name = name;
this.age = age;
System.out.println("名字:" + this.name + "年龄:" + this.age);
}
// 受保护的构造方法
protected Student(boolean n){
System.out.println("受保护的方法: " + n);
}
// 私有构造方法
private Student(Integer age){
this.age = age;
System.out.println("私有构造方法, 年龄:" + this.age);
}
public void show1(String s1){
System.out.println("调用了公有的成员方法show1,参数为: " + s1);
}
protected void show2(){
System.out.println("调用了受保护的无参方法show2()");
}
void show3(){
System.out.println("调用了:默认的无参方法show3()");
}
private String show4(String s2){
System.out.println("调用了私有方法show4(),参数为:" + s2);
return s2;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", mobliePhone='" + mobliePhone + '\'' +
'}';
}
public static void main(String[] args) {
System.out.println("Student的Main方法执行了....");
}
}
创建配置文件ctr.txt内容:
className = reflect.Student
methodName = show2
测试:
package reflect;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class Demo {
public static String getValue(String key) throws IOException{
Properties properties = new Properties(); // 获取配置文件对象
FileReader fileReader = new FileReader("D:\\study\\reflect\\src\\ctr.txt"); // 绝对路径,获取输入流
properties.load(fileReader); // 将流加载到配置文件对象中
fileReader.close();
return properties.getProperty(key);
}
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class className = Class.forName(getValue("className"));
Method methodName = className.getDeclaredMethod(getValue("methodName"));
methodName.invoke(className.getConstructor().newInstance());
}
}
结果为:
7、通过反射越过泛型检查
泛型用在编译期,编译过后泛型擦除,所以是可以通过反射越过泛型检查
package reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class Demo2 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ArrayList<String> strList = new ArrayList<>();
strList.add("abc");
strList.add("efg");
// strList.add(100);
Class aClass = strList.getClass(); // 得到strList对象的字节码对象
// 获取add()方法
Method m = aClass.getMethod("add", Object.class);
// 调用add()方法
m.invoke(strList, 100);
for (Object o : strList) {
System.out.println(o);
}
}
}
控制台输出:
abc
efg
100