为了账号安全,请及时绑定邮箱和手机立即绑定

Java进阶--------反射

标签:
Java
Java反射

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

二、反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。
1、Class类(lang包下的)
阅读API的Class类得知,Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的

获取Class对象的三种方式
方式一: 通过Object类中的getObject()方法
Person p = new Person();
Class c = p.getClass();

方式二: 通过 类名.class 获取到字节码文件对象(任意数据类型都具备一个class静态属性,看上去要比第一种方式简单)。
Class c2 = Person.class;

方式三: 通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)。
Class c3 = Class.forName("Person");
注意:第三种和前两种的区别
前两种你必须明确Person类型.
后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了
示例:

package com.lm;

import com.bean.Person;

/*
 * 获取一个类的class文件对象的三种方式
 * 1、对象获取
 * 2、类名获取
 * 3、Class类的静态方法获取
 */
public class ReflectDemo {

    public static void main(String[] args) {
        //1、对象获取
        Person p=new Person();
        //调用Person类的父类的方法getClass()
        Class c=p.getClass();
        System.out.println(c);

        //2、类名获取
        //每个类型,包括基本和引用,都会赋予一个静态属性,属性名为class
        Class c1=Person.class;
        System.out.println(c1);
        System.out.println(c.equals(c1)); //true 
        System.out.println(c==c1); //true  只在类加载的时候产生一个class文件对象

        //3、Class类的静态方法获取forName(全包名+类名)
        try {
            Class c2=Class.forName("com.bean.Person");
            System.out.println(c2);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

附Person类

public class Person {
    //成员变量
    public String name;
    public int age;
    private String address;

    //构造方法
    public Person() {
        System.out.println("空参数构造方法");
    }

    public Person(String name) {
        this.name = name;
        System.out.println("带有String的构造方法");
    }
    //私有的构造方法
    private Person(String name, int age){
        this.name = name;
        this.age = age;
        System.out.println("带有String,int的构造方法");
    }

    public Person(String name, int age, String address){
        this.name = name;
        this.age = age;
        this.address = address;
        System.out.println("带有String, int, String的构造方法");
    }

    //成员方法
    //没有返回值没有参数的方法
    public void method1(){
        System.out.println("没有返回值没有参数的方法");
    }
    //没有返回值,有参数的方法
    public void method2(String name){
        System.out.println("没有返回值,有参数的方法 name= "+ name);
    }
    //有返回值,没有参数
    public int method3(){
        System.out.println("有返回值,没有参数的方法");
        return 123;
    }
    //有返回值,有参数的方法
    public String method4(String name){
        System.out.println("有返回值,有参数的方法");
        return "哈哈" + name;
    }
    //私有方法
    private void method5(){
        System.out.println("私有方法");
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", address=" + address+ "]";
    }
}

2、通过反射获取构造方法并使用
在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法:
返回一个构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)
返回多个构造方法
public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法
public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法(包含私有的)
3、通过反射获取成员变量并使用
在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量:
返回一个成员变量
public Field getField(String name) 获取指定的 public修饰的变量
public Field getDeclaredField(String name) 获取指定的任意变量
返回多个成员变量
public Field[] getFields() 获取所有public 修饰的变量
public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)
4、通过反射获取成员方法并使用
在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:
返回获取一个方法:
public Method getMethod(String name, Class<?>... parameterTypes)
获取public 修饰的方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
获取任意的方法,包含私有的
参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型
返回获取多个方法:
public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
public Method[] getDeclaredMethods() 获取本类中所有的方法(包含私有的)
三、练习
1、泛型擦除

package com.lm;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

/*
 * 反射泛型擦除
 * 定义集合类,泛型String
 * 要求向集合中添加Integer类型
 * 
 * 反射方式,获取出集合ArrayList类的class文件对象
 * 通过class文件对象,调用add方法
 */
public class ReflectTest {

    public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        ArrayList<String> array=new ArrayList<String>();
        array.add("a");
        Class c=array.getClass();
        //获取ArrayList.class文件中的方法add(编译后的class文件里没有泛型)
        Method method=c.getMethod("add",Object.class);//注意这里用的是Object
        //使用invoke运行ArrayList方法add
        method.invoke(array, 2);
        method.invoke(array, 12);
        method.invoke(array, 13);
        method.invoke(array, 14);
        System.out.println(array.size());
        System.out.println(array);
        //不能用增强for循环遍历,因为类型不一样,可以考虑迭代器
    }
}

