Java工作笔记-对反射的进一步理解
发布日期:2021-06-30 10:41:03 浏览次数:2 分类:技术文章

本文共 8446 字,大约阅读时间需要 28 分钟。

目录

 

 


 

基本概念

反射的进一步理解:

将类的各个组成部分封装为其他对象(将一个类的组成部分封装为其他对象)
就是反射。

 

Java代码经历的三个阶段:

1. 写好代码.java后使用javac,编译为字节码文件:xxx.class
    这个阶段称之为source源代码阶段。

3. 当程序要new一个对象时
    这个阶段称之为Runtime运行时阶段

2. 把字节码文件.class加载进内存里面(使用类加载器ClassLoader)
在内存里面会存在一个对象用于描述字节码文件。这个对象为Class对象。
所以java里面有个Class类。专门用来搞这个字节码文件的
如:
成员变量(Fields)、构造方法(Constructor)、成员方法(Method)
这个阶段称之为Class类对象阶段

 

第三阶段都是同过第二阶段创建起来的。第二个阶段需要第一个阶段。

 

反射好处:

          1. 在程序运行过程中操作这些对象。(IDEA这个程序的 某个对象方法的提示,就是反射)
          2. 可以解耦。提高程序可扩展性。

 

获取Class对象的方式:

有3种方式,对应java代码经历的3个阶段;
第一个阶段只有.class字节码文件,并没有在内存中,要把.class加载到内存中生成对象。对应的API为:Class.forName("全类名") 返回class对象;
第二个阶段:这个.class字节码文件已经到内存了,这个时候,只要获取他就可以了,类名.class,这种方式。
第三个阶段:在Runtime时,已经有对象了,这个时候只要 对象.getClass()就可以了。这个getClass()类是在Object里面
的。

 

 

总结下:

          1.Class.forName("全类名");将字节码文件加载到内存,返回class对象。
          2.类名.class;通过类名的属性class获取。
          3.对象.getClass();getClass()方法在Object类中定义

 

 

 

 

代码与实例

如下代码:

Person.java

package Object;public class Person {    private String name;    private Integer age;    public Person(String name, Integer age) {        this.name = name;        this.age = age;    }    public Person(){    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }    @Override    public String toString() {        return "Person{" +                "name='" + name + '\'' +                ", age=" + age +                '}';    }}

Main1.java

