您的位置:首页 > 职场人生

黑马程序员:java中的Class类和反射(二)

2013-08-14 16:46 316 查看

黑马程序员:java中的Class类和反射(二)

-----------------------------Android培训java培训、期待与您交流!------------------------------
(本文主要对java加强课程中相应内容进行总结。)

import java.lang.reflect.Array;

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

import java.util.Arrays;

public class Reflect2 {

 public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, ClassNotFoundException {

  // TODO Auto-generated method stub

/*------------------------------------------------------字段反射--------------------------------------------------------------------------------*/  

  ReflectPoint pt1=new ReflectPoint(3,5);

  ReflectPoint pt2=new ReflectPoint(4,6);

  ReflectPoint pt3=new ReflectPoint(7,10);

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

  /*利用反射得到一个字段。此处的fieldY代表的是pt1这个对象的字节码文件中的变量,而不是这个对象中的变量

   * 也就是说此处fieldY的值并不是5.也就是说,pt1,pt2,pt3对应的是同一个fieldY字段对象,但这个字段对象

   * 的值在三个对象中是不一样的,分别为5,6,10.*/

  //要取出fieldY在每个对象中对应的值:

  System.out.println(fieldY.get(pt1));

    

  //Field fieldX1=pt1.getclass().getField("x");//报错,getField()只能得到对外部类可见的那些字段。

  //x是类私有的成员变量,对外部类是不可见的。

  Field fieldX2=pt1.getClass().getDeclaredField("x");//getDeclaredField()用于得到所有申明的字段。

  //System.out.println(fieldX2.get(pt1));//报错,仍然没有权限

  

  //暴力反射

  fieldX2.setAccessible(true);

  System.out.println(fieldX2.get(pt1)); 

  

  changeStringValue(pt1);

  System.out.println(pt1); 

/*--------------------------------------------------成员方法的反射-------------------------------------------------------------------------*/  

//成员方法的反射 :

 /*Method类:代表的就是类中的方法,即字节码中的方法,和对象没有关系,属于类本身,方法的调用必须通过对象来进行

  * 方法的调用方式:

  *  普通方式:str.charAt(1)------直接通过对象调用

  *  反射方式: */

  //成员方法的反射

  String str1="ball";

  Method methodCharAt = String.class.getMethod("charAt", int.class);  //第二个参数为方法参数的类型

  System.out.println(methodCharAt.invoke(str1, 1));//在str1这个对象上,调用methodCharAt这个方法对象所代表的

  //方法,并给调用过程传递方法的参数1.

  /*invoke方法是methodCharAt这个方法对象的方法,但其含义是在某个指定的对象上调用前面调用invoke方法的方法对象所代表的方法。 */

   /* invoke:

   * Parameters:

   obj - 方法从哪个对象上面调用

   args - 调用方法时的参数

   Returns: obj对象利用传递的参数,调用方法之后的结果

   * 关于参数:

   *   在一个特定的对象上调用这个Method对象所代表的方法。单个参数自动被解析为原始类型,而多个参数时,原始类型和引用类型的阐述会在必要时由方法调用本身来识别和支配。

   * 关于方法类型:

   *   如果方法是静态的,参数对象可以被忽略。另外当方法是静态的,申明这个方法的类如果没有已经被初始化,那么就会

   * 被初始化。

   *   如果方法是实例方法,可以按照在The Java Language Specification中描述的动态方法查找的方式进行调用,尤其

   * 是可以通过靶标对象在运行时的类型来对方法进行重写。

   * 关于返回值:

   *   如果方法使用完成,其返回值会赋给调用invoke的对象,如果返回值是基本数据类型,它首先会被包装在一个相应的对象

   * 中。但是如果返回值是引用类型的,如数组,数组中的元素不会被包装在对象中,换言之,就会直接返回一个含有基本数据

   * 类型元素的数组。如果返回值是void,那么这个调用过程返回null。

   *

   * e.g System.out.println(methodCharAt.invoke(null, 1));表示方法是静态的。当需要调用静态方法是,就可将Object写成

   * null。原理就是静态方法的调用不需要对象。

   */

  //专家模式:把变量设为私有的,变量在谁身上,那么利用这些变量的方法就在谁身上。

  

  

  System.out.println(methodCharAt.invoke(str1, new Object[]{2}));

  /*如果不利用上面的可变参数的方式,即JDK1.5以前的版本,无论参数有多少个,是什么类型,都可以参数列表放到一个对象数组中,

  根据需要指定参数的个数和内容,如例子中对象数组中有一个对象,即Integer对象,数值为2。*/

  //例如new Object[]{new String("abc"), 1, new int[]{}},单个对象元素分别为String,int和int数组。它们都是对象,其中

  //'1'在JDK1.5之后可以被自动封装成一个Integer对象。

/*----------------------------------------------------------------------------------------------------------------------------------*/    

 

 

/*----------------------------------用反射的方式执行某个类中的main方法-----------------------------------------------------------*/    

  //用反射的方式执行某个类中的main方法:首先定义一个TestArguments类,见下面。

   //普通调用方式:

   TestArguments.main(new String[]{"111","222","333"});

   //反射方式调用:

   /*为什么使用反射方法调用:

    * 首先,在写程序时不需要知道要执行的类的名称,那么在执行程序时可以通过将类名作为参数传给程序,就可以调用这个类的main方法。

    */

   String startingClassName=args[0];

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

   mainMethod.invoke(null, new Object[]{new String[]{"abc","123"}});

   /*如果将new String[]{"abc","123"}直接传递给invoke,这个String数组会被“打开”,而其中的每个元素都会

    * 被当成一个对象,作为参数传给mainMethod所对应的方法。而main方法只接受一个String[]类型的数组作为参数

    * 如果采用这种方法,相当于将两个参数传给了main方法。所以可以先将这个String[]类型的数组包裹为一个对象

    * 当将它传给invoke时,在被解包后,就成了将一个String数组传递给main方法,那样就合理了*/

   //另外一种方法:

   mainMethod.invoke(null, (Object)new String[]{"abc","123"});

   /*这种方法也可以,相当于将字符数组转成一个对象,将这个对象传给main方法。可以理解为不让程序将数组拆包*/

/*----------------------------------------------------------------------------------------------------------------------------------*/   

 

 

/*---------------------------------------------------数组的反射------------------------------------------------------------------------------*/     

 //数组的反射:Every array belongs to a class that is reflected as a Class object that is shared

   //by all arrays with the same element type and number of dimensions.维数和类型一致的数组

   //对应的字节码是同一份。

  

   int [] a1=new int[]{1,2,3};

   int [] a2=new int[4];

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

   String [] a4=new String[]{"a","b","c"};

   //System.out.println(a1.getClass()==a3.getClass());//通不过编译器

   //System.out.println(a1.getClass()==a4.getClass());//通不过编译器

   System.out.println(a1.getClass() == a2.getClass());//true

   System.out.println(a1.getClass().getName());//[I  [表示数组,I表示int  查看getName的说明文档

   System.out.println(a1.getClass().getSuperclass().getName());//java.lang.Object,即父类是Object

   System.out.println(a4.getClass().getSuperclass().getName());

   System.out.println(a3.getClass().getName());//[[I

  

   Object aObj1=a1;

   Object aObj2=a3;

   Object aObj3=a4;

   //Object[] aObj4=a1;//基本类型的一维数组中的每个元素不是Object;

   Object[] aObj5=a3;//a3是一个二维数组,即数组的数组。因为每一个数组可以作为一个Object,所以Object数组里面可以是数组。

   Object[] aObj6=a4;

   

   //如何打印出一个数组里的元素?

   System.out.println(a1);//[I@590e130c---表示这是一个一维数组,后面跟的是其hashcode值

   System.out.println(a4);//[Ljava.lang.String;@2b04a681---表示这是一个一维字符串类型数组,后面也是其hashcode值

   System.out.println(aObj1); //[I@2b04a681

   System.out.println(aObj3); //[Ljava.lang.String;@3e4f7537

   System.out.println(aObj6); //[Ljava.lang.String;@3e4f7537

   //Arrays这个类中有对数组进行操作的静态方法

   /*asList()方法:public static List (T...a)返回一个list对象.其参数是可变参数,在老版本中是对象数组。

    * This method acts as bridge between array-based and collection-based API, in combination

    * with Collection.toArray(). The returned list id serializable and implements RandomAccess.*/

   

   System.out.println(Arrays.asList(a1));//[[I@2b04a681]

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

   /*在JDK1.4版本中,会将a1中的每个元素当成一个对象,及传给asList的参数个数即int[]数组中元素个数,然后转化

    * 成List。而int数组中每个元素时不可以作为对象的。所以打印出的是上面的结果,在JDK1.5即以后的版本中,会

    * 将int[]数组整体作为一个对象传递给asList(可变参数的性质),即作为一个参数,*/

   

   

/*----------------------------------------------------------------------------------------------------------------------------------*/       

 

   

   Object obj=null;

   printObject(obj);

   

 }  

/*-------------------------------------使用java.lang.reflect.Array这个类进行数组反射---------------------------------------------*/      

 //数组的反射:使用java.lang.reflect.Array这个类进行数组反射。

   public static void printObject(Object obj){

    //首先要确定传递进来的对象时一个对象还是一堆对象,如果是一个,直接打印这个对象,如果是一堆,就逐个打印

    Class cls=obj.getClass();

    if (cls.isArray()){

     int len=Array.getLength(cls);

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

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

     }

    }else{

     System.out.println(obj);

    }

   }

 //如果对象是数组类型,那么如何知道其中元素的类型?

   //Object[] a=new Object[]{"abcd",12};在此处,如何知道a的某个String类型还是int类型?------不知道!

   //只能通过a[0].getClass().getName()来获得具体每个元素的类型,而不能通过a来得知。

   //如果需要自己开发框架,对这些知识一定要清楚。

/*----------------------------------------------------------------------------------------------------------------------------------*/    

  

 

/*---------------------------------------------------成员变量反射的综合案例----------------------------------------------------------*/   

 //成员变量反射的综合案例:一定要理解和掌握,自己可以写出这个程序就掌握了反射。

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

 public static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException{

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

  for (Field field:fields){

   //if (field.getType().equals(String.class)){}

   /* equals()   Indicates whether some other object is "equal to" this one.

    Parameters:

    obj - the reference object with which to compare.

    Returns:

    true if this object is the same as the obj argument; false otherwise.

    */   

   /*为什么最好不用equals而是用等号来比较:

    * 如果field为String,那么field.getType()获得的字节码和String.class对应的是同一份字节码,虽然

    * 用equals也可以,但语义不准确。因此使用等号比较比较合理。------对字节码的比较应该用等号比*/

   

   /*The getType() method returns a Class Object that identifies the declared type for the field

    represented by this Field object.*/

  

   if (field.getType() == String.class){

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

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

    field.set(obj, newValue);

   }

  }

 }

/*----------------------------------------------------------------------------------------------------------------------------------*/   

 



/*--------------------------------------------用反射的方式执行某个类中的main方法----------------------------------------------*/  

//用反射的方式执行某个类中的main方法。

 /*题目:写一个程序,这个程序可以根据用户提供的类名来调用类中的main方法。*/

//首先定义一个类

class TestArguments{

 public static void main(String[] args){

  for(String arg:args){

   System.out.println(arg);

  } 

  

 }

 

}

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息