反射,注解,动态代理

类加载机制

反射,注解,动态代理_第1张图片
类加载机制

当调用某个类时,系统会通过加载,连接,初始化三个步骤来对该类进行初始化操作。

加载
  • 加载是指将类的字节码文件(.class)加载到内存中,并为之创建一个java.lang.Class对象,称为字节码对象。

  • 加载过程由类加载器控制完成,类加载器通常由Jvm提供,也可以通过继承classloader自定义类加载器。

  • 当加载结束,类的二进制数据会合并到Jre中。

连接
  • 验证:检测被加载类的内部结构是否正确。

  • 准备:负责为类的static变量分配内存,并设置默认值。

  • 解析:把类的二进制数据中的符号引用,替换为直接引用。

初始化
  • 在此阶段,Jvm负责对类进行初始化,主要是对static变量进行初始化。

  • 如果类中有初始化语句(静态代码块),则系统依次执行这些初始化语句。

  • 如果该类的直接父类还未被初始化,则先初始化其父类。

  • 如果该类还未被加载和连接,则先加载并连接该类。


反射

反射是指获得类的元数据的过程,在运行时期,动态的获取某一个类中的成员信息(构造器,方法,字段,内部类,接口,父类等),反射损耗性能。

  • 当字节码文件加载到内存时,Jvm会对其进行解刨,分析其成员,创建对应的Class对象,将该字节码文件的信息全部存储到Class对象中。

  • 通过操作该Class对象,就可以获取使用该字节码文件的信息。

获取Class的三种方式
public static void demo() throws ClassNotFoundException {

        // 1.通过完整类名获取Class,推荐这种
        Class clz = Class.forName("domain.PersonDemo");

        // 2.通过类来获取
        Class clz1 = PersonDemo.class;

        // 3.通过对象来获取
        Class clz2 = new PersonDemo().getClass();
}


public class PersonDemo {

    private String name;
    private int age;

    public PersonDemo() {
    }

    // 私有构造函数
    private PersonDemo(String name) {
        this.name = name;
    }

    public PersonDemo(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void test() {
        System.out.println("获取公共方法");
    }

    private void test(String string) {
        System.out.println("获取私有方法");
    }

    @Override
    public String toString() {
        return "PersonDemo{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
1.获取构造方法
public static void demo() throws ClassNotFoundException {

        Class clz = Class.forName("domain.PersonDemo");

        // 获取一个类所有非私有的构造方法
        Constructor[] cons = clz.getConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
        }

        // 获取一个类所有构造法方法(包括私有)
        Constructor[] declaredCons = clz.getDeclaredConstructors();
        for (Constructor con : declaredCons) {
            System.out.println(con);
        }
    }
通过Class获取单个构造方法,生成对象
public static void demo() throws Exception {

        Class clz = Class.forName("domain.PersonDemo");

        // 获取单个构造方法(不能取出私有构造方法)
        // 传入一个可变参数,对应构造方法的参数类型
        Constructor constructor = clz.getConstructor(String.class, int.class);
        // 通过构造方法创建对象
        PersonDemo personDemo = (PersonDemo) constructor
                .newInstance("公共构造函数", 18);
        System.out.println(personDemo);

        // 获取单个构造方法(包括私有构造方法)
        Constructor declaredConstructor = clz.getDeclaredConstructor(String.class);
        //暴力反射
        declaredConstructor.setAccessible(true);
        PersonDemo personDemo1 = (PersonDemo) declaredConstructor
                .newInstance("私有构造函数");
        System.out.println(personDemo1);
    }
2.获取方法(Method)
public static void demo() throws Exception {

        Class clz = Class.forName("domain.PersonDemo");

        // 获取所有公共方法(包括父类继承下来的)
        Method[] methods = clz.getMethods();
        for (Method method : methods)
            System.out.println(method);


        // 获取所有方法(不包括父类继承下来的)
        Method[] declaredMethods = clz.getDeclaredMethods();
        for (Method method : declaredMethods)
            System.out.println(method);
}
获取单个方法,调用
public static void demo() throws Exception {

        Class clz = Class.forName("domain.PersonDemo");
        PersonDemo personDemo = new PersonDemo();

        // 获取单个方法,传入参数要获取的方法名,和对应参数类型
        Method method = clz.getMethod("test", null);
        //调用,传入参数方法的调用对象,方法所需要的参数
        method.invoke(personDemo, null);


        Method declaredMethod = clz.getDeclaredMethod("test", String.class);
        declaredMethod.setAccessible(true);
        declaredMethod.invoke(personDemo, "私有方法参数");

        //静态的调用传null即可
        //method.invoke(null, "测试参数");

        //数组类型
        //clz.getDeclaredMethod("test", String[].class);
}
获取成员变量
 public static void demo() throws Exception {

        Class clz = Class.forName("domain.PersonDemo");
        PersonDemo personDemo = new PersonDemo();

        Field[] fields = clz.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        //获取私有的
        Field[] declaredFields = clz.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println(field);
        }

        //获取单个私有属性
        Field field = clz.getDeclaredField("name");
        field.setAccessible(true);
        field.set(personDemo, "给私有属性赋值");
        System.out.println(personDemo);

    }
获取静态成员
public class PersonDemo {

