一. 反射
1. 反射的引入
Java中编译类型有两种:
- 静态编译:在编译时确定类型,绑定对象即通过。
- 动态编译:运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以减低类之间的耦合性。
一句话概括就是使用反射可以赋予jvm动态编译的能力,否则类的元数据信息只能用静态编译的方式实现,例如热加载,Tomcat的classloader等等都没法支持。
在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法。
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
2. 反射机制的相关类
类名 | 用途 |
---|---|
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的成员变量(成员变量也称为类的属性) |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
以下关于反射的使用例子参考
获得反射各类的方法参考
3. 获取Class对象
- getClass方法
- Class.forName
(1)getClass方法
String name = "Huanglinqing"; //获取某个类,就写某个的 的【包名.类名】
Class c1 = name.getClass();
System.out.println(c1.getName());
//输出:java.lang.String
(2)Class.forName
String name = "java.lang.String";//获取某个类,就写某个的 的【包名.类名】。
Class c1 = null;
try {
c1 = Class.forName(name);
System.out.println(c1.getName());
} catch (ClassNotFoundException e) {
}
//输出:java.lang.String
4. 获取类的构造函数
- 获取类的所有构造方法:getDeclaredConstructors()
- 获取类中特定的构造方法:通过getDeclaredConstructor()方法传参获取特定参数类型的构造方法
- 调用私有构造方法:设置constructors.setAccessible(true)
- 调用构造方法:借助于newInstance方法
为便于测试,定义一个测试类:
Test类中我们定义是三个私有变量,生成两个公有的含参构造方法和一个私有的含参构造方法以及一个公有的无参构造方法。
public class Test {
private int age;
private String name;
private int testint;
public Test(int age) {
this.age = age;
}
public Test(int age, String name) {
this.age = age;
this.name = name;
}
private Test(String name) {
this.name = name;
}
public Test() {
}
}
(1)获取类的所有构造方法:getDeclaredConstructors(),包括私有方法
//获取类的所有构造方法
Test test = new Test();
Class c = test.getClass();
Constructor[] constructors = c.getDeclaredConstructors() ;
//具体想要输出看看结果:可以参考上面的博客。
(2)获取类中特定的构造方法:通过getDeclaredConstructor()方法传参获取特定参数类型的构造方法
Test test = new Test();
Class c = test.getClass();
//获取无参构造方法直接不传参数
Constructor c1 = c.getDeclaredConstructor();
//想获取有两个参数分别为int和String类型的构造方法,
Constructor c2 = c.getDeclaredConstructor(int.class,String.class);
(3)调用私有构造方法:设置constructors.setAccessible(true)
参考
(4)调用构造方法:借助于newInstance方法
constructors = c.getDeclaredConstructor(int.class,String.class);
constructors.newInstance(24,"HuangLinqing");
那么调用私有构造方法呢,和上面一样,只是我们要设置constructors.setAccessible(true);
代码如下:
constructors = c4.getDeclaredConstructor(String.class);
constructors.setAccessible(true);
constructors.newInstance("HuangLinqing");
5. 获取普通方法
- 获取所有方法(包括父类):
getMethods()
,获取包括自身和继承过来的所有的public方法 - 获取本类的所有方法:
getDeclaredMethods()
,:获取自身所有的方法(不包括继承的,和访问权限无关),可以获得私有的方法。 - 根据方法名获取特定的方法:
getMethod(“方法名字”, 方法参数)
,表示调用指定的一个公共的方法(包括继承的) - 通过invoke执行非私有方法:method.invoke(类的实例化对象),
Object invoke(Object instance, Object... parameters);
- 通过invoke执行私有方法:
method.invoke(类的实例化对象)
,设置method.setAccessible(true);
(1)获取所有方法(包括父类):getMethods()获取包括自身和继承过来的所有的public方法
//甚至连Object的方法都获取到了
Method[] methods = cls.getMethods();
(2)获取本类的所有方法:getDeclaredMethods(),包括私有方法:获取自身所有的方法(不包括继承的,和访问权限无关)
Method[] methods = cls.getDeclaredMethods();
(3)根据方法名获取特定的方法:getMethod(“方法名字”,参数列表),表示调用指定的一个公共的方法(包括继承的)
Method methods = cls.getMethod("toString");//如果方法需要传参,就在后面传入参数。
(4)通过invoke执行非私有方法:method.invoke(类的实例化对象)
//package net.xsoftlab.baike;
import java.lang.reflect.Method;
public class Main34 {
public void run(){
System.out.println("调用Person类的run方法");
}
public void Speak(int age, String name){
System.out.println("调用Person类的Speak方法");
System.out.println("age -> " + age + ". name -> " + name);
}
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("Main34");
// 调用Person类中的run方法
Method method = clazz.getMethod("run");
method.invoke(clazz.newInstance());
// Java 反射机制 - 调用某个类的方法1.
// 调用Person的Speak方法
method = clazz.getMethod("Speak", int.class, String.class);
method.invoke(clazz.newInstance(), 22, "小明");
// Java 反射机制 - 调用某个类的方法2.
// age -> 22. name -> 小明
}
}
/*
调用Person类的run方法
调用Person类的Speak方法
age -> 22. name -> 小明
*/
(5)通过invoke执行私有方法:
import java.lang.reflect.Method;
public class Main34 {
private void welcome(String name){
System.out.println(name);
}
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("Main34");
Method method = clazz.getDeclaredMethod("welcome", String.class);//记住要获得私有方法,得通过这种方式,不能使用getMethod方法
method.setAccessible(true);
method.invoke(clazz.newInstance(),"Bob");
}
}
6. 获得类的属性
getFields
获取类中以及父类中所有的public属性Field[] fields = 类名.getDeclaredFields();
获取自己类中所有属性,包括私有的和保护的属性- 获得子类以及父类中public属性的某一个属性:
getField
- 获取自己类中所有类型public;private等所有属性中的某一个属性
getDeclaredField
(1)getFields
获取类中以及父类中所有的public属性
public class Main33{
public int age;
private String name;
protected int pro;
}
public class Main34 extends Main33{
public int n;
private String son;
protected String proson;
private Class clazz;
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("Main34");
Field[] fields = clazz.getFields();
for(int i=0;i<fields.length;i++){
System.out.println(fields[i]);
}
}
}
/*
public int Main34.n
public int Main33.age
*/
(2)Field[] fields = 类名.getDeclaredFields();
获取自己类中所有属性,包括私有的和保护的属性
上述代码只改变一句话:Field[] fields = clazz.getDeclaredFields();
输出为:
public int Main34.n
private java.lang.String Main34.son
protected java.lang.String Main34.proson
(3)获得子类以及父类中public属性的某一个属性:getField
public class Main33{
public int age;
private String name;
protected int pro;
}
public class Main34 extends Main33{
public int n;
private String son;
protected String proson;
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("Main34");
Field f = clazz.getField("n");
System.out.println(f);
Field f1 = clazz.getField("age");
System.out.println(f1);
}
}
/*
public int Main34.n
public int Main33.age
*/
(4)获取自己类中所有类型public;private等所有属性中的某一个属性getDeclaredField
public class Main34 extends Main33{
public int n;
private String son;
protected String proson;
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("Main34");
Field f = clazz.getDeclaredField("son");
System.out.println(f);
}
}
//private java.lang.String Main34.son