发表于 2024-5-14 20:42:15

java反射

java反射

概念

反射就是java中一种可以获取一个对象的类的方法,类可以通过反射拿到所有方法,并且调用。
获取类方法

获取一个类的方法有三种:

[*]对象直接调用getclass()
[*]每个类都有一个静态class属性
[*]Class类自带的静态方法forName(String className)
package java_learn;

import java.io.Serializable;
public class Student implements Serializable{
    private static final long serialVersionUID = -5215701594592700115L;
    private String name;
    private String number;
    private String gender;
    public String capt;
    private transient int grade;
    public Student(String name,String number,String gender,int grade) {
      this.name=name;
      this.number=number;
      this.gender=gender;
      this.grade=grade;
      System.out.println("pp");
    }
    public Student(){
      System.out.println("np");
    }
    public void getInformation(){
      System.out.println("name:"+this.name+" number:"+this.number+" gender:"+this.gender+" grade:"+this.grade);
    }
    public void woparam(){
      System.out.println("无参数");
    }
    public void doparam(String pr){
      System.out.println(pr);
    }
}package learn_java;
import learn_java.Students;

public class reflect {
    publicstatic voidmain(String args[]) throws ClassNotFoundException {
      Students A = new Students("capt","11111","male",2);
      //第一种获取Class的方法
      Class clazz1 = A.getClass();
      System.out.println(clazz1);
      //通过静态属性获取Class
      Class clazz2 = Students.class;
      System.out.println(clazz1 == clazz2);
      //通过类的静态方法获得Class
      Class clazz3 = Class.forName("learn_java.Students");
      System.out.println(clazz3 == clazz1);
    }
}注意到所有返回类型是Class类型,该类型的对象中包含了对应类的类名、方法等属性。
反射使用方法

反射中常用的方法有三个:

[*]Invoke方法用于实行函数
[*]forName方法用于获取实例的类
[*]getMethod方法用于获取该类中的方法
[*]newInstance()方法用于实例化该类的对象
demo:
package java_learn;

import java.lang.reflect.InvocationTargetException;

public class reflect {
    publicstatic voidmain(String args[]) throws ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, InstantiationException {
      Class clazz3 = Class.forName("java_learn.Student");
      // clazz3.getMethod("woparam", new Class[]{}).invoke(clazz3.newInstance());
      // clazz3.getMethod("doparam", String.class).invoke(clazz3.newInstance(),"capt");
      Class cls = Class.forName("java.lang.Runtime");
      cls.getMethod("exec",String.class).invoke(cls.getMethod("getRuntime").invoke(cls),"open -a Calculator");

    }
}这里需要注意的是

[*]newInstance()方法在实例化对象时,会默认调用类中的无参数构造函数,当类中不存在无参数构造函数或者无参数构造函数是私有的时,函数会实行失败。
[*]Runtime类是一个典范的单例模式,即该类只能存在一个对象,可以进入到该包中检察,其构造方法是私有的。该类的实例只能通过其public方法getRuntime方法获取。
invoke()概述

对于invoke函数的第一个参数:

[*]如果这个方法是一个平凡方法,第一个参数就是类对象
[*]如果这个方法是一个静态方法,第一个参数就是该类(Class)
一般需要先通过getMethod方法得到Method对象,然后该Method对象调用invoke方法,从而实现该Method方法的运行。
同时invoke方法是支持多态机制的,即子类对象也可以通过invoke方法实行父类的方法,父类对象则不可以通过invoke方法实行子类方法。
第二种实例化方法

在上面提到newInstance()在实例化中大概会碰到两种问题,我们已经通过Runtime举例解决了无参数构造函数是私有的这种环境,下面分析要实例化类中不存在无参数构造函数的环境。

[*]getConstructor()方法:该方法可以获取类的构造函数,并且因为类的构造函数是支持重载的,有时会有存在多个构造方法,getConstructor()函数还可以通过这些构造函数的传参类型进行区分。在进行实例化时使用newInstance()。
将Student类中的无参数构造方法删除,我们实行这个demo发现,乐成创建实例:
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
      Class clazz = Class.forName("java.lang.ProcessBuilder");
      Method start = clazz.getMethod("start");
      Constructor c = clazz.getConstructor(String[].class);
      Object pb = c.newInstance((Object) new String[]{"open", "-a", "Calculator"});
      start.invoke(pb);
    }
[*]ProcessBuilder类:这是另一个常用于实行下令的类,该类用于创建操作体系历程,其start()方法可以创建一个新的Process实例。它存在两个构造方法:

[*]public ProcessBuilder(List command)
demo:
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
      Class clazz = Class.forName("java.lang.ProcessBuilder");
      Method start = clazz.getMethod("start");
      Constructor c = clazz.getConstructor(List.class);
      Object pb = c.newInstance(Arrays.asList("open","-a","Calculator"));
      start.invoke(pb);
    }这里因为Objec是祖先类,以是pb可以使用Obejct来引用。
[*]public ProcessBuilder(String... command)
这里需要的参数是一个可变长参数,参数数目不固定,可以当作数组来对待。
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
      Class clazz = Class.forName("java.lang.ProcessBuilder");
      Method start = clazz.getMethod("start");
      Constructor c = clazz.getConstructor(String[].class);
      Object pb = c.newInstance((Object) new String[]{"open", "-a", "Calculator"});
      start.invoke(pb);
    }可以发现,这里在调用newInstance方法时,将String数组强制转化成了Object类型。因为newInstance()方法其实也是接收可变长参数的,如果直接传输数组,newInstance()方法会将传输的字符串拆开,作为自己的参数,如许就会导致下令无法实行。

获取私有方法、属性

在反射中,使用的类存在两种:

[*]getMethod()方法来获取类中的公有方法,getField()方法来获取类中的公有属性。getConstructor()方法来获取类的公有构造方法。
[*]getDeclearedMethod()方法来获取类中的私有方法,getDeclearedField()方法来获取类中的私有属性。getDeclearedConstructor()方法来获取类的私有构造方法。
通常是由getMethod()方法来获取public方法,通过getDeclearedMethod()获取类的私有方法。
如许对于之前Runtime类的使用就有了新的方式。我们不需要调用getRuntime()方法来实现对Runtime对象的引用,可以直接使用getDeclearedConstructor()获取其私有的构造器:
Class clazz = Class.forName("java.lang.Runtime");
      Constructor con = clazz.getDeclaredConstructor(null);
      con.setAccessible(true);
      Method ex = clazz.getMethod("exec", String.class);
      ex.invoke(con.newInstance(), "open -a Calculator");这里需要注意的是在获取私有构造方法(属性、构造器)之后,需要使用setAccessible(true),对权限进行修改,将newInstance()判定中的override设置成了true。
总结


[*]首先需要相识反射获取某个方法所属类的三种手段:.class、forName()、getClass()
[*]在进行下令实行时可以用到Runtime类和ProcessBuilder类
[*]newInstance()方法存在两种不可实行的环境

[*]类中没有无参数构造函数:使用getConstructor()方法获取
[*]类中无参数构造函数私有:

[*]单例模式下一般存在别的方法可以直接返回该实例的引用,如getRuntime();
[*]使用getDeclearedConstrucotr()直接获取私有方法,setAccessible(true)。



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: java反射