反射

1.反射的使用

    @Test
    public void test() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        Class clazz = Person.class;
        // 1.通过反射创建Person类的对象
        Constructor constructor = clazz.getConstructor(String.class, int.class);
        Object obj = constructor.newInstance("Tom", 12);
        System.out.println(obj);    // Person{name='Tom', age=12}
        // 2.通过反射调用对象内部的属性和方法
        Person p = (Person) obj;
        Field age = clazz.getDeclaredField("age");
        age.set(p, 10);
        System.out.println(p);
        // 调用方法
        Method test1 = clazz.getDeclaredMethod("test1");
        test1.invoke(p);
        // 通过反射可以调用私有结构,如私有构造器、方法、属性
        Constructor declaredConstructor = clazz.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Person p1 = (Person) declaredConstructor.newInstance();
        // 调用私有属性及方法
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p1, "Hal");
        System.out.println(p1);
        Method test2 = clazz.getDeclaredMethod("test2");
        test2.setAccessible(true);
        String str = (String) test2.invoke(p);
        System.out.println(str);
    }

2.疑问

2.1.通过直接new或反射的方式均可以调用公共结构,开发中用哪个?

建议直接使用new方式
什么时候使用反射:根据反射的特征——动态性,在编译期间无法确定new谁,则用反射

2.2.反射机制与面向对象中的封装性是不是矛盾的,如何看待两个技术

不矛盾,反射只是提供一种方式,而封装性代表不建议使用

3.关于java.lang.Class的理解

类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)
之后使用java.exe命令对某个字节码文件进行解释运行,相当于将某个字节码文件加载到内存中,此过程称为类的加载,加载到内存中的类,称为运行时类
运行时类,就作为Class的一个实例

4.获取Class实例的方式

