跟我学Java 反射的基本用法

Java的反射机制,首先得从Java字节码入手,为了方便理解给大家举个例子,如图我们可以把Java的反射机制看做是一个照妖镜,它可以让类的字节码(Class文件)现出原形,也就是获取到字节码的各个组成部分。
在这里插入图片描述

在Java的世界里万物皆对象,类的字节码和类的各个组成部分都是对象,而每一个对象都有与之对应的类,关系如下

类的组成部分类名
字节码Class表示
成员变量Field表示
构造方法Constructor表示
成员方法Method表示

获取字节码

要去做反射的操作,首先第一步需要获取类的字节码,获取字节码有三种方式

Class cls1=类名.class;
Class cls2=对象名.getClass();
Class cls3=Class.forName("类的全路径名");

类名.class
任何一个类都有字节码,甚至是基本类型也有字节码,格式如下

//格式:类名.class
Class cls1=Student.class;
Class cls2=String.class;
Class cls3=int.class;

Class.forName(“包名.类名”)
通过Class类的forName("全类名");也可以获取一个类的字节码,如下代码

Class cls1=Class.forName("com.itheima.Student");

对象.getClass()
如果已经有了对象,通过对象的getClass()方法也可以获取字节码

Student stu=new Student();
Class cls1=stu.getClass();

不管用哪种方式获取字节码,只要是同一个类,字节码就是同一个

反射的API方法

在Class类中提供了和反射相关的方法,这些方法的命名其实是很有规律,掌握了这些规律学习反射其实挺简单的。

先来学习几个单词,把这些单词按照规律组合起来,就是反射的各个功能方法。

单词含义
Field成员变量
Constructro构造方法
Method成员方法
get获取
Declared使公开的,用于获取非public修饰的成员

获取公有的Constructor、Field、Method

1.反射获取构造方法(Constructor)
//获取一个构造方法
Constructor getConstructor(Class...cls)
//获取多个构造方法
Constructor[] getConstructors();

2.反射获取成员变量(Field)
//获取一个成员变量
Field getField(String name)
//获取多个成员变量
Field[] getFields()			

3.反射获取成员方法(Method)
//获取一个成员方法
Method getMethod(String methodname,Class...cls)
//获取多个成员方法
Method[] getMethods()

获取私有的的Constructor、Field、Method

1. 反射获取构造方法(Constructor)
//获取一个构造方法
Constructor getDeclaredConstructor(Class...cls)
//获取多个构造方法
Constructor[] getDeclaredConstructors()

2. 反射获取成员变量(Field)
//获取一个成员变量
Field getDeclaredField(String name)
//获取多个成员变量
Field[] getDeclaredFields()

3. 反射获取成员方法(Method)
//获取一个成员方法
Method getDeclaredMethod(String methodname,Class...cls)
//获取多个成员方法
Method[] getDeclaredMethods()

反射的API方法命名,有下面一些规律,自己体会一下。

  • 后缀带s的,表示获取多个;后缀不带s获取其中一个
  • 方法名带有Declared的,表示可以获取私有修饰的成员

在做反射操作之前,先打一个预防针,可能你刚开始会觉得反射的代码很麻烦(确实很麻烦),先别管为什么要这么写,先学会这些API的方法用会,后面再给大家讲具体在什么场景下使用。

反射获取构造方法

有如下的Student类,有两个构造方法

public class Student{
    private String name;
    private int age;
    //空参数构造方法
    public Student(){

    }
    //有参数构造方法
    public Student(String name,int age){
        this.name=name;
        this.age=age;
    }
}

下面通过反射的方式,来获取Student类的构造方法,并创建对象

public class Demo1{
    public static void main(String[] args){
        //获取类的字节码
        Class clazz=Class.forName("com.itheima01.Student");

        //反射获取空参数构造方法
        Constructor c1=clazz.getContructor();
        //使用空参数构造方法创建对象,等价于 Object obj1=new Student();
        Object obj1=c1.newInstance();

        //反射获取有参数构造方法
        Constructor c2=clazz.getContructor(String.class,int.class);
        //使用有参数构造方法创建对象,等价于 Object obj2=new Student("张三“,18)l
        Object obj2=c2.newInstance("张三",18);

        /*
        还可以通过Class中的newInstance()方法,可以直接创建类的对象。
        底层依赖的是空参数构造方法,如果没有写空参数构造方法,则不使用该方法创建对象。
         等价于:Object obj3=new Student();
        */
        Object obj3=clazz.newInstance();

    }
}

