一、什么是反射?
在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有),这种动态获取的信息,及调用对象的方法的功能就称为Java的反射机制。
二、获取类对象
1、什么是类对象?
创建Dog类和Cat类
public class Dog {
private String name;
private int age;
public Dog() {
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
/*getter and setter*/
}
public class Cat {
private String name;
private String hobby;
/*getter and setter*/
}
实例化两个Dog类的对象
Dog dog1 = new Dog("大黄", 5);
Dog dog2 = new Dog("小黑", 3);
在理解类对象之前,先说我们熟悉的对象之间的区别:
dog1和dog2都是Dog对象,他们的区别在于,各自有不同的名称、年龄。
然后说说类之间的区别
Dog和Cat都是类,他们的区别在于有不同的方法,不同的属性。
类对象,就是描述一个类都有什么属性,什么方法的。所有的类,都存在一个类对象,这个类对象用于提供类本身的信息.
2、获取类对象的三种方式
1. Class.forName
2. Dog.class
3. new Dog().getClass()
try {
Class class1 = Class.forName("yingshe.Dog");
System.out.println(class1);
Class class2 = Dog.class;
System.out.println(class2);
Class class3 = new Dog().getClass();
System.out.println(class3);
System.out.println(class1 == class2);
System.out.println(class1 == class3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
执行结果

在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。
3、获取类对象的时候会导致类属性被初始化
Dog类加上代码
static String msg;
static {
System.out.println("初始化msg");
msg="被初始化了";
}
获取类对象

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

三、创建对象
与传统的通过new 来获取对象的方式不同,反射机制,会先拿到Hero的“类对象”,然后通过类对象直接创建对象或获取“构造器对象” ,再通过构造器对象创建一个对象
T newInstance()
创建由此 类对象表示的类的新实例。
Constructor getConstructor()
返回一个 Constructor对象
import java.lang.reflect.Constructor;
public class Test2 {
public static void main(String[] args) {
//获取类对象
Class classDog = Dog.class;
try {
//用类对象直接创建
Dog dog1 = (Dog)classDog.newInstance();
dog1.setName("大黄");
dog1.setAge(5);
System.out.println(dog1);
//获取构造器再创建
//构造器
Constructor constructor = classDog.getConstructor();
//通过构造器实例化
Dog dog2 = (Dog) constructor.newInstance();
dog2.setName("小黑");
dog2.setAge(4);
System.out.println(dog2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
四、访问属性
为了访问属性,将name修改为public属性
对于private修饰的成员,需要使用setAccessible(true)才能访问和修改。
通过反射修改属性值
import java.lang.reflect.Field;
public class Test3 {
public static void main(String[] args){
Dog dog =new Dog();
//使用传统方式修改name的值为大黄
dog.name = "大黄";
System.out.println(dog.name);
try {
//获取类Dog的名字叫做name的字段
Field f1= dog.getClass().getDeclaredField("name");
//修改这个字段的值
f1.set(dog, "小黑");
//打印被修改后的值
System.out.println(dog.name);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
五、部分函数
1、Field[] getFields()
返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段、类对象。
2、Field[] getDeclaredFields()
动态的返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象
增加Dog属性
public class Dog {
public String name; //public
private int age; private
String health; //默认
protected int weigth; //protected
//其他函数
}
import java.lang.reflect.Field;
public class Test4 {
public static void main(String[] args){
Class classDog=Dog.class;
Field[] fields1 = classDog.getFields();
System.out.println("类的public属性");
for (Field f : fields1){
System.out.println(f.getName());
}
Field[] fields2 = classDog.getDeclaredFields();
System.out.println("类的所有属性");
for (Field f : fields2){
System.out.println(f.getName());
}
}
}
执行结果

3、Method[] getMethods()
返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象,包括那些由类或接口和那些从超类和超接口继承的声明。
4、Method[] getDeclaredMethods()
返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
5、Object invoke(Object obj, Object… args)
在具有指定参数的 方法对象上调用此 方法对象表示的底层方法
例如:取得setName方法映射给method,method调用invoke函数第一个对象为操作对象,第二个参数为参数(可以是数组)
import java.lang.reflect.Method;
public class Test5 {
public static void main(String[] args) {
Class classDog = Dog.class;
try {
Dog dog = (Dog) classDog.newInstance();
Method method = classDog.getMethod("setName", String.class);
method.invoke(dog, "名字");
System.out.println(dog.name);
} catch (Exception e) {
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();
new Service2().doService2();
}
}
3、反射方式
使用反射方式,首先准备一个配置文件,就叫做spring.txt吧, 放在src目录下。 里面存放的是类的名称,和要调用的方法名。
在测试类Test中,首先取出类名称和方法名,然后通过反射去调用这个方法。
当需要从调用第一个业务方法,切换到调用第二个业务方法的时候,不需要修改一行代码,也不需要重新编译,只需要修改配置文件spring.txt,再运行即可。
这也是Spring框架的最基本的原理,只是它做的更丰富,安全,健壮。
spring.txt
class=yingshe.Service1 method=doService1
TestF.java
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;
public class TestF {
@SuppressWarnings({"rawtypes", "unchecked"})
public static void main(String[] args) throws Exception {
//从spring.txt中获取类名称和方法名称
File springConfigFile = new File("g:\\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);
}
}
执行结果

来源:https://www.cnblogs.com/GG-Bond/p/10644278.html