zoukankan      html  css  js  c++  java
  • 笔记——类型信息与反射

    类型信息与反射

    2.1.1 .class文件和.java文件

    Java的编译器在编译Java类文件时,会将原有的文本文件翻译成二进制的字节码,并将这些字节码存储在.class文件中。对于只含有一个类或接口的Java类文件,编译后只产生一个.class文件;而对于一个含有多个类或接口的Java类文件,分情况为:①.Java只含一个类或接口,编译后只产生一个.class文件;②.Java存在内部类时,会产生多个.class文件,内部类文件命名为:外部类名+$+内部类名,③.Java文件中存在多个并行类或接口,编译时也会产生多个.class文件,文件命名为类名/接口名。

    2.1.2 类型信息的加载

    Java提供两种加载方式:①预先装载②按需装载

    按需装载类的条件: 在程序运行过程中,当一个类的静态成员被第一次引用时,JVM就会去装载它,静态成员包括:静态方法、静态属性、构造方法。

     

    类加载器:三种

    1)bootstrap类加载器用于引导JVM,一旦调用Java.exe程序,bootstrap类加载器就开始工作。

    2)extension类加载器负责加载标准扩展目录下的类,是编写程序变得简单。

    3)system加载器是默认的加载器,它在环境变量CLASSPATH目录下查找相应的类。

     

    表2-2 ClassLoader中与加载类相关的方法

    方法说明
    getParent() 返回该类加载器的父类加载器
    loadClass(String name) 加载名称为name的方法,返回java.lang.Class类的实例
    findClass(String name) 查找名称为name的类,返回java.lang.Class类的实例
    findLoadedClass(String name) 查找名称为name的已经被加载过的类,返回java.lang.Class类的实例
    resolveClass(Class<?> c) 链接指定的Java类
       

     

    类加载的顺序:当一个类具有继承关系时,装载是从顶级类开始的,以此类推制止加载到这个类本身。

    2.2核心类

    2.2.1 Class类

    获取Class对象的方法有多种:

    ① xxx.class Class<Person> clazz = Person.class;

    ②xxx.getClass()方法 Person.getClass()

    ③xxx.forname()方法 Class<?> clazz = Class.forName(String);

    2.2.2获取Constructor对象

    Constructor类专门用来描述构造函数

    方法说明
    getConstructior(Class parameterTypes... ) 用于获取para类的的Constructor对象,获取的构造方法必须为公有
    getConstructors() 获取制动类的共有构造函数列表,如果没有则返回长度为0的Constructor数组
    getDeclaredConstructor(Class ... parameterTypes) 获取(可公有、保护、私有)指定参数类型的构造函数描述对象
    getDeclaredConstructors() 获取(所有)构造函数描述的对象列表

    2.2.3获取Method对象

    Method类对象用于描述类的单个方法(不含构造方法)。可通过Method类来获取方法的访问权限、参数类型、返回值类型等信息。

    Class类提供了4个方法供编程人员获取方法的描述对象

    方法说明
    getMethod(String name,Class...paramrterTypes) 用于获取指定名称和参数类型的公有方法描述对象(包含继承自父类)
    getMethods() 用于获取公有的方法描述对象列表(包含继承自父类)
    getDeclaredMethod(String name,Class...paraTypes) 可获取非公有的指定名称和参数类型的方法描述对象
    getDeclaredMethods() 用于获取类本身定义的所有方法描述对象

     

    2.2.4获取Field对象

    Field类的对象用于描述类的单个字段。可通过对Field对象来获取字段的访问权、字段信息等类型,并且可通过Field对象来动态地修改字段值

    方法说明
    getField(String name) 用于获取指定名称的公有Field对象
    getFields() 用于获取指定类的共有属性描述对象Field列表
    getDeclaredField(String name) 用于获取(可非公有)的指定名称的Field属性
    getDeclaredFields() 获取一份Field数组,该数组记录指定类的所有属性的描述对象

     

    2.4类型信息应用——反射

    Java反射机制是指运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法,修改它的任意属性;这种动态获取的信息以及动态调用对象成员的功能称为Java语言的反射机制。

    反射的应用:

    举例:以下是一个继承Person类的Teacher类,教师有职称、薪水属性,能speak和getSalary

    package org.ddd.reflect.example29;
    ​
    public abstract class Person{
        public abstract String toString();
    }
    ​
    ​
    package org.ddd.reflect.example29;
    ​
    ​
    public final class Teacher extends Person{
        
        public String position;
        private int salary;
        public void speak(String message) {
            System.out.println("Speak:" + message);
        }
        
        @Override
        public String toString() {
            return "[Position: "+ position + " Salary: " + salary + "]";
        }
        
        private int getSalary() {
            return this.salary;
        }
    }
    ​
     

    使用反射技术实现以下要求:

    ①显示的将这个类载入内存

    ②实例化这个类

    ③执行方法speak(String message)

    ④修改属性position

    ⑤修改受保护属性salary,以及执行私有方法getSalary()


    1) 使用Class类提供的显示加载方法forName(String name)

    public class Bootsrap {
        public static String className = "org.ddd.reflect.example29.Teacher";
        public static void main(String[] args) {
            try {
                System.out.println("开始加载类!");
                Class clazz = Class.forName(className);
                System.out.println("类加载完成!");
                
            }catch(ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    ​
    }
    ​
     

    运行结果:

    2)通过反射实例化类,有两种动态创建对象的方法,如Class类的newInstance方法实例化对象,或Constructor的newInstance方法

    //实例1
    package org.ddd.reflect.example29;
    ​
    public class Bootsrap {
        public static String className = "org.ddd.reflect.example29.Teacher";
        public static void main(String[] args) {
            try {
                Class clazz = Class.forName(className);
                Person person = (Person)clazz.newInstance();
                System.out.println(person.toString());
                
            }catch(Exception e) {
                e.printStackTrace();
            }
        }
    ​
    }
    ​
     
    
    //示例二
    import java.lang.reflect.Constructor;
    ​
    public class Bootsrap {
        public static String className = "org.ddd.reflect.example29.Teacher";
        public static void main(String[] args) {
            try {
                Class clazz = Class.forName(className);
                Constructor constructor = clazz.getConstructor();
                Person person = (Person)constructor.newInstance();
                System.out.println(person.toString());
                
            }catch(Exception e) {
                e.printStackTrace();
            }
        }
    ​
    }
    ​
     

     

    3)通过反射执行方法

    该方法名为“speak”,参数类型为String。class。要动态获取speak方法,首先要获取Method对象,method类拥有invoke方法,如下:

     

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    ​
    public class Bootsrap {
        public static String className = "org.ddd.reflect.example29.Teacher";
        public static void main(String[] args) {
            try {
                Class clazz = Class.forName(className);
                Constructor constructor = clazz.getConstructor();
                Object teacher = constructor.newInstance();
                Method method = clazz.getMethod("speak", String.class);
                method.invoke(teacher, "Lesson one!");
                
            }catch(Exception e) {
                e.printStackTrace();
            }
        }
    ​
    }
    ​
     

    运行结果:

    4)通过反射修改属性,通过Field类的方法set,动态的给属性赋值

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    ​
    public class Bootsrap {
        public static String className = "org.ddd.reflect.example29.Teacher";
        public static void main(String[] args) {
            try {
                Class clazz = Class.forName(className);
                Constructor constructor = clazz.getConstructor();
                Object teacher = constructor.newInstance();
                Field field = clazz.getField("position");
                System.out.println(teacher.toString());
                field.set(teacher, "Master");
                System.out.println(teacher.toString());
                
            }catch(Exception e) {
                e.printStackTrace();
            }
        }
    ​
    }

     

    运行结果:

    5)修改访问权限

    正常情况下我们无法访问private修饰的salary,但是constructor的父类AccessbleObject提供了setAccessable(boolean flag)

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    ​
    public class Bootsrap {
        public static String className = "org.ddd.reflect.example29.Teacher";
        public static void main(String[] args) {
            try {
                Class clazz = Class.forName(className);
                Constructor constructor = clazz.getConstructor();
                Object teacher = constructor.newInstance();
                Method method = clazz.getDeclaredMethod("getSalary");
                method.setAccessible(true);
                Integer salary = (Integer)method.invoke(teacher);
                System.out.println("Teacher salary: " + salary);
                
            }catch(Exception e) {
                e.printStackTrace();
            }
        }
    ​
    }

    运行结果:

     

    动态代理模式可参考以下博客:https://www.cnblogs.com/gonjan-blog/p/6685611.html

     

  • 相关阅读:
    IIS 6.0下安装SSL数字证书实现https访问
    使用SQL2005自带扩展函数对字符串进行MD5加密(转)
    Posix多线程编程学习笔记(三)—信号灯(1)--转自湖光倒影
    利用pthread_mutex对多进程上锁
    ObjectiveC中的锁
    Linux内存管理之mmap详解
    cocoa应用程序生命周期
    多线程编程互斥锁
    [New Book]Flex第一步 国内第一本关于Flex的书籍
    ASP.NET中实现二级或多级域名(修改UrlRewrite)
  • 原文地址:https://www.cnblogs.com/wxx23-IOU/p/14438385.html
Copyright © 2011-2022 走看看