java反射

     反射这一词,大部分人肯定都听过,但是懂得怎么用的人应该不多,本文咱们就一起学学反射相关的知识

     本文笔记部分内容参考自学习网站http://how2j.cn,这个网站挺不错,推荐大家前往学习。

一、如何获取类对象?

  • 首先理解反射,需要理解什么是类对象?

       类对象概念: 所有的类,都存在一个类对象,类的本质就是一个对象(类对象),程序中第一次使用该类的时候被创建,在整个程序中只有一份。此后每次使用都是这个类对象,它在程序运行时一直存在。这个类对象用于提供类本身的信息,比如有几种构造方法, 有多少属性,有哪些普通方法。

举个例子:garen(盖伦)和teemo(提莫)都是Hero对象,他们的区别在于,各自有不同的名称,血量,伤害值。

然后说说类之间的区别
Hero和Item都是类,他们的区别在于有不同的方法,不同的属性。

  • 类对象的获取方式

       获取类对象有3种方式
        1. Class.forName
        2. Hero.class
        3. new Hero().getClass()

import com.ljm.reflection.Hero;

public class TestReflection {

    public static void main(String[] args) {
        try {
            Class pClass1=Class.forName("com.ljm.reflection.Hero");
            Class pClass2= Hero.class;
            Class pClass3=new Hero().getClass();
            System.out.println(pClass1==pClass2);
            System.out.println(pClass1==pClass3);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

运行上述代码可知,上述三种方式创建的类对象是一样的。

另外值得一提的是:无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。(除了直接使用 Class c = Hero.class 这种方式,这种方式不会导致静态属性被初始化)

二、如何创建对象?

  • 创建一个对象

实体类

java反射_第1张图片

测试类

import com.ljm.reflection.Hero;
import java.lang.reflect.Constructor;
public class TestReflection {

    public static void main(String[] args) {
        //传统的使用new的方式创建对象
        Hero h1 =new Hero();
        h1.name = "teemo";
        System.out.println(h1);

        try {
            //使用反射的方式创建对象
            String className = "com.ljm.reflection.Hero";
            //类对象
            Class pClass=Class.forName(className);
            //构造器
            Constructor c= pClass.getConstructor();
            //通过构造器实例化
            Hero h2= (Hero) c.newInstance();
            h2.name="gareen";
            System.out.println(h2);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

控制台打印:

  • 通过配置文件获取对象

给定一个配置文件config.txt放置于src下,内容为:

测试类

import com.ljm.reflection.Hero;

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.Properties;

public class TestReflection {

    public static void main(String[] args) throws Exception {
        //创建配置
        Properties p = new Properties();
        InputStream stream = TestReflection.class.getClassLoader()
                .getResourceAsStream("config.txt");
        //加载流中数据
        p.load(stream);
        //获取流中数据
        String className = p.getProperty("className");
        //类对象
        Class pClass=Class.forName(className);
        //构造器
        Constructor c= pClass.getConstructor();
        //通过构造器实例化
        Hero h2= (Hero) c.newInstance();
        h2.name="gareen";
        System.out.println(h2);

    }
}

运行结果:

三、如何访问属性?

  • 修改Hero类(get/set方法不截图说明)

java反射_第2张图片

测试类

import com.ljm.reflection.Hero;

import java.lang.reflect.Field;

public class TestReflection {

    public static void main(String[] args) {
        Hero h =new Hero();
        //使用传统方式修改name的值为garen
        h.name = "garen";
        h.setHp(10);
        System.out.println(h.toString());
        try {
            //获取类Hero的名字叫做name的字段
            Class hClass = h.getClass();
            Field f1 = hClass.getDeclaredField("name");
            Field f2 = hClass.getDeclaredField("hp");
            //修改这个字段的值
            f1.set(h, "teemo");
            //为私有属性设置可修改
            f2.setAccessible(true);
            f2.set(h, 30);
            //打印被修改后的值
            System.out.println(h.toString());

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

控制台打印:

  • getField和getDeclaredField

用Class的getField(String name)或getDelaredField(String name)可以得到目标类的指定属性,返回类型是Field。

但这两个是有区别的:

getField(String name)只能获取public的字段,包括父类的;

而getDeclaredField(String name)只能获取自己声明的各种字段,包括public,protected,private(注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加上setAccessible(true))。

四、如何调用方法?

在Hero中添加一个attack方法:

测试类

import com.ljm.reflection.Hero;

import java.lang.reflect.Method;

public class TestReflection {

    public static void main(String[] args) throws Exception {
        Class hClass = Class.forName("com.ljm.reflection.Hero");
        Hero hero = (Hero) hClass.newInstance();

        //第一种方法
        System.out.println("第一次攻击:");
        hero.attack("物理");
        System.out.println("------------------------分割线------------------------");

        //第二种方法
        System.out.println("第二次攻击:");
        //方法参数:方法名,方法参数类型(可变参数)
        Method attack = hClass.getMethod("attack", String.class);
        //执行方法,方法参数:底层方法对象,传入参数(可变参数)
        attack.invoke(hero, "魔法");
        System.out.println("------------------------分割线------------------------");

    }
}

测试结果:

java反射_第3张图片

五、反射有什么用?

      我们现在已经学习了反射了,但是我们学习它有什么用呢?有时候调用方法还不如对象直接调用来着方便。

      其实,我们项目所使用的框架的底层大都有用到,例如spring的aop,现在举一个场景来说明反射的作用。

      假如现在有两个业务类,对应本身的业务方法

java反射_第4张图片java反射_第5张图片

       如果我们原本调用的是业务一的方法,然后这时候我们需要调用业务二的,这时候我们就需要修改相应的java代码,极大的限制了项目的扩展性。这时候我们将类和方法写在配置文件中,若有改动直接修改配置文件岂不是美哉。

你可能感兴趣的:(java)