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());
    }
}
View Code

通过反射修改属性的值

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  
    }
 
}
View Code
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);
         
    }
}

 

posted @ 2022-04-13 17:02  2022年总冠军gogogo  阅读(34)  评论(0)    收藏  举报