2、反射配置文件
(1)通过反射配置文件,运行配置文件中指定类的对应方法
读取config.properties文件中的数据,通过反射技术,来完成Person对象的创建
config.properties文件内容如下:

className=com.bean.Person
methodName=eat

(2)读取配置文件,调用指定类中的对应方法
package com.lm;

import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;

/*

  • 有可能调用Person的方法,有可能调用Student的方法,有可能调用worker的方法
  • 类不确定,方法也不不确定
  • 解决办法:
  • 通过配置文件实现此功能
  • 运行的类名和方法名,以键值对的形式写在文件里
  • 运行哪个类就读取配置文件即可
  • 实现步骤:
  • 1、准备配置文件,键值对
  • 2、IO读取配置文件,Reader
  • 3、文件中的键值对存储到集合的Properties
  • 集合保存的键值对,就是类名和方法名
  • 4、反射获取指定类的class文件
  • 5、class文件对象,获取指定的方法
  • 6、运行方法
    */

public class Test {

public static void main(String[] args) throws Exception {
    //IO读取配置文件
    FileReader r=new FileReader("config.properties");
    //创建集合对象
    Properties pro=new Properties();
    pro.load(r);
    r.close();

    String className=pro.getProperty("className");
    String methodName=pro.getProperty("methodName");

    Class c=Class.forName(className);
    Object obj=c.newInstance();
    Method method=c.getMethod(methodName);
    method.invoke(obj);
}

}
四、总结
(1)如何获取.Class文件对象
a, 通过Object类 getClass()方法获取 Class对象
b, 通过类名.class 方式 获取 Class对象
c, 通过反射的方式, Class.forName(String classname) 获取Class对象
public static Class<?> forName(String className)throws ClassNotFoundException
返回与带有给定字符串名的类或接口相关联的 Class 对象

(2)通过反射, 获取类中的构造方法,并完成对象的创建
获取指定的构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
获取指定的public修饰的构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
获取指定的构造方法,包含私有的
获取所有的构造方法
public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法
public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法,包含私有的

(3)通过反射, 获取类中的构造方法,并完成对象的创建
步骤:
a,获取字节码文件对象
b,通过字节码文件对象 ,获取到指定的构造方法
getConstructor(参数);
c,通过构造方法,创建对象
public T newInstance(Object... initargs)

(4)私有构造方法,创建对象
a,获取字节码文件对象
b,通过字节码文件对象 ,获取到指定的构造方法
getDeclaredConstructor (参数);
c,暴力访问
con.setAccessible(true);
d,通过构造方法,创建对象
public T newInstance(Object... initargs)

(5)通过反射,获取Class文件中的方法
获取指定的方法
public Method getMethod(String name, Class<?>... parameterTypes)
获取指定的public方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
获取指定的任意方法,包含私有的
获取所有的方法
public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
ublic Method[] getDeclaredMethods()获取本类中所有的方法,包含私有的

(6)通过反射,调用方法
步骤:
a,获取Class对象
b,获取构造方法,创建对象
c,获取指定的public方法
d,执行方法
public Object invoke(Object obj, Object... args)

(7)私有方法的调用:
a,获取Class对象
b,获取构造方法,创建对象
c,获取指定的private方法
d,开启暴力访问
m5.setAccessible(true);
e,执行方法
public Object invoke(Object obj, Object... args)

(8)通过反射,获取成员变量(字段)
获取指定的成员变量
public Field getField(String name) 获取public修饰的成员变量
public Field getDeclaredField(String name) 获取任意的成员变量,包含私有
获取所有的成员变量
public Field[] getFields() 获取所有public修饰的成员变量
public Field[] getDeclaredFields() 获取司所有的成员变量,包含私有

(9)通过反射,获取成员 变量,并赋值使用
步骤:
a,获取字节码文件对象
b,获取构造方法,创建对象
c,获取指定的成员变量
d,对成员变量赋值\获取值操作
public void set(Object obj, Object value) 赋值
public Object get(Object obj) 获取值

(10)私有成员变量的使用
步骤:
a,获取字节码文件对象
b,获取构造方法,创建对象
c,获取指定的成员变量
d,开启暴力访问
e,对成员变量赋值\获取值操作
public void set(Object obj, Object value) 赋值
public Object get(Object obj) 获取值

点击查看更多内容
6人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消