欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

反射与内省.md

程序员文章站 2024-01-20 16:05:52
...

1、 什么是反射

得到Class 类对象有三种方法

  1. Object 类的对象中的getClass() 方法
  2. 类.class
  3. 通过Class 类的forName方法

没有任何方式能够阻止反射到达并调用非公共访问权限的方法。对于域来说,的确如此,即便是 private域;


    public static void main(String[] args){
        Dog dog1 = new Dog("往往");
        // 通过对象的getClass方法
        Class aClass = dog1.getClass();
        System.out.println(aClass);

        // 通过类.class
        Class godClass = Dog.class;

        // 通过Class.forName 方法
        try {
            Class class1 = Class.forName("code.Dog");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

2、 Class 类

Class<?> 优于平凡的Class,即便他们是等价的;但是 Class不会产生编译器警告信息;

向 Class 引用添加泛型语法的原因仅仅是为了提供编译期类型检查,如果操作有误,编译期就可以发现;如果是没有带泛型的Class引用,则直到运行时才可以发现该错误;

public void test2() {
    Class<Dog> dogClass = Dog.class;
    try {
        // 实例化一个对象,调用了默认无参的构造方法
        Dog dog = (Dog) dogClass.newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

3、 通过Class 类获取类信息

获得构造方法,并调用构造方法

@Test
public void test3() {
    Class<Dog> dogClass = Dog.class;
    Constructor<?>[] constructors = dogClass.getConstructors();
    for (int i = 0; i < constructors.length; i++) {
        System.out.println(constructors[i].getName());
        System.out.println(constructors[i].getParameterCount());
    }
    Constructor<Dog> constructor = null;
    try {
        constructor = dogClass.getConstructor(String.class);
        Dog dog = constructor.newInstance("小白");
    }catch (InstantiationException|IllegalAccessException|InvocationTargetException|NoSuchMethodException e) {
        e.printStackTrace();
    }
}

查询类的成员变量

@Test
public void test4() {
    Class<Dog> dogClass = Dog.class;
    // 查询公有属性数量
    Field[] fields = dogClass.getFields();
    System.out.println(fields.length);
    Field[] declaredFileds = dogClass.getDeclaredFields();
    System.out.println(declaredFileds.length);
    for(int i = 0; i < declaredFileds.length; i++){
        int modifier1 = declaredFileds[i].getModifiers();
        System.out.println(Modifier.toString(modifier1) + " "+ declaredFileds[i].getType()+" "+declaredFileds[i].getName());
    }
}

4、 通过Class 类调用属性或方法

调用方法(包括私有方法)

@Test
public void test5(){
    Dog testDog = new Dog("往往");
    Class<Dog> dogClass = Dog.class;
    // 获取类的包名
    Package aPackage = dogClass.getPackage();
    System.out.println(aPackage.getName());
    // 获取公有方法
    Method[] methods = dogClass.getMethods();
    for(int i = 0; i < methods.length; i++){
        System.out.println(methods[i]);
        if(methods[i].getName().equals("toString")){
            try {
                String s = (String)methods[i].invoke(testDog);
                System.out.println("toString   "+s);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
    System.out.println("*****************");
    // 访问私有方法,不包括父类的方法
    Method[] declaredMethods = dogClass.getDeclaredMethods();

    for(int i = 0; i < declaredMethods.length; i ++){
        System.out.println(declaredMethods[i]);
        if(declaredMethods[i].getName().equals("privateMethod"));
        declaredMethods[i].setAccessible(true);
        try {
            declaredMethods[i].invoke(testDog);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

5、 动态代理

通过代理类:Proxy的代理,接口和实现类之间可以不直接发生联系,而可以在运行期实现动态关联

Java动态代理主要是通过Java.lang.reflect 包中的两个类

InvocationHandler 类

public Object invoke(Object obj, Method method, Object[] obs)

其中第一个参数obj 指的是代理类,method 是被代理的对象,obs是指被代理的方法的参数组,此方法由代理类来实现

Proxy 类

动态代理其实是运行时生成class,所以,必须提供一组interface,然后告诉他class 已经实现了这些interface,而在生成Proxy的时候,必须给他提供一个Handler,让他来接管实际的工作

接口

public interface Hotel {
    public void reserve();
}
public interface Subject {
    public void shopping();
}

接口实现

public class Person implements Subject, Hotel{
    public void shopping(){
        System.out.println("付款,买到心仪的商品");

    }


    @Override
    public void reserve() {
        System.out.println("预定付款,打飞机飞往目的地");
    }
}
public class CreateProxy implements InvocationHandler {
    private Object target;

    // 用于创建代理对象的方法
    public Object create(Object target){
        this.target = target;
        Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
        return proxy;
    }

    /**
     * 代理对象要执行的方法
     * @param proxy  代理类对象
     * @param method  被代理对象的方法
     * @param args   被代理对象方法的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("上海寻找客户需要的产品");
        System.out.println("客户确定物品");
        method.invoke(target, args);
        System.out.println("完成海淘");

        return null;
    }
}

测试

public class testDemo {

    @Test
    public void testProxy(){
        CreateProxy cp = new CreateProxy();

        Hotel hotel = new Person();
        Hotel proxy = (Hotel) cp.create(hotel);

        Subject subject = new Person();
        Subject proxy1 = (Subject) cp.create(hotel);

        proxy.reserve();
        proxy1.shopping();
    }
}

5、类加载器原理分析

1、类的加载过程

JVM将类加载过程分为三个步骤:装载(Load), 链接(Link),初始化(Initialize),链接又分为三个步骤

  1. 装载 : 查找并加载类的二进制数据
  2. 链接 (验证:确保被加载类的正确性, 准备:为类的静态变量分配内存,并将其初始化为默认值 解析:把类中的符号引用转换为直接引用)
  3. 初始化:为类的静态变量赋予正确的初始值

装载:查找并加载类的二进制数据

  1. 类的初始化,类什么时候才被初始化 ? ( 6 种情况 )

  2. 创建类的实例,也就是new 一个对象

  3. 访问某个类或接口的静态变量,或者对该静态变量赋值

  4. 调用类的静态方法

  5. 反射(Class.forName(“com.vince.Dog”))

  6. 初始化一个类的子类(首先初始化子类的父类)

  7. JVM 启动时表明的启动类,即文件名和类名相同的那个类

3 类的加载

指的是将类的.class 文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后再堆内创建一个这个类的Java.lang.Class 对象,用来封装类在方法区类的对象。

6、 JavaBean 概念

什么是JavaBean

Bean 理解为组件的意思,JavaBean 就是Java 组件,在广泛的理解就是一个类,对于组件来说关键在于要具有“能够被IDE构建工具侦测其属性和事件”的能力,通常在Java 中。

JavaBean 命名规则

  1. 对于一个命名为xxx的属性,通常要写两个方法getXxx(), setXxx() 方法,任何浏览这些方法的工具,都会把get或set后面的第一个字母自动转换为小写
  2. 对于布尔型属性,可以使用以上get 和set 的方式,不过也可以把get 替换成is
  3. Bean 的普通方法不必遵循以上的命名规则,不过他们必须是public 的
  4. 对于事件,要使用Swing 中处理监听器的方式,如addWindowsListener removeWindowListener

7、 内省基本概念

introspector 是java 语言对Bean 类属性、事件的一种缺省处理方法。例如类A中有属性name,那我们可以通过getName setName来得到其值或者设置其值,通过getName 和setName 来访问name 属性,这就是默认的规则

Java 中提供了一套API 用来访问某个属性的getter/setter 方法,通过这些API 可以使你不需要了解这些规则,这些API存放于包Java.beans 中,一般做法是通过类Introspector 的getBeanInfo 方法,来获取某个对象的BeanInfo 信息,然后通过BeanInfo 来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以描述某个属性对应的getter/setter 方法,然后我们就可以通过反射机制来调用这些方法。

1 Introspector 类

Introspector 类公国工具学习有关受目标Java Bean 支持的属性、事件和方法的只是提供一个标准方法

static BeanInfo getBeanInfo(Class<?> beanClass) 在Java Bean 上进行内省,了解其所有属性、公开的方法和事件

2、 BeanInfo 类

该类实现此BeanInfo 接口并提供有关其bean 方法、属性、事件等显式信息。

MethodDescriptor[] getMethodDescriptors() 获得beans MethodDescriptors() ;

PropertyDescriptor[] getPropertyDescriptors() 获得beans PropertyDescriptor.

Properties 属性文件工具类的使用

3、 PropertyDescriptor 类

propertyDescriptor 描述Java Bean 通过一对存储器方法导出的一个属性

Method getReadMethod() 获得应该用于读取属性值的方法

Method getWriteMethod() 获得应该用于写入属性值的方法

4、 MethodDescriptor 类

MethodDescriptor 描述了一种特殊方法

即Java Bean 支持从其他组件对其进行外部访问

Method getMethod() 获得此MethodDescriptor 封装的方法

8、 理解可配置的AOP配置

1、 AOP 的概念:Aspect Oriented Programming(面向切面编程)

9、 单例模式优化

  1. 使用同步保证线程安全
  2. 使用volatile 关键字

volatile 提醒编译器它后面所定义的变量随时都有可能变化,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据,如果没有volatile 关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象

  1. 防止反射调用私有构造方法
  2. 让单例类序列化安全

懒汉式

public class Singleton implements Serializable {

    private static Singleton singleton = null;
    private Singleton() {
        if(singleton!=null){
            throw new RuntimeException("此类为单例模式,已经被实例化了");
        }
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

}

饿汉式

public class Test {
        private Test() {
        }
        public static Test instance = new Test();
        public Test getInstance() {
                return instance;
        }
}