import Object.Person;public class Main1 {    public static void main(String[] args) throws ClassNotFoundException {        //全类名        Class cls1 = Class.forName("Object.Person");        System.out.println(cls1);        //类名.class        Class cls2 = Person.class;        System.out.println(cls2);        //对象.getClass()        Person p = new Person();        Class cls3 = p.getClass();        System.out.println(cls3);        //比较下对象:        System.out.println(cls1 == cls2);        System.out.println(cls1 == cls3);    }}

程序运行截图如下:

结论:

同一个字节码文件.class在一次程序运行过程中,只会被加载一次,不论通过哪种方式获取的class对象都是同一个。
第一种方式,多用于配置文件中,读取文件,加载类
第二种方式,多用于参数的传递;
第三种方式,多用于对象的获取字节码
 

下面是

Class对象功能:

1.获取成员变量;
          Field[] getFields()
          Field getField(String name)
          Field[] getDeclaredFields()
          Field getDeclaredField(String name)
2.获取构造方法;
          Constructor<?>[] getConstructors()
          Constructor<T> getConstructor(类<?>... parameterTypes)
          Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
          Constructor<?>[] getDeclaredConstructors()
3.获取成员方法;
          Method[] getMethods()
          Method[] getMethod(String name, 类<?>... parameterTypes)
          Method[] getDeclaredMethods()
          Method getDeclaredMethod(String name, 类<?>... parameterTypes)
4.获取类名;
          String getName()

 

下面先给出变量名的实例

Person.java

package Object;public class Person {    private String name;    private Integer age;    public String a;    protected String b;    String c;    private String d;    public Person(String name, Integer age) {        this.name = name;        this.age = age;    }    public Person(){    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }    @Override    public String toString() {        return "Person{" +                "name='" + name + '\'' +                ", age=" + age +                ", a='" + a + '\'' +                ", b='" + b + '\'' +                ", c='" + c + '\'' +                ", d='" + d + '\'' +                '}';    }}

Main2.java

import Object.Person;import java.lang.reflect.Field;public class Main2 {    public static void main(String[] args) throws Exception {        //Field[] getFields()获取所有public修饰的成员变量        Class aClass = Class.forName("Object.Person");        Field[] fields = aClass.getFields();        for(Field field : fields){            System.out.println(field);        }        //获取及设置a的值        Field a = aClass.getField("a");        Person person = new Person();        Object value = a.get(person);        System.out.println("the a is : " + value);        a.set(person, "你妹");        System.out.println(person);        System.out.println("----------华丽的分割线----------");        //Field getDeclaredField(String name)        Field[] declaredFields = aClass.getDeclaredFields();        for(Field field : declaredFields){            System.out.println(field);        }        //忽略修饰符暴力反射,设置值        Field d = aClass.getDeclaredField("d");        d.setAccessible(true);        d.set(person, "猪小明");        System.out.println(person);    }}

运行截图如下:

下面是关于构造函数的反射:

Person还是与上面的一样。

import Object.Person;import java.lang.reflect.Constructor;public class Main3 {    public static void main(String[] args) throws Exception {        Class personClass = Person.class;        Constructor constructor = personClass.getConstructor(String.class, Integer.class);        System.out.println(constructor);        //创建对象        Object p = constructor.newInstance("王二麻子", 99);        System.out.println(p);        System.out.println("----------华丽的分割线----------");        Constructor constructor2 = personClass.getConstructor();        Object p2 = constructor2.newInstance();        System.out.println(p2);    }}

程序运行截图如下:

下面是获取成员方法的实例。

Person.java

package Object;public class Person {    private String name;    private Integer age;    public String a;    protected String b;    String c;    private String d;    public Person(String name, Integer age) {        this.name = name;        this.age = age;    }    public Person(){    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }    @Override    public String toString() {        return "Person{" +                "name='" + name + '\'' +                ", age=" + age +                ", a='" + a + '\'' +                ", b='" + b + '\'' +                ", c='" + c + '\'' +                ", d='" + d + '\'' +                '}';    }    public void eat(){        System.out.println("eat... ...");    }    //带参数的    public void eat(String food){        System.out.println("eat... ..." + food);    }}

Main4.java

import Object.Person;import java.lang.reflect.Method;public class Main4 {    public static void main(String[] args) throws Exception {        Class personClass = Person.class;        //获取指定名称的方法        Method eat = personClass.getMethod("eat");        Person p = new Person();        eat.invoke(p);        System.out.println("----------华丽的风格线----------");        Method eat1 = personClass.getMethod("eat", String.class);        eat1.invoke(p, "西北风");        //获取所有Public修饰的方法        Method[] methods = personClass.getMethods();        for(Method method : methods){            System.out.println(method);            String name = method.getName();            System.out.println(name);            //method.setAccessible(true);        }    }}

程序运行截图如下:

下面是关于获取类名的实例:

Main5.java

import Object.Person;public class Main5 {    public static void main(String[] args){        Class personClass = Person.class;        System.out.println(personClass.getName());    }}

程序运行截图如下:

import Object.Person;public class Main5 {    public static void main(String[] args){        Class personClass = Person.class;        System.out.println(personClass.getName());    }}

程序运行截图如下:

 

 

下面来仿一个案例,像Spring Boot那样的通过配置application.properties既可以调用相应的类,和函数

这里应用到的就是反射

Main6.java

import Object.Person;import java.io.InputStream;import java.lang.reflect.Method;import java.util.Properties;public class Main6 {    public static void main(String[] args) throws Exception {        /***         * 加载配置文件         *      创建Properties对象         *      加载配置文件,转换为集合         *      获取class目录下的配置文件         */        Properties pro = new Properties();        ClassLoader classLoader = Person.class.getClassLoader();        InputStream resourceAsStream = classLoader.getResourceAsStream("application.properties");        pro.load(resourceAsStream);        //配置文件中定义的数据        String className = pro.getProperty("className");        String methodName = pro.getProperty("methodName");        //加载该类到内存        Class aClass = Class.forName(className);        Object o = aClass.newInstance();        //获取方法并执行        Method method = aClass.getMethod(methodName);        method.invoke(o);    }}

通过这种配置文件,即可调用对应的类和方法:

程序运行截图如下:

源码打包下载地址:

转载地址:https://it1995.blog.csdn.net/article/details/103556575 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Java工作笔记-注解的进一步理解
下一篇:Niginx工作笔记-通过error.log定位错误(记录一个寻找问题的方法)

发表评论

最新留言

表示我来过!
[***.240.166.169]2024年04月25日 12时35分38秒