1.获取类对象
1.1类对象概念:
所有的类,都存在一个类对象,这个类对象用于提供类本身的信息,比如有几种构造方法,有多少属性,有哪些普通方法。
在理解类对象之前,先说我们熟悉的对象之间的区别:
garen和teemo都是Hero对象,他们的区别在于,各自有不同的名称,血量,伤害值。
然后说说类之间的区别
Hero和Item都是类,他们的区别在于有不同的方法,不同的属性。
类对象,就是用于描述这种类,都有什么属性,什么方法的
1.2获取类对象Class
一种类,只会有一个类对象存在。所以以下三种方式取出来的类对象,都是一样的。
- class.forName("com.cxy.Hero")
- Hero.class
- Hero hero = new Hero(); hero.getClass()
准确的讲是一个ClassLoader(类加载器)下,一种类,只会有一个类对象存在。通常一个JVM下,只会有一个ClassLoader。
调用Class对象的getConstructor(Class<?>... parameterTypes)获取构造方法对象
调用是构造方法类Constructor的newInstance(Object... initargs)方法新建对象
调用Class对象的getMethod(String name, Class<?>... parameterTypes)获取方法对象
调用方法对象类Method的invoke(Object obj, Object... args)方法,调用对象上相应方法
1.3获取类对象的时候,会导致类属性被初始化
通过在Hero类中增加static属性,以及在代码块进行初始化的方式进行验证
static String copyright;
static{
System.out.println("初始化 copyright");
}
除了直接使用 Class c = Hero.class 这种方式,其他途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。
应用在jdbc
2.创建对象
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();
}
}
}
3.修改属性
为了访问属性,把name修改为public。
对于private修饰的成员,需要使用setAccessible(true)才能访问和修改。不在此知识点讨论。
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();
}
}
}
getField和getDeclaredField的区别getField和getDeclaredField的区别 |
4.调用方法
通过反射机制调用Hero的setName
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();
}
}
}
4.有什么用?
通常来说,需要在学习了Spring 的依赖注入,反转控制之后,才会对反射有更好的理解,所以在这里举两个例子,来演示一下反射的一种实际运用。
业务类
首先准备两个业务类,这两个业务类很简单,就是各自都有一个业务方法,分别打印不同的字符串
package reflection;
public class Service1 {
public void doService1() {
System.out.println("业务方法1");
}
}
package reflection;
public class Service2 {
public void doService2() {
System.out.println("业务方法2");
}
}
场景模拟:当需要从第一个业务方法切换到第二个业务方法的时候
4.1非反射方式
必须修改代码,并且重新编译运行,才可以达到效果
package reflection;
public class Test {
public static void main(String[] args) {
//new Service1().doService1(); //修改代码,重新编译
new Service2().doService2();
}
}
4.2反射方式
首先准备一个配置文件,就叫做spring.txt吧, 放在src目录下。 里面存放的是类的名称,和要调用的方法名。
class = reflection.Service2
method = doService2
当需要从调用第一个业务方法,切换到调用第二个业务方法的时候,不需要修改一行代码,也不需要重新编译,只需要修改配置文件spring.txt,再运行即可。
这也是Spring框架的最基本的原理,只是它做的更丰富,安全,健壮。
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 {
File springConfigFile = new File("D:\\project\\test\\src\\spring.txt");
Properties pps = new Properties();
pps.load(new FileInputStream(springConfigFile));
String className = (String) pps.get("class");
String methodName = (String) pps.get("method");
//根据类名称获取类对象
Class clazz = Class.forName(className);
//根据方法名称,获取方法对象
Method m = clazz.getMethod(methodName);
//获取构造器
Constructor c =clazz.getConstructor();
//实例化对象
Object service =c.newInstance();
m.invoke(service);
}
}