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

黑马程序员——Java 反射技术

2015-08-09 13:19 796 查看
-----------android培训java培训、java学习型技术博客、期待与您交流!------------

一、概述

 1.理解反射:

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

 2.反射中常用的类:

  Class类:是反射的基石,由它可得到字节码中构造方法、成员变量、成员方法、包等信息。

  Constructor类:用来描述java类中的构造方法。

  Field类:用来描述java类中的成员变量。

  Method类:用来描述java类中的成员方法。

  Package类:用来描述java类属于的包。

 3.反射的应用:

  实现框架功能:就是指提供一个基本功能的程序框架,很多具体功能的实现类不需要在框架中体现,而是利用反射技术在开发人员根据不同需要和功能在后续进行加载,通过反射可以给开发人员提供更多的拓展空间和更加灵活的功能实现。

二、Class类

 1.概述:

  java程序中的各个Java类都具有共同的属性,属于同一类事物,而在java中用来对这类事物进行描述的类,就是Class类。

 2.作用:

  通过java类的Class类可以获取相应类中的各种成分信息,如类中成员变量,成员方法,构造方法,修饰符,包等,这些信息用相应java类来进行描述,分别为Field、Method、Contructor、Modifiers、Package等。

 3.Class实例对象的三种获取方式:

  1)类名.class:如Class c=Person.class。

  2)调用对象的getClass()方法,示例如下:

   Person p=new Person();

   Class c=p.getClass();

  3)Class.forName("类名"),示例如下:

   Class c=Class.forName("java.lang.String");

   说明:该方法返回类的字节码,如果该字节码已经加载到虚拟机,则直接返回该字节码。如果该字节码未加载到虚拟机中,则类加载器会将该字节码加载到虚拟机,然后返回该字节码。

 4.常见的Class实例对象:

  1)八个基本Class实例对象:基本数据类型包装类调用TYPE字段获得的字节码与基本类型的字节码相同,对应关系如下:

   boolean.class==Boolean.TYPE;

   byte.class==Byte.TYPE;

   short.class==Short.TYPE;

   char.class==Character.TYPE;

   int.class==Integer.TYPE;

   float.class==Float.TYPE;

   long.class==Long.TYPE;

   double.class==Double.TYPE;

  2)无返回值类型的关键字void的Class实例对象:

   void.class;

  3)其他的Class实例对象:

   数据类型:int[].class等。

   包装类类型:Integer.class等。

   字符串类型:String.class。

  注:每一种数据类型都有一份字节码,而每一份数据类型的字节码都是Class类的实例对象。也就是说任何数据类型都有对应的Class类的实例对象。

 5.常用的方法:

  1)获取表示成员变量的Field对象:

   Field getField(String name):返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。

   Field[] getFields():返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。

   Field getDeclaredField(String name):返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。

   Field[] getDeclaredFields():返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。

  2)获取表示构造方法的Constructor对象:

   Constructor<T> getConstructor(Class<?>... parameterTypes):返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。

   Constructor<?>[] getConstructors(): 返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。

   Constructor<?>[] getDeclaredConstructors():返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。

   Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes): 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。

  3)获取表示成员方法的Method对象:

   Method[] getMethods():返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。

   Method getMethod(String name, Class<?>... parameterTypes):返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。

   Method[] getDeclaredMethods():返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

   Method getDeclaredMethod(String name, Class<?>... parameterTypes): 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。

  4)获取表示此类所属的包的Package对象:

   Package getPackage():获取此类的包。

  5)其他:

   static Class<?> forName(String className):返回与带有给定字符串名的类或接口相关联的 Class 对象。

   String getName():以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。

   Class<? super T> getSuperclass():返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。

   boolean isArray():判定此 Class 对象是否表示一个数组类。

   boolean isPrimitive():判定指定的 Class 对象是否表示一个基本类型。

   T newInstance():创建此 Class 对象所表示的类的一个新实例。

