Java-反射机制

获取类对象

1. 什么是类对象

类对象概念:所有的类,都存在一个类对象,这个类对象用于提供类本身的信息,比如有几种构造方法,有多少属性,有哪些普通方法。

  • 对象之间的区别:garen 和 teemo 都是 Hero 对象,他们的区别在于,各自有不同的名称,血量,伤害值。
  • 类之间的区别:Hero 和 Item 都是类,他们的区别在于有不同的方法,不同的属性。

类对象,就是用于描述这种类,都有什么属性,什么方法。

2. 获取类对象

获取类对象有三种方式:

  1. Class.forName
  2. Hero.class
  3. new Hero().getClass()

在一个JVM中,一种类。只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。

3. 获取类对象的时候,会导致类属性被初始化

为Hero增加一个静态属性,并在静态初始化块里进行初始化。

package charactor;

public class Hero {
	public String name;
	public float hp;
	public int damage;
	public int id;

	static String copyright;

	static {
		System.out.println("初始化 copyright");
		copyright = "版权由Riot Games公司所有";
	}

}

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

package reflection;

import charactor.Hero;

public class TestReflection {

	public static void main(String[] args) {
			String className = "charactor.Hero";
			try {
				Class pClass1=Class.forName(className);
				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();
			}
	}
}

创建对象

与传统的通过 new 来获取对象的方式不同。反射机制,会先拿到Hero的“类对象”,然后通过类对象获取“构造器对象”,再通过构造器对象创建一个对象。

package reflection;
import java.lang.reflect.Constructor;
import charactor.Hero;
public class TestReflection {
 
    public static void main(String[] args) {
    	//传统的使用new的方式创建对象
        Hero h1 =new Hero();
        h1.name = "teemo";
        System.out.println(h1);
         
        try {
        	//使用反射的方式创建对象
            String className = "charactor.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();
        }
    }
}

访问属性

1. Hero.java

为了访问属性,把name修改为public。对于private修饰的成员,需要使用setAccessible(true)才能访问和修改。 

package charactor;

public class Hero {
	public String name;
	public float hp;
	public int damage;
	public int id;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Hero(){
		
	}
	public Hero(String string) {
		name =string;
	}

	@Override
	public String toString() {
		return "Hero [name=" + name + "]";
	}
	public boolean isDead() {
		// TODO Auto-generated method stub
		return false;
	}
	public void attackHero(Hero h2) {
		System.out.println(this.name+ " 正在攻击 " + h2.getName());
	}

}

2. TestRelection

通过反射修改属性的值

package reflection;

import java.lang.reflect.Field;

import charactor.Hero;
 
public class TestReflection {
 
    public static void main(String[] args) {
            Hero h =new Hero();
            //使用传统方式修改name的值为garen
            h.name = "garen";
            try {
            	//获取类Hero的名字叫做name的字段
                Field f1= h.getClass().getDeclaredField("name");
                //修改这个字段的值
                f1.set(h, "teemo");
                //打印被修改后的值
                System.out.println(h.name);
                
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }
}

3. getField 和 getDeclareField 的区别

这两个方法都是用于获取字段

  • getField 只能获取public的,包括从父类继承来的字段。
  • getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加上setAccessible(true))

调用方法

首先为Hero的name属性,增加setter和getter

用反射机制调用Hero的setName

package reflection;

import java.lang.reflect.Method;

import charactor.Hero;

public class TestReflection {

	public static void main(String[] args) {
		Hero h = new Hero();

		try {
			// 获取这个名字叫做setName,参数类型是String的方法
			Method m = h.getClass().getMethod("setName", String.class);
			// 对h对象,调用这个方法
			m.invoke(h, "盖伦");
			// 使用传统的方式,调用getName方法
			System.out.println(h.getName());

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

	}
}

用处

反射非常强大。

1. 业务类

首先准备两个业务类,这两个业务类很简单,就是各自都有一个业务方法,分别打印不同的字符串

package reflection;

public class Service1 {

	public void doService1(){
		System.out.println("业务方法1");
	}
}
package reflection;

public class Service2 {

	public void doService2(){
		System.out.println("业务方法2");
	}
}

2. 非反射方式

当需要从第一个业务方法切换到第二个业务方法的时候,使用非反射方式,必须修改代码,并且重新编译运行,才可以达到效果

package reflection;

public class Test {

	public static void main(String[] args) {
		new Service1().doService1();
	}
}
package reflection;

public class Test {

	public static void main(String[] args) {
//		new Service1().doService1();
		new Service2().doService2();
	}
}

3. 反射方式

使用反射方式,首先准备一个配置文件,就叫做spring.txt吧, 放在src目录下。 里面存放的是类的名称,和要调用的方法名。
在测试类Test中,首先取出类名称和方法名,然后通过反射去调用这个方法。
当需要从调用第一个业务方法,切换到调用第二个业务方法的时候,不需要修改一行代码,也不需要重新编译,只需要修改配置文件spring.txt,再运行即可。
这也是Spring框架的最基本的原理,只是它做的更丰富,安全,健壮。

spring.txt:

class=reflection.Service1
method=doService1

Test.java: 

package reflection;

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;

public class Test {

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static void main(String[] args) throws Exception {

		//从spring.txt中获取类名称和方法名称
		File springConfigFile = new File("G:\\JavaWeb\\Workspaces\\TestReflection\\src\\spring.txt");
		Properties springConfig= new Properties();
		springConfig.load(new FileInputStream(springConfigFile));
		String className = (String) springConfig.get("class");
		String methodName = (String) springConfig.get("method");
		
		//根据类名称获取类对象
		Class clazz = Class.forName(className);
		//根据方法名称,获取方法对象
		Method m = clazz.getMethod(methodName);
		//获取构造器
		Constructor c = clazz.getConstructor();
		//根据构造器,实例化出对象
		Object service = c.newInstance();
		//调用对象的指定方法
		m.invoke(service);
		
	}
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值