    public PersonDemo() {
    }

    private static String name;

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

    public static String getName() {
        return name;
    }
}

public static void demo() throws Exception {

        Class clz = Class.forName("domain.PersonDemo");
        //当构造方法为public且无参时可以用此直接构建对象
        PersonDemo personDemo = (PersonDemo) clz.newInstance();

        Field name = clz.getDeclaredField("name");
        name.setAccessible(true);
        //因为是静态的可以不传对象
        name.set(null,"为静态属性赋值");

        Method method = clz.getDeclaredMethod("setName", String.class);
        method.setAccessible(true);
        method.invoke(personDemo,"更改赋值");
        System.out.println(PersonDemo.getName());
    }
获取数组(可变参数)
public class PersonDemo {

    public PersonDemo() {
    }

    public void getStringArr(String... arrs) {
        System.out.println(Arrays.toString(arrs));
    }

    public void getIntArr(int[] intarr, int i) {
        System.out.println(Arrays.toString(intarr) + ":" + i);
    }
}

public static void demo() throws Exception {

        Class clz = Class.forName("domain.PersonDemo");
        PersonDemo personDemo = (PersonDemo) clz.newInstance();

        Method getStringArr = clz.getMethod("getStringArr", String[].class);
        getStringArr.invoke(personDemo,
                new Object[]{  //用一个Object包裹
                        new String[]{"1", "2", "3", "4", "5"}});

        Method intArr = clz.getMethod("getIntArr", int[].class, int.class);
        intArr.invoke(personDemo,
                new Object[]{
                new int[]{5, 4, 3, 2}, 1});
}

Class常用API
public class Demo {

    public static void main(String[] args) {

        String name = Demo.class.getName();
        System.out.println("获取完整类名:" + name);

        String simpleName = Demo.class.getSimpleName();
        System.out.println("获取类名:" + simpleName);

        Class object = Demo.class.getSuperclass();
        System.out.println("获取父类:" + object);

        Class[] IClass = Demo.class.getInterfaces();
        System.out.println("获取接口:" + Arrays.toString(IClass));

        int modifier = Demo.class.getModifiers();
        System.out.println("获取修饰符:" + modifier);

        String packageName = Demo.class.getPackage().getName();
        System.out.println("获取包名:" + packageName);
        
    }
}
加载资源路径
public class Demo {

    public static void main(String[] args) throws IOException {
        test2();
    }

    //方式1使用绝对路径
    public static void test1() throws IOException {
        Properties properties = new Properties();
        properties.load(new FileInputStream("E:\\AndroidFile\\Repeat\\resource\\db.properties"));
        System.out.println(properties);
    }

    //方式2使用相对路径,相对于classPath字节码输出的目录即bin目录,常用
    public static void test2() throws IOException {
        Properties properties = new Properties();
        //使用ClassLoader类加载器
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream("db.properties");
        properties.load(inputStream);
        System.out.println(properties);
    }

    //方式3使用相对路径,相对于当前加载资源文件的字节码路径
    public static void test3() throws IOException {
        Properties properties = new Properties();
        //这里需要注意用Class.getResourceAsStream同ClassLoader的路径不一样
        InputStream inStream = Demo.class.getResourceAsStream("db.properties");
        properties.load(inStream);
        System.out.println(properties);
    }
}
根据配置文件生成类
//定义接口规范
public interface IWork {

    void work();
}
//接口实现类
public class Key implements IWork {

    @Override
    public void work() {
        System.out.println("Load Key");
    }
}

public class Mouse implements IWork {

    @Override
    public void work() {
        System.out.println("Load Mouse");
    }
}
//配置文件
Key=完整类名
Mouse=完整类名
public class Computer {

