JAVA - Reflection

First, get the class object

Class object concept (not class object): For all classes, there is a class object , which is used to provide information about the class itself , such as how many construction methods there are, how many attributes, and which common methods there are.

Before understanding class objects , let's talk about the differences between familiar objects:
garen and teemo are both Hero objects , and the difference between them is that they have different names, HP, and damage values.

Then talk about the difference between classes.
Hero and Item are both classes. The difference between them is that they have different methods and different properties .

Class objects are used to describe this class, what properties and methods it has

1.1 Get the class object

There are 3 ways to get the class object
1. Class.forName
2. Hero.class
3. new Hero().getClass()

In a JVM, a class, only one class object exists. Therefore, the class objects taken out by the above three methods are all the same.
Note: To be precise, under a ClassLoader, a class, only one class object exists. Usually, under a JVM, there is only one ClassLoader.

1.2 When obtaining a class object, it will cause the class properties to be initialized

Add a static property to Hero and initialize it in the static initialization block ,

static String copyright;
//静态初始化块初始化
static {
    System.out.println("初始化 copyright");
    copyright = "版权由Riot Games公司所有";
}

No matter what way to get the class object , it will cause the static property to be initialized , and it will only be executed once. (Apart from using Class c = Hero.class directly, which will not cause static properties to be initialized)

1.3 Add synchronized to the static method, what is the synchronization object?

When synchronized modifies a static method, the synchronization object is the class object of this class .

As in the example in the code, when the first thread enters method1, it needs to occupy TestReflection.class to execute.

When the second thread enters method2, it does not enter. It can only be executed after the first thread releases the occupation of TestReflection.class . In reverse, the second thread also needs to occupy TestReflection.class. Then TestReflection.class is the synchronization object of method2.

In other words, when a static method is modified as synchronized, its synchronization object is the class object of the current class .

package reflection;

public class TestReflection {
    public static void main(String[] args) throws InterruptedException {
        Thread t1= new Thread(){
            public void run(){
                //调用method1
                TestReflection.method1();
            }
        };
        t1.setName("第一个线程");
        t1.start();

        //保证第一个线程先调用method1
        Thread.sleep(1000);

        Thread t2= new Thread(){
            public void run(){
                //调用method2
                TestReflection.method2();
            }
        };
        t2.setName("第二个线程");
        t2.start();
    }

    public static void method1() {

        synchronized (TestReflection.class) {
            // 对于method1而言,同步对象是TestReflection.class,只有占用TestReflection.class才可以执行到这里
            System.out.println(Thread.currentThread().getName() + " 进入了method1方法");
            try {
                System.out.println("运行5秒");
                Thread.sleep(5000);
            } catch (InterruptedException e) {

                e.printStackTrace();
            }
        }
    }

    public static synchronized void method2() {
        // 对于mehotd2而言,必然有个同步对象,通过观察发现,当某个线程在method1中,占用了TestReflection.class之后
        // 就无法进入method2,推断出,method2的同步对象,就是TestReflection.class
        System.out.println(Thread.currentThread().getName() + " 进入了method2方法");
        try {
            System.out.println("运行5秒");
            Thread.sleep(5000);
        } catch (InterruptedException e) {

            e.printStackTrace();
        }

    }
}

2. Create objects

Different from the traditional way of obtaining objects through new, the
reflection mechanism will first get Hero's "class object" , then obtain the "constructor object" through the class object
, and then create an object through the constructor object

2.1 Create an object

package reflection;
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();
        }
    }
}

2.2 Get objects through configuration files

First prepare a text file: hero.config. Save the full name of the class in this file, either character.APHero or character.ADHero

Then design a method called:

public static Hero getHero()

In this method, read the data of *hero.config, take out the class name*, instantiate the object according to the class name, and then return the object.

Hero h = getHero();
System.out.println(h);

By printing h, you can find that when the content in the configuration file changes, you will get different objects .

The source code does not need to be changed , just modify the configuration file to change the logic of the program. This is a configuration-based programming idea .

The bottom layer of IOC and DI in the Spring framework is implemented based on this mechanism.

public class TestReflection {
    public static void main(String[] args) throws InterruptedException {
        Hero h = getHero();
        System.out.println(h);
    }

    public static Hero getHero() {
         //配置文件
        File f = new File("E:/project/j2se/hero.config");

        try (FileReader fr = new FileReader(f)) {
            String className = null;
            char[] all = new char[(int) f.length()];
            fr.read(all);
            className = new String(all);
            Class clazz=Class.forName(className);
            Constructor c= clazz.getConstructor();
            Hero h= (Hero) c.newInstance();
            return h;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }
}

3. Access properties

Modify the properties of an object through reflection

To access the property, change the name to public.
For private-modified members, you need to use setAccessible(true) to access and modify them.

3.1 Modify the value of the attribute through reflection

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

3.2 The difference between getField and getDeclaredField

The difference between getField and getDeclaredField
These two methods are used to get the field
getField can only get public , including the fields inherited from the parent class . (public inheritance)
getDeclaredField can get all fields of this class, including private ones , but cannot get inherited fields . (All, non-inheritance)
(Note: Only the private field can be obtained here, but the value of the private field cannot be accessed unless setAccessible(true) is added)
Add f1.setAccessible(true); the method setting can be written

Fourth, the calling method

Invoke a method of an object through reflection

4.1 Calling the method

First, for Hero's name property, add setters and getters
to call Hero's setName through reflection mechanism

public class TestReflection {

    public static void main(String[] args) {
        Hero h = new Hero();

        try {
            // 获取这个名字叫做setName,参数类型是String的方法,用Method接收
            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();
        }

    }
}

powerful use

Reflection is very powerful, but after learning it, you will not know how to use it. Instead, you feel that it is not as direct and convenient as calling the method directly.

Generally speaking, you need to learn Spring's dependency injection and invert control before you can have a better understanding of reflection. However, students who have just learned here may not necessarily come into contact with Spring, so here are two examples. Let's demonstrate a practical application of reflection.

First, prepare two business classes. These two business classes are very simple, that is, each has a business method that prints different strings.

public class Service1 {

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

public class Service2 {

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

non-reflective method

When it is necessary to switch from the first business method to the second business method, the non-reflection method must be used to modify the code and recompile and run to achieve the effect.

package reflection;

public class Test {

    public static void main(String[] args) {
//      new Service1().doService1();
        new Service2().doService2();,这里要改业务类和业务方法
    }
}

reflection method

To use the reflection method, first prepare a configuration file , called spring.txt, and put it in the src directory. It stores the name of the class and the name of the method to be called.
In the test class Test, first take out the class name and method name , and then call the method through reflection.

When you need to switch from calling the first business method to calling the second business method, you don't need to modify a line of code or recompile. You only need to modify the configuration file spring.txt and run it again.

This is also the most basic principle of the Spring framework, but it is richer, safer and more robust.

sping.txt is as follows

class=reflection.Service1
method=doService1

The business code is as follows

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);

    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324485059&siteId=291194637