Java 类型与反射

2020-12-06  本文已影响0人  BitterOutsider

Java的类与Class

public class Main {
    public static void main(String[] args) {
        Item obj = new RPC().getItemById(1);
        // class com.github.lazyben.Goods
        System.out.println(obj.getClass());
    }
}

interface Item { }

class Goods implements Item { }

class RPC {
    public Item getItemById(int id){
        return new Goods();
    }
}

有了以上的知识后我们就可以更好的理解 instanceof 和 强制类型转换 了。因为在运行时 JVM 清楚地知道该对象到底是什么类型的(它是由哪一份Class“说明书”装配出来的)。我们也可以更好地理解静态变量:它可以理解为属于这个Class对象的一个变量。

类加载与ClassLoader

假如我们有一堆的Class对象说明书CatDogObject,这些Class对象又是从哪里来的呢?

package com.github.lazyben;

public class Main {
    public static void main(String[] args) {
        new WhiteCat();
    }
}

class Animal{}

class Cat extends Animal{}

class WhiteCat extends Cat{}

我们使用-verbose:class打印出类加载过程,并用grep lazyben过滤出自己的类(方便查看)。这里很容易看出类是从target目录下来的,且加载顺序符合类的加载顺序。java自带的包在不同的地方,同样的我们可以grep Object一探究竟。

java -verbose:class -classpath /Library/Java/JavaVirtualMachines/jdk1.8.0_251.jdk/......./target/classes com.github.lazyben.Main | grep lazyben

// 打印出以下的信息
[Loaded com.github.lazyben.Main from file:/Users/lazyben/..../target/classes/]
[Loaded com.github.lazyben.Animal from file:/Users/lazyben/..../target/classes/]
[Loaded com.github.lazyben.Cat from file:/Users/lazyben/..../target/classes/]
[Loaded com.github.lazyben.WhiteCat from file:/Users/lazyben/..../target/classes/]

下图是类的加载过程,经过下面几个过程,我们就将一个Class对象变到类内存里面,使得我们可以根据这份说明书去创造实例对象。


 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                }                 
            // .......
            return c;
        }
    }
public class MyClassLoader extends ClassLoader {
    // 存放字节码文件的目录
    private final File bytecodeFileDirectory;

    public MyClassLoader(File bytecodeFileDirectory) {
        this.bytecodeFileDirectory = bytecodeFileDirectory;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        final File file = new File(bytecodeFileDirectory, name + ".class");
        if (file.exists()) {
            try {
                final byte[] bytes = Files.readAllBytes(file.toPath());
                return defineClass(name, bytes, 0, bytes.length);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        throw new ClassNotFoundException(name);
    }

    public static void main(String[] args) throws Exception {
        File projectRoot = new File(System.getProperty(...);
        MyClassLoader myClassLoader = new MyClassLoader(projectRoot);

        Class testClass = myClassLoader.loadClass(...);
        Object testClassInstance = testClass.getConstructor().newInstance();
        String message = (String) testClass.getMethod(...).invoke(testClassInstance);
    }
}

反射

用一句话概括反射:它是运行时行为,动态调用。如何根据参数动态创建⼀个对象?如何根据参数动态调⽤⼀个⽅法?如何根据参数动态获取⼀个属性?答案都是反射。这意味着我们有了无与伦比的灵活性。但是一般来说反射的性能比较差,因为JDK无法预测被调用的方法,无法实施优化。
根据参数动态创建⼀个对象,其中forName根据一个类名(全限定类名)返回一个Class对象。

Class klass = Class.forName(args[0]);
Object obj = klass.getConstructor().newInstance();

根据参数动态调⽤⼀个⽅法

Cat cat = new Cat();
cat.getClass().getMethod(args[0]).invoke(cat);

根据参数动态获取⼀个属性

Cat cat = new Cat();
cat.getClass().getField(args[0]).get(cat);
public class MapBeanConverter {
    public static Map<String, Object> beanToMap(Object bean) {
        return Stream.of(bean.getClass().getDeclaredMethods())
                .filter(MapBeanConverter::isJavaBeanMethod)
                .collect(Collectors.toMap(MapBeanConverter::getMethodName, method -> getMethodReturnValue(method, bean)));
    }

    private static Object getMethodReturnValue(Method method, Object bean) {
        try {
            return method.invoke(bean);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String getMethodName(Method method) {
        String methodName = method.getName();
        String name = methodName.startsWith("get") ? methodName.substring(3) : methodName.substring(2);
        return Character.toLowerCase(name.charAt(0)) + name.substring(1);
    }

    private static boolean isJavaBeanMethod(Method method) {
        final String methodName = method.getName();
        boolean isStartWithGet = methodName.startsWith("get") && methodName.length() > 3;
        boolean isStartWithIs = methodName.startsWith("is") && methodName.length() > 2;
        return (isStartWithGet || isStartWithIs)
                && (method.getParameterCount() == 0)
                && Character.isUpperCase(methodName.charAt(isStartWithGet ? 3 : 2));
    }

    public static <T> T mapToBean(Class<T> klass, Map<String, Object> map) {
        try {
            final T instance = klass.getConstructor().newInstance();
            map.forEach((key, value) -> {
                String methodName = "set" + Character.toUpperCase(key.charAt(0)) + key.substring(1);
                try {
                    klass.getMethod(methodName, value.getClass()).invoke(instance, value);
                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            });
            return instance;
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e ) {
            e.printStackTrace();
        }
        return null;
    }

    public static class DemoJavaBean {
        private Integer id;
        private String name;
        private final String privateField = "privateField";

        public int isolate() {
            System.out.println(privateField);
            return 0;
        }

        public String is() { return ""; }

        public Integer getId() { return id; }

        public void setId(Integer id) { this.id = id; }

        public String getName() { return name; }

        public String getName(int i) { return name + i; }

        public void setName(String name) { this.name = name; }

        public boolean isLongName() { return name.length() > 10; }
    }

    public static void main(String[] args) {
        DemoJavaBean bean = new DemoJavaBean();
        bean.setId(100);
        bean.setName("AAAAAAAAAAAAAAAAAAA");
        System.out.println(beanToMap(bean));

        Map<String, Object> map = new HashMap<>();
        map.put("id", 123);
        map.put("name", "ABCDEFG");
        System.out.println(mapToBean(DemoJavaBean.class, map));
    }
}
上一篇下一篇

猜你喜欢

热点阅读