Deep learning about Java--贯穿Java的反射机制(2)

在上一篇的文章中,笔者简要地写了如何通过反射机制来获取类对象。对于一个Java中的类对象来说,通过反射机制能够访问到整个类中包含的所有东西,总结下来主要是:Class对象、Field对象、Method对象、Constructor对象和Modifier对象。今天我们来谈谈如何使用这些对象来构造实例化这个目标类的对象,再者,我们来深入学习一下反射。
这里写图片描述

1.使用反射获取的各类对象

主要是Class、Constructor、Field和Method。

package com.unicorn.reflect.service;

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

import org.junit.Test;

/**
 * 通过反射改变域的值,也就是不通过setter来改变类属性的值
 * @author Unicorn
 *
 */
public class ModifyingField {

    @Test
    public void modifyFieldsVal(){

        /**
         * 回顾一下获取Class对象的办法:
         * 笔者前面说过有四种,其实概括来说应该是三种
         * 1.通过类对象的getClass()方法
         * 2.通过类的静态属性.class
         * 3.通过静态方法Class.forName(类的全限定名):做开发常用
         */
        try {
            Class<?> cl = Class.forName("com.unicorn.reflect.pojo.Emp");
            Constructor<?> cons = cl.getDeclaredConstructor();//如果没有这个构造器会触发NoSuchMethodException
            Object obj = cons.newInstance();//可以传入参数的.class来调用不同的构造器,如果没有参数,那么默认调用的是无参构造器
            //如果这个构造器不存在,就会触发InstantiationException

            //属性是可以通过字符串来获取相应的变量
            Field f1 = cl.getDeclaredField("empName");//如果这个Field不存在,会触发一个NoSuchFieldException
            //而且,如果这field的访问权限是private或者protected,反射时Java进行权限检查,这时是不允许访问的
            //但是我们可以暴力访问,强制修改权限
            f1.setAccessible(true);
            f1.set(obj, "123");//这里使用了set,如果没有进行accessible暴力改变权限会触发IllegalAccessibleException
            System.out.println(obj);
        } catch (ClassNotFoundException | NoSuchFieldException 
                | SecurityException | IllegalArgumentException | IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

这里写图片描述

package com.unicorn.reflect.service;

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

import org.junit.Test;

/**
 * 通过反射来调用类中的方法,
 * @author Unicorn
 *
 */
public class ModifyingMethod {

    @Test
    public void callingMethods(){
        try {
            //获取类
            Class<?> cl = Class.forName("com.unicorn.reflect.pojo.Emp");

            //获取构造器
            Constructor<?> cons = cl.getDeclaredConstructor(Long.class);
            cons.setAccessible(true);
            Object obj = cons.newInstance(6895L);

            //获取方法
            Method m = cl.getDeclaredMethod("setEmpName", String.class);//第一个参数基本是需要获取对象的名称
            //如果访问权限是private
            m.setAccessible(true);
            m.invoke(obj, "笔者棒极了!");

            System.out.println(obj);

        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

这里写图片描述
使用反射过程:
a.获取类的Class
b.获取类的Constructor
c.通过获取的Constructor来构造Object对象
d.访问Field or Method

2.深入学习反射

我们先来了解下在反射执行之前都发生了什么。
2.1.类的加载
当程序需要使用某个类的时候,假如这个类还没有被加载到内存(jvm->Java virtual machine)中,那么系统会三个步骤实现对这个类的初始化,简要来说就是:加载、连接和初始化。
什么是加载?
加载就是指将.class文件读入内存,并为其创建一个Class对象;任何类在被使用的时候,系统都会为该类创建一个Class对象。
什么是连接?
连接主要是包括三个步骤:
验证(检验这个类是否具有正确的内部结构冰河其他的类协调一致)
准备(负责为类的静态成员分配内存并设置默认初始化值)
解析(将类的二进制数据中的符号引用替换为直接引用)
什么是初始化?
这个阶段主要是开始执行Java的代码,负责为类变量(类的静态变量)赋予正确的初始化值,此时Java编译器把所有的类变量的术赤化语句和静态初始化器全部收集到< clinit >()方法中,该方法只能被jvm调用,专门来用于初始化。如下的5个情况才能主动触发类的初始化过程:

  • 第一种:遇到new、getstatic、putstatic和invokestatic这四条字节码指令时,此时如果类还没有进行初始化,则需要先触发其初始化。
  • 第二种:使用Java.lang.reflect.*;的方法对类进行反射调用时,如果类还没有进行初始化,则需要先触发其初始化。
  • 第三种:当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
  • 第四种:当jvm启动时,用户需要指定一个要执行的主类,jvm会先执行该主类。
  • 第五种:当使用JDK1.5支持时,如果一个java.lang.incoke.MethodHandler实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
    总结起来,规范类的初始化时间为:在首次主动使用时初始化(initialize on first active use)。

另一种简单的说法是:

  • 创建类的实例–new,反射,克隆或反序列化
  • 访问类的静态变量,或者为静态变量赋值
  • 调用类的静态方法
  • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
  • 初始化一个类的子类
  • 直接使用java.exe命令来运行某个主类(main)

2.2类加载器
负责将.class文件加载到内存(jvm)中,并为之生成对应的Class对象。
类加载器的组成主要包括:
Boostrap ClassLoader(根类加载器,或称引导类加载器:负责Java核心类的加载,主要是在jdk中jre的lib目录下的rt.jar文件中)
Extension ClassLoader(拓展类加载器:负责JRE的扩展目录中jar包的加载,主要在在jdk中jre的lib目录下ext目录)
System ClassLoader(系统类加载器:负责在jvm启动时加载来自Java命令的.class文件,以及classpath环境变量所指定的jar包和类路径)

2.3反射机制
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

2.4反射中最重要的使用–动态代理
动态代理:在程序运行过程中产生的对象,也就是通过反射生成的对象。
动态代理技术主要包括Proxy(对接口类的代理,主要由jdk提供,这里主要做的是面向接口编程)和CGLib(类对象的代理,主要由cglib.jar提供,常在框架中使用)。

这次就先写到这了,下一篇主要写写这个动态代理技术,嘿嘿!
转载请注明出处,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值