Java:反射机制学习笔记

1、反射机制

一、概述

反射机制:将类的各个组成部分封装成其余对象,在运行状态中,能够动态获取类信息和调用类对象的方法。程序员

二、优缺点

  • 优势
    • 能够在程序运行过程当中,操做类的组成部分。
    • 能够解耦,提升程序的可扩展性。
  • 缺点
    • 产生一系列的解释操做,性能较差。

三、类加载的过程

java语言有着”一处编译,到处运行“的优良特色,可以很好地适应不一样的平台,过程大体以下:数组

  • 咱们经过javac命令将.java文件编译,会在磁盘上生成不面向平台的字节码文件.class
  • 类加载器经过一系列的操做,如加载、链接和初始化以后,将.class文件加载进内存中。
  • 将.class字节码文件表明的静态存储结构转化为方法区的运行时数据结构。
  • 类加载时还会再内存中建立一个java.lang.Class的对象,该对象包含类的信息,做为程序从方法区访问各类数据的接口。

2、获取Class对象的三种方式

这个Class对象包含类的全部信息,也就是说,咱们得到了这个Class类的对象,就能够访问到JVM中的这个类。那么,如何获取呢?主要有如下三种方式。安全

一、Class.forName("全类名")

经过Class的静态方法forName("全类名"),返回和全类名对应类的Class对象。数据结构

//Class.forName("全类名");
    Class cls1 = Class.forName("com.my.base.Person");
    System.out.println(cls1);
  • 多用于配置文件,将类名定义在配置文件中,即可以动态读取文件,加载类。
  • 全类名指:完整包名.类名,若是找不到,会抛出ClassNotFoundException的异常。

二、类名.class

经过类名的class属性获取,返回该类对应的Class对象。性能

//类名.class;
    Class cls2 = Person.class;
    System.out.println(cls2);
  • 该方式在程序编译阶段就能够检查须要访问的Class对象是否存在,相对来讲更加安全。
  • 该方式无需调用方法,程序性能方面相对较好。

三、对象.getClass()

getClass()方法在Object中定义,返回该对象所属类对应得Class对象。code

//对象.getClass();
    Person p = new Person();
    Class cls3 = p.getClass();
    System.out.println(cls3);

同一个字节码文件.class在一次程序运行过程当中,只会被加载一次,不论经过哪一种方式获取的Class对象都是同一个。视频

// == 比较三个对象,都是true

    System.out.println(cls1 == cls2);
    System.out.println(cls1 == cls3);
    System.out.println(cls3 == cls2);

3、反射相关的方法

Class对象包含着类的全部信息,而这些信息被封装在java.lang.reflect包下,成了一个个的类,不彻底统计以下:对象

Method Field Annotation Constructor Package Modifier Parameter
方法类 字段类 注解类 构造器类 包类 修饰符类 方法参数类

Class类中提供了相应的方法,获取这些信息,因为方法众多,具体的还须要参看JDK官方文档。接口

我在这里总结几点通用的:

  • 调用对应的getXxx一般是得到对应public修饰的单个信息,getDeclaredXxx则不考虑修饰符,找不到,则往超类找。

  • 调用对应的getXxxs一般得到对应的public修饰信息的数组,getDeclaredXxxs则不考虑修饰符,找不到,则往超类找。

  • 上面二者遇到参数的状况不一样,若是自己用于获取public修饰信息的方法强行去获取达不到的权限,则会抛出异常:
    • 对于Field而言,括号内是: "参数名"
    • 对于Method而言,括号内是: "方法名",参数类型对应的类
    • 对于Constructor而言,括号内是:"参数对应的类"
  • Field类中有获取值的方法:Object get(Object obj),也有设置值的方法:void set(Object obj, Object value)
  • Method类能够调用方法:Object invoke(Object obj, Object... args)
  • Constructor类能够建立实例:T newInstance(Object... initargs),若是类中有无参构造器,能够直接利用Class类中的newInstance()方法建立实例。

  • 在反射中,没有什么是私有的,若是有,那就使用xxx.setAccessible(true)暴力破解。
  • 判断修饰符时,getModifiers()返回的时int值,是各个修饰符表示数的和,能够用位运算判断,(xx.getModifiers()&Modifier.STATIC)!=0表示存在static修饰符。

ps:确定有遗漏的API,到时候用的时候,翻翻API就完事了。

4、Demo×2

一、尝试本身写一个clone()方法

package com.my.reflect.practice;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
 * @auther Summerday
 */

@SuppressWarnings("unchecked")
public class ImplementClone {
    public Object clone(Object o) throws Exception{
        //获取对象的实际类型
        Class<Object> clz = (Class<Object>) o.getClass();

        //获取全部构造方法
        Constructor<Object>[] cs = (Constructor<Object>[]) clz.getDeclaredConstructors();
        //任取一个构造方法
        Constructor<Object> c = cs[0];

        //防止取出构造方法为私有
        c.setAccessible(true);

        //该构造方法参数有or无?

        //获取参数类型
        Class[] ps = c.getParameterTypes();
        //存储参数的数组
        Object[] os = new Object[ps.length];

        for(int i = 0;i<ps.length;i++){
            //判断是否为基本类型
            if(ps[i].isPrimitive()){
                if(ps[i] == boolean.class)
                    os[i] = false;
                else if(ps[i] == char.class)
                    os[i] = '\u0000';
                else
                    os[i] = 0;
            }else{
                os[i] = null;
            }
        }
        //执行构造方法建立对象
        Object obj = c.newInstance(os);

        //获取属性数组
        Field[] fs = clz.getDeclaredFields();

        for (Field f : fs){

            //若是final修饰则返回,没法修改
            if((f.getModifiers()&Modifier.FINAL)!=0){
                continue;
            }
            //暴力破解
            f.setAccessible(true);

            //取出原属性值
            Object value = f.get(o);

            //将取出的属性值赋值给新对象的属性
            f.set(obj, value);
            
        }
        return obj;
    }
}

二、利用配置文件动态加载

package com.my.reflect.practice;

/**
 * @auther Summerday
 */

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

/**
 *
 *     参考黑马程序员教学视频
 *
 * 实现:一、配置文件  二、反射
 *
 * 1、将须要建立的对象的全类名和须要执行的方法定义在配置文件中
 *
 * 2、在程序中加载读取配置文件
 *
 * 3、使用反射来加载类文件进内存
 *
 * 4、建立对象
 *
 * 5、执行方法
 *
 */
public class ReflectTest {

    public static void main(String[] args) throws Exception {

        //一、加载配置文件

        //1.1 建立Properties对象
        Properties pro = new Properties();

        //1.2 加载配置文件,转换为一个集合

        //1.2.1获取class目录下的配置文件

        //建立类加载器
        ClassLoader classLoader = ReflectTest.class.getClassLoader();

        InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties");
        pro.load(resourceAsStream);

        //二、获取配置文件中定义的数据
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");

        //三、加载该类进内存
        Class cls = Class.forName(className);

        //四、建立对象
        Object obj = cls.newInstance();

        //五、获取方法
        Method method = cls.getMethod(methodName);

        //六、执行方法
        method.invoke(obj);

    }
}

之后会有愈来愈多真实的场景须要用到反射这项灵魂技术,总之,好好看,好好学。

相关文章
相关标签/搜索