Java反射(实战训练)

一、获取字节码Class对象的方式

1.Class.forName(“全类名”):

将字节码文件加载进内存,返回Class对象。

2.类名.class:

通过类名的属性class获取。

3.对象.getClass():

通过对象的getClass()方法获取。

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

二、获取成员对象、方法

1.获取对象方法

  • Method[] getDeclaredMethods(),返回所有自有方法。
  • Method[] egtMethods(),返回所有包含父类的方法。

2.获取对象的成员

  • Field[] getFields() :获取所有public修饰的成员变量

  • Field getField(String name) 获取指定名称的 public修饰的成员变量

  • Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符

执行指定的方法(在Method中)

  • Object invoke(Object obj, Object… args) ,第一个参数为要操作的对象,第二个参数为方法参数

三、什么是反射

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

要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。

总之,反射就是把java类中的各种成分映射成一个个的Java对象

四、Java类的生命周期

类从被加载到虚拟机内存中开始, 到卸载出内存为止, 它的整个生命周期包括: 加载( Loading) 、 验证( Verification) 、 准备( Preparation) 、 解析(Resolution) 、 初始化( Initialization) 、 使用( Using) 和卸载( Unloading) 7个阶段。 其中验证、 准备、 解析3个部分统称为连接( Linking) 。

在这里插入图片描述

什么时候开始类加载呢?

1.遇到new、 getstatic、 putstatic或invokestatic这4条字节码指令时, 如果类没有进行过初始化, 则需要先触发其初始化。 生成这4条指令的最常见的Java代码场景是: 使用new关键字实例化对象的时候、 读取或设置一个类的静态字段( 被final修饰、 已在编译期把结果放入常量池的静态字段除外) 的时候, 以及调用一个类的静态方法的时候。

2.使用java.lang.reflect包的方法对类进行反射调用的时候, 如果类没有进行过初始化,则需要先触发其初始化。

3.当初始化一个类的时候, 如果发现其父类还没有进行过初始化, 则需要先触发其父类的初始化。

4.当虚拟机启动时, 用户需要指定一个要执行的主类( 包含main( ) 方法的那个类) , 虚拟机会先初始化这个主类。

5.当使用JDK 1.7的动态语言支持时, 如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、 REF_putStatic、 REF_invokeStatic的方法句柄, 并且这个方法句柄所对应的类没有进行过初始化, 则需要先触发其初始化。

注意:

当一个类在初始化时, 要求其父类全部都已经初始化过了, 但是一个接口在初始化时, 并不要求其父接口全部都完成了初始化, 只有在真正使用到父接口的时候( 如引用接口中定义的常量) 才会初始化。

五、Java堆和栈

堆和栈是内存的两种分类。

1.堆内存

  • 什么是堆内存?

堆内存是是Java内存中的一种,它的作用是用于存储Java中的对象和数组,当我们new一个对象或者创建一个数组的时候,就会在堆内存中开辟一段空间给它,用于存放。

可以说,堆是一个运行时数据区,类的对象从堆中分配空间。这些对象通过new等指令建立,通过垃圾回收垃圾回收器来销毁。

  • 堆内存的特点
  1. 类似队列,先进先出;
  2. 堆可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,但缺点是,由于要在运行时动态分配内存,存取速度较慢。
  • new对象在堆内存中的分配

由JVM的垃圾回收机制来管理。

2.栈内存

  • 什么是栈内存?

栈中主要存放一些基本数据类型的变量(byte,short,int,long,float,double,boolean,char)和对象的引用

  • 栈内存特点
  1. 先进后出;
  2. 存取速度比堆要快,仅次于寄存器,栈数据可以共享,但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性

3.堆和栈的区别

JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。

他们的差异是:

1.堆内存用来存放由new创建的对象和数组;

2.栈内存用来存放方法或者局部变量等(包括数组之类的引用)。

相同点是:

1.都是属于Java内存的一种;

2.系统都会自动去回收它,但是对于堆内存一般开发人员会自动回收它。

六、反射实例代码演示

Excel中有:

“姓名” “手机号码” “性别”

梁润 1888888888 女

海军 1666666666 女

李通 1555555555 女

心如 1333333333 男

模拟从Excel中获取到数据,进行展示,并使用反射机制,创建这个类,并且调用对应的get方法,输出个人信息。

需求分析:

  • excel的操作需要导入依赖,所以创建Maven工程并导入依赖:
		<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.1.1</version>
        </dependency>

就可以通过EasyExcel操作表格啦。

  • 首先,我们创建people对象,并给他添加应有属性:name,mobile,sex;
public class People {
    public String  name;
    public String mobile;
    public String sex;

    public People(String name, String number, String sex) {
        this.name=name;
        this.mobile=number;
        this.sex=sex;
    }

    public People() {

    }

    public String getName() {

        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}
  • 其次,我们在配置文件中配置我们的类名;
className=refect2.People  //refect2是我自己的包名,这里要替换成自己的包名
  • 最后,就可以写我们的主类了。导入配置文件后,首先调用Class.forName(className)方法获得Class对象,在调用 class对象的newInstance()方法创建操作对象。这里我们使用反射获取属性名,通过属性名调用方法名来输出属性,这样会更加灵活,不用将方法名写死。比如,我们返回了name属性,那么我们就在这里调用getName方法获取属性并付给操作对象的name属性,最后通过invoke再次调用输出。代码如下:
package refect2;

import com.alibaba.excel.EasyExcel;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Properties;

public class Test {
    public static void main(String[] args) {
        //通过EasyExcel导入表格数据
        List<People> peoples = EasyExcel.read("xl.xlsx").head(People.class).sheet(0).doReadSync();


        //将表格数据导入people数组
        People[] arrays = new People[4];
        int i1 = 0;
        for (People people : peoples) {
            arrays[i1]=people;
            i1++;

        }



        //导入配置文件,固定操作
        InputStream inputStream = People.class.getClassLoader().getResourceAsStream("refect2/pro.properties");
        Properties properties = new Properties();
        try {
            properties.load(inputStream);
            //通过导入的配置文件对象获取配置的类名
            String className = properties.getProperty("className");

            //获取类对象
            Class cls = Class.forName(className);
            Object object = cls.newInstance();

            //循环给Object对象注入属性,外层循环是people对象,内层循环是属性对象
            for (int i = 0; i < arrays.length; i++) {
             // object=arrays[i];这句会直接将People对象赋值给操作对象object,省去了下面的循环赋值
				//获取全部属性集合
               Field[] fields = cls.getDeclaredFields();
				//循环获取属性名
                for (Field field : fields) {
                    //查看属性名
                    System.out.println(field.getName());
                    //将属性名组装成方法名
                    String name = "get"+field.getName().substring(0,1).toUpperCase()+field.getName().substring(1);
                    //查看拼装的方法名
                    System.out.println(name);
                    //判断属性并调取相应方法赋值
                    if (name.equals("getName")){
                        field.set(object,arrays[i].getName());
                    }else if (name.equals("getMobile")){
                        field.set(object,arrays[i].getMobile());
                    }else if (name.equals("getSex")){
                        field.set(object,arrays[i].getSex());
                    }
					//调用组装的方法名
                    Method method =cls.getDeclaredMethod(name);
                    System.out.println(method.invoke(object)); ;
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }


}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值