Java反射-Java反射概念、如何使用以及一个简单案例

1 篇文章 0 订阅

反射定义

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

java代码三个阶段:

  1. 将.java文件编译为.class文件(字节码),源代码阶段
  2. 将.class文件加载到内存中,Class类对象阶段
  3. 在java中创建该类的一个对象,运行时阶段

在这里插入图片描述

获取Class对象的三种方式

在java中万物皆对象

  1. Class.forName(“全类名”),例如:Class.forName(“com.xxx.xxx.Person”),该方法将字节码加载到内存中,常在配置文件中使用(因为传一个字符串就可以得到Class对象);
  2. 类名.class,例如Class cls=Person.class,该方法在字节码加载到内存后获得Class对象;
  3. 对象.getClass(),例如:new Person().getClass(),该方法在运行时获取Class对象。

在这里插入图片描述
测试代码:
建一个Person类:

IDEA快捷键(Alt+Insert),快速创建构造方法和get、set方法

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    //快捷键Alt+Insert,生成构造方法,getter、setter
    public Person(String name,int age) {
        this.name = name;
        this.age=age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试三种获取Class对象的方法:

IDEA快捷键:Ctrl+Alt+V自动补全返回值

/**
 * 获取Class对象的三种方式
 */

public class ReflectDemo {
    public static void main(String[] args) throws Exception{
        //快捷键Ctrl+Alt+v,自动补全返回值
        Class class1=Class.forName("Person");
        System.out.println(class1);//class Person

        Class class2=Person.class;
        System.out.println(class2);//class Person

        Person person=new Person("xiaoming",5);
        Class class3 = person.getClass();
        System.out.println(class3);//class Person

        //==判断类地址是否相同
        System.out.println(class1==class2);//true
        System.out.println(class1==class3);//true
    }
}

最后“==”判断两个对象的地址是否相同,结果三种方法指向同一地址,即字节码加载到内存中的地址。

Class对象功能

获取成员变量并对其使用get和set方法

获取成员变量的四种方法:

  1. Field getField(String name)
    返回一个 Field对象,只能是public方法
  2. Field[] getFields()
    返回一个Field数组,public方法的集合
  3. Field getDeclaredField(String name)
    返回一个Field对象,可以是public\protect\private修饰的方法
  4. Field[] getDeclaredFields()
    返回 Field数组,是所有方法的集合

测试:
在原Person类中新创建了a,b,c,d四个成员变量

public class Person {
    private String name;
    private int age;

    public String a;
    protected String b;
    String c;
    private String d;
    public Person() {
    }

    //快捷键Alt+Insert,生成构造方法,getter、setter
    public Person(String name,int age) {
        this.name = name;
        this.age=age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }
}

分别使用四种方法获取成员变量:

public class ReflectDemo2 {
    public static void main(String[] args)throws Exception {
        /**
         * 获取成员变量
             *1 Field getField(String name)
             返回一个 Field对象反映的类或接口的 类对象表示的指定公共成员。

             *2 Field[] getFields()
             返回一个数组包含 Field物体反射的类或接口的 类对象代表所有可访问的公共领域。

            *3 Field getDeclaredField(String name)
             返回一个对象,反映了 Field指定声明字段的类或接口的 类对象表示。

             *4 Field[] getDeclaredFields()
             返回 Field物体反射所有字段的类或接口的 类对象表示声明数组。
         */

        //创建一个Class对象
        Class personClass = Person.class;
		//方法一:
        //getField()和getFields()方法只能得到public修饰的成员变量
        Field f1=personClass.getField("a");
        System.out.println(f1);//输出:public java.lang.String Person.a

        //Field f2=personClass.getField("b");
        //System.out.println(f2); 报错:Exception in thread "main" java.lang.NoSuchFieldException: b

        //新建一个Person对象
        Person person=new Person();
        //测试Field类的get和set方法:
        //获取成员变量a的值
        Object value=f1.get(person);
        System.out.println(value);//输出:null
        //为对象设置值,Feild对象调用
        f1.set(person,"xiaoming");
        System.out.println(person.toString());//输出:Person{name='null', age=0, a='xiaoming', b='null', c='null', d='null'}
		
		//方法二:
        Field[] f3 = personClass.getFields();
        for(Field f:f3)
        {
            System.out.println(f);//输出:public java.lang.String Person.a  ,只有a是public成员变量
        }
		//方法三:
        //getDeclaredField()和getDeclaredFields()方法,可以获取所有成员变量
        Field f4=personClass.getDeclaredField("a");
        System.out.println(f4);//输出:public java.lang.String Person.a
        Field f5=personClass.getDeclaredField("b");
        System.out.println(f5);//输出:protected java.lang.String Person.b
        Field f6=personClass.getDeclaredField("c");
        System.out.println(f6);//输出:java.lang.String Person.c
        Field f7=personClass.getDeclaredField("d");
        System.out.println(f7);//输出:private java.lang.String Person.d
		
		//方法四:
        Field[]f8=personClass.getDeclaredFields();
        for(Field f:f8)
        {
            System.out.println(f);
            /*输出:
            private java.lang.String Person.name
            private int Person.age
            public java.lang.String Person.a
            protected java.lang.String Person.b
            java.lang.String Person.c
            private java.lang.String Person.d
             */
        }
        //忽略访问权限修饰符的安全检查,也称为暴力反射
        f7.setAccessible(true);
        //获取私有变量的值
        Object value2=f7.get(person);
        System.out.println(value2);//在未忽略访问权限修饰符的安全检查时会报错:java.lang.IllegalAccessException: Class ReflectDemo2 can not access a member of class Person with modifiers "private"}
}

当我们获取到Field对象时,我们便可以调用get和set方法来获取和设置成员变量的值(当然和成员变量的修饰符有关)。
在这里插入图片描述
在这里插入图片描述

获取构造函数并创建对象

和获取成员变量一样,获取构造函数也有四种方法:

  1. Constructor getConstructor(Class<?>… parameterTypes)
    返回一个Constructor对象,public修饰的构造函数。
  2. Constructor<?>[] getConstructors()
    返回一个Constructor数组,public修饰的构造函数集合。
  3. ConstructorgetDeclaredConstructor(Class<?>…parameterTypes)
    返回一个 Constructor对象(public、private修饰的构造函数)。
  4. Constructor<?>[] getDeclaredConstructors()
    返回一个Constructor数组,包含所有构造函数(public、private修饰的构造函数)。

测试:

Person还是之前的Person

import java.lang.reflect.Constructor;

/*
使用反射获取类的构造方法
* */
public class ReflectDemo3 {
    public static void main(String[] args)throws Exception {
        Class cls=Person.class;
        Constructor constructor1=cls.getConstructor();
        System.out.println(constructor1);//public Person()

        Constructor constructor2=cls.getConstructor(String.class, int.class);
        System.out.println(constructor2);//public Person(java.lang.String,int)

        //创建对象:
        //constructor2.newInstance()返回值为Object,在本例中实际就是一个Person对象
        System.out.println(constructor2.newInstance("张三",12));//Person{name='张三', age=12, a='null', b='null', c='null', d='null'}

        //创建空参对象,可以直接调用Class对象的newInstance()方法
        System.out.println(cls.newInstance());//Person{name='null', age=0, a='null', b='null', c='null', d='null'}
    }
}

这里只测试了前两种方法,获取构造函数当然是为了初始化对象,在示例中展示了两种创建对象的方法,一种是使用Constructor对象创建的Person对象,另一种是使用Class对象构造的Person对象,这种方式适用于无参构造函数。

获取成员方法并使用该方法

通过上面的学习,我想我们能更轻松的掌握如何获取成员方法以及如何使用该方法了,下面来直接做展示:
同样有四种方法获取成员方法:

  1. Method getDeclaredMethod(String name, 类<?>… parameterTypes)
    返回一个Method 对象反映指定声明方法的类或接口的 类对象表示。
  2. Method[] getDeclaredMethods()
    返回一个数组包含 方法物体反射所有声明的方法的类或接口的类对象,代表包括公众、保护,默认(包)的访问,和私有方法,但不包括继承的方法。
  3. Method getMethod(String name, 类<?>… parameterTypes)
    返回一个方法对象反映的类或接口的 类对象表示的指定公共成员方法。
  4. Method[] getMethods()
    返回一个数组包含 方法物体反射所有的类或接口的类对象表示的公共方法,包括那些由类或接口的超类和超接口继承的声明。

测试:

import java.lang.reflect.Method;

/*
使用反射获取类的成员方法
* */
public class ReflectDemo4 {
    public static void main(String[] args)throws Exception {
        Class cls=Person.class;
        Method method1=cls.getMethod("eat");
        System.out.println(method1);//public void Person.eat()
        method1.invoke(new Person());//执行eat()方法,输出:eat...

        Method[] methods=cls.getMethods();
        for(Method method:methods){
            System.out.println(method.getName());//获取所有方法,并输出方法名
            /*输出:toString
                    getName
                    setName
                    eat
                    eat
                    getAge
                    setAge
                    wait
                    wait
                    wait
                    equals
                    hashCode
                    getClass
                    notify
                    notifyAll*/
        }
        Method method2=cls.getMethod("eat",String.class);
        method2.invoke(new Person(),"noodles");//执行eat(String food),方法,输出:eat noodles
    }
}

该测试展示了如何获取成员方法,以及如何执行该方法(invoke()方法)。

用反射实现框架实例

在不改动代码,只改动配置文件的情况下就可以创建任意类的对象,执行该类的任意方法

步骤:

  1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
  2. 在程序中加载读取配置文件
  3. 使用反射技术来加载类文件进内存
  4. 创建对象
  5. 执行方法

Person类已经含有一个eat()方法
创建一个Student类,创建一个Sleep()方法:

public class Student {
    public Student() {
    }

    public void Sleep()
    {
        System.out.println("Sleep...");
    }
}

创建一个配置文件命名为pro.properties

className=Person
methodName=eat

创建一个Frame类,实现通过加载配置文件,来执行配置文件中的类和方法:

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;

import java.io.InputStream;

public class Frame {
    public static void main(String[] args)throws Exception {
        //1.加载配置文件
        //1.1创建properties对象
        Properties properties=new Properties();
        //1.2加载配置文件,转化为一个集合
        //1.2.1获取class目录下的配置文件
        ClassLoader classLoader=Frame.class.getClassLoader();//Frame.class:获取Frame类的字节码文件;getClassLoader()方法获取该字节码的类加载器
        InputStream is = classLoader.getResourceAsStream("pro.properties");//找到该文件对应的字节流
        properties.load(is);//加载该文件信息

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

        //3.加载“className”进内存
        Class cls = Class.forName(className);

        //4.创建类的对象
        Constructor constructor = cls.getConstructor();
        Object obj = constructor.newInstance();

        //5.执行methodName对应的方法
        Method method = cls.getMethod(methodName);
        method.invoke(obj);
    }
}

执行结果:
在这里插入图片描述
将配置文件改为:

className=Student
methodName=Sleep

执行结果为:
在这里插入图片描述

总结

本文内容主要来源于B站视频的学习笔记,建议去看视频。
介绍了Java反射原理、获取类对象的三种方式、如何获取类成员变量、构造方法、成员方法,最后通过一个“框架”案例介绍了Java反射的用途。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值