反射

 

 

内容

  • 获取源头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 用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类

⑶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);

posted on 2019-12-26 15:37  萌萌手好冷  阅读(131)  评论(0编辑  收藏  举报