黑马程序员——反射

来源:互联网 发布:美工师 编辑:程序博客网 时间:2024/04/28 15:17

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

 

反射的基石 Class类

class
Class类,代表一类什么样的事物?
java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class.
比如:众多的人用Person表示。众多的java类用Class表示。

对比说明:
Person p1 = new Person();
Person p2 = new Person();

Class cls1 = Date.class字节码1;//给Class类型变量赋值,很遗憾没有new Class();
Class cls2 = Person.class字节码2;//如:Date类、Math类、Person类 等都可称为是字节码,一个类一个字节码,每一个     字节码都是Class的实例对象

p1.getClass();//这也是一种得到类的字节码的方式

Class.forName("java.lang.String");//这也是一种得到类的字节码的方式(反射常用)

类名.class;//这也是一种得到类的字节码的方式

字节码的两种情况:1、类的字节码已经加载到内存中(java虚拟机)。2、用内加载器加载

八个基本类型加上一个void类型,构成九个预定义Class对象。

数组类型的Class实例对象

Class.isArray()

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

例:

public class ReflectTest {

 /**
  * @param args
  */
 public static void main(String[] args) {
  String str1="abc";//字符串对象
  Class cls1=str1.getClass();//getClass(),string字节码
  Class cls2=String.class;//string字节码
  Class cls3=null;
  try {
    cls3=Class.forName("java.lang.String");//string字节码
  } catch (ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  System.out.println(cls1==cls2);//3个引用对象指向同一个字节码
  System.out.println(cls1==cls3);
  System.out.println(cls2==cls3);
  
  System.out.println(cls1.isPrimitive());//是否是基本类型字节码
  System.out.println(int.class.isPrimitive());//是否是基本类型字节码
  System.out.println(int.class==Integer.class);//类型不同,字节码不同
  System.out.println(int.class==Integer.TYPE);//基本类型与包装类型里的基本类型比较
  System.out.println(int[].class.isPrimitive());//数组类型字节码是否是基本类型字节码
 }

}

反射

什么叫反射?

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

Class类是java所有类的统称,通过字节码可以找到所有成分。

Constructor类

一个Class代表一个字节码,一个Method代表这个字节码的方法,而一个Constructor就代表这个字节码的构造方法。

例:得到某个类所有的构造方法:
Constructor[] constructors=Class.forName("java.lang.String").getConstructors();

例:得到某一个构造方法:
Constructor constructor=Class.forName("java.lang.String").getConstructor(StringBuffer.class);
//获得方法时要用到类型

例://new String(new StringBuffer("abc"));//这是正常情况下
Constructor constructor1=String.class.getConstructor(StringBuffer.class,int.class);//找出字节码
String str=(String)constructor1.newInstance(new StringBuffer("abc"));//通过字节码new出实例对象
System.out.println(str.charAt(2));//这是用反射情况下,实现的功能都是一样的。
//调用获得的方法时要用到上面相同类型的实例对象

class-->constructor-->new object

反射比较占用时间,降低性能。

Field类

代表某个字节码中的成员变量,不代表某个对象中的成员变量。

例:


public class ReflectPoint {
 private int x;
 public int y;
 
 public ReflectPoint(int x,int y){
  super();
  this.x=x;
  this.y=y;
 }
}

public class ReflectTest1 {

 /**
  * @param args
  */
 public static void main(String[] args) {
  ReflectPoint pt1=new ReflectPoint(3,5);
  Field fieldY=pt1.getClass().getField("y");
 //要得到一个类某个身上的字段(某个成员变量),先要得到类的字节码,具体要得到哪个成员变量,用变量名来区分
 //fieldY的值是多少?是5,错!fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值。
  System.out.println(fieldY.get(pt1));
   //Field fieldX=pt1.getClass().getField("x");//报错,是私有的不可见。
  Field fieldX=pt1.getClass().getDeclaredField("x");//getDeclaredField(),不管可见不可见,全见
  fieldX.setAccessible(true);//这个叫做暴力反射,直接拿来用。
  System.out.println(fieldX.get(pt1));//报错,看见了却不能用。
  
 }
}

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

public class ReflectPoint1 {
 public String str1 = "ball";
 public String str2 = "basketball";
 public String str3 = "itcast"; 
 
 @override//检查是否重写正确
 public String toString(){
  return str1 + ":" + str2 + ":" + str3;
 }
}

public class ReflectTest2{
 public static void main(String[] args){
  ReflectPoint1 pt1=new ReflectPoint1();
  changeStringValue(pt1);
  System.out.println(pt1);
 }
 private static void changeStringValue(Object obj) throw Exception{
  //扫描这个类这个对象所有String类型的变量
  Field[] fields=obj.getClass().getFields();//通过得到字节码,来得到所有的字段
  for(Field field:fields){
   if(field.getType() == String.class){//对字节码的比较用“==”,因为是同一个字节码
    String oldValue = (String)field.get(obj);//取得值
    String newValue = oldValue.replace('b','a');
    filed.set(obj,newValue);//把pt1值改变
   }
  }
 }

}

Method类

代表某个类(字节码)里的一个成员方法,拿着方法调用对象。

通常方式:System.out.println(str.charAt(1));

反射方法:System.out.println(charAt.invoke(str,1));

//com开头的包名为非标准的内部使用,java开头的反之。
import java.lang.reflect

public String str="abc";
Method methodCharAt = String.class.getMethod("charAt",int.class);
//通过字节码来得到方法,方法里的参数为“方法名”和“参数类型”。

System.out.println(methodCharAt.invoke(str,1));//(字符串对象,参数个数)

System.out.println(methodCharAt.invoke(null,1));//表示为静态方法,静态方法不需要对象

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

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

在不知道具体要调用哪个类main方法的时候使用。

public class ReflectTest3{
 public static void main(String[] args){
  TestArguments.main(new String[]{"111","222","333"});//平常情况
  
  String startingClassName=args[0];
  Method mainMethod=Class.forName(startingClassName).getMethod("main",String[].class);
  //main为名字,后面的为参数列表
  mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}});
  //把参数装成一个包,不要把它当成多个参数来看待。
  mainMethod.invoke(null,(Object)new String[]{"111","222","333"});
  //编译器会作特殊处理,编译时不把参数当作数组看待,也就不会把数组打散成若干个参数了。
 }
}

class TestArguments{
 public static void main(String[] args){
  for(String arg:args){
   System.out.println(arg);
  }
 }
}

数组的反射

public class ReflectTest4{
 public static void main(String[] args){
  TestArguments.main(new String[]{"111","222","333"});//平常情况
  
  String startingClassName=args[0];
  Method mainMethod=Class.forName(startingClassName).getMethod("main",String[].class);
  //main为名字,后面的为参数列表
  mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}});
  //把参数装成一个包,不要把它当成多个参数来看待。
  mainMethod.invoke(null,(Object)new String[]{"111","222","333"});
  //编译器会作特殊处理,编译时不把参数当作数组看待,也就不会把数组打散成若干个参数了。

  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()==a2.getClass());//比较字节码
  System.out.println(a1.getClass()==a4.getClass());
  System.out.println(a1.getClass()==a3.getClass());
  System.out.println(a1.getClass().getName());//[I    "["表示数组,"I"表示int
  System.out.println(a1.getClass().getSuperclass().getName());//输出父类的名字 
  System.out.println(a4.getClass().getSuperclass().getName());
  
  Object obj1=a1;
  Object obj2=a4;
  //Object[] obj3=a1;//基本类型不属于Object
  Object[] obj4=a3;
  Object[] obj5=a4;

  System.out.println(a1);
  System.out.println(a4);//得到数组的值

  System.out.println(Arrays.asList(a1));//把数组转换为集合,int不是Object类型,作为一个参数
  System.out.println(Arrays.asList(a4));//String作为可变参数

  printObject(a4);//打印Object

  printObject("xyz");
 }

 private static void printObject(Object obj){
  Class clazz = obj.getClass();
  if(clazz.isArray()){//判断是不是数组
   int len = Array.getLength(obj);
   for(int i=0;i<len;i++){
    System.out.println(Array.get(obj,i));
   }
  }else{
   System.out.println(obj);
  }
 }
}

反射的作用,实现框架功能

框架调用用户提供的类,用户调用工具类。框架-->用户类-->工具类

在写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象了,而要用反射方式来做。

例:

首先建立文件:new-->File-->config.properties

className=java.util.ArrayList

public class ReflectTest5{
 public static void main(String[] args) throws Exception{
  //1、一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的。  
  //InputStream ips= new FileInputStream("config.properties");
  //加载properties文件,尽量做到面向父类编程
  //2、内加载器去加载文件,配置绝对路径加载。
  //InputStream ips = ReflectTest5.class.getClassLoader().getResourceAsStream        ("org/ReflectTest/config.properties");
  //3、配置当前config.properties相对文件路径,框架配置文件放在classPath目录下。
  InputStream ips = ReflectTest5.class.getResourceAsStream("config.properties");

  Properties props = new Properties();//会存储很多值
  props.load(ips);
  ips.close();//把指向操作系统的进程关闭,不是把对象关闭,对象在jvm上自动回收
  String className = props.getProperty("className");//得到类名
  Collection collections= (Collection)Class.forName(className).newInstance();
  //调用不带参数的构造方法
  
  ReflectPoint5 pt1 = new ReflectPoint5(3,3);
  ReflectPoint5 pt2 = new ReflectPoint5(5,5);
  ReflectPoint5 pt3 = new ReflectPoint5(3,3);
  collections.add(pt1);
  collections.add(pt2);
  collections.add(pt3);
  collections.add(pt1);
 
  System.out.println(collections.size());
 }
}

public class ReflectPoint5{
 private int x;
 public int y;
 public ReflectPoint5(int x,int y){
  super();
  this.x=x;
  this.y=y;
     
 }
}

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

详细请查看:http://edu.csdn.net/heima

原创粉丝点击