Java反射

Posted 在劫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java反射相关的知识,希望对你有一定的参考价值。

类的加载
    当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,链接,初始化三步来实现对这个类进行初始化。
    加载
        就是指将class文件读到内存,并为之创建一个class对象
        任何类被使用时系统都会建立一个class对象
    链接
        验证是否有正确的内部结构,并和其他类协调一致
        准备负责为类的静态成员分配内存,并设置默认初始化值
    初始化就是之前的初始化步骤
    类初始化的加载时机:
        创建类的加载实例
        访问类的静态变量或为静态变量赋值
        调用类的静态方法
        使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
        初始化某个类的子类
        直接使用java.exe命令来运行某个主类
    类加载器
        负责将.class文件加载到内存中,并为之生成对应的Class对象
        虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
    类加载器的组成
        Bootstrap ClassLoader 根类加载器
            也被称为引导类加载器,负责Java核心类的加载
                比如System String等,在JDK中JRE的lib目录下rt.jar文件中
        Extension ClassLoader 扩展类加载器
            负责JRE的扩展目录中jar包的加载
                在JDK中JRE的lib目录下ext目录
        System ClassLoader 系统类加载器
            负责在JVM启动时加载来自java命令的class文件
            以及classpath环境变量所指定的jar包和类路径

反射
    Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它任意一个方法阿和属性;这种动态获取的信息以及动态调用对象的方法的功能 成为Java语言的反射机制。
    要想解剖一个类,必须先要获取到该类的字节码文件对象,而要解剖使用的Class类中的方法,所以要先要获取到每一个字节码文件对应的Class类型的对象。

/*
 *  反射:就是通过class文件对象,去使用该文件中的成员变量、构造方法、成员方法
 *        要使用这个对象,首先必须得到class文件对象,其实也就是得到Class类的对象
 *    Class类:
 *        成员变量 Field
 *        构造方法 Constructor
 *        成员方法 Method
 *    获取class文件对象的方式
 *        1、Object类中的getClass()方法;
 *        2、数据类型的静态属性class
 *        3、Class类中的静态方法forName()
 *    一般我们使用什么呢?
 *        1、自己玩 任选一种,第二种比较方便
 *        2、开发 第三种 这种是一个字符串,而不是一个具体的看到的类名,这样我们就可以把这样的字符串配置到配置文件中
*/
public class ClassTest {

    public static void main(String[] args) throws Exception {
        //1、Object类中的getClass()方法;
        Person p1 = new Person();
        Class c = p1.getClass();
        
        Person p2 = new Person();
        Class c2 = p2.getClass();
        
        System.out.println(p1 == p2);//false,都是新建立的,并不相等
        System.out.println(c == c2);//true,只有一个class文件,获得的class文件相等。
        
        //方式2
        Class c3 = Person.class;
        System.out.println(c == c3);
        
        
        //注意一定要写全路径,不然找不到报错
        //可以下拉Person.java文件,在Person文件上右键复制限定名也可以
        Class c4 = Class.forName("classDemo.Person");
        
    }

}
import java.lang.reflect.Constructor;

/*    Constructor<?>[] getConstructors() 
        返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
        
    Constructor<?>[] getDeclaredConstructors() 
        返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。 
        
    Constructor<T> getConstructor(Class<?>... parameterTypes) 参数表示你要获取构造方法的参数构造方法的class字节码
          返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
          
    T newInstance(Object... initargs)
          使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。  
*/
public class ClassDemo2 {
    public static void main(String[] args) throws Exception {
        //获取字节码
        Class c = Class.forName("classDemo.Person");
        
        //返回单个构造方法
        Constructor con = c.getConstructor();
        
        //输出的是Person类的toString()方法,所以这里返回的不是Object类,而是Person类
        Object obj = con.newInstance();
        System.out.println(obj);
        
    }
}
import java.lang.reflect.Constructor;

public class ClassDemo2 {
    public static void main(String[] args) throws Exception {
        //获取字节码
        Class c = Class.forName("classDemo.Person");
        
        //传入的是Class文件,获取带参构造方法
        //public Person(String name, int age, String address)
        Constructor con = c.getConstructor(String.class, int.class, String.class);
        
        //构建带参构造对象
        Object obj = con.newInstance("zed", 19, "akl");
        System.out.println(obj);
        
    }
}

获取私有构造

public class Person {
    String name;
    int age;
    String address;
//    public Person(){}
    private Person(String name){
        this.name = name;
    }
/*    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }
    public Person(String name, int age, String address){
        this.name = name;
        this.age = age;
        this.address = address;
    }*/
    
    public void menthod(){
        System.out.println("公有方法");
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", address=" + address
                + "]";
    }
    private void show(){
        System.out.println("私有方法");
    }
    
    
}
//----------------------------------------------------------------
import java.lang.reflect.Constructor;

public class ClassDemo2 {
    public static void main(String[] args) throws Exception {
        //获取字节码
        Class c = Class.forName("classDemo.Person");
        
        //传入的是Class文件,获取私有构造方法
        //NoSuchMethodException异常
        //Constructor con = c.getConstructor(String.class);
        //Constructor getDeclaredConstructor() 返回 Constructor 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。  
        Constructor con = c.getDeclaredConstructor(String.class);
        //父类类 AccessibleObject中setAccessible()方法,值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。 
        //值为true都可以访问
        con.setAccessible(true);

        
        //构建带参构造对象
        Object obj = con.newInstance("zed");
        System.out.println(obj);
        
    }
}

