java_反射详解
1.类对象概念:
所有的类,都存在一个类对象,这个类对象用于提供类本身的信息,比如有几种构造方法, 有多少属性,有哪些普通方法。
2.获取类对象的三种方式:
public static void main(String[] args) { String className = "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(); } } /* 在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。 准确的讲是一个ClassLoader下,一种类,只会有一个类对象存在。通常一个JVM下,只会有一个ClassLoader。 */
3.获取类对象的时候,会导致类属性被初始化:
都会导致静态属性被初始化,而且只会执行一次,但是使用类名.class的方式获取的类对象,不会导致类属性被初始化。
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公司所有"; } } public class TestReflection { public static void main(String[] args) throws ClassNotFoundException { String className = "Reflection.Hero"; Class pClass1 = Class.forName(className); // Class pClass2 = Hero.class; // Class pClass3 = new Hero().getClass(); } }
4.反射机制创建对象:
与传统的通过new 来获取对象的方式不同,反射机制,会先拿到 Hero类 的“类对象”,然后通过类对象获取“构造器对象”,再通过构造器对象创建一个对象。
public class TestReflection2 { public static void main(String[] args) { // 传统的使用new的方式创建对象 Hero h1 = new Hero(); h1.name = "teemo"; System.out.println(h1); try { // 使用反射的方式创建对象 String className = "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(); } } }
5.反射机制访问属性:
// 为了访问属性,把name修改为public。 // 对于private修饰的成员,需要使用setAccessible(true)才能访问和修改。 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()); } }
通过反射修改属性的值
public class TestReflection3 { 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 只能获取public的,包括从父类继承来的字段。
getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (注: 这里只能获取到public的字段,但并不能访问该private字段的值,除非加上setAccessible(true))
6.反射机制访问方法:
通过反射机制,调用一个对象的方法,首先为Hero的name属性,增加setter和getter,通过反射机制调用Hero的setName。
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) { // TODO Auto-generated method stub } }
import java.lang.reflect.Method; 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(); } } }
7.反射机制有什么用:
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
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("e:\\project\\j2se\\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); } }

浙公网安备 33010602011771号