说明:反射就是把Java类中的各种成分映射成该成分对应的Java类。
一,Class类
Class类对象为内存中类的字节码,例如:String.class
1.获取类的Class对象
方法一:
Class.forName("java.lang.String"):
1)若该类已加载,则直接返回该类字节码。
2)若该类不在Java虚拟机内,则会先通过类加载器加载该类,然后返回该类字节码。
方法二:
类名.class
方法三:
对象.getClass()
public class Test {
public static void main(String[] args) throws Exception{
//获取方法Class对象的三种方法
Class cls1=Class.forName("java.lang.String");
Class cls2=String.class;
Class cls3=new String().getClass();
}
}
2.一个类被类加载器加载到内存中,占用一片存储空间,空间内存放内容就是类的字节码,不同类的字节码不同,所以它们在内存中的内容页不同,这些字节码就是Class类的一个个对象。
注意:基本数据类型的Class对象和数据包装类不是同一类。
例如int.class和Integer.class不是同一个对象。
Integer.TYPE和int.class是同一个字节码文件。
public class Test {
public static void main(String[] args) throws Exception{
System.out.println(Integer.class==int.class);
System.out.println(Integer.TYPE==int.class);
}
}
运行结果:
false
true
3.判断Class对象的类型
-----isPrinitive():判断Class对象是否为基本类型
----isArray():判断Class对象是否为数组类
public class Test {
public static void main(String[] args) throws Exception{
//判断Integer类是否为基本类型
System.out.println(Integer.class.isPrimitive());
//判断int.class是否为基本类型
System.out.println(int.class.isPrimitive());
int[] a=new int[3];
//判断一个Class是否为数组类型
System.out.println(a.getClass().isArray());
}
}
运行结果;
false
true
true
二。构造方法的反射:Constructor类
常用方法:
Class类中
----getConstructors():获取Class对象所表示类的所有公有构造方法;
----getComstructor(Class<?>...parameterTypes):获取Class对象的指定公有构造函数
----getDeclaredConstructors():获取Class对象所表示类的所有公有构造方法;
----getDeclaredComstructor(Class<?>...parameterTypes):获取Class对象的指定公有构造函数
Constructor类中
----newInstance(Object...initargs):使用Constructor对象所表示的构造函数创建对象,
import java.lang.reflect.*;
public class Test {
public static void main(String[] args) throws Exception{
//获取String中的指定构造函数,构造函数的参数为字符串类型
Constructor con=String.class.getConstructor(String.class);
//通过Constructor对象创建String类对象
String str=(String) con.newInstance("构造函数的反射");
System.out.println(str);
}
}
注:也可以通过Class类的newInstance方法直接为本类创建一个无参数的构造函数。
三,成员变量的反射:Field类
常见方法:
Class类中
----getFields():获取一个类的公有的所有成员变量;
----getField(String name):获取类中指定的公有成员变量;变量名为name;
import java.lang.reflect.*;
public class Test {
public static void main(String[] args) throws Exception{
//获取指定成员变量
Field field=Student.class.getField("a");
System.out.println(field);
}
}
class Student{
public int a=3;
int b;
public Student(){
}
}
运行结果:
public int Test.Student.a
注意:上面语句获取的成员变量都是属于类的,而不是属于某个对象。如果要获取给类某个指定对象的成员变量值,可以通过Field 类中方法
Field常用方法:
----get(Object obj):获取指定对象的该成员变量值。
----set(Object obj,Object Value):将指定对象中该变量的值设为value。
import java.lang.reflect.*;
public class Test {
public static void main(String[] args) throws Exception{
//获取指定成员变量
Field field=Student.class.getField("age");
Student s=new Student(27);
//获取指定对象的成员变量值
System.out.println(field.get(s));
}
}
class Student{
public int age;
public String name;
public Student(int age){
this.age=age;
}
}
运行结果:‘
27
示例:反射类中的私有成员变量
import java.lang.reflect.*;
public class Test {
public static void main(String[] args) throws Exception{
//获取指定成员变量
Field field=Student.class.getDeclaredField("name");
Student s=new Student(27,"LiSi");
//暴力反射,使私有变量变为可以操作
field.setAccessible(true);
//获取指定对象的成员变量
String name=(String) field.get(s);
System.out.println(name);
}
}
class Student{
private int age;
private String name;
public Student(int age,String name){
this.age=age;
this.name=name;
}
}
运行结果:
LiSi
四,成员方法的反射:Method类
常见方法:
Class类中:
----getMethods():获取类中所有公有方法
----getMethod(String name,Class<?>...parameterTypes):根据方法名和参数类型获取类中指定方法。
Method类中:
----invoke(Object obj,Object ...args):调用指定类的该方法。
如果要调用静态方法则可以写为:
----invoke(null,Object...arg)
import java.lang.reflect.*;
public class Test {
public static void main(String[] args) throws Exception{
//获取方法
Method method=String.class.getMethod("charAt", int.class);
String str="Method.class";
//由指定对象调用该方法
char ch=(char) method.invoke(str, 3);
System.out.println(ch);
}
}
运行结果:
h
注意:如果一个要调用的函数需要的参数是数组类型的参数时,直接将数组给invoke如:
method.invoke(null,new String[]{"abc","def","gh"});
此时会产生错误,因为编译器会自动将new String[]{"abc","def","gh"}拆包成三个元素,而需要传递的数组是一个元素,所以会发生越界异常。
处理方式:
1.将数组作为Object数组的一个元素传递
method.invoke(null,new Object[]{new String[]{"abc","def","gh"}});
2.将数组向上转型为一个Object对象。此时编译器会认为这是一个Object类的对象而不是一个数组,所以不会进行拆包。
method.invoke(null,(Object)new String[]{"abc","def","gh"});
五,数组的反射:Array类
具有相同数据类型和维数的数组共享一个.class文件。
数组的父类是Object类,所以数组可以向上转型为Object类,
基本数据类型不能向上转型为Object类,但是基本数据的数组可以作为整体向上转型为Object类,
import java.lang.reflect.*;
public class Test {
public static void main(String[] args) throws Exception{
int[] arr1=new int[3];
//基本类型的数组整体作为一个对象向上转型为Object类
Object obj1=arr1;
/*Object[] obj2=arr1;
* 上面语句报错,因为单个基本数据类型不能向上转型为Object
*/
int[][] arr2=new int[3][4];
//将二维数组转型为Object数组,也就是讲二维数组中的每个一维数组向上转型为Object类
Object[] obj3=arr2;
String[] str1=new String[]{"dfv","wef","efwf"};
//引用数据类型数组作为整体向上转型为Object类
Object obj4=str1;
//数组黄总每个元素单独向上转型为Object类
Object[] obj5=str1;
}
}
数组反射操作常见方法:
Array类中:
----get(Object array,int index):返回数组中指定位置的值
----set(Object array,int index,Object value):为数组指定位子设置值。
import java.lang.reflect.*;
public class Test {
public static void main(String[] args) throws Exception{
String[] str=new String[]{"dfv","wef","efwf"};
Class cls=str.getClass();
if(cls.isArray()){
//为数组中元素重新赋值
Array.set(str, 0, "abcd");
//获取数组指定位置的元素
String s=(String) Array.get(str, 0);
System.out.println(s);
}
}
}
运行结果:
abcd