获取成员变量

import java.lang.reflect.Field;
//获取成员变量并使用
public class ClassDemo2 {
    public static void main(String[] args) throws Exception {
        //获取字节码
        Class c = Class.forName("classDemo.Person");
         
        //获取变量
        
        //获取公共成员字段
        Field[] fields = c.getFields();
        
        //获取所有成员变量
        //Field[] getDeclaredFields() 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
        Field[] fieldss = c.getDeclaredFields();
        for(Field f : fieldss){
            System.out.println(f);
        }
        
    }
}
public class ClassDemo2 {
    public static void main(String[] args) throws Exception {
        //获取字节码
        Class c = Class.forName("classDemo.Person");
        Object obj = c.newInstance();
        
        //获取单个指定成员变量
        //此处返回的是Field类对象,通过Field类中的各种set方法设置为你想要的类型
        Field nameField = c.getField("name");    
        //set(Object obj, Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
        //这里就是讲obj类中的name的值设置为zed
        nameField.set(obj, "zed");
    }
}

获取指定成员变量并赋值

import java.lang.reflect.Field;

public class ClassDemo2 {
    public static void main(String[] args) throws Exception {
        //获取字节码
        Class c = Class.forName("classDemo.Person");
        Object obj = c.newInstance();
        System.out.println(obj);
        
        //获取单个指定成员变量
        Field nameField = c.getField("name");    
        
        //Field getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 
        Field nameField = c.getDeclaredField("name");    
        
        
        //此处返回的是Field类对象,通过Field类中的各种set方法设置为你想要的类型
        //set(Object obj, Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
        //这里就是讲obj类中的name的值设置为zed
     nameField.setAccessible(true);用之前调用这个方法取消检查
nameField.set(obj, "zed"); System.out.println(obj); } }

获取调用方法

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ClassDemo2 {
    public static void main(String[] args) throws Exception {
        //获取字节码
        Class c = Class.forName("classDemo.Person");
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();
        
/*         Method getMethod(String name, Class<?>... parameterTypes) 
         返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 
         Method[] getMethods()  获取自己的,包括父类的公共方法
         返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
         Method getDeclaredMethod(String name, Class<?>... parameterTypes) 
         返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。 
          Method[] getDeclaredMethods() 
         返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 
 
*/
        //获取方法对象
        Method m = c.getDeclaredMethod("shows");
        //Object invoke(Object obj, Object... args) 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
        //obj - 从中调用底层方法的对象
        //args - 用于方法调用的参数,没有参数的话空着就可以。
        //执行方法
        m.invoke(obj);    
    }
}
    public int setI(int i){
        return i;
    }
//--------------------或者强制类型转换------------------------------
   Method m = c.getDeclaredMethod("setI", int.class);
   //m.invoke()返回的是一个Object类型
   Object obj2 =  m.invoke(obj, 1);
   System.out.println("obj2: " + obj2);
   //其实变量的类为Integer
   System.out.println(obj2.getClass());

获取执行私有方法

        //获取私有方法对象
        Method m = c.getDeclaredMethod("show");
        //不检查是否私有
        m.setAccessible(true);
        m.invoke(obj);

 反射实现根据配置执行类方法

public class Student {
    public void show(){
        System.out.println("爱生活,爱学习");
    }
}
//--------------------------------------------------
public class Me {
    public void show(){
        System.out.println("爱生活,爱编程");
    }
}
//--------------------------------------------------
public class Other {
    public void show(){
        System.out.println("other");
    }
}
//--------------------------------------------------
配置文件:
ClassName=classDemo.Other
Method=show
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;

public class ClassDemo {

    public static void main(String[] args) throws IOException, Exception {
        //获取配置文件
        Properties prop = new Properties();
        FileReader fr = new FileReader("D:\\EclipseWorkSpace\\StudyDemo\\src\\classDemo\\配置.txt");
        prop.load(fr);
        
        //获取配置文件中ClassName和要执行的方法Method
        String className = prop.getProperty("ClassName");
        String method = prop.getProperty("Method");
        
        //注意:Class c = Class.forName("className");这样是不对的,字符串直接传就可以,蛋疼
        //获取字节码
        Class c = Class.forName(className);
        
        //获取无参构造,并创建无参对象
        Constructor con = c.getDeclaredConstructor();
        Object obj = con.newInstance();
        
        //获取要执行的方法名
        Method m = c.getMethod(method);
        //执行方法
        m.invoke(obj);
    }

}

 

 

 

 

 

 

 


   

































以上是关于Java反射的主要内容,如果未能解决你的问题,请参考以下文章

反射机制入门

反射机制入门

反射机制入门

使用反射在外部JAR / CLASS上调用包含Hibernate事务的方法(Java EE)

为啥我的 Ray March 片段着色器反射纹理查找会减慢我的帧速率?

OpenGL片段着色器不照亮场景