反射是Java的特征之一,是一种间接操作目标对象的机制。Java反射机制是在与运行状态中,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为Java的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
反射就是把Java类中的各个成分映射成一个个的Java对象。例如:一个类有:成员变量,方法,构造方法和包等信息。利用反射技术可以对一个类进行解刨,把个个组成部分映射成一个个对象。
类名 | 用途 |
---|---|
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的成员变量(成员变量也称为类的属性) |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
方法 | 用途 |
---|---|
asSubclass(Class clazz) | 把传递的类的对象转换成代表其子类的对象 |
Cast | 把对象转换成代表类或是接口的对象 |
getClassLoader() | 获得类的加载器 |
getClasses() | 返回一个数组,数组中包含该类中所有公共类和接口类的对象 |
getDeclaredClasses() | 返回一个数组,数组中包含该类中所有类和接口类的对象 |
forName(String className) | 根据类名返回类的对象 |
getName() | 获得类的完整路径名字 |
newInstance() | 创建类的实例 |
getPackage() | 获得类的包 |
getSimpleName() | 获得类的名字 |
getSuperclass() | 获得当前类继承的父类的名字 |
getInterfaces() | 获得当前类实现的类或是接口 |
方法 | 用途 |
---|---|
getField(String name) | 获得某个公有的属性对象 |
getFields() | 获得所有公有的属性对象 |
getDeclaredField(String name) | 获得某个属性对象 |
getDeclaredFields() | 获得所有属性对象 |
方法 | 用途 |
---|---|
getAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的公有注解对象 |
getAnnotations() | 返回该类所有的公有注解对象 |
getDeclaredAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的所有注解对象 |
getDeclaredAnnotations() | 返回该类所有的注解对象 |
方法 | 用途 |
---|---|
getConstructor(Class...> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructor(Class...> parameterTypes) | 获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() | 获得该类所有构造方法 |
方法 | 用途 |
---|---|
getMethod(String name, Class...> parameterTypes) | 获得该类某个公有的方法 |
getMethods() | 获得该类所有公有的方法 |
getDeclaredMethod(String name, Class...> parameterTypes) | 获得该类某个方法 |
getDeclaredMethods() | 获得该类所有方法 |
方法 | 用途 |
---|---|
isAnnotation() | 如果是注解类型则返回true |
isAnnotationPresent(Class extends Annotation> annotationClass) | 如果是指定类型注解类型则返回true |
isAnonymousClass() | 如果是匿名类则返回true |
isArray() | 如果是一个数组类则返回true |
isEnum() | 如果是枚举类则返回true |
isInstance(Object obj) | 如果obj是该类的实例则返回true |
isInterface() | 如果是接口类则返回true |
isLocalClass() | 如果是局部类则返回true |
isMemberClass() | 如果是内部类则返回true |
Field代表类的成员变量(成员变量也称为类的属性)。
方法 | 用途 |
---|---|
equals(Object obj) | 属性与obj相等则返回true |
get(Object obj) | 获得obj中对应的属性值 |
set(Object obj, Object value) | 设置obj中对应属性值 |
Method代表类的方法。
方法 | 用途 |
---|---|
invoke(Object obj, Object... args) | 传递object对象及参数调用该对象对应的方法 |
Constructor代表类的构造方法。
方法 | 用途 |
---|---|
newInstance(Object... initargs) | 根据传递的参数创建类的对象 |
在程序运行期间,Java运行时系统始终为所有对象维护一个被称为被运行时的类型标识。这个信息追踪这每个对象属于的类。虚拟机利用运行时类型信息选择相应的方法执行。然而,可以通过专门的类访问这些信息,保存这些信息的类被称为Class。
Class类在Java中的API详解:
Class类的实例表示正在运行的Java应用程序中的类和接口。也就是JVM中有N多的实例每个类都有该Class对象(包含基本数据类型)。Class没有公共构造方法。Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass方法自动构造的。不需要我们自己创建,JVM已经帮我们创建好了。
先写一个Person1类:
package test;
public class People1 {
public String name;
public int age;
// public void People(String name,int age)
// {
// this.age=age;
// this.name=name;
// }
}
获取类对象有三种方式:
在JVM中,一种类,只会有一个类对象存在。所以上面三种方法取出来的类对象,都是一样。虚拟机为每个类型管理一个Class对象。因此可以利用==运算符实现两个类对象比较的操作。
package test;
public class ObjectTest {
public static void main(String[] args) {
String className="test.People1";
try{
Class pClass1=Class.forName(className);
Class pClass2=People1.class;
Class pClass3=new People1().getClass();
System.out.println(pClass1==pClass2);
System.out.println(pClass2==pClass3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出结果为:
三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。
2.利用反射机制创建对象
与传统的通过new 来获取对象的方式不同。反射机制会先拿到People1的“类对象”,然后通过类对象获取“构造器对象”,再通过构造器对象创建一个对象。具体步骤:
1.获取类对象 Class class=Class.forName("test.People1");
2.获得构造器对象 Constructor con=class.getConstructor(形参.class);
3.获得对象 People1 people=com.newInstance(实参);
当构造方法不是无参构造方法时,获得构造器对象略有不同。
package test;
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 aClass=Class.forName("test.People1");
//2.获得所有公有构造方法
System.out.println("*************所有公有构造方法*****");
Constructor[] conArray=aClass.getConstructors();
for(Constructor c:conArray)
{
System.out.println(c);
}
System.out.println("*************所有构造方法(包括:私有,受保护,默认,公有)*****");
conArray=aClass.getDeclaredConstructors();
for(Constructor c:conArray)
{
System.out.println(c);
}
System.out.println("*************获得(公有,无参数)构造方法*****");
Constructor con=aClass.getConstructor(null);
// 因为是无参数的构造方法,所以类型是null,不写也可以.
//返回的是面熟这个无参构造函数的类对象
System.out.println("con"+con);
//调用构造方法
Object object=con.newInstance();
System.out.println("*************获得私有构造方法*****");
con=aClass.getDeclaredConstructor(Integer.class);
System.out.println("con"+con);
con.setAccessible(true);
//调用构造方法
object=con.newInstance(100);
}
}
输出结果是:
3.获得成员变量并使用
基本步骤:
//1.获得类对象 上面的new 方法
//2.获得属性 Field f1=h.getDeclardField("属性名");
//3.修改属性 f1.set(h,实参);
在上面与反射机制相关类中详细介绍了方法,可以参考上面的。
注意:getField只能获取public的,包括从父类继承的字段。getDeclaredField可以获得本类所有的字段,包括private,但是不能获取继承的字段(只能获取private的字段,但不能访问,除非加上setAccessible(true))。
4.获得成员方法并使用
1.获得类对象 h
2.获取成员方法
3.调用方法
Method-->public Object invoke(Object obj,Object..args);
参数说明:
第一个参数是隐式参数,其余对象提供了显式参数。
对于静态方法,第一个参数可以忽略,既可以设置为null
obj:要调用方法的对象
args:调用方法时所传递的实参
如果返回类型是基本类型,invoke方法会返回其包装器类型
第二个步骤获取成员方法在上面与反射机制相关类中详细介绍了,可以参考上面。利用下面的代码改变属性:
package test;
public class PeoplePlus {
public String name;
public Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public PeoplePlus(){
}
public PeoplePlus(String string)
{
name=string;
}
@Override
public String toString()
{
return "People [name="+name+"]";
}
}
package test;
import java.lang.reflect.Field;
public class ParaTest {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
PeoplePlus peoplePlus=new PeoplePlus();
peoplePlus.name="Marry";
Field field=peoplePlus.getClass().getDeclaredField("name");
field.set(peoplePlus,"henrr");
System.out.println(peoplePlus.name);
}
}
输出结果为:
从输出结构可以发现,属性值发生了改变。
5.获得main方法并使用
给PeoplePlus添添加main方法:
package test;
public class PeoplePlus {
public String name;
public Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public PeoplePlus(){
}
public PeoplePlus(String string)
{
name=string;
}
@Override
public String toString()
{
return "People [name="+name+"]";
}
public static void main(String[] args) {
System.out.println("执行main方法");
}
}
通过以下步骤获得main方法
package test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MainTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//1.获得对象的字节码
Class aclass=Class.forName("test.PeoplePlus");
//2.获得main方法,第一个参数:方法名称 第二个参数:方法形参的类型
Method methodMain=aclass.getMethod("main", String[].class);
//3.调用Main方法
//methodMain.invoke(null,new String[]{"a","b","c"});
//第一个参数,对象类型,因为方法是static静态的,所以null可以.第二个参数时String数组
//这里拆开的时候,拆成三个对象,需要强制转换。
methodMain.invoke(null,(Object)new String[]{"a","b","c"});
}
}
输出结果:
参考文章:
https://blog.csdn.net/sinat_38259539/article/details/71799078#commentBox