• 反射


     

     

    内容

    • 获取源头Class
    • 构造器
    • 实例化对象(重点)
    • 接口与父类
    • 修饰符
    • 属性
    • 方法
    • 数组
    • 类加载器
    • 反射相关操作(重点)

     

     “程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”,如Python,

    Ruby是动态语言;显然C++,Java,C#不是动态语言,但是JAVA有着一个非常突出

    的动态相关机制:Reflection。

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方

    法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以

    及动态调用对象的方法的功能称为java语言的反射机制。如

    /**

     * 入门级示例:通过对象获取  包名.类名

     * @author Administrator

     */

    public class Simple {

           public static void main(String[] args) {

                  Simple s=new Simple();

                  System.out.println(s.getClass().getName());

           }

    }

    Java反射机制,可以实现以下功能:

    ①在运行时判断任意一个对象所属的类;

    ②在运行时构造任意一个类的对象;

    ③在运行时判断任意一个类所具有的成员变量和方法;

    ④在运行时调用任意一个对象的方法;

    ⑤生成动态代理。

    相关的api为

    一、  获取源头Class(重点)

    打开权限:

    add.setAccessible(true);

    所有类的对象其实都是Class的实例。这个Class实例可以理解为类的模子,就是包含了类的结构信息,类似于图纸。我们日常生活中,需要创造一个产品,如想山寨一个iphone手机,怎么办?

     

    有三种方式可以实现:

    ⑴买个iphone手机,拆的七零八落的,开始山寨;

    ⑵到iphone工厂参观,拿到iphone磨具,开始山寨;

    ⑶跑到美国盗取iphone的图纸,开始山寨,最后一种最暴力,最爽。

    序列化:实现serializable接口,

    反序列化

    克隆:实现cloneable接口,重写clone()方法,修改权限为public

    New 反射

    同理,获取类的class对象,也有三种方式:

    ①Class.forName(”包名.类名”)//一般尽量采用该形式

    ②类.class

    ③对象.getClass()

    示例如下:

    public class Source {

           public static void main(String[] args) {

                  //第一种方式:对象.class

                  Source s=new Source();

                  Class<?>c1=s.getClass();

                  //第二种方式:类.class

                  Class<?>c2=Source.class;

                  //第三种方式(推荐方式):Class.forName()

                  Class<?>c3=null;

                  try {

                         c3=Class.forName("com.shsxt.ref.simple.Source");

                  } catch (ClassNotFoundException e) {

                         e.printStackTrace();

                  }

                  System.out.println(c1.getName());

                  System.out.println(c2.getName());

                  System.out.println(c3.getName());

           }

    }

    有了class对象,我们就有了一切,这就是反射的源头,接下来就是“庖丁解牛”。

    二、  构造器

    根据Class对象,我们可以获得构造器,为实例化对象做准备。调用以下api即可

    public class GetConstructor {

           public static void main(String[] args) {

                  try {

                         Class<?>clz=Class.forName("com.shsxt.ref.simple.User");

                         //1、获取所有的public 权限的构造器

                         Constructor<?>[]con=clz.getConstructors();

                         //注意查看构造器的顺序

                         for(Constructor<?> c:con){

                                System.out.println(c);

                         }

                         //2、获取所有的构造器

                         con=clz.getDeclaredConstructors();

                         System.out.println("--------------");

                         for(Constructor<?> c:con){

                                System.out.println(c);

                         }

                         System.out.println("----------------");

                         //3、获取指定的构造器(放入具体的类型)

                         Constructor<?> c=clz.getConstructor(String.class);

                         System.out.println(c);

                         //非public权限

                         System.out.println("----------------");

                         c=clz.getDeclaredConstructor(String.class,String.class);

                         System.out.println(c);

                  } catch (ClassNotFoundException e) {

                         // TODO Auto-generated catch block

                         e.printStackTrace();

                  } catch (NoSuchMethodException e) {

                         // TODO Auto-generated catch block

                         e.printStackTrace();

                  } catch (SecurityException e) {

                         // TODO Auto-generated catch block

                         e.printStackTrace();

                  }

           }

    }

    三、  实例化对象(重点)

    之前我们讲解过创建对象的方式,有new 、克隆、反序列化,再加一种,根据Class对象,使用newInstance() 或者构造器实例化对象。调用以下api即可

                 //获取源头

                      Class<?> clz = Class.forName("com.shsxt.ref.simple.User");

                         //第一种:通过newInstance()创建对象

                         User user=(User)clz.newInstance();

                         user.setUname("sxt");

                         user.setUpwd("good");

    //第二种:通过getDeclaredConstructors()创建对象,取得全部构造函数(注意顺序)

                         Constructor<?>[] cons=clz.getDeclaredConstructors();

                         for(Constructor<?>c:cons){

                                System.out.println(c);

                         }

    //注意观察上面的输出结果,再实例化,否则参数容易出错

                         User u1=(User)cons[0].newInstance("shsxt","good");

                         User u2=(User)cons[1].newInstance("sxt");

                         User u3=(User)cons[2].newInstance();

                         System.out.println(u1.getUname()+u1.getUpwd());

    注意:newInstance()是调用空构造,如果空构造不存在,会出现异常。由此可知,使用其他构造器创建对象比较麻烦,使用空构造非常简单。确保空构造存在           

    四、  接口与父类

    通过api获取接口与父类

    //获取源头

                         Class<?> clz =Class.forName("com.shsxt.ref.simple.User");

                         //获取所有接口

                         Class<?>[] inters=clz.getInterfaces();

                         for(Class<?> in:inters){

                                System.out.println(in.getName());

                         }

                         //获取父类

                         Class<?> cls=clz.getSuperclass();

                         System.out.println("继承的父类为:"+cls.getName());

    五、  修饰符

    获取修饰符,使用Modifier即可

    Class<?>clz=Class.forName("com.shsxt.ref.simple.User");

                         //获得修饰符

                         int n=clz.getModifiers();

                         //使用Modifier转换为相应的字符串

                         System.out.println(Modifier.toString(n));

     

    PUBLIC: 1     (二进制  0000 0001)

    PRIVATE: 2    (二进制  0000 0010)

    PROTECTED: 4 (二进制  0000 0100)

    STATIC: 8 (二进制  0000 1000)

    FINAL: 16 (二进制  0001 0000)

    SYNCHRONIZED: 32  (二进制  0010 0000)

    VOLATILE: 64  (二进制  0100 0000)

    TRANSIENT: 128  (二进制  1000 0000)

    NATIVE: 256   (二进制 0001  0000 0000)

    INTERFACE: 512  (二进制  0010 0000 0000)

    ABSTRACT: 1024  (二进制  0100 0000 0000)

    STRICT: 2048  (二进制 1000 0000 0000)

    六、  属性

    获取所有属性(包括父类或接口) ,使用Field 即可操作

                  Class<?> clz = Class.forName("com.shsxt.ref.simple.User");

                  //获取属性

    System.out.println("===============本类属性==========");

            // 取得本类的全部属性

            Field[] field = clz.getDeclaredFields();

            for (int i = 0; i < field.length; i++) {

                    // 1、权限修饰符

                       int mo = field[i].getModifiers();

                       String vis = Modifier.toString(mo);

                       // 2、属性类型

                       Class<?> type = field[i].getType();

                       //3、名字

                       String name = field[i].getName();

                    System.out.println(vis + " " + type.getName() + " "+ name + ";");

            }

    System.out.println("=========公开的属性包括接口或者父类属性======");

            field = clz.getFields();

            for (int i = 0; i < field.length; i++) {

               System.out.println(field [i]);

             }

    七、  方法

    获取所有方法(包括父类或接口),使用Method即可。

    public static void test() throws Exception {

                  Class<?> clz = Class.forName("com.shsxt.ref.simple.User ");

                  //获取属性

    System.out.println("===============本类方法===============");

            // 取得全部公共方法

                  Method[] methods =clz.getMethods();

                  for(Method m:methods){

                         //1、权限

                         int mod=m.getModifiers();

                         System.out.print(Modifier.toString(mod)+" ");

                         //2、返回类型

                         Class<?> returnType=m.getReturnType();

                         System.out.print(returnType.getName()+" ");

                         //3、名字

                         String name =m.getName();

                         System.out.print(name +"(");

                         //4、参数

                         Class<?>[] para=m.getParameterTypes();

                         for(int i=0;i<para.length;i++){                        

                                Class<?> p =para[i];

                                System.out.print(p.getName() +" arg"+i);

                                if(i!=para.length-1){

                                       System.out.print(",");

                                }

                         }

                         //异常

                         Class<?>[] exce=m.getExceptionTypes();

                if(exce.length>0){

                    System.out.print(") throws ");

                    for(int k=0;k<exce.length;++k){

                        System.out.print(exce[k].getName()+" ");

                        if(k<exce.length-1){

                            System.out.print(",");

                        }

                    }

                }else{

                    System.out.print(")");

                }

                System.out.println();

                  }            

           }

    八、  数组

    操作数组需要借助Array类

    //1、创建数组

                  Object obj =Array.newInstance(int.class, 5);

                  //2、获取大小

                  if(obj.getClass().isArray()){  //3、判断是否为数组

                         System.out.println(Array.getLength(obj));

                         //4、设置值

                         Array.set(obj,0, 100);

                         //5、获取值

                         System.out.println(Array.get(obj,0));

                  }

    九、  类加载器

    在java中有三种类类加载器:

    ⑴Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。

    ⑵Extension ClassLoader 用来进行扩展类的加载,一般对应的是jrelibext目录中的类

    ⑶AppClassLoader 加载 classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。 了解即可。

    public static void main(String[] args) throws Exception {

                   System.out.println("类加载器  "+ClassLoader.class.getClassLoader().getClass().getName());

           }

    了解一下类的生命周期:

    在一个类编译完成之后,下一步就需要开始使用类,如果要使用一个类,肯定离不开JVM。在程序执行中JVM通过装载链接初始化这3个步骤完成。

    类的装载是通过类加载器完成的,加载器将.class文件的二进制文件装入JVM的方法区,并且在堆区创建描述这个类的java.lang.Class对象。用来封装数据。 但是同一个类只会被类装载器装载一次。

    链接就是把二进制数据组装为可以运行的状态。链接分为校验,准备,解析这3个阶段

    1、校验一般用来确认此二进制文件是否适合当前的JVM(版本),

    2、准备就是为静态成员分配内存空间。并设置默认值

    3、解析指的是转换常量池中的代码作为直接引用的过程,直到所有的符号引用都可以被运行程序使用(建立完整的对应关系)

    完成之后,类型也就完成了初始化,初始化之后类的对象就可以正常使用了,直到一个对象不再使用之后,将被垃圾回收。释放空间。当没有任何引用指向Class对象时就会被卸载,结束类的生命周期

    十、  反射相关操作(重点)

    1、操作属性

    //1、获取Class对象

                         Class<?> clz=Class.forName("com.shsxt.ref.simple.User");

                         //2、获取对象

                         User u=(User)clz.newInstance();

                         //3、设置属性

                         Field field=clz.getDeclaredField("uname");

                         field.setAccessible(true);//打开权限

                         field.set(u, "0523");

                         //4、获取此属性

                         System.out.println(field.get(u));

    2、调用方法

    调用方法,都是直接对象.方法([实参]);反射之后,动态调用方法需要使用 invoke即可。

    1)、方法调用

    //1、获取Class对象

                  Class<?> clz=Class.forName("com.shsxt.ref.simple.User");

                  //2、获取对象

                  User u=(User)clz.newInstance();

                  //3、获取方法

                  Method m =clz.getMethod("coding", String.class,String.class);

                  //4、成员方法的调用

                  m.invoke(u, "反射","多个参数");

                  //若是静态方法,传递null即可   因为静态方法属性类,不属于对象

                  m=clz.getMethod("testStatic",int.class);

                  m.invoke(null, 100);//与对象无关

    2)、操作settergetter访问器

    /**

         * @param obj     操作的对象

         * @param att      操作的属性

         * @param value  设置的值

         * */

        public static void setter(Object obj, String att,Object value) {

            try {

                   //setUname setUpwd -->第一个字母大写

                   att=att.substring(0,1).toUpperCase()+att.substring(1);

       Method method = obj.getClass().getMethod("set" + att, value.getClass());

                method.invoke(obj, value);

            } catch (Exception e) {

                e.printStackTrace();

            }

        }

        public static  Object getter(Object obj, String att) {

            try {

                   //getUname getUpwd -->第一个字母大写

                   att=att.substring(0,1).toUpperCase()+att.substring(1);

                Method method = obj.getClass().getMethod("get" + att);

               return  method.invoke(obj);

            } catch (Exception e) {

                e.printStackTrace();

            }

            return null;

    }

    main方法

    //1、获取Class对象

                  Class<?> clz=Class.forName("com.shsxt.ref.simple.User");

                  //2、获取对象

                  User u=(User)clz.newInstance();

                  //3、设置值

                  setter(u,"uname","shsxt");

                  //获取值

                  String str=(String) getter(u,"uname");

               System.out.println(str);

  • 相关阅读:
    查看python关键字
    命令终端执行python
    Codeforces-462C. A Twisty Movement
    Codeforces-462A. A Compatible Pair
    Codeforces-446C. Pride
    Codeforces-Hello 2018C. Party Lemonade(贪心)
    Codeforces-33C. Wonderful Randomized Sum
    Codeforces-118D. Caesar's Legions(lazy dynamics)
    codeforces-73C. LionAge II
    Gym 101510C-Computer Science
  • 原文地址:https://www.cnblogs.com/lotus-wmm/p/12102367.html
Copyright © 2020-2023  润新知