反射的核心就是四个类,Class,Method,Constructor,Filed,这四个类分别对应类,类的方法,类的构造方法,类的成员变量,需要先获得Class对象才能获得剩下的三个类对象
一.Class类(阅读注意区别C的大小写)
我们都知道,我们编写好的java代码文件时.java文件,要想运行这个.java文件,就要通过编译器编译,编译器会讲.java文件编译成.class文件,并且每一个类对应一个.class文件,就算一个.java文件中,定义了两个类,编译器就会将一个java文件,编译成两个.class文件.
Class类对象的内容,就是这些class文件,每一个Class类对象的内容就是其他类所有的方法,变量;现在还是有点抽象,下面的代码可以帮助理解
public static void main(String[] args){
Class<?> c=String.class;
//c这个Class对象获得String类的所有信息(类名、父类、实现的接口、构造方法、字段、
//方法等),我们可以通过这个对象来获取String类的相关信息。
}
Class对象的获取方法有三种
(1)通过实例化出来的对象调用getClass()方法获取;
public static void main(String[] args){
String s=new String ();
Class<?> cls=s.getclass();
//通过实例化的对象获得Class对象
}
(2)通过类名.class获取;
public static void main(String[] args){
Class<?> c=String.class;
//通过类名获取
}
(3)通过Class的类方法forName("文件路径")获取,这是最常用的方法;
public static void main(String[] args){
try {
Class<?> c = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
System.out.println("Class not found: " + e.getMessage());
}
//通过Class类方法forName获取,forName方法会抛出异常,需要用try,catch处理
}
这个路径和声明(import)某个类的路径是一样的;
获得Class类对象后,就可以通过Class对象的newInstance()方法来实例化Class对象指向的类
public static void main(String[] args){
try {
Class<?> c=Class.forName("java.lang.String");
String s1= (String) c.newInstance();
//newInstance的返回类型为object类型,需要强转成String类型
//通过c调用newInstance()实例化String对象
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
二.Constructor类
Constructor类就是用来接收类对象的构造方法的类,每一个Constructor类对象都对应一个构造方法,
Class类获得Constructor对象的方法
getConstructor()和getDeclareConstructor()
getConstructor()方法只能获取到public修饰的构造方法,包括父类的构造方法
getDeclareConstructor()方法获取所有权限的构造器private,protected,public,default,但是不包括父类的构造器,但是当要使用这些不是public权限的构造器时,如果不在该权限所在的类内,需要使用setAccessible(true);方法绕开这些权限的限制才能使用这些构造器
class MyClass {
public MyClass() {}
protected MyClass(int x) {}
MyClass(String s) {} // default访问修饰符
private MyClass(double d) {}
}
public class Main{
public static void main(String[] args){
Class<?> c=Myclass.class;
Constructor con1[]=c.getConstructor();
Constructor con2[]=c.getDeclareConstructor();
System.out.println(con1.length);//打印1
System.out.println(con2.length);//打印4
}
}
也可以用这两个方法来获得特定的构造方法,只需要在括号中传入特定构造函数的参数的class对象
class MyClass {
public MyClass() {}
protected MyClass(int x) {}
MyClass(String s) {} // default访问修饰符
private MyClass(double d) {}
}
public static void main(String[] args){
Class<?> c=Myclass.class;
Constructor con=c.getDeclareConstructor(double.class);
//这样就获取的Myclass类中以double类型为唯一参数的构造函数
}
使用setAccessible方法来绕开权限限制实例化对象
class MyClass {
public MyClass() {}
protected MyClass(int x) {}
MyClass(String s) {} // default访问修饰符
private MyClass(double d) {}
}
public class Main{
public static void main(String[] args){
Class<?> c=Myclass.class;
Constructor con=c.getDeclareConstructor(double.class);
con.setAccessible(true);//设置完后,才能使用con来实例化对象
//constructor实例化对象也是使用newInstance
Myclass m1=(Myclass)con.newInstance(12.0);
}
}
三.Method类
method类获取的就是类中的各种方法(除了构造方法);
实例化Method对象的方法还是需要用class对象调用
getMethod和getDeclareMethod方法,和Constructor类似,有Declare的能获得所有权限的方法,没有的只能获得公共的方法
获得特定方法需要传参数,getMethod(“方法名”,方法参数用逗号隔开);
class MyClass {
public void func1(int a,int b){}
private void func2(int a){}
public static void func3(){}
}
public class Main{
public static void main(String[] args){
Class<?> c=Myclass.class;
//获取func1方法
Method method=c.getMethod("func1",int.class,int.class);
}
}
要想调用func1方法还需要实例化一个真实的Myclass对象,然后调用invoke(对象名,参数,参数)方法就调用了Method对象中存储的Myclass对象的方法;
在Java中,调用一个类的实例方法通常有两种方式:直接使用对象(即obj.method()
)或使用反射机制(即method.invoke(obj, args...)
)。
第一种方式是直接调用,这是一种常规的方式,代码量少,执行速度相对较快。而第二种方式则是通过反射机制来实现方法的动态调用,调用过程需要借助Method
对象、参数列表等信息,使用起来比较繁琐,但具备一定的灵活性和可扩展性。
另外,使用反射机制可以在运行时获取和操作类的成员变量和方法,包括私有成员、静态成员和构造方法等。这使得我们可以通过反射机制实现如框架、插件和代码生成等高级功能,从而增强了程序的灵活性和可定制性。
class MyClass {
public void func1(int a,int b){}
private void func2(int a){}
public static void func3(){}
}
public class Main{
public static void main(String[] args){
Class<?> c=Myclass.class;
//获取func1方法
Method method=c.getMethod("func1",int.class,int.class);
//实例化一个Myclass对象
MyClass my=new MyClass();
//通过invoke方法调用类对象的方法
method.invoke(my,10,10);
}
}
至于获取私有权限的方法和调用私有权限的方法和Constructor差不多,都是使用setAccessible(true)的方式
获取静态方法的过程也和上面的代码差不多,只有一点细微差别,就是因为是静态方法,所以在invoke调用的时候不需要对象,在对象的那个位置用null即可,即:
method.invoke(null,10,10);
Method方法还能获取到方法的参数类型,返回类型(getParameterTypes()、getReturnType())等
四.Field类
Field
类是 Java 反射机制中的一种重要类型,用于表示类的成员变量(也称为属性或字段)。通过 Field
对象可以获取、设置对象的某个字段的值,也可以获取、设置静态变量的值。
下面简单介绍一下 Field类中常用的几个方法:
-
getField(String name)
:获取指定名称的 public 字段。如果该字段不是 public 的,则抛出NoSuchFieldException
异常。 -
getDeclaredField(String name)
:获取指定名称的字段,可以是 public、protected、default 或 private 访问级别的字段。如果没有找到指定字段,则抛出NoSuchFieldException
异常。 -
getName()
:获取字段的名称。 -
getType()
:获取字段的类型,返回Class
类型的对象。 -
getModifiers()
:获取字段的修饰符,返回一个整数值,每个修饰符都对应一个特定的二进制位,可通过位运算进行判断和解析。
除此之外,还有其他一些方法可以用于获取或设置字段的值,例如:
-
get(Object obj)
:获取指定对象的字段值。 -
set(Object obj, Object value)
:设置指定对象的字段值为给定值。
需要注意的是,如果要访问非 public 的字段,需要先通过 setAccessible(true)
方法将其设为可访问状态,否则会抛出 IllegalAccessException
异常。