反射获取成员变量

有如下的Student类,类中有两个成员变量,其中name是私有的,age是公有的

public class Student{
  public String name;
  private int age;
}

采用反射的方式,创建Student对象,并给name和age赋值,然后获取name和age的值

public class Demo2{
  public static void main(String[] args){
    //获取Student类的字节码
    Class clazz=Class.forName("com.itheima.Student");
    //反射方式创建Student对象,默认用Object接收
    Object obj=clazz.newInstance();
    //反射获取public修饰的name属性,用f1表示
    Field f1=clazz.getField("name");
    //反射获取private修饰的age属性,用f2表示
    Filed f2=clazz.getDeclaredField("age");
    
    //给obj对象的name属性,赋值为"张三"
    f1.set(obj,"张三"); 
    //给obj对象的age属性,赋值为20
    f2.setAccessible(true); //使age属性对外公开(取消私有权限)
    f2.set(obj,20);

    //获取obj对象的name属性值
    Object v1=f1.get(obj); 
    //获取obj对象的age属性值
    Object v2=f2.get(obj);
    System.out.println("对象地址:"+obj);
    System.out.println("name属性值:"+v1);
    System.out.println("age属性值:"+v2);
  }
}

反射获取成员方法

有如下的Student类,类中有两个成员变量,其中study()是私有的,eat(String food)是公有的

public class Student{
  public void eat(String food){
    System.out.println("学生正在吃..."+sub+"...食物");
  } 
  private void study(String sub){
    System.out.println("学生正在学习..."+sub+"...课程");
  }
}

使用反射的方式,获取eat(String food)方法和study(String sub)并调用

public class Demo3{
  public static void main(String[] args){
    //获取Student类的字节码
    Class clazz=Class.forName("com.itheima.Student");
    //根据空参数构造方法创建Student对象,默认用Object接口
    Object obj=clazz.newInstance();

    //获取public修饰的eat(String food)方法
    //第1个参数: eat 表示方法名
    //第2个参数:表示eat方法的参数有一个,并且是String类型
    Method m1=clazz.getMethod("eat",String.class);
    //执行Student对象的m1方法,也就是eat方法
    //第1个参数:表示上面创建的Student对象
    //第2个参数:表示eat方法接收的实际参数
    //等价于: obj.eat("冰激凌");
    m1.invoke(obj,"冰淇淋");

    //获取private修饰的study(String sub)方法
    Method m2=clazz.getDeclaredMethod("study",String.class);
    //取消study方法的私有权限
    m2.setAccessible(true);
    //调用obj的m2方法,也就是study方法
    //第1个参数:表示上面创建的Student对象
    //第2个参数:表示study方法接收的实际参数
    //等价于: obj.study("语文");
    m2.invoke(obj,"语文");
  }
}

反射练习

通过properties配置文件加反射的方式,执行任意类中的任意方法

先写一个Game类

public class Game{
  public void play(){
    System.out.println("游戏开始,杀杀杀,把自己杀死了,有Bug");
  }
}

再写一个新的NewGame类

public class NewGame{
  public void show(){
    System.out.println("游戏开始,很愉快!!");
  }
}

下面是config.properties配置文件的内容

# classname表示类名
classname=com.itheima.test.Game
# method表示要执行的方法名
method=play

在测试类中读取config.properties配置文件,并且利用反射来执行配置文件中classname对应的类和method对应的方法。

public class Test {
    public static void main(String[] args) throws Exception{
        //读取配置文件
        Properties pro=new Properties();
        pro.load(new FileReader("day01-code\\src\\config.properties"));

        //获取类名
        String classname = pro.getProperty("classname");
        //获取字节码
        Class clazz = Class.forName(classname);
        //通过字节码创建对象
        Object obj = clazz.newInstance();

        //获取方法名
        String method = pro.getProperty("method");
        Method m = clazz.getMethod(method);
        m.invoke(obj);
    }
}

执行的结果为:开始玩游戏,杀杀杀,把自己杀死了....有Bug

这时,当我们把config.properties改为下面的的内容

# 要加载的类
classname=com.itheima.test.NewGame
# 要执行的方法
method=show

测试类不用改动,就会根据配置文件自动执行NewGame类的show方法

执行结果为:开始玩游戏,很愉快....

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值