java反射机制原理和使用
反射的机制原理和使用
反射概况:
反射是什么我们首先不谈,直接先讲一讲反射能干什么。比如一个类,里面有私有方法,私有属性,还有私有的构造函数,这三样东西,哪怕我们已经取到了这个类的一个实例对象,但是还是无法根据这个对象去调用和取值的(类中没有get,set方法)。那么反射就可以做到,只要我们有一个实例,我们就可以调用里面的所有私有方法,和私有属性,当然也包括公有级别的。
实例:
1.根据对象获取类名
创建一个String对象,用getClass()获取类, getName()获取类名,getSuperclass()获取父类
String str = "hello";
Class aClass = str.getClass();//class java.lang.String
System.out.println(aClass.getName());//java.lang.String
System.out.println(aClass.getSuperclass());//class java.lang.Object
2.基本属性的TYPE
/**
* 基本属性的Type 和class
*/
Class cx1 = int.class;//这种就只有class
Class cx2 = Double.TYPE;//包装器就有TYPE
System.out.println(cx1);//int
System.out.println(cx2);//double
3.Class.forName()方法
其实就是根据类名来初始化,动态加载和创建Class 对象 返回的是一个class对象,那么这个有什么用呢?其实就是后面我们可以根据得到的这个class对象来实例化!实例化用的方法是newInstance(),这个后面也会讲到
总的来说就是根据类名,得到一个class对象,我们再根据这个class对象来实例化
String str3 = "java.lang.String";//必须是类名
Class c2 = null;
try{
Class c2 = Class.forName(str3);//返回的就是一个class了
Object o = c2.newInstance();//实例化
System.out.println(c2.getName());
System.out.println(c2.getSuperclass());//获取父类
}catch (Exception e){
System.out.println(e);
}
4.获取类的构造函数
1)获取所有的构造函数
这里我们定义个实体类,里面有五个不同的构造函数:四个公有,一个私有
public class Test {
private int age;
private String name;
private int testint;
public Test(int age, String name, int testint){
this.age = age;
this.name = name;
this.testint = testint;
System.out.println("name: "+name+" age: "+age+" testint: "+testint);
}
public Test(int age) {
this.age = age;
System.out.println("age: "+age);
}
public Test(int age, String name) {
this.age = age;
this.name = name;
System.out.println("name: "+name+" age: "+age);
}
private Test(String name) {
this.name = name;
System.out.println("name: "+name);
}
public Test() {
}
}
这里我们来获取这个类所有的构造方法:
- 这里是首先我们实例化一个对象test,然后用getClass()获取它的类名。
- 定义一个构造器数组来接收该类所有的构造函数。之后进行遍历,
- getDeclaredConstructors()用来获取类所有的构造函数
- getModifiers()方法其实返回的是一个整形数字,这个整形数字代表了这个方法是什么级别的(公有,私有等),之后用Modifier.toString()方法来根据这个整形数值来输出public,private等信息。
- getParameterTypes()方法获取方法的参数列表,返回值是一个class数组,最后进行输出
我们可以看到结果,的确是将所有的构造方法都获取到了!
/**
* 获取类的所有构造方法
*/
Test test = new Test();
Class cn1 = test.getClass();
Constructor[] constructor;//用来接收构造方法的构造器数组
constructor = cn1.getDeclaredConstructors();//返回类的所有构造方法
for(int i = 0; i<constructor.length; i++){
//System.out.println(constructor[i].getModifiers());//这里返回的是一个int
//Modifier中的toString方法根据传回来的int来判断方法是什么类型
System.out.print("构造函数类型:"+
Modifier.toString(constructor[i].getModifiers())
+" 参数类型: ");
Class[] parameterTypes = constructor[i].getParameterTypes();//获取方法参数列表
for(int j = 0; j < parameterTypes.length; j++){
System.out.print(parameterTypes[j].getName());
}
System.out.println();
}
//输出如下
构造函数类型:public 参数类型:
构造函数类型:private 参数类型: java.lang.String
构造函数类型:public 参数类型: int java.lang.String
构造函数类型:public 参数类型: int
构造函数类型:public 参数类型: int java.lang.String int
2)获取指定级别的构造函数(这里只获取public的)
其实getConstructors()方法就是只获取public的构造函数,所以跟上面没什么区别
/**
* 根据指定的构造方法类型来获取(这里获取的是public级别的)
*/
Test test1 = new Test();
Class cn2 = test1.getClass();
Constructor[] constructors1;
constructors1 = cn2.getConstructors();
for(int i = 0; i<constructors1.length; i++){
//Modifier中的toString方法根据传回来的int来判断方法是什么类型
System.out.print("构造函数类型:"+
Modifier.toString(constructors1[i].getModifiers())
+" 参数类型: ");
Class[] parameterTypes1 = constructors1[i].getParameterTypes();
for(int j = 0; j < parameterTypes1.length; j++){
System.out.print(parameterTypes1[j].getName()+" ");
}
System.out.println();
}
//结果:没有私有的
构造函数类型:public 参数类型:
构造函数类型:public 参数类型: int java.lang.String
构造函数类型:public 参数类型: int
构造函数类型:public 参数类型: int java.lang.String int
3)获取指定参数列表的构造方法
我们可以通过getDeclaredConstructor()方法传参获取特定参数类型的构造方法,这里注意是getDeclaredConstructor()不是 getDeclaredConstructors() ,所以返回的是一个Class对象而不是一个Class数组。
注意传参即可~!获取无参构造方法直接不传参数
/**
* 获取指定参数的构造方法
*/
Test test2 = new Test();
Class cn3 = test2.getClass();
try{
Constructor constructor1 =cn3.getDeclaredConstructor(int.class,String.class);
System.out.print("构造函数类型:"+
Modifier.toString(constructor1.getModifiers())
+" 参数类型: ");
Class[] parameterTypes = constructor1.getParameterTypes();
for(int j = 0; j < parameterTypes.length; j++){
System.out.print(parameterTypes[j].getName()+" ");
}
}catch (Exception e){
System.out.println(e);
}
4)调用构造方法
为了方便查看是否调用了,我们将构造方法中进行输出!
//公有构造方法
public Test(int age, String name, int testint){
this.age = age;
this.name = name;
this.testint = testint;
System.out.println("name: "+name+" age: "+age+" testint: "+testint);
}
//私有构造方法
private Test(String name) {
this.name = name;
System.out.println("name: "+name);
}
调用公有构造方法:
我们可以看到,首先获取相应的构造函数,然后用newInstance()方法实例化,只要参数列表对了,就直接调用了类的公有构造方法。
/**
* 调用指定的public构造方法
* newInstance实例化
*/
Test test3 = new Test();
Class cn4 = test3.getClass();
try{
//只要在这修改参数就可以知道是得到的是哪个构造函数
Constructor cons = cn4.getDeclaredConstructor(int.class,String.class,int.class);
Object o = cons.newInstance(24, "fanglei",30);//用newInstance方法,得到的是一个对象
System.out.println(o.toString());
}catch (Exception e){
System.out.println(e);
}
//输出
name: fanglei age: 24 testint: 30
Test{age=24, name='fanglei', testint=30}
调用私有构造方法:
这里和上面是一样的,只不过要多一项cons.setAccessible(true);
/**
* 调用私有的构造方法 要设置constructors.setAccessible(true)
*/
Test test4 = new Test();
Class cn5 = test4.getClass();
try{
Constructor cons = cn5.getDeclaredConstructor(String.class);
cons.setAccessible(true);
Object o = cons.newInstance("刘军");
System.out.println(o.toString());
}catch (Exception e){
System.out.println(e);
}
//输出
name: 刘军
Test{age=0, name='刘军', testint=0}
5.获取和调用类的私有成员方法
/**
* 私有的成员函数
* @return
*/
private void welcome(String msg){
System.out.println("传入的msg是: "+msg);
}
这里和上面就有不同了,这里用的是getDeclaredMethod()函数,第一个参数是方法名,第二个函数参数列表,这时候我们用的就是Method来接收一个方法。然后通过setAccessible(true)来设置,用invoke()方法调用私有方法,第一个参数是对象(也就是调用哪个实例对象的方法),第二个参数就是私有方法的参数列表。
/**
* 调用私有的成员函数
*/
Class[] p ={String.class};
Test test5 = new Test();
Class cn6 = test5.getClass();
try{
//得到私有方法 前面是方法名,后面参数类型
Method method = cn6.getDeclaredMethod("welcome", String.class);
method.setAccessible(true);//设置
Object msg[] = {"hello world"};//参数
method.invoke(test5,msg);//用invoke调用方法
}catch (Exception e){
System.out.println(e);
}
//输出
传入的msg是: hello world
6.获取私有字段并修改值
这里要清楚,如果没有set,get方法,我们不可能通过一个类的实例去获取和修改类的私有属性的!
这里我们用getDeclaredField方法来获取属性字段,参数是属性名,然后setAccessible(true)设置,之后用set方法来修改属性名,第一个参数是对象名,第二个是参数
/**
* 获取类私有字段并修改值
*/
Test test6 = new Test();
Class cn7 = test6.getClass();
try{
Field field = cn7.getDeclaredField("name");
field.setAccessible(true);
field.set(test6,"fanglei");
System.out.println(test6.toString());
}catch (Exception e){
System.out.println(e);
}
//输出
Test{age=0, name='fanglei', testint=0}
总结:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
至于反射里面的其他方法,可以参考这篇文章:
https://www.jianshu.com/p/9be58ee20dee
本篇文章的内容是根据这篇博客打出来的,并不是复制,虽然基本相同,但是都是我手动实现的,为了自己学习的更加深刻: