黑马程序员_高新技术之反射


------- android培训java培训、期待与您交流! ----------


反射

反射的基石--->Class

Java程序中的各个java类属于同一类食物,描述这类事物的java类名就是Class

每一个java类应该都有类名、父类、接口、方法、字段、构造方法等等特点,

所以Class这个类就是对这些共同特点进行抽象化的结果。

----------

!!!!过程:先从硬盘上的class文件加载到内存中来,

再用字节码去复制对象,

所以可以通过对象来获取该对象所属的类的字节码:new Date().getClass();

!!!!!

----------

每一个class文件即为Class类的实例对象。

Class.forName("java.lang.String");得到String类的字节码,

个人理解抽象体系:

Class

1

对象1

对象2

2

对象1

对象2

透彻分析反射的基础

明白class类的含义:它用于描述一类事物的共性,该类事物有什么属性、没有什么属性,至于属性的值则由这个类的实例对象来确定,不同实例对象有不同的属性值。

Class cls1 = 字节码1

Class cls2 = 字节码2

1.获取类的字节码的方式?System.classnew Date().getClass()Class.forName("类的全限定名");

2.一个类的字节码的是唯一的,而同一个类可以有多个对象!

Class cls1 = str1.getClass();

Class cls2 = String.class();

Class cls3 = Class.forName("java.lang.String");//可以接收一个变量,等运行时再指定!读配置文件!

cls1 == cls2 == cls3;//true

int.class == Integer.class;//false

Integer.TYPE == int.class;//true,

注意:基本数据类型包装类中都有一个TYPE属性来代表其基本数据类型的class对象

int[].class.isPrimitive();//不是原始类型,因为它是数组。

总之只要是源程序中出现的类型,都有各自的Class实例对象,包括int[],void...

反射就是把Java类中的各种成分映射成相应的java类。

3.9个预定义的Class实例对象

基本的 Java 类型(booleanbytecharshortintlongfloat 和 double)和关键字 void 也表示为 Class 对象。

3.反射的应用

Constructor

Constructor constructor1 = String.class.getConstructor(StringBuffer.class);

//获取一个参数类型为StringBuffer的构造方法。通过指定参数类型和个数来指定需要获取的构造方法!!!

注意:编译时只进行语法检查,等式右边没有赋给左边。

所以编译器只知道constructor1是一个构造器对象,而不知道它具体是那个类的构造器!再通过该构造器创建对象时,必须进行强制转换!

//获取构造器类对象,编译时只检查,而没有执行=号的语法。

故不明确到底是那个类的构造器,运行时创建对象应传递与它的参数一致,而且将新创建的对象强转成实际的类类型。

String str2 = Stringconstructor1.newInstance(new StringBuffer("abc"));

//编译时只知道是个构造方法,而不知道使用该构造方法创建的对象到底是哪个类的对像!

创建对象的过程:class--->Contructor类对象---> Object newInstance();

总结:1.在通过class对象获取构造器,需传递指定class类型的参数来指定构造方法是哪一个;例子见以上。

   2.通过该构造器出创建对象时,必须进行强制转换。原因见以上!

                   3.创建对象时,必须传递与获取构造器时传递的参数类型一致的实际参数!

   4.也可以调用Class类中的getInstance()获取类中的空参数的构造方法。它提供了简便的方法。

Filed

代表类中的一个成员变量

public class ReflectPoint{

private int x;

public int y;

}

ReflectPoint pt1 = new ReflectPoint(3,5);

Field fieldY = pt1.getClass().getField("y");

fieldY的值是多少?注意:它代表字节码中的字段而没有对应到对象上,不同对象的y值可以是不一样的,所以它不是5

它不代表具体的值,所以要用以下方法获取,该方法需要指明是哪一个对象。

fieldY不是对象上的变量,而是类上某个对象上对应的值。所有要获取对象上的成员,必须接受对象参数!

fieldY.get(pt1);//pt1对象的该字段的值

注意:获取到对象上的私有成员之前需要先 fieldY.setAccesible(true);

Filed变量反射的综合案例!!!

需求:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改成“a”。

public class ReflectPoint{

private int x;

prinvate int y;

public String str1 = "abc";

public String str2 = "basketball";

public String str3 = "itcast";

}

main{

changeStringValue(pt1);

}

private static void changeStringValue(Object obj)throws Exception{

Field[] fields = obj.getClass().getFields();

for(Field field : fields){

//if(field.getType().equals(String.class)){ 注意String类型的字节码只有一份,直接拿==号来比!equals也可以,但语义不准确!

if(field.getType() == String.class){ //是否是同一个字节码

String oldValue = (String)field.get(obj);

String newValue = oldValue.replace('b','a');

field.set(obj,newValue);

}

}

}

---------------------------------------------------------------------------------------------

Method----------------->注意打包与拆包的问题!!

代表某个类中的一个成员方法

注意:方法与对象无关,应该先通过字节码获取该方法,然后再通过对象去调用该方法。

需求:调用str1.charAt(1);

Method methodCharAt = String.class.getMethod("charAt",int.class);

