Java工作笔记-对反射的进一步理解

370 篇文章 20 订阅
118 篇文章 4 订阅

目录

 

 

基本概念

代码与实例


 

基本概念

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

 

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://github.com/fengfanchen/Java/tree/master/ReflectDemo

本文档是本人5年JAVA经验整理的文档(持续性的),由于CSDN只能单个附件只能上传15MB,所以只好分包压缩了(4个包),希望各位同行能够理解。 本文档包括JAVA/FLEX/数据建模/Database/移动办公/项目管理/单点登录/软件架构/测试等相关性技术。 本文档还在持续维护中,各位可以通过《笔记变更说明》查看升级日志。 在这里声明下,本文档一部分是个人从学习、工作中整理出来的(代码都是经过调试的),还有一部分是从网上整理的。 本站相关文档都是经过后期整理的,或是在实际工作中整理的实例代码总结而成的。 由于本人目前允许上传的资源太小,所以无法上传整个的技术学习笔记JAVA 五年的工作经验和学习笔记),待以后有机会会分享给大家。 技术体系包括: J2SE/J2ME/J2EE/JAVA代码优化/Flex(BlazeDS、PureMVC等技术)/LDAP/C++/Portal/即时通讯/数据建模/UML/UML设计工具(Rose、EA、PD等)/移动办公(Android、Symbian、Wap等技术)/项目管理(敏捷开发等)/软件架构(NoSQL、SaaS、设计模式等)/数据库(MySQL、Oracle、EDB、SQLServer等)/测试(单元测试、压力测试)/linux服务器等 其中含有大量实例源代码。 这里需要说明的时,该文档是本人5年工作经验的积累,文档中大部分知识点来源于实际工作中的总结,(除了JAR等资源文件外)其代码都是可运行的,还有一部分知识来源于网络或者其他书籍,这里做一些收集,使该体系更加完善。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT1995

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值