三、Field类(成员变量的反射)

 1.概述:

  用来描述java类中的成员变量。

 2.用反射获取Field对象的步骤:

  1)获取表示指定类或接口的Class类实例对象。

  2)调用Class类实例对象中的获取Filed对象的方法。

  示例:假设存在一个Person类,获取Person类表示成员变量的Filed对象。

  代码:

    Class cla=Class.forName("Person");//方法的类名参数一般为完整的类名或者能让虚拟机找到即可

    Field field=cla.getField("指定成员变量名");//获取Field对象的方法有多个,根据需要选择

 3.常用的方法:

  Object get(Object obj):返回指定对象上此 Field 表示的字段的值。

  int getInt(Object obj):获取 int 类型或另一个通过扩展转换可以转换为 int 类型的基本类型的静态或实例字段的值。

  String getName():返回此 Field 对象表示的字段的名称。

  Class<?> getType():返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。

  void set(Object obj, Object value): 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。

  void setInt(Object obj, int i): 将字段的值设置为指定对象上的一个 int 值。

  void setChar(Object obj, char c): 将字段的值设置为指定对象上的一个 char 值。

  代码示例:

import java.lang.reflect.*;
class FieldDemo
{
public static void main(String[] args) throws Exception
{
//创建ReflectDemo对象
ReflectDemo re=new ReflectDemo(3,6);

//返回Class表示的类中的公有字段y的field对象
Field fieldY=re.getClass().getField("y");

//获取对象re在fieldY表示的字段的值
int y=(int)fieldY.get(re);

System.out.println("y="+y);
System.out.println(fieldY);

//		Field fieldX=re.getClass().getField("x");//不能用getField方法获取,因为字段x不是共有的

//返回Class表示的类中的私有字段x的field对象
Field fieldX=re.getClass().getDeclaredField("x");

//值为true表示反射的对象在使用时不进行Java语言访问检查,即可访问私有的字段
fieldX.setAccessible(true);

//获取对象re在fieldX表示的字段的值
int x=(int)fieldX.get(re);

System.out.println("x="+x);
System.out.println(fieldX);
}
}

class ReflectDemo
{
private int x;
public int y;

ReflectDemo(int x,int y)
{
this.x=x;
this.y=y;
}
}
   程序运行后的结果如下截图所示:



 4.代码练习:

  题目:将任意一个对象中的所有的String类型的成员变量所对应的字符串内容中的‘b’改成‘a’。

import java.lang.reflect.*;
class FieldTest
{
public static void main(String[] args) throws Exception
{
//创建ReflectDemo对象
ReflectDemo re=new ReflectDemo(3,6);

System.out.println("改变前的字符串:"+re);
changeStr(re);
System.out.println("改变后的字符串:"+re);
}

public static void changeStr(Object obj)throws Exception
{
//返回obj对象的Class类中的所有字段的field对象数组
Field[] fields=obj.getClass().getFields();

//对field对象数组进行遍历
for(Field field:fields)
{
//判断field表示字段的数据类型是否为String类型
if (field.getType()==String.class)
{
String str=(String)field.get(obj);//获取field表示字段的值,需进行类型强制转换
String strNew=str.replace('b','a');//将字符串中的字符‘b’替换为‘a'
field.set(obj,strNew);//将field在obj对象表示字段的值设置为strNew
}
}
}
}

class ReflectDemo
{
private int x;
public int y;
public String str1="ball";
public String str2="basketball";
public String str3="base";

ReflectDemo(int x,int y)
{
this.x=x;
this.y=y;
}

public String toString()
{
return str1+":"+str2+":"+str3;
}
}
  程序运行后的结果如下截图所示:



四、Constructor类(构造函数的反射)

 1.概述:

  Constructor类用来描述java类中的构造函数。

 2.用反射获取Constructor对象的步骤:

  1)获取表示指定类或接口的Class类实例对象。

  2)调用Class类示例对象的获取Constructor对象的方法。

  示例:假设存在一个Person类,获取Person类表示成员变量的Constructor对象。

  代码:
    Person p=new Person();
    Class cla=p.class.getClass();//获取表示Person类的Class类实例对象
    Constructor con=cla.getConstructor("参数类型的字节码");//表示获取指定构造函数的Constructor对象。应传入构造函数对应参数类型的字节码文件对象,且传入的个数应与构造函数的参数个数一致
 3.常用的方法:
  String getName():以字符串形式返回此构造方法的名称。
  T newInstance(Object... initargs): 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
  Class<T> getDeclaringClass():返回 Class 对象,该对象表示声明由此 Constructor 对象表示的构造方法的类。
  问题思考:Class类中newInstance()方法与Conctructor类中newInstance(Object...
initargs)方法有什么区别?

   答:Class类中newInstance()方法只能创建由Class类实例对象表示的类中的空参数构造函数的对象,而Constructor类中newInstance(Object...
initargs)方法可以创建任意指定参数类型的构造函数对象,且每调用一次就构造一个对象。
 4.代码示例:

import java.lang.reflect.*;
class ConstructorDemo
{
public static void main(String[] args) throws Exception
{
//获取String字节码中参数类型为StringBuffer的Constructor对象
Constructor<String> con=String.class.getConstructor(StringBuffer.class);

//调用Constructor类中newInstance方法创建对象,创建时需要根据参数类型传入实际参数
String str=con.newInstance(new StringBuffer("abc"));

System.out.println(str);

}
}
  程序运行后的结果如下截图所示:



五、Method类(成员方法的反射)

 1.概述:
  Method类用来描述java类中的成员方法。
 2.用反射获取Method对象的步骤:

  1)获取表示指定类或接口的Class类实例对象。

  2)调用Class类示例对象的获取Method对象的方法。

  示例:假设存在一个Person类,获取Person类表示成员方法的Method对象。

  代码:

    Person p=new Person();

    Class cla=p.class.getClass();//获取表示Person类的Class类实例对象

    Method me=cla.getMethod(String name,"参数类型的字节码");//表示获取指定成员方法的Method对象。应传入成员方法对应参数类型的字节码文件对象和方法名,且传入的个数应与成员方法的参数个数一致

 3.常用的方法:

  Object invoke(Object obj, Object... args):对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。

  Class<?> getDeclaringClass():返回表示声明由此 Method 对象表示的方法的类或接口的 Class 对象。

  String getName():以 String 形式返回此 Method 对象表示的方法名称。

  其中,invoke(Object obj, Object... args)方法对于Method对象表示为静态方法时,调用格式为:
   Object invoke(null, Object... args):需要传入指定对象,因此传入的指定对象为null。
  代码示例:

import java.lang.reflect.*;
class MethodDemo
{
public static void main(String[] args) throws Exception
{
String str="abc";

//获取String的字节码中的Method对象,该Method对象表示方法名为charAt,参数类型为int的方法
Method me=String.class.getMethod("charAt",int.class);

//调用Method对象中的invoke方法,需传入调用对象和实际参数
//如果Method表示的方法为静态,则调用时,传入的调用对象为null。
char ch=(char)me.invoke(str,1);

System.out.println("ch="+ch);
}
}
  程序运行后的结果如下截图所示:



 4.代码练习:
  题目:写一个程序,根据用户提供的类名,去执行该类中的main方法。

import java.lang.reflect.*;
class MethodTest
{
public static void main(String[] args) throws Exception
{
//获取运行java命令传入的参数,该参数为类名的字符串表示
String className=args[0];

//获取类名的Class对象,并获取Class类中表示main方法的Method对象
Method mainMethod=Class.forName(className).getMethod("main",String[].class);
Object obj=new String[]{"ava"};

//调用invoke方法,并传入main函数中的实际参数,对于静态方法调用对象为null
//运行指定类中的main方法
mainMethod.invoke(null,obj);

//注意可变参数的参数传递规则,对于引用类型的数组需强制转换成Object类型
//		mainMethod.invoke(null,(Object)new String[]{"java"});
//		mainMethod.invoke(null,new Object[]{new String[]{"java"}};
}

}
  程序运行后的结果如下截图所示:(执行FieldDemo类中的main方法)



  注:此示例用eclipse运行时,需要在Run As——>RunConfigurations——>Arguments——>Program arguments中添加要执行的类名。 

六、数组的反射

 1.概述:

  具有相同的元素类型和维度的数组都属于同一类型数组,该同一类型的数组经过反射得到的Class类实例对象都是同一份字节码。

  示例:

   int[] a1=new int[3];//对应的字节码为int[].class。

   int[] a2=new int[4];//对应的字节码为int[].class。

   int[][] a3=new int[2][3];//对应的字节码为int[][].class。

   String[] a4=new String[3];//对应的字节码未String[].class。

  说明:a1和a2有相同的元素类型(即int)和维度(即一维数组),因此属于同一类型数组,具有同一份字节码,即int[].class。同理a1和a3、a1和a4等都不属于同一类型数组,因此都不是同一份字节码。

 2.获取指定数组类型的Class实例对象:

  1)创建指定数组类型的对象。

  2)调用对象的getClass()方法获取Class对象。

  示例:

   int[] a={3,4,4};//创建int类型一维数组

   Class cls=a.getClass();//获取该类型数组的Class对象

 3.Array工具类用于完成对数组的反射操作:

  static Object get(Object array, int index):返回指定数组对象中索引组件的值。

  static void set(Object array, int index, Object value):将指定数组对象中索引组件的值设置为指定的新值。

  static int getLength(Object array):以 int 形式返回指定数组对象的长度。  

 4.代码示例:

import java.lang.reflect.*;
class ArrayReflect
{
public static void main(String[] args) throws Exception
{
String str="java";
int[] in={3,43,35};
printObject(str);//打印字符串
printObject(in);//打印数组
}

public static void printObject(Object obj)throws Exception
{
//获取obj对象的Class实例对象
Class cla=obj.getClass();

//判断cla类型是否为数组类型
if (cla.isArray())
{
//获取数组的长度
int len=Array.getLength(obj);

//遍历打印数组中的元素
for (int x=0;x<len ;x++ )
{
System.out.println(Array.get(obj,x));
}
}
else
System.out.println(obj);//如果不是数组类型,则直接打印
}
}


  程序运行后的结果如下截图所示:



七、反射的应用案例——简单框架功能的实现

 思路:

  1.新建一个配置文件,并将需要反射的类,以键值对的形式储存到配置文件中。

  2.创建Properties集合,并用IO流将配置文件的信息加载到集合中。

  3.获取需要被反射的类,并使用反射技术获取Class类实例对象。

  4.创建Class类表示的类的实例对象。

 代码:

<pre name="code" class="java">import java.util.*;
import java.io.*;
class ReflectUseDemo
{
public static void main(String[] args) throws Exception
{
//创建字符读取流对象,并关联存储需要被反射的类的配置文件
FileReader fr=new FileReader("e:\\heima\\measure\\config.properties");
Properties prop=new Properties();//创建Properties集合

prop.load(fr);//将配置文件的信息加载到集合
fr.close();//关闭资源
String name=prop.getProperty("className");

Class<?> cla=Class.forName(name);//获取表示指定类的Class对象实例

//创建Class对象表示的类的实例对象,并强制类型转换为Collection
Collection coll=(Collection)cla.newInstance();

//创建ReflectDemo对象
ReflectDemo ref1=new ReflectDemo(3,5);
ReflectDemo ref2=new ReflectDemo(4,3);
ReflectDemo ref3=new ReflectDemo(3,5);

//向Collection集合添加元素
coll.add(ref1);
coll.add(ref2);
coll.add(ref3);
coll.add(ref1);

//ref1.y=7;
//coll.remove(ref1); //对于HashSet集合,ref1的值在添加后进行了修改,此时删除无效,因为哈希值发生了变化

System.out.println(coll.size());
}
}
class ReflectDemo
{
private int x;
public int y;

ReflectDemo(int x,int y)
{
this.x=x;
this.y=y;
}

}



  1)当配置文件信息为:



  运行的结果如下截图所示:



  2)当配置文件信息为:



  运行的结果如下截图所示:

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