//第一个参数指定要获取的String中的方法名,第二个参数指明该方法的参数类型,

因为存在方法重载的情况,故必须指明该方法接受的参数。

即获取String类中名称为charAt,参数类型为int的方法。

methodCharAt.invoke(str1,1);//调用方法!第一个参数接收对象,调用该对象中的带有第二个参数的方法。方法启动!

methodCharAt.invoke(null,1);//静态方法调用,不需要对象。

注意:

methodCharAt.invoke(str1,new Object[]{2});//JDK1.4 invoke

//使用Object[]数组来表示要获取方法中的参数的个数以及具体值(参数个数是1,类型为int)。

methodCharAt.invoke(Object obj,Object... args);//JDK1.5 invoke

可以传递任意个数的对象参数,注意第二个参数是对象!整数有自动装箱的功能,比较特殊。

划圆的例子:将划园的方法定义在圆这个类中,

因为该方法要访问圆的私有属性(圆心和半径),

如果定义在其他类中,那么其他类就要访问圆的私有属性,不合适。

应用:用反射的方式执行某个类中的main方法

为什么要用反射的方式来调用?

我们不知道类的名字。

main{

String startingClassName = args[0];//将该类的名字告诉测试类的主函数,执行这个类,需要手动的将目标的全限定名赋值给args[0]!

Method mainMethod = Class.forName(startingClassName).getMethod("main",String[].class);

//mainMethod.invoke(null,new String[]{"11","22","33"} );//调用目标类中的静态的main方法,------->error

JDK1.5为了兼容1.4invoke,会将传递进来的第二个参数进行拆包,拆包后的每一个元素分别做为一个元素。

相等于3个字符串元素的参数。而我们main方法只要接收一个String类型的数组对象,故我们可以继续打一次包!

mainMethod.invoke(null,new Object[] {new String[] {"11","22","33"} });

//再用new Object[]{}来打一次包,这样当java拆掉包之后,就只有一个对象参数了!!!!!!!!!!

或者:

methodCharAt.invoke(str1,Objectnew String[]{"abc","123,","233"});//相等于告诉java当成一个Object,别拆包!

}

Method methodCharAt = String.class.getMethod("charAt"String.class);//参数为一个字符串类型的!

methodCharAt.invoke(str1,new String[]{"abc","123,","233"});//这个会报错,因为java会将字符串值拆包,看成是3个字符串参数!这样就与获取类中的方法接受的参数的个数不一致了!应该按照以下方法:

methodCharAt.invoke(str1,new Object[]{new String[]{"abc","123,","233"}});//java认为只有一个参数,因为Object[]{}的长度为1,如果是长度为2,则在Object数组中new两个String数组!!new Object[]{new String[]{"abc","123,","233"}new String[]{"abc","123,","233"}}故必须用Object[]{}来打包!java会拆包!

也可以这样:

methodCharAt.invoke(str1,Objectnew String[]{"abc","123,","233"});//相等于告诉java当成Object

----------------------------------------------------------------------------------------------

数组的反射!

每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。

int[] a1 = new int[3];

int[] a2 = new int[4];

int[][] a3 = new int[2][3];

String[] a4 = new String[3];

a1.getClass() == a2.getClass();//true

a1.getClass() == a3.getClass();//false

a1.getClass() == a4.getClass();//false

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

System.out.println(a1.getSuperclass().getNam()); //java.lang.Object

System.out.println(a4.getClass().getSuperclass().getName()); //java.lang.Object

Object aObj1 = a1;可以

Object bObj2 = a4;可以

Object[] aObj3 = a1;不可以,因为a1int类型的数组,里面装的是int,不可以将其转成Object类型!

Object[] aObj4 = a3;可以

Object[] aObj5 = a4;可以

System.out.println(a1);

System.out.println(a4);

为了打印数组中的元素,可使用Arrays中的asList()的方法把数组转成List的集合:

System.out.println(Arrays.asList(a1));//[[I @1...]整数的数组打印成这样了

System.out.println(Arrays.asList(a4));//[a,b,c]

JDK1.4

public staitc List asList(Object[] a)

//它接收的是Object[],传入字符串数组,它吧元素取出让后放入集合打印!

当传入的是int类型数组时,不符合它的参数要求,它就交给JDK1.5来处理

JDK1.5

public staitc List asList(T...t)//它把该int类型的数组当成是一个Object对象来看待了,然后把它的地址值放入集合中打印了

数组反射应用

需要应用Array类:java.lang.reflect.Array

参考:ReflectTest.java

//使用反射打印数组中的每一个元素------->万能的打印方法!!!!!!牛!!!!!!

private static void printObject(Object obj){

Class clazz = obj.getClass();

if(clazz.isArray()){

int length = Array.getLength(obj);

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

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

}

}else{

System.out.println(obj);

}

}

思考:怎么得到数值中的元素的类型?

Object[] obj = new Object[]{"abc",12};

只能获取指定obj[0].getClass().getName();的类型!而不能得到该obj数组的类型,

------- android培训java培训、期待与您交流! ----------
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值