java基础之反射

来源:互联网 发布:c语言|是什么 编辑:程序博客网 时间:2024/05/24 01:05

目录

  • Class类
  • 反射的概念
  • Constructor类:构造函数的反射
  • Field类:成员变量的反射
  • Method类:成员方法的反射
  • 数组的反射

Class类

Java中的各个java类属于同一类事物,于是用Class类来描述这类事物。

获取Class类对象的3个方法:
1、通过加载进内存的类的名来获取:比如要创建Person的一个对象时,将Person的class文件从硬盘加载到内存,然后根据其内的构造函数创建Person对象: p1,在这个过程中,加载到内存的Person.class就是Class类的一个对象。
Class cls1 = Person.class;
2、通过实例对象来获取:也可以通过Person对象来获得:Class cls1 = p1.getClass( );
3、直接从硬盘上获取:用静态方法查询或加载:通过类名(字符串)直接由硬盘上的字节码文件,加载进内存,创建对象:比如Class cls2 = Class.forName(“java.lang.Math”); 实际使用中,第3中方法使用最多
9个预定义的Class对象及数组:
8个基本数据类型的class和一个void.class
判断是否为基本数据类型的类对象:isPrimitive( );
数组类型的Class对象:int[ ].class 判断:isArray()

总之,在源程序中出现的类,都有各自的Class对象.

反射:

把java类中的各种成分映射成相应的java类。

    如构造函数映射成Constructor类,方法映射成Method类,成员变量映射成Field类,Package类等。也就是说,一个类中的每个成员都可以用反射API类中的实例对象来表示,通过Class的方法可以得到这些对象。

反射的价值:实现框架功能。框架与框架要解决的核心问题:写框架时无法知道用户的类名,在程序中无法直接new对象,这就需要利用反射来做。

Constructor类:

获得本类对象:
1、得到某个类的所有构造方法:Constructor[ ] cons = String.class().getConstructors();
2、得到某个类的某一个构造方法:
Constructor con = String.class().getConstructor(StringBuffer.class)
相当于得到了构造方法String(StringBuilder builder)
Constructor类的方法有:
1、getDeclaringClass()—获得所在的类
2、 getModifiers()—得到方法前面的修饰符
3、newInstense(Object… initargs)—有参数或无参数 ,可构造实例对象( 相当于拿着字节码去创建一个对象),返回的是一个Object类对象,要强转回真正的类才能使用。 class — Constructor—-newInstanse 反射经历了中间的较为消耗 系统资源,而Class类中,有newInstanse()方法,可直接创建对象。

Field类:

表征java类中的成员变量:成员变量就是与对象一同储存在堆内存中(注:其中静态变量是在静态区)

         public class ReflectPoint         {             private int x;             public int y ;             public ReflectPoint(int x, int y)             {                  super();                  this.x = x;                  this.y = y;              }          }          ReflectPoint rp1 = new ReflectPoint(3,5);
获取步骤:
1、由Class类的getField方法指定欲得到的成员名字,得到Field对象,表示ReflectPoint类的成员y,不是对象上的具体值。与Constructor类的get方法一样,本类与也有getFields()方法,得到的是Field类型的数组。
Field fieldY = rp1.getClass().getField( “y”);
Field[ ] fields = rp1.getClass().getFields( );
2、要获得具体对象中的y的值,用Field的get方法,传入具体对象,获得一个Object类型的对象,再强转为实际类型。
int z = (int )fieldY.get (rp1);
3、暴力反射:getField只能得到非私有成员,要拿到私有成员的值,可以如此:getDeclaredField()能够得到所有已声明的变量。比如x可用这样获得:Field fieldX = rp1.getClass(). getDeclaredField( “x” );
这时,要拿到对象rp1中x的具体数值,还不能直接用get方法,虽然getDeclaredField看了成员,但成员还是私有,没法直接访问(拿到–get),需要将之设置为可访问的(这就称为暴力反射)。这不会影响原类中的x的属性,只是为了拿到私有化成员的一个工具。fieldX.setAccessible( true );

Method类:

成员方法属于类,得到方法(在内存的方法区),然后再让对象(堆内存)调用方法。

成员方法的反射:
1、Method mcharAt = String.class.getMethod( “charAt”, int.class);//指定方法名和该方法的参数列表,获取该类对象。
2、使用该方法:调用(invoke()):Method类说,字符串,我把索引(int)给你,你调用一下你的charAt方法:
char cha = (char)mcharAt.invoke( “abcd”, 1); 结果相当于char cha = “abcd”.charAt(1);

mcharAt.invoke(null, 1);第一个参数(对象)是null时,说明此方法不需要对象即调用,是一个静态方法。

利用反射调用某个类(如Person类)中的main方法:
1、静态方式的调用:Person.main();
2、反射的方式调用:为什么要用反射的方式调用?写调用main方法的代码时,可能不确定类名
String className = “MainD”;
Method othermain = Class. forName(className).getMethod(“main”, String[].class);

注意:main方法接收一个参数:main(String[] args).如果在invoke中直接写
new String[] { “123”, “456” },String数组会被拆包,相当于传了多个参数。那么有两种方法:
    将数组再打包成只含一个元素的Object数组:
    othermain.invoke(“MainD”, new Object[]{new String[] {“123”, “456”}});

    或者将数组强制转换成一个Object类型,表明该数组只是一个数组元素,无需拆包。
    othermain.invoke( “MainD” , (Object) new String[] { “123”, “456” });

数组的反射

特点
1、类型和纬度相同的数组,每一个数组反射出的字节码都是同一个,与内部元素个数无关。
如一维 数组名字都是[I,[L等,二维数组[[I等。
2、代表数组的Class实例对象的getSuperclass()返回父类Object对应的Class
3、基本数据类型的一维数组可以被当做一个Object类型使用,不能当做Object[]类型使用,引用类型的一维数组,既能当做Object类型使用,也能当做Object[]类型使用。(故上面调用main方法时需要制指定,否则默认当做Object[]类型使用而自动拆包)
基本数据类型数组的特点:
注意:8中基本数据类型不是Object类型,该类型数组无法元素无法直接打印(char[ ]除外),要而用到工具:Arrays.toString()
    System.out.println(obj):调用obj对象的toString方法,然后打印:
    对于数组来说,它的toString方法是:数组类名@哈希值
    System.out.println( new int[] {1,2,3});打印出来的是[I@32edeea8

    用数组的工具类Arrays.asList(arr)处理int[]和String[]的差异:
    基本数据类型的一维数组,用这个工具还是得到[I@32edeea8,为什么?
    public static List asList(T… a),基本数据类型的一维数组不能当做是Object[],只能当做一个元素,那么还是得到[I@32edeea8。而String类型的一维数组可以当做是Object[],所以用这个方法可以打印出全部元素。


用反射方法操作数组:
得到数组的长度、得到或设置指定角标的元素: java.lang.reflect.Array工具类:
Class类的isArray方法判断是否为数组,用Array类的getLength()方法得到长度,遍历时用get方法得到具体元素。
注:无法直接获得整个数组的具体类型,需先得到具体元素的类型从而知道数组的类型:

Array.get( obj, i).getClass().getName()

示例代码:
        public static void printObj(Object obj )        {               Class clazz = obj .getClass();               if(clazz.isArray())               {                    int len = Array.getLength(clazz);                    for(int i =0;i<len;i++)                    {                       System. out.println(Array.get( obj, i));                    }               }               else               {                   System. out.println(obj);               }        }
0 0