Java---反射入门

类对象

所有的类,都存在一个类对象,提供类的属性和方法等信息。一个ClassLoader下,一种类,只会有一个类对象存在。

获取类对象

在这里插入图片描述
通过Class.forName或getClass获取类对象,会导致静态初始化块的执行。多次获取类对象也只会执行一次静态初始化块。Hero.class不会执行静态初始化块。

package reflection;

import character.Hero;

public class TestReflection {
	public static void main(String[] args){
		String className = "character.Hero";
		try {
			Class hClass1 = Class.forName(className); // 会执行静态初始化块
			Class hClass2 = Hero.class; // 不会执行静态初始化块
			Class hClass3 = new Hero().getClass(); // 会执行静态初始化块
			System.out.println(hClass1 == hClass2);
			System.out.println(hClass1 == hClass3);
			System.out.println(hClass2 == hClass3);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

package character;

public class Hero {
	private int id;
	private String name;
	private float hp;
	private int damage;
	
    static {
        System.out.println("执行静态初始化块...");
    }
}

创建类对象

package reflection;

import java.lang.reflect.Constructor;

import character.Hero;

public class TestReflection {
	public static void main(String[] args){
		// 1. 传统方式创建对象
		Hero hero1 = new Hero();
		System.out.println("new对象: " + hero1);
		
		// 2. 反射方式创建对象
		String className = "character.Hero";
		try {
			Class pClass = Class.forName(className);
			Constructor constructor = pClass.getConstructor();
			Hero hero2 = (Hero)constructor.newInstance();
			System.out.println("反射对象: " + hero2);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

访问属性和方法

package character;

public class Hero {
	private int id;
	private String name;
	private float hp;
	private int damage;
	
	private String getName() {
		return name;
	}
	private void fun(String name) {
		System.out.println(name + " fun...");
	}
}

package reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import character.Hero;

public class TestReflection {
	public static void main(String[] args){
		String className = "character.Hero";
		try {
			Class pClass = Class.forName(className);
			Constructor constructor = pClass.getConstructor();
			Hero hero = (Hero)constructor.newInstance();
			// 反射获取属性name
			// getField只能获取public的,包括从父类继承来的字段(方法同理)
			// getDeclaredField可以获取本类所有字段,包括private的,但不能获得继承来的字段(方法同理)
			Field fName = pClass.getDeclaredField("name");
			fName.setAccessible(true); // 访问私有属性需要设置下
			fName.set(hero, "悟空");
			
			// 反射调用方法getName
			Method mGetName = pClass.getDeclaredMethod("getName");
			mGetName.setAccessible(true);
			String name = (String)mGetName.invoke(hero);
			System.out.println(mGetName.invoke(hero));
			// 反射调用方法fun
			Method mFun = pClass.getDeclaredMethod("fun", String.class);
			mFun.setAccessible(true);
			mFun.invoke(hero, "八戒");
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述

反射应用举例

有两个业务类及测试类,如下。每个业务类都有对应的方法。
在这里插入图片描述

package reflection;

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

package reflection;

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

  1. 传统方式。需要硬编码来确定做哪个业务。
public class Test {
	public static void main(String[] args) throws Exception{
		// new Service1().doService1();
		new Service2().doService2();
	}
}
  1. 反射方式。结合配置文件确定做哪个业务。
    在这里插入图片描述

spring.txt

className=reflection.Service1
methodName=doService1
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 {
	public static void main(String[] args) throws Exception{
		File springConfigFile = new File("C:\\CCNU\\Java\\project\\j2se\\src\\spring.txt");
		Properties springConfig = new Properties();
		springConfig.load(new FileInputStream(springConfigFile));
		
		String className = springConfig.getProperty("className");
		String methodName = springConfig.getProperty("methodName");
		
		Class clazz = Class.forName(className);
		Constructor constructor = clazz.getConstructor();
		Object service = constructor.newInstance();
		Method m = clazz.getDeclaredMethod(methodName);
		m.invoke(service);
	}
}

在这里插入图片描述

当需要从调用第一个业务方法,切换到调用第二个业务方法的时候,不需要修改一行代码,也不需要重新编译,只需要修改配置文件spring.txt,再运行即可。

参考资料

[1] How2J
[2] 《疯狂Java讲义(第4版)》 李刚
[3] 《Java核心技术 卷I》
[4] 《Java核心技术 卷II》

发布了323 篇原创文章 · 获赞 193 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/ccnuacmhdu/article/details/104034762