美文网首页
反射,注解,动态代理

反射,注解,动态代理

作者: 云承寒 | 来源:发表于2017-05-23 23:17 被阅读0次

    类加载机制

    类加载机制

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

    加载
    • 加载是指将类的字节码文件(.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<Object> 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 <T> List<T> 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);
    }
    

    相关文章

      网友评论

          本文标题:反射,注解,动态代理

          本文链接:https://www.haomeiwen.com/subject/saagxxtx.html