目录
一、反射的基本概念
1、反射的定义
概念:反射就是把Java类中的各个组成部分映射成相应的Java类。
简单来说,Java反射机制指的是程序在运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。为了更好的学习反射,我们需要了解两个概念:编译期和运行期。
编译期:把Java代码编译成class文件的过程。编译器只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作。比如:检查语法错误。
运行期:把编译后的文件交给计算机执行,直到程序运行结束。
二、Class类
1、获取Class实例的三种方法
方法一:直接通过一个class的静态变量class获取。
Class cls = String.class;
方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()获取。
String s = "Hello";
Class cls = s.getClass();
方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName获取。
Class cls = Class.forName("java.lang.String");
2、Class常用方法
类型 | 访问方法 | 返回值类型 | 说明 |
---|---|---|---|
包路径 | getPackage() | Package对象 | 获取该类的存放路径 |
类名称 | getName() | String对象 | 获取该类的名称 |
继承类 | getSuperclass() | Class对象 | 获取该类继承的类 |
实现接口 | getInterfaces() | Class型数组 | 获取该类实现的所有接口 |
构造方法 | getConstructors() | Constructor型数组 | 获取所有权限为public的构造方法 |
getDeclaredConstructors() | Constructor对象 | 获取当前对象的所有构造方法 | |
方法 | getMethods() | Methods型数组 | 获取所有权限为public的方法 |
getDeclaredMethods() | Methods对象 | 获取当前对象的所有方法 | |
成员变量 | getFields() | Field型数组 | 获取所有权限为public的成员变量 |
getDeclaredFields() | Field对象 | 获取当前对象的所有成员变量 |
三、调用构造方法
1、Constructor类
为了调用任意的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息,可以创建一个实例。Constructor对象是一个构造方法,调用结果总是返回实例。
public class Main {
public static void main(String[] args) throws Exception {
// 获取构造方法
Constructor cons1 = Integer.class.getConstructor(int.class);
// 调用构造方法
Integer n1 = (Integer) cons1.newInstance(123);
System.out.println(n1);
// 获取构造方法
Constructor cons2 = Integer.class.getConstructor(String.class);
Integer n2 = (Integer) cons2.newInstance("456");
System.out.println(n2);
}
}
四、获取继承关系
1、获取父类的Class
public class Main {
public static void main(String[] args) throws Exception {
Class i = Integer.class;
Class n = i.getSuperclass();
System.out.println(n);//class java.lang.Number
Class o = n.getSuperclass();
System.out.println(o);//class java.lang.Object
System.out.println(o.getSuperclass());//null
}
}
运行代码发现,Integer的父类类型是Number,Number的父类是Object,Object的父类是null。除Object外,其他任何非interface的Class都必定存在一个父类类型。
2、获取interface
我们通过Class就可以查询到实现的接口类型。
Class s = Integer.class;
Class[] is = s.getInterfaces();
for (Class i : is) {
System.out.println(i);//interface java.lang.Comparable
}
3、继承关系
调用isAssignableFrom()判断向上转型是否成立。
System.out.println("Interger=>Integer:"+
Integer.class.isAssignableFrom(Integer.class));//true
System.out.println("Interger=>Double:"+
Double.class.isAssignableFrom(Integer.class));//false
System.out.println("Interger=>Number:"+
Number.class.isAssignableFrom(Integer.class));//true
System.out.println("Number=>Integer:"+
Integer.class.isAssignableFrom(Number.class));//false
System.out.println("Interger=>Comparable:"+
Comparable.class.isAssignableFrom(Integer.class));//true
五、访问字段
1、获取Field字段
- getName():返回字段名称。
- getType():返回字段类型,也是一个Class实例。
- getModifiers:返回字段的修饰符,它是一个int。
Class cls = obj.getClass();
Field[] fields = cls.getDeclaredFields();
for(Field field : fields) {
System.out.println("访问修饰符:"+field.getModifiers());
System.out.println("访问修饰符:"+Modifier.toString(field.getModifiers()));
System.out.println("成员变量类型:"+field.getType());
System.out.println("成员变量名称:"+field.getName());
if(!field.isAccessible()) {
field.setAccessible(true);
}
System.out.println("成员变量内容:"+field.get(obj));
System.out.println();
六、调用方法
1、Method类
- getName():返回方法名称。
- getReturnType():返回方法返回值类型,也是一个Class实例。
- getParameterTypes():返回方法的参数类型,是一个Class数组。
- getModifiers():返回方法的修饰符,它是一个int,不同的value表示不同的访问修饰符。
Class cls = Order.class;
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods) {
System.out.println("访问修饰符:"+Modifier.toString(method.getModifiers()));
System.out.println("返回值类型:"+method.getReturnType());
System.out.println("方法名称:"+method.getName());
System.out.println();
Parameter[] params = method.getParameters();
for(Parameter par : params) {
System.out.println(par.getName());
System.out.println(par.getType());
System.out.println();
}
}
2、调用方法
//获取Class对象
Class cls = Base.class;
Object obj = cls.newInstance();//创建Base对象
//按照方法名称和“参数类型”获取Method方法对象
Method method = cls.getMethod("create",int.class);
//Method对象的invoke()作用
//以反射的方式执行create()方法
int b = (int) method.invoke(obj, 1000);
System.out.println(b);
class Base{
public void create() {
System.out.println("随机数为10");
}
public int create(int a) {
return (int)(Math.random()*a);
}
}
3、调用静态方法
如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null。
//计算以10为底的对数
//获取某个指定数字的位数
System.out.println((int)Math.log10(123456789)+1);//9
//反射调用
Class cls = Math.class;
Method methodLog10 = cls.getMethod("log10", double.class);
int size = Double.valueOf((double)methodLog10.invoke(null, 12345552)).intValue() + 1;
System.out.println(size);//8
4、调用非public方法
为了调用非public方法,我们通过Method.setAccessible(true)允许其调用。
public static void main(String[] args) throws Exception {
Person p = new Person();
Method m = p.getClass().getDeclaredMethod("setName", String.class);
m.setAccessible(true);
m.invoke(p, "Bob");
System.out.println(p.name);
}
class Person {
String name;
private void setName(String name) {
this.name = name;
}
}
5、多态
使用反射调用方法时,仍然遵循多态原则。
public class Main {
public static void main(String[] args) throws Exception {
// 获取Person的hello方法
Method h = Person.class.getMethod("hello");
// 对Student实例调用hello方法
h.invoke(new Student());
}
}
class Person {
public void hello() {
System.out.println("Person:hello");
}
}
class Student extends Person {
public void hello() {
System.out.println("Student:hello");
}
}