    //声明配置文件
    private static Properties properties = new Properties();
    static {
        
        //获取相对路径bin(classPath)
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        InputStream inStream = classLoader.getResourceAsStream("load.properties");
        try {
            properties.load(inStream);
            
            //获取包名,反射生成类
            Collection values = properties.values();
            for (Object item : values) {
                String name = (String) item;
                Class className = Class.forName(name);
                IWork work = (IWork) className.newInstance();
                work(work);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void work(IWork work) {
        work.work();
    }
}

public class Run {

    public static void main(String[] args) {
        
        Computer computer = new Computer();
        
    }
}

注解

从JDK5开始,Java开始支持元数据Annotation(元数据即描述数据的数据),通常用来为程序元素(类,方法,成员变量等)设置元数据。

注解是一种特殊的接口
public @interface Override {

}
常见注解
@Override 标记重写的父类方法

@Deprecated 标记过时,不推荐使用的方法和类

@SuppressWarings 抑制编译器发出的警告,仅仅是看不到警告(问题依然存在)

@SuppressWarings("all") 抑制所有的警告

@SafeVarargs 抑制堆污染发出的警告,同时出现可变参数和泛型。
@SuppressWarnings("varargs")
public static  List asList(T... a) {
     return new ArrayList<>(a);
}

元注解

自定义注解时,用来贴在注解上的注解,用来制定自定义注解的使用范围。

@Retention 决定注解可以保存到哪个时期。
@Target 决定了该注解可以贴在什么地方

注解的使用
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface Demo {
    //抽象方法 属性
}
@Retention(RetentionPolicy.RUNTIME)
注解的有效期有三个,封装在枚举:RetentionPolicy类中。

RetentionPolicy.SOURCE
表示注解只存在于源文件中,不会被编译到字节码中。

RetentionPolicy.CLASS
表示注解会被编译到字节码中,但是JVM不加载注解。

RetentionPolicy.RUNTIME
表示注解会被编译到字节中,随着字节码加载进JVM,可以通过反射读取。
@Target({ElementType.TYPE})
决定了该注解可以贴在什么地方

ElementType.PACKAGE:贴在包上(极少使用)
ElementType.ANNOTATION_TYPE:贴在注解上
ElementType.TYPE: 贴在类、接口或枚举上

ElementType.CONSTRUCTOR:贴在构造方法上
ElementType.METHOD:贴在方法上
ElementType.FIELD:贴在字段上(包括枚举常量)
ElementType.LOCAL_VARIABLE:贴在局部变量上   
ElementType.PARAMETER: 贴在参数上 
@Documented 表示注解会被javadoc指令编辑到API中

@Inherited 表示注解会遗传给子类
注解的使用
//自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface AnnotationDemo {
    int level() default 1;

    String info();

    String[] data();
}

//使用注解
@AnnotationDemo(level = 1, info = "注解演示", data = {"data1", "data2"})
class Emp {

}

//获取注解属性
public static void demo() throws ClassNotFoundException {

        //通过反射找到注解依附的类
        Class clz = Class.forName("domain.Emp");

        //判断是否有这个类型的注解
        if (clz.isAnnotationPresent(AnnotationDemo.class)) {
            //获取注解
            AnnotationDemo anno = (AnnotationDemo) 
                    clz.getAnnotation(AnnotationDemo.class);
            
            //获取属性
            System.out.println(anno.level());
            System.out.println(anno.info());
            System.out.println(Arrays.toString(anno.data()));
        }
    }

动态代理

interface IDataInfo {
    int add(int i, int j);
    int sub(int i, int j);
}


class DataInfoImpl implements IDataInfo {

    @Override
    public int add(int i, int j) {
        return i + j;
    }

    @Override
    public int sub(int i, int j) {
        return i - j;
    }
}


public static void demo() {
    DataInfoImpl dataInfoImple = new DataInfoImpl();

    IDataInfo proxy = (IDataInfo) Proxy.newProxyInstance(
            dataInfoImple.getClass().getClassLoader(),
            new Class[]{IDataInfo.class}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args)
                          throws Throwable {

                    System.out.println("在代理真实对象前我们可以添加一些自己的操作");

                    //当代理对象调用真实对象的方法时,
                    //会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
                    Object res = method.invoke(dataInfoImple, args);
                    System.out.println(res.toString());

                    System.out.println("在代理真实对象后我们可以添加一些自己的操作");

                    return res;
                }
            });

    proxy.add(1, 1);
}

你可能感兴趣的:(反射,注解,动态代理)