反射
前言
本文主要讲述反射的原理和用法
提示:以下是本篇文章正文内容,下面案例可供参考
一、反射
1.1反射的简单介绍
Java的反射是在我们的某一个类在运行的过程中可以动态的获取类的属性,构造方法,方法,父类,接口等内部的信息机制。反射的本质就是反着来。我们new一个对象的时候,实际上是由Java虚拟机根据Class类对象在运行的时候构建出来的。反射通过Class类对象获取类定义的信息(有属性,成员方法,构造方法等)。
换句话说:就是换一种方式获取类对象的属性,方法,父类,接口等信息。
1.2反射原理的概念图
HelloWorld.java这个文件通过编译器编译以后变成了一个HelloWorld.class文件(字节码文件)。
HelloWorld.clas这个文件,是应该有HelloWolrdjava文件的所有东西
有属性,方法,构造方法等。
一个java程序的加载,就是把当前的.class文件,进行加载,这个.class文件会被加裁加裁内存的方法区里面,在内存中会有一个.class文件对应的内存区域,这个内存包含了,class文件所有的与java对应的成员变量,构造方法,成员方法等。
java万事万物皆对象,可以把咱们的方法区的HelloWorld.class文件看成一个对象,通过这个对象可以扶取变量,方法,构造方法等。
咱们之前是直接new一个对象出来,然后操作属性,方法等。现在用了反射以后,需要借助于.class文件获取属性和方法。换一种方式获取类对象的属性,方法,父类,接口等。
二、反射的基本用法
2.1获取Class对象【.class文件对象】
javac这个命令将.java文件编译成.class文件,这个.class文件就会包含对类原始定义的信息。属性,方法,构造方法等。.class文件在运行的时候,会被classLoader加载到JVM里面。当一个.class文件被加载以后,JVM随即生成了一个Class对象
如何创建出来Class对象?有三种方式
①static Class forName(String name);//静态方法,通过Class类名的调用,得到所对应的Class对象
传入的参数是一个完整的类名(包名+类名)
②Class 类名.class 通过类名获取Class对象
③Class 类对象.getClass();//获取Class对象
代码如下(示例):
class Person{
private int id;
private String name;
public Person(){
}
public Person(int id,String name){
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person { id = "+id + ", name = " + name
+", weight = " + weight + ", height = " + height + " }";
}
}
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
//第一种获取Class对象的方式
//static Class forName(String name);//传入的参数是一个完整的类名(类名+包名)
Class<?> aClass = Class.forName("com.qfedu.java.Person");
System.out.println(aClass);
//第二种获取Class对象的方式
//Class 类名.class
Class<Person> personClass = Person.class;
System.out.println(personClass);
//第三种获取Class对象的方式
//Class 类对象.getClass();
Person person = new Person();
Class<? extends Person> aClass1 = person.getClass();
System.out.println(aClass1);
}
}
2.2获取Constructor类对象【构造方法】
下面的方法都是通过Class对象调用的
Constructor[] getConstructors();//获取当前类所有的非私有化的构造方法
Constructor[] getDeclaredConstructors();//获取当前类所有的构造方法
Constructor getConsructor(Class .... initParameterType);//通过构造方法的参数,获取对应的非私有化的构造方法
Constructor getDeclaredConstructor(Class ...initParameterType);//通过传递构造方法的参数,获取对应的私有化的构造方法
以下的方法是通过constructor对象调用的
Object newInstance(Object.. param);//通过Constructor对象获取类对象
代码如下(示例):
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
class Person{
//私有化的成员属性
private int id;
private String name;
//公开的成员属性
public double weight;
public double height;
//公开的空参构造方法
public Person(){
}
//公开的全参构造方法
public Person(int id,String name,double weight,double height){
this.id = id;
this.name = name;
this.weight = weight;
this.height = height;
}
//私有的带参构造方法
private Person(int id,String name){
this.id = id;
this.name = name;
}
private Person(int id){
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person { id = "+id + ", name = " + name
+", weight = " + weight + ", height = " + height + " }";
}
}
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//1.先获取Class对象?为什么先获取Class对象?
//因为获取Constructor对象的方法需要通过Class对象调用
Class<?> aClass = Class.forName("com.qfedu.java.Person");
System.out.println();
System.out.println("===当前类的所有非私有化的构造方法===");
//2.要想获取类对象,先获得Constructor对象
//获取Constructor对象的方法有4种
//①Constructor[] getConstructors();//获取当前类的所有非私有化的构造方法
Constructor<?>[] constructors = aClass.getConstructors();
// System.out.println(constructors);//[Ljava.lang.reflect.Constructor;@1b6d3586地址值
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println();
System.out.println("=====当前类所有的构造方法==========");
//②Constructor[] getDeclaredConstructors();//获取当前类所有的构造方法
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
System.out.println();
System.out.println("-----获取对应的非私有化的空参构造方法------------");
//③Constructor getConstructor(Class ...initParameterType);
//传入的参数为XXX.class,即参数类型.class
// 通过构造方法的参数获取对应的非私有的构造方法
//获取非私有化的空参构造方法
Constructor<?> constructor = aClass.getConstructor(null);
System.out.println(constructor);
//获取非私有化的带参构造方法
Constructor<?> constructor1 = aClass.getConstructor(int.class, String.class, double.class, double.class);
System.out.println(constructor1);
System.out.println();
System.out.println("******获取对应的私有化的构造方法**********");
//④Constructor getDeclaredConstructor(Class ...initParameter);
//通过构造方法的参数获取对应的私有化的构造方法
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(int.class, String.class);
System.out.println(declaredConstructor);
System.out.println();
System.out.println("=========通过Constructor对象获取类的实例化对象==============");
//Object newInstance(Object ...param);//获取类的实例化对象
Person person = (Person) constructor.newInstance();//Person person = new Person();
person.setId(1);
person.setName("狗蛋");
System.out.println(person);//Person { id = 1, name = 狗蛋, weight = 0.0, height = 0.0 }
// Person person1 = (Person) constructor1.newInstance(2, "张三", 56.4, 175.5);
// System.out.println(person1);//Person { id = 2, name = 张三, weight = 56.4, height = 175.5 }
Object object = constructor1.newInstance(2, "张三", 56.4, 175.5);
System.out.println(object);//Person { id = 2, name = 张三, weight = 56.4, height = 175.5 }
//私有化的带参构造方法能不能赋值?
//可以使用暴力反射,强行给私有化的东西进行赋值
declaredConstructor.setAccessible(true);
Object person2 = declaredConstructor.newInstance(3, "赵四");
System.out.println(person2);//Person { id = 3, name = 赵四, weight = 0.0, height = 0.0 }
}
}
2.3获取Method类对象【成员方法】
以下的方法都是通过Class对象来调用的
Method[] getMethods();//获取当前类下面所有的非私有化的方法
包括从父类和超类继承过来的方法
Method[] getDeclaredMethods();//获取当前类下面的所有的方法(包括私有化的方法)
不包括从父类继承过来的方法
Method getMethod(String name,Class ...parameters);//通过方法的参数,获取指定的非私有化的方法
Method getDeclaredMethod(String name,Class ...parameters);//获取当前类下面的私有化的方法
以下的方法都是通过Method对象来调用的
Object invoke(Object obj,Object ..., args);//通过Method对象调用类对象的当前方法
因为咱们以后要封装一些类,这些就是调用方法,可以方法方法执行
invoke这个方法就是让哪个方法对象调用的,就执行哪个方法
代码如下(示例):
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//1.获取class对象
Class<?> aClass = Class.forName("com.qfedu.java.Person");
//2.获取Method对象,有4种方法
/**
* Method[] getMethods();//获取当前类下面所有的非私有化的方法
* 包括从父类和超类继承过来的方法
* Method[] getDeclaredMethods();//获取当前类下面的所有的方法(包括私有化的方法)
* 不包括从父类继承过来的方法
* Method getMethod(String name,Class ...parameters);//通过方法的参数,获取指定的非私有化的方法
* Method getDeclaredMethod(String name,Class ...parameters);//获取当前类下面的私有化的方法
* */
//①Method[] getMethods();获取当前类下面的所有的非私有化的方法,
//包括从父类继承过来的方法
Method[] methods = aClass.getMethods();
// for (Method method : methods) {
// System.out.println(method);
// }
//②Method[] getDeclaredMethods();获取当前类下所有【包括私有化的】的方法
//不包括从父类继承过来的方法
Method[] declaredMethods = aClass.getDeclaredMethods();
// for (Method declaredMethod : declaredMethods) {
// System.out.println(declaredMethod);
// }
//③Method getMethod(String name, Class... parameters);
//根据方法的参数获取对应的非私有化的方法
//(1)获取非私有化的空参构造方法
Method method = aClass.getMethod("test",null);
System.out.println(method);
//(2)获取非私有化的带参构造方法
Method game = aClass.getMethod("game", String.class);
System.out.println(game);
//④Method getDeclaredMethod(String name, Class.. parameters);
//根据方法的参数获取对应的私有化的方法
//(1)获取私有化的空参构造方法
Method eat = aClass.getDeclaredMethod("eat", null);
System.out.println(eat);
//(2)获取私有化的带参构造方法
Method sleep = aClass.getDeclaredMethod("sleep", int.class);
System.out.println(sleep);
//Object invoke(Object obj,Object ..., args);//通过Method对象调用类对象的当前方法
//第一个参数是啥?obj 就是这个method所在的类对象
//方法对象有了,你得告诉我你这个方法是在哪个对象下面吧。
Object obj = aClass.getConstructor(null).newInstance(null);
//第二个参数是方法的参数
//调用了obj这个对象的下面的method方法
//即调用了Person这个对象下面的test方法
method.invoke(obj,null);
game.invoke(obj,"李太白");
//能不能调用私有的成员方法
//需要进行暴力反射
sleep.setAccessible(true);
sleep.invoke(obj,5);
}
}
2.4获取Field对象【属性】
以下的方法都是Class类对象调用的
Field[] getFields();//获取所有的非私有化的属性
Field[] getDeclaredFields();//获取全部的属性
Field getField(String name);//获取非私有化的指定的属性,参数为属性的名字
Field getDeclaredField(String name);//通过指定属性的名字,获取对应的私有化的属性
以下的方法都是Field对象调用的
set(Object obj,Object value);//将指定对象参数上的此 Field对象表示的字段设置为指定的新值。
代码如下(示例):
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
class Person{
//私有化的成员属性
private int id;
private String name;
//公开的成员属性
public double weight;
public double height;
//公开的空参构造方法
public Person(){
}
//公开的全参构造方法
public Person(int id,String name,double weight,double height){
this.id = id;
this.name = name;
this.weight = weight;
this.height = height;
}
//私有的带参构造方法
private Person(int id,String name){
this.id = id;
this.name = name;
}
private Person(int id){
this.id = id;
}
//公开的成员方法
public void test(){
System.out.println("我是公开的不带参数的成员方法");
}
public void game(String name){
System.out.println("我是公开的带参数的成员方法:name = " + name);
}
//私有的成员方法
private void eat(){
System.out.println("我是私有的不带参数的成员方法");
}
private void sleep(int id){
System.out.println("我是私有的带参数的成员方法:id = " + id);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person { id = "+id + ", name = " + name
+", weight = " + weight + ", height = " + height + " }";
}
}
public class Demo4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//1.获取Class对象
Class<?> aClass = Class.forName("com.qfedu.java.Person");
//2.获取Field的对象,有4种方法
/**
* Field[] getFields();//获取所有的非私有化的属性
* Field[] getDeclaredFields();//获取全部的属性
* Field getField(String name);//获取非私有化的指定的属性,参数为属性的名字
* Field getDeclaredField(String name);//通过指定属性的名字,获取对应的私有化的属性
* */
//①Field[] getFields();//获取所有的非私有化的属性
Field[] fields = aClass.getFields();
// for (Field field : fields) {
// System.out.println(field);
// }
//②Field[] getDeclaredFields();获取全部的属性
Field[] declaredFields = aClass.getDeclaredFields();
// for (Field declaredField : declaredFields) {
// System.out.println(declaredField);
// }
//③Field getField(String name);//获取非私有化的指定的属性,参数为属性的名字
Field weight = aClass.getField("weight");
System.out.println(weight);
//④Field getDeclaredField(String name);通过指定属性的名字,获取对应的属性
Field name = aClass.getDeclaredField("name");
System.out.println(name);
//获取属性对象的目的,就是对属性进行赋值
//set(Object obj, Object value);将指定对象参数上的此 Field对象表示的字段设置为指定的新值。
Object obj = aClass.getConstructor(null).newInstance(null);
weight.set(obj,180.6);
System.out.println(obj);//Person { id = 0, name = null, weight = 180.6, height = 0.0 }
//能不能对私有化的属性进行赋值
//进行暴力反射就可以对私有化的属性进行赋值
name.setAccessible(true);
name.set(obj,"赵六");
System.out.println(obj);//Person { id = 0, name = 赵六, weight = 180.6, height = 0.0 }
}
}
2.5: 获取main方法并使用
获取main方法实例如下
①创建一个类Test
代码如下(示例):
package com.qfedu.java;
public class Test {
public static void main(String[] args) {
System.out.println("我是main方法");
}
}
②获取Test类里面的Main方法并使用
代码如下(示例):
package com.qfedu.java;
import java.lang.reflect.Method;
public class Demo5{
public static void main(String[] args) {
try {
//1、获取HeroPlus对象的字节码
Class clazz = Class.forName("com.qfedu.java.Test");
//2、获取main方法,第一个参数:方法名称,第二个参数:方法形参的类型,
Method methodMain = clazz.getMethod("main", String[].class);
//3、调用main方法
// methodMain.invoke(null, new String[]{"a","b","c"});
//第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
//这里拆的时候将 new String[]{"a","b","c"} 拆成3个对象。所以需要将它强转。
methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一
// methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果为: