反射

来源:互联网 发布:java游戏服务器源码 编辑:程序博客网 时间:2024/05/17 14:19
Class类
JAVA程序中的各个Java类属于同一类事物,描述这类事物的java类名就是Class。
Class类提供的方法很多,例如:getName,getMethod,getPackage.....
      
Class引用 没有构造方法或构造方法私用。所以不能用new的方式获取实例。
如何得到各个字节码对应的实例对象(Class类型)
1.    类名.class,例如:System.class
2.    对象.getClass(),例如:new  Date().getClass()
3.    Class  forName(“类名”),例如:Class  forName(“java.util.Date”);
以上三种获取的Class都是在内存中的同一个
 
九个预定义Class实例对象
八个基本数据类型加一个void.class();
 
.isPrimitive():判断是否是基本类型的字节码
 
包装类.TYPE:代表包装的基本类型的字节码,例如:int.class == Integer.TYPE
 
.isArray():判断class是不是数组
 
 
 
反射:
反射就是把Java类中的各种成分映射成相应的java类。
 
 
Constructor类
代表某个类中的一个构造方法
得到某个类所有的构造方法:
例子:Constructor[]constructor = Class.forName(“java.lang.String”).getConstructors()
 
得到某个类中的一个构造方法:
例子:Construtor  constructor = String.class.getConstructor( 第一个参数(StringBuffer.class),第二个参数(int.class) );
获得方法时要用到类型
 
创建实例对象:
通常方式:String  str  =new String(new  StringBuffer(“abc”));
反射方式:Constructor  constructor =String.class.getConstructor(StringBuffer.class);
 String str  =  (String) constructor.newInstance(new  StringBuffer(“abc”));
 
反射会导致程序性能下降
Class.newInstance()方法:
例子:String  obj  =(String)  Class.forName(“java.lang.String”).newInstance();
该方法先得到默认的构造方法,然后用构造方法创建实例。
用到了缓存机制来保存默认构造方法的实例对象。
 
 
Field类
代表某个类中的成员变量
 
获取所有成员变量
getFields()
 
获取一个成员变量
classAaa{
private int  x;
public int  y;
      
Aaa(){
this.x = 3;
this.y = 5;
}
}
 
Aaa  a = new Aaa();
Field  fieldY = a.getClass().getField(“y”);
这里的fieldY指的是一个变量,而不是里面的值
fieldY.get(a);取出对象a的fieldY变量的值。
 
 
Field  fieldX = a.getClass().getDeclaredField(“x”);//获取私有的变量
fieldX.setAccessibie(true);//暴力反射。这样就可以获取私有变量的值了
 
例:用反射将一个类中所有为String类型的变量的值中的a改为b
 
Axx  a =  Axx.class.getInstanct(); //实例化对象
Field[]  fields = a.getClass().getFields(); //获取所有成员变量,但不包括私有成员变量
 
//Field[]fieldArr = sc.getClass().getDeclaredFields(); 获取所有成员变量,包含私有的。
//Field.setAccessible(fields,true);将一个Field的数组中的私有成员变量设为可以访问的。
 
for(Field  fieldVal :   fields){
if(fieldVal.getType() == String.class){   //这里比较不要用equase(),因为字节码对象只会产生一个
String  oldVal = fieldVal.get(a);   //获取值
String  newVal = oldVal.replace(‘a’,’b’);
fieldVal.set(‘a’,newVal);     //将值写入对应的成员变量中
}     
}
 
      
Method类
代表某个类中的一个成员方法
      
获取类中的某一个方法:
Method  method =  String.class.getMethod(“方法名称”,参数类型1.class, 参数类型2.class);
      
调用方法:
返回值类型  变量 =method.invoke(“对象”,参数1,参数2);
如果这个方法是静态方法:
返回值类型  变量 =method.invoke( null,参数1,参数2);
      
jdk1.4和jdk1.5的invoke方法的区别
因为jdk1.4没有可变参数:invoke(“对象”, Object[]  obj);
      
例:当一个类启动另一个类的main函数,而这个类的类名是由参数传递进来。这里就需要用到反射。
正常写法:      A.main(new String(){“aa”,”bb”});    如果A类的名称未来,我们又如何来调用main函数。
反射写法:
StringstartClass = args[0];
Method startMain =Class.forName(startClass).getMethod("main", String[].class);
startMain.invoke(null,(Object)new String[]{"123","abc","xyz"});  在String数组对象前为什么要再(Object)?因为当String[]{"123","abc","xyz"}传入main(String[] args)时,会将String数组解包,之后就有三个参数。会报参数不匹配异常。这种情况就可以写成。 new Object[]{new String[]{"123","abc","xyz"}}或(Object)newString[]{"123","abc","xyz"}。
 
 
数组的反射
相同的元素数据类型,和相同的维度。具有相同的Class实例对象
int[ ]   a1 =  new  int[3];
int[ ]   a2 =  new  int[4];
a1.getClass()== a2.getClass() //true
 
getSuperclass():返回父类为Object类对应的Class。
      
基本类型的一维数组可以被当做Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可当做Object[]类型使用。
数组就是一个对象,所以:
int[] a1 = new int[]{1,3,5};
int[] a2 =new int[5];
int[][] a3 = newint[][]{{3,4,5,2},{1,2,3,5},{1,2,3,5}};
String[] a4 =new String[]{"ab","de","fg"};
 
Object  obj1 = a1;因为数组就是一个Object,所以这里是正确的
//Object[]  obj2 = a2; 因为a2数组是int型的,但这里数组里装的是Object类型。所以不正确。
Object[]  obj3 = a3;     因为a3数组是int型的,这里的数组里装的是Object类型,而a3里的int[]也是Object型的。
Object[]  obj4 = a4;     因为a4数组是String型的,String就是Object的子类,所以正确。
 
 
Arrays.asList()方法处理int[] 和 String[] 的差异。
jdk1.4的List  asList(Object[]  obj)
jdk1.5的List  asList(T...  a)
      
当一维基本类型数组传入之后,会去匹配jdk1.5的asList方法。这里会把整个数组当做一个元素传入,然后再返回成List类型,所以这个List里存入的是这个int数组的hashcode值。
当其它类型或者二维基本类型数组传入之后,因为其它类型和二维基本类型数组中的一维数组是Object的子类,所以会去匹配JDK1.4的asList()方法,asList接收的是一个Object类型的数组。可以将整个数组传入。并正确返回List的值。
那为什么二维基本类型数组打印出来的是hashCode值呢? 这个跟toString方法有关系。
      
 
数组反射的应用:
Array.get();
Array.set();
      
例:
int[]  obj =  new  obj[]{1,3,5,6};
Class objClass = obj.getClass();
if(objClass.isArray()){
intlen = Array.getLength(obj);
for(intx=0;x<len;x++){
//Array.set(obj,x, 7);      设置数组某个元素角标的值
System.out.println(Array.get(obj,x));    //取某个元素角标的值
}
}else{
System.out.println(obj);
}
      
可以获取数组中元素的类型,但是不可以获取数组的类型。
str[1].getClass().getName();
 
反射的作用->实现框架功能
框架和工具类:框架调用我写的类,而我调用工具类。
      
从外部调用配置文件,将类的名称以键值对形式放入。用properties载入。
 
第一种:此种方式可以读取配置文件,也可以写入配置文件
InputStream ips = new FileInputStream("config.properties");
 Propertiespros = newProperties();
 pros.load(ips);
 ips.close();


 StringclassName = pros.getProperty("classname");
配置文件的路径不能用相对路径
javaweb中得到项目路径:getRealPath( );   再用这个目录加上内部的地址。
一定要用完整的路径,但是这个完整的路径不是硬编码,而是通过运算得来的。
 
第二种:通过类加载器加载普通的文件
InputStreamips =ReflectLoadClass.class.getClassLoader().getResourceAsStream("cn//code//resource//config.properties");
是在classpath的根目录下找config.properties文件。如果不是在根目录下则找不到!
InputStreamips =ReflectLoadClass.class.getResourceAsStream("config.properties");
以  ReflectLoadClass此类为参考,config.properties的相对路径。
InputStreamips =ReflectLoadClass.class.getResourceAsStream("//cn//code//resource//config.properties");
从根目录开始指定完整的路径
原创粉丝点击