加载到内存中的运行时类,会缓存一定时间,在此时间之内,可以通过不同的方式获取此运行时类实例
下面的方法都是内存中存在的同一个运行时类实例
(1)调用运行时类的属性
Class<Person> personClass = Person.class;
(2)通过运行时类的对象
Class personClass = p.getClass();
(3)调用Class的静态方法

        try {
            Class personClass = Class.forName("com.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

(4)使用类的加载器

        // TestReflect为测试类名
        try {
            Class personClass = TestReflect.class.getClassLoader().loadClass("com.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

常使用第三种方式,通常反射的目的是创建对象,第一种直接写死,第二种则已经存在当前类的对象

5.拥有Class类对象的类型

(1)class
外部类,成员(成员内部类、静态内部类)、局部内部类、匿名内部类
(2)interface
(3)数组(相同维度和元素乐行认为是同一个类对象,如String[].class、int.class
(4)枚举(ElementType.class)
(5)annotation注解
(6)基本数据类型
(7)void(void.class)

6.类的加载过程

当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤对该类进行初始化
(1)类的加载
将类的class文件读入内存,并为之创建一个java.lang.Class对象,此过程由类加载器完成
(2)类的链接
将类的二进制数据合并到JRE中
(3)类的初始化
JVM负责对类进行初始化

7.类加载器

        ClassLoader classLoader = TestReflect.class.getClassLoader();
        // 对于自定义类,使用系统类加载器进行加载
        System.out.println(classLoader);    // sun.misc.Launcher$AppClassLoader@18b4aac2
        // 调用系统类加载的getParent(),获取扩展类加载器
        System.out.println(classLoader.getParent());    // sun.misc.Launcher$ExtClassLoader@33c7353a
        //  引导类加载器主要加载java核心类库,无法加载自定义类
        System.out.println(classLoader.getParent().getParent());    // null 引导类加载器无法获取

使用类加载器读取文件:

        // 读取配置文件方式一,此时以module为基础路径
        Properties pros = new Properties();
        File file = new File("src/main/resources/jdbc.properties");
        FileInputStream fis = new FileInputStream(file);
        pros.load(fis);
        System.out.println(pros.getProperty("user"));
        System.out.println(pros.getProperty("password"));
        // 读取配置文件方式二,此时以classpath为基础路径
        ClassLoader classLoader = TestReflect.class.getClassLoader();
        InputStream resourceAsStream = classLoader.getResourceAsStream("jdbc.properties");
        pros.load(resourceAsStream);
        System.out.println(pros.getProperty("user"));
        System.out.println(pros.getProperty("password"));

8.创建运行时类的对象

        Class<Person> personClass = Person.class;
        Person person = personClass.newInstance();
        System.out.println(person);

newInstance底层调用空参构造器,如果希望此方法正常创建运行时对象,需要满足以下两点:
(1)运行时类必须提供空参构造器
(2)空参构造器的访问权限应该合理
在JavaBean中要求提供一个空参构造器,便于通过反射创建运行时类的对象的同时也便于子类继承此运行类时,默认调用super()时,保证父类有此构造器

8.1.获取运行时类的属性

        Class<Person> clazz = Person.class;
        // 获取属性结构,getFields能够获取当前运行时类及其父类中声明为public访问权限的属性
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        // getDeclaredFields能够获取当前运行时类中所有的属性(忽略权限且不包含父类中声明的属性)
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println(field);
            // 权限修饰符(返回整型)
            int modifiers = field.getModifiers();
            // 此方法可以将权限修饰符对应数值进行翻译
            System.out.println(Modifier.toString(modifiers));
            // 数据类型
            System.out.println(field.getType().getName());
            // 变量名
            System.out.println(field.getName());
        }

java.lang.reflect包中的Modifier类中定义权限修饰符对应数值:

8.2.获取运行时类的方法

        Class<Person> clazz = Person.class;
        // 获取当前运行时类及其父类所有声明为public的方法
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println();
        // 获取当前运行时类所有的方法(不考虑权限,且不包含父类所声明的方法)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method);
            // 获取方法声明的注解
            System.out.println(Arrays.toString(method.getAnnotations()));
            // 获取权限修饰符
            System.out.println(Modifier.toString(method.getModifiers()));
            // 获取返回值类型
            System.out.println(method.getReturnType().getName());
            // 获取方法名
            System.out.println(method.getName());
            // 获取形参列表
            Class[] parameterTypes = method.getParameterTypes();
            System.out.println(Arrays.asList(parameterTypes));
            if (parameterTypes != null && parameterTypes.length > 0) {
                for (int i = 0; i < parameterTypes.length; i++) {
                    System.out.println(parameterTypes[i].getName() + " args-" + i + "\t");
                }
            }
            // 抛出异常
            Class<?>[] exceptionTypes = method.getExceptionTypes();
            if (exceptionTypes != null && exceptionTypes.length > 0) {
                for (Class item : exceptionTypes) {
                    System.out.println(item.getName());
                }
            }
        }

8.3.获取运行时类的构造器

        Class<Person> clazz = Person.class;
        // 获取当前运行时类中声明为public的构造器
        Constructor<?>[] constructors = clazz.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println();
        // getDeclaredConstructors获取当前运行时类中声明的所有构造器
        for (Constructor<?> declaredConstructor : clazz.getDeclaredConstructors()) {
            System.out.println(declaredConstructor);
        }

8.4.获取运行时类的父类

        Class<Person> clazz = Person.class;
        // 获取运行时类的父类
        Class<? super Person> superclass = clazz.getSuperclass();
        System.out.println(superclass);
        // 获取运行时类的带泛型的父类
        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);
        // 获取运行时类的父类的泛型
        ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
        System.out.println(Arrays.asList(parameterizedType.getActualTypeArguments()));

8.5.获取运行时类实现的接口

        Class<Person> clazz = Person.class;
        // 获取运行时类的接口
        Class<?>[] interfaces = clazz.getInterfaces();
        System.out.println(Arrays.toString(interfaces));
        // 获取运行时类的父类的接口
        Class<?>[] interfaces1 = clazz.getSuperclass().getInterfaces();
        System.out.println(Arrays.toString(interfaces1));

8.6.获取运行时类所在的包

        Class<Person> clazz = Person.class;
        // 获取运行时类所在的包
        Package aPackage = clazz.getPackage();
        System.out.println(aPackage.getName());

8.7.获取运行时类声明的注解

        Class<Person> clazz = Person.class;
        // 获取运行时类所在的包
        Annotation[] annotations = clazz.getAnnotations();
        System.out.println(Arrays.asList(annotations));

9.调用类中指定结构

9.1.调用类中指定属性

        Class<Person> clazz = Person.class;
        try {
            Field field = clazz.getDeclaredField("name");
            Person person = Person.class.newInstance();
            // 保证当前属性是可访问的
            field.setAccessible(true);
            field.set(person, "123");
            System.out.println(field.get(person));
        } catch (Exception e) {
            e.printStackTrace();
        }

9.2.调用类中指定方法

        Class<Person> clazz = Person.class;
        try {
            // 调用非静态方法
            Method method = clazz.getDeclaredMethod("show", String.class);
            Person person = Person.class.newInstance();
            method.setAccessible(true);
            method.invoke(person, "China");
            // 调用静态方法
            Method method1 = clazz.getDeclaredMethod("showDesc", String.class);
            method1.setAccessible(true);
            method1.invoke(Person.class);
        } catch (Exception e) {
            e.printStackTrace();
        }

9.3.调用类中指定构造器

        Class<Person> clazz = Person.class;
        try {
            Constructor<Person> declaredConstructor = clazz.getDeclaredConstructor(String.class);
            declaredConstructor.setAccessible(true);
            Person person = declaredConstructor.newInstance("123");
            System.out.println(person);
        } catch (Exception e) {
            e.printStackTrace();
        }

通常都使用xxx.class.newInstance()创建运行时类的对象,而不采用此种方式创建

10.静态代理

代理类与非代理类在编译期间就确定

package com;

interface ClothFactory {
    void produceCloth();
}

// 代理类
class ProxyClothFactory implements ClothFactory {
    private ClothFactory clothFactory;

    public ProxyClothFactory(ClothFactory clothFactory) {
        this.clothFactory = clothFactory;
    }

    @Override
    public void produceCloth() {
        System.out.println("代理准备工作");
        clothFactory.produceCloth();
        System.out.println("代理收尾工作");
    }
}

class NikeClothFactory implements ClothFactory {

    @Override
    public void produceCloth() {
        System.out.println("Nike Produce");
    }
}

public class StaticProxyTest {
    public static void main(String[] args) {
        ProxyClothFactory proxyClothFactory = new ProxyClothFactory(new NikeClothFactory());
        proxyClothFactory.produceCloth();
    }

}

11.动态代理

欲实现动态代理,要解决的问题:
(1)问题一:如何根据加载到内存的被代理类,动态创建一个代理类及其对象
(2)问题二:当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法

package com;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Human {
    String getBelife();

    void eat(String food);
}

class SuperMan implements Human {

    @Override
    public String getBelife() {
        return "belife";
    }

    @Override
    public void eat(String food) {
        System.out.println("eat " + food);
    }
}

class Handler implements InvocationHandler {
    // 需要使用被代理对象
    private Object obj;

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

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

class ProxyFactory {
    // 调用此方法返回一个代理类的对象,解决问题一,obj为被代理类对象
    public static Object getProxyInstance(Object obj) {
        Handler handler = new Handler();
        handler.bind(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
    }
}

public class ProxyTest {
    public static void main(String[] args) {
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(new SuperMan());
        System.out.println(proxyInstance.getBelife());
        proxyInstance.eat("汉堡");
    }
}
posted @ 2021-08-18 08:59  kanaliya  阅读(40)  评论(0)    收藏  举报