java reflection

Mon, Aug 16, 2021 3-minute read

Q1. reflection vs encapsulation

  • Reflection breaks encapsulation principles by giving access to private fields and methods, encapsulation is only a technique, one that makes designing behaviour easier, provided consumers agree use an API you have defined. If somebody chooses to circumvent your API using reflection or any other technique

Q2. reflection vs new object in the real left

  • prefer new object directly

Q3. java.lang.Class

  • 类的加载过程
    • 程序经过 javac.exe 命令后,生成一个或多个字节码文件.class, 用 java.exe 对某个字节码文件进行解释运行。 加载到内存中的类,为运行时序.
    • 获取 class 实例 :
    • method1:调用运行时类的属性: .class
    Class clazz1 = Person.class;
    
    • method2:调用运行时类的对象:
    Person p1 = new Person();
    Class clazz2 = p1.getClass();
    
    • method3:调用 class 的静态方法:for Name(String classPath)
    Class clazz3 = Class.forName("com.exa.java.Person");
    clazz3 = Class.forName("java.lang.String");
    
    • method4:类的加载器 ClassLoader
    ClassLoader classLoader = ReflectionTest.class.getClassLoader();
    Class clazz4 = classLoader.loadClass("com.exa.java.Person");
    
    • Class 实例对应一个运行时类
    • 加载到内存中的运行时类,会缓存一定时间,在此时间之类,可以通过不同方式获取此运行时类

Q4. Properties: 读取配置文件 1: 注意文件位置

public void test() {
    Properties pros = new Properties();
    FileInputStream fis = new FileInputStream("jdbc.properties");//文件默认位置要在module下
    // FileInputStream fis = new FileInputStream("src\\jdbc1.properties");//文件默认位置要在module下
    pros.load(fis);

    String name = pros.getProperty("user");
}

Q5. ClassLoader: 读取配置文件 2

public void test() {
    Properties pros = new Properties();
    ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
    InputStream is = classLoader.getResourceAsStream("jdbc1.properties"); //文件默认位置要在当前module的src下
    pros.load(is);
}

Q6. 创建对应的运行时类的对象

public class NewInstanceTest() {

    @Test
    public void test1() throws Exception{
        
        Class clazz = Person.class;
        Object obj = clazz.newInstance();
        // newInstance(): 调用此方法,创建对应的运行时类对象,要求:
        1. 运行时类必须提供空参构造器
        2.空参构造器访问权限要够通常设置为public
        Class<Person> clazz = Person.class;
        Person obj1 = clazz.newInstance();
    }
}
public class NewInstanceTest() {

    ....
    调用下面方法
    Object obj = getInstance(classPath);
    sout(obj);

    @Test
    public Object getInstance(String classPath) throws Exception{

        Class clazz = Class.forName(classPath); // "java.util.Date"; "com.exe.java.Person"
        return clazz.newInstance();
      
    }
}

Q7. 静态代理:被代理类和代理类在编译期间就确定下来了

interface ClothFactory {
    void produceCloth();
}

class ProxyClothFactory implements ClothFactory {

    private ClothFactory facotory;

    public ProxyClothFactory(ClothFactory facotory) {
        this.factory = factory;
    }

    @Override
    void produceCloth() {
        sout("代理工厂准备");

        factory.produceCloth();

        sout("代理工厂后续工作");
    }
}

class NikeClothFactory implements ClothFactory {

   
    @Override
    void produceCloth() {
        sout("Nike工厂生成衣服"); 
    }
}

public class StaticProxyTest {
    main(){
        NikeClothFactory nike = new NikeClothFactory();

        ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike);

        //method2: 

        // ClothFactory nike = new NikeClothFactory();

        // ClothFactory proxyClothFactory = new ProxyClothFactory(nike);

        proxyClothFactory.produceCloth();
    }
}

    //  "代理工厂准备";

    //Nike工厂生成衣服

     //"代理工厂后续工作";

Q8. 动态代理:被代理类和代理类在编译期间就确定下来了

实现动态代理,需要解决的问题

Q1: 如何根据加载到内存中的被代理类,动态的创建一个代理类以及对象

Q2: 当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法

interface Human {
    String getBelief();
    void eat(String food);
}

class SuperMan implements Human {

    @Override
    String getBelief() {
       return "I can fly";
    }

    @Override
    void eat(String food) {
       sout( "I like eat" + food);
    }
}

class ProxyFactory {
    // Q1: 如何根据加载到内存中的被代理类,动态的创建一个代理类以及对象
    // 调用此方法,返回一个代理类,解决问题1
    public static Object getProxyInstance(Object obj) { //obj:被代理类的对象
        MyInvocationHander handler = new MyInvocationHander();

        handler.bind(obj);

        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler); // 代理类要看被代理类哪个类加载器的,实现同样的接口, 
    }
}

class MyInvocationHander implements InvocationHandler {

    private Object obj; //需要使用被代理类的对象进行赋值

    public void bind(Object obj) {
        this.obj = obj;
    }

    // 通过代理类对象时,调用方法a时,就会自动调用如下方法: invoke()
    // 将被代理类要执行的方法a的功能就声明在invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        //代理类对象调用的方法,此方法也就作为被代理类对象要调用的方法

        Object returnValue = method.invoke(obj, args);
        return returnValue;
    }
}


public class ProxyTest {
    main(){
        SuperMan superMan = new SuperMan();

        Human proxyInstance = (Human)ProxyFactory.getProxyInstance(superMan); 
        proxyInstance.getBelief();
        proxyInstance.eat("xxx");
    }
}

反射理解

将类的各个组成部分封装为其他对象,这就是反射机制

好处:

  1. 可以在程序运行过程中,操作这些对象。
  2. 可以解耦,提高程序的可扩展性。

获取 Class 对象的方式

1. Class.forName(“全类名”)

将字节码文件加载进内存,返回 Class 对象 多用于配置文件,将类名定义在配置文件中,读取文件,加载类

Class aClass = Class.forName(“day01.itcast.junit.test.Calculator”);

2. 类名.class:通过类名的属性 class 获取

多用于参数的传递

  • 获取成员变量们

    • Field[] getFields() :获取所有 public 修饰的成员变量
    • Field getField(String name) 获取指定名称的 public 修饰的成员变量
    • Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
    • Field getDeclaredField(String name)
  • 获取构造方法们

    • Constructor<?>[] getConstructors()
    • Constructor getConstructor(类<?>.. parameterTypes)
    • Constructor getDeclaredConstructor(类<?>.. parameterTypes)
    • Constructor<?>[] getDeclaredConstructors()
    • 创建对象
      • T newInstance(Object.. initargs)
      • 如果使用空参数构造方法创建对象,操作可以简化:Class 对象的 newInstance 方法

Class personClass = Person.class; Constructor constructor = personClass.getConstructor(String.class, int.class); Object person= constructor.newInstance(“aaa”, 11);

  • 获取成员方法们

    • Method[] getMethods()
    • Method getMethod(String name, 类<?>.. parameterTypes)
    • Method[] getDeclaredMethods()
    • Method getDeclaredMethod(String name, 类<?>.. parameterTypes)
    • Mthod: 方法对象

Object invoke(Object obj, Object…args)

  • 获取方法名称

String getName()

Class personClass = Person.class; Method setName = personClass.getMethod(“setName”, String.class); Person person = new Person(); setName.invoke(person, “hello”);

3. 对象.getClass():getClass()方法在 Object 类中定义着

有对象

多用于对象的获取字节码的方式