欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java反射机制原理和使用

程序员文章站 2024-01-21 21:05:40
...

反射的机制原理和使用

反射概况:

反射是什么我们首先不谈,直接先讲一讲反射能干什么。比如一个类,里面有私有方法,私有属性,还有私有的构造函数,这三样东西,哪怕我们已经取到了这个类的一个实例对象,但是还是无法根据这个对象去调用和取值的(类中没有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

本篇文章的内容是根据这篇博客打出来的,并不是复制,虽然基本相同,但是都是我手动实现的,为了自己学习的更加深刻:

https://blog.csdn.net/huangliniqng/article/details/88554510