Java篇-反射

作者: TianTianBaby223 | 来源:发表于2018-07-06 16:46 被阅读5次

    一 : Class解析

    java.lang.Class:是反射的源头。
    首先我们创建了一个类,通过编译(javac.exe),生成对应的.class文件。之后我们使用java.exe加载(JVM的类加载器完成的),此.class文件加载到内存以后,就是一个运行时类,存在在缓存区。那么这个运行时类本身就是一个Class的实例.

    获取运行时类实例
    Person p = new Person();
            Class clazz = p.getClass();//通过运行时类的对象,调用其getClass(),返回其运行时类。
    

    1.每一个运行时类只加载一次!
    2.有了Class的实例以后,我们才可以进行如下的操作:
    1)创建对应的运行时类的对象
    2)获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注解、...)
    3)调用对应的运行时类的指定的结构(属性、方法、构造器)
    4)反射的应用:动态代理

    二 : 获取Class的实例操作

    • 调用运行时类本身的.class属性
    Class clazz1 = Person.class;
            System.out.println(clazz1.getName());
            
    Class clazz2 = String.class;
            System.out.println(clazz2.getName());
    
    • 通过运行时类的对象获取
    Person p = new Person();
            Class clazz3 = p.getClass();
            System.out.println(clazz3.getName());
    
    • 通过Class的静态方法获取.通过此方式,体会一下,反射的动态性
    String className = "com.atguigu.java.Person";
            Class clazz4 = Class.forName(className);
            System.out.println(clazz4.getName());
    
    • 通过类的加载器
    ClassLoader classLoader = this.getClass().getClassLoader();
            Class clazz5 = classLoader.loadClass(className);
            System.out.println(clazz5.getName());
    
    证明每一个运行时类只加载一次

    内存地址空间相同,证明为统一个运行时类

    System.out.println(clazz1 == clazz3);//true
            System.out.println(clazz1 == clazz4);//true
            System.out.println(clazz1 == clazz5);//true
    

    三 : 类的加载器

    类加载的过程

    当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化.


    类加载过程

    类加载器

    类加载器
    系统类加载器
    ClassLoader loader1 = ClassLoader.getSystemClassLoader();
            System.out.println(loader1);
    
    扩展类加载器
    ClassLoader loader2 = loader1.getParent();
            System.out.println(loader2);
    
    引导类加载器

    打印结果(null) 自动加载,无法直接获取

    ClassLoader loader3 = loader2.getParent();
            System.out.println(loader3);
    
    系统类加载器加载Person 类
    Class clazz1 = Person.class;
            ClassLoader loader4 = clazz1.getClassLoader();
            System.out.println(loader4);
    
    引导类加载器自动加载

    打印结果null

    String className = "java.lang.String";
            Class clazz2 = Class.forName(className);
            ClassLoader loader5 = clazz2.getClassLoader();
            System.out.println(loader5);
    

    四 : 通过反射获取Class对象和完整结构

    • 获取对象

    创建对应的运行时类的对象。使用newInstance(),实际上就是调用了运行时类的空参的构造器。
    要想能够创建成功:
    ①要求对应的运行时类要有空参的构造器。
    ②构造器的权限修饰符的权限要足够。

    person空参构造器不能用private修饰

    public void test1() throws Exception{
            String className = "com.atguigu.java.Person";
            Class clazz = Class.forName(className);
            Object obj = clazz.newInstance();
            Person p = (Person)obj;
            System.out.println(p);
        }
    
    • 获取设置运行时类的属性
    ① getFields()

    获取到运行时类中及其父类中声明为public的属性

    public void test1(){
            Class clazz = Person.class;
            Field[] fields = clazz.getFields();
            for(int i = 0;i < fields.length;i++){
                System.out.println(fields[i]);
            }
            System.out.println();
        }
    
    ② getDeclaredFields()

    获取运行时类本身声明的所有的属性

    Field[] fields1 = clazz.getDeclaredFields();
            for(Field f : fields1){
                System.out.println(f.getName());
            }
    
    ③获取 权限修饰符 变量类型 变量名
    Class clazz = Person.class;
            Field[] fields1 = clazz.getDeclaredFields();
            for(Field f : fields1){
                //1.获取每个属性的权限修饰符
                int i = f.getModifiers();
                String str1 = Modifier.toString(i);
                System.out.print(str1 + " ");
                //2.获取属性的类型
                Class type = f.getType();
                System.out.print(type.getName() + " ");
                //3.获取属性名
                System.out.print(f.getName());
                
                System.out.println();
            }
    
    ④获取运行时类中声明为public的指定属性名为fieldName的属性

    getField(String fieldName)
    获取运行时类中声明为public的指定属性名为fieldName的属性

    Class clazz = Person.class;
    Field name = clazz.getField("name");
    
    ⑤将运行时类的指定的属性赋值
    Person p = (Person)clazz.newInstance();
    name.set(p,"TZ");
    System.out.println("%"+name.get(p));
    
    ⑥获取运行时类中指定的名为fieldName的属性

    不惧权限修饰符
    getDeclaredField(String fieldName)

    Field age = clazz.getDeclaredField("age");
            //由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作。
    
              //声明为protected 不在一个包的情况下也要加`age.setAccessible(true)` 否则不用加
            age.setAccessible(true);
            age.set(p,10);
            System.out.println(p);
    
    • 获取运行时类的方法
    ①getMethods():

    获取运行时类及其父类中所有的声明为public的方法

    Method[] m1 = clazz.getMethods();
            for(Method m : m1){
                System.out.println(m);
            }
            System.out.println();
    
    ②getDeclaredMethods()

    获取运行时类本身声明的所有的方法

    Method[] m2 = clazz.getDeclaredMethods();
            for(Method m : m2){
                System.out.println(m);
            }
    
    ③获取 注解 权限修饰符 返回值类型 方法名 形参列表 异常
    Method[] m2 = clazz.getDeclaredMethods();
            for(Method m : m2){
                //1.注解
                Annotation[] ann = m.getAnnotations();
                for(Annotation a : ann){
                    System.out.println(a);
                }
                
                //2.权限修饰符
                String str = Modifier.toString(m.getModifiers());
                System.out.print(str + " ");
                //3.返回值类型
                Class returnType = m.getReturnType();
                System.out.print(returnType.getName() + " ");
                //4.方法名
                System.out.print(m.getName() + " ");
                
                //5.形参列表
                System.out.print("(");
                Class[] params = m.getParameterTypes();
                for(int i = 0;i < params.length;i++){
                    System.out.print(params[i].getName() + " args-" + i + " ");
                }
                System.out.print(")");
                
                //6.异常类型
                Class[] exps = m.getExceptionTypes();
                if(exps.length != 0){
                    System.out.print("throws ");
                }
                for(int i = 0;i < exps.length;i++){
                    System.out.print(exps[i].getName() + " ");
                }
                System.out.println();
            }
    
    ④调用运行时类中指定的方法
    Class clazz = Person.class;
            //getMethod(String methodName,Class ... params):获取运行时类中声明为public的指定的方法
            Method m1 = clazz.getMethod("show");
            Person p = (Person)clazz.newInstance();
            //调用指定的方法:Object invoke(Object obj,Object ... obj)
    
                注 :   'returnVal' 为方法返回值
            Object returnVal = m1.invoke(p);//我是一个人
    
            //无返回值
            System.out.println(returnVal);//null
            
            Method m2 = clazz.getMethod("toString");
            Object returnVal1 = m2.invoke(p);
    
              //打印返回值
            System.out.println(returnVal1);//Person [name=null, age=0]
            //对于运行时类中静态方法的调用
            Method m3 = clazz.getMethod("info");
            m3.invoke(Person.class);
            
            //getDeclaredMethod(String methodName,Class ... params):获取运行时类中声明了的指定的方法
     
    注 : `其中 display 为private 修饰方法`
            Method m4 = clazz.getDeclaredMethod("display",String.class,Integer.class);
            m4.setAccessible(true);
            Object value = m4.invoke(p,"CHN",10);//我的国籍是:CHN
            System.out.println(value);//10
    
    ⑤获取所有构造器
    public void test2() throws ClassNotFoundException{
           String className = "com.atguigu.java.Person";
           Class clazz = Class.forName(className);
           
           Constructor[] cons = clazz.getDeclaredConstructors();
           for(Constructor c : cons){
               System.out.println(c);
           }
       }
    
    ⑥获取父类
    @Test
        public void test1(){
            Class clazz = Person.class;
            Class superClass = clazz.getSuperclass();
            System.out.println(superClass);
        }
    
    ⑦获取带着泛型的父类
    public void test2(){
            Class clazz = Person.class;
            Type type1 = clazz.getGenericSuperclass();
            System.out.println(type1);
        }
    
    ⑧获取父类的泛型
    public void test3(){
            Class clazz = Person.class;
            Type type1 = clazz.getGenericSuperclass();
            
            ParameterizedType param = (ParameterizedType)type1;
            Type[] ars = param.getActualTypeArguments();
            
            System.out.println(((Class)ars[0]).getName());
        }
    
    ⑨获取实现的接口
    public void test4(){
            Class clazz = Person.class;
            Class[] interfaces = clazz.getInterfaces();
            for(Class i : interfaces){
                System.out.println(i);
            }
        }
    
    ⑩获取所在的包
    public void test5(){
            Class clazz = Person.class;
            Package pack = clazz.getPackage();
            System.out.println(pack);
        }
    
    11.获取注解
    public void test6(){
            Class clazz = Person.class;
            Annotation[] anns = clazz.getAnnotations();
            for(Annotation a : anns){
                System.out.println(a);
            }
        }
    
    12.调用指定的构造器

    调用 private Person(String name,int age)这个构造器

    public void test3() throws Exception{
            String className = "com.atguigu.java.Person";
            Class clazz = Class.forName(className);
            
            Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
            cons.setAccessible(true);
            Person p = (Person)cons.newInstance("TZ",20);
            System.out.println(p);
        }  
    

    相关文章

      网友评论

        本文标题:Java篇-反射

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