黑马程序员_高新技术2(Class类,反射Reflect(重点知识))

来源:互联网 发布:中国人口老龄化数据图 编辑:程序博客网 时间:2024/05/17 04:47

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

一.Class类

1.反射的基石:Class类
  1)java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class.
  2)对比提问:众多的人用一个什么类表示?众多的java类用一个什么类表示?
       人-->Person
       Java类-->Class
 3)对比提问:Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,Class
      类代表Java类,它的各个实例对象又分别对应什么呢?
  <1>对应各个类在内存中的字节码,例如:Person类的字节码,ArrayList类的字节码,等等。
  <2>一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,
  <3>不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个空间可分别用一个个
   的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?
4)如何得到各个字节码对应的实例对象(Class类型)
  <1>类名.class,例如:System.class
  <2>对象.getClass(),例如:new Date().getClass()
  <3>Class.forName("类名"),例如:Class.forName("java.util.Date")//查询或加载
 2.class与Class的区别:
  class是指java类,java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,

  至于这个属性的值是什么,则由这个类的实例对象来确定,不同的实例对象有不同的属性值。
  java程序中的各个类,也属于同一类事物,Class类是用来描述java程序中的各个java类这一事物的,

  Class类描  述了类的名字,类的访问属性,类所属的包名,字段名称的列表,方法名称的列表等等。
 3.Class.forName("java.lang.String");中forName()的作用:
   返回字节码。有两种方式:第一,字节码已加载过。第二,字节码未被加载过,在java虚拟机中还不存在,
   则用类加载器去加载,然后缓存到虚拟机中,以后就不用再加载了。

二.反射概念 


   反射就是把Java类中的各种成分映射成相应的java类。例如,一个java类可用一个Class类的对象来
   表示,一个类中的组成部分有:成员变量,方法,构造方法,包等等信息也可用一个个的java类来
   表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个类。表示java类的Class类显然
   要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用
   相应类的实例对象来表示,它们是Field,Method,Contructor,Package等等。
   一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法
   可以得到这些实例对象。然后再使用这些对象。


三.构造方法的反射应用


  1)Constructor类代表某个类中的一个构造方法。
  2)得到某个类所有的构造方法:
    例子:Constructor[] contructors = Class.forName("java.lang.String").getConstructors();
  3)得到某一个构造方法:
  例子:Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
  //获取方法时要用到类型
 4)创建实例对象:
  <1>通常方式:String str = new String(new StringBuffer("abc"));
  <2>反射方式:String str = (String)constructor.newInstance(new StringBuffer("abc"));
  5)Class.newInstance()方法:
  <1>例子:String obj = (String)Class.forName("java.lang.String").newInstance();
  <2>该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
 6)一个类有多个构造方法,用什么方式可以区分清楚想得到其中的哪个方法呢?根据参数的个数
  和类型,例如:Class.getMethod(name,Class...args)中的args参数就代表所要获取的那个方法的
  各个参数的类型的列表。重点:参数类型用什么方式表示?用Class实例对象。

 

四.成员变量的反射

 

 1.Field类
  1)Field类代表某个类中的一个成员变量。
  2)注意:得到的Field对象是对应到类上面的成员变量,不是对应到对象上的成员变量,
     字段fieldX代表的是x的定义,而不是具体的x变量。
  3)代码演示:

[java] view plaincopy
  1. package cn.itcast.day1;  
  2. public class ReflectPoint   
  3. {  
  4.  private int x;  
  5.  public int y;  
  6.  public ReflectPoint(int x, int y) {  
  7.   super();  
  8.   this.x = x;  
  9.   this.y = y;  
  10.  }  
  11.   
  12. }  
  13.   
  14. package cn.itcast.day1;  
  15.   
  16. import java.lang.reflect.Constructor;  
  17. import java.lang.reflect.Field;  
  18.   
  19. public class ReflectTest   
  20. {  
  21.  public static void main(String[] args)throws Exception  
  22.  {  
  23.    
  24.     ReflectPoint pt1 = new ReflectPoint(3,5);  
  25.   Field fieldY = pt1.getClass().getField("y");  
  26.   //fieldY的值是多少?不是5,fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值。  
  27.   System.out.println(fieldY.get(pt1));  
  28.   Field fieldX = pt1.getClass().getDeclaredField("x");//x 是私有变量,是看不见的,此方法则可看见,但不能用  
  29.   fieldX.setAccessible(true);//暴力反射:可强制使用已看到但不能用的私有变量。  
  30.   System.out.println(fieldX.get(pt1));  
  31.  }  
  32. }  

 

五.成员方法反射


 1.Method类
  1)Method类代表某个类中的一个成员方法
  2)得到类中的某一个方法:
  例子:Method charAt = Class.forName("java.lang.String").getMethod("charAt",int.class);
  3)调用方法:
  <1>通常方式:System.out.println(str.charAt(1));
  <2>反射方式:System.out.println(charAt.invoke(str,1));
  如果传递给Method对象的invoke()方法的一个参数为null,说明该Method对象对应的是一个静态方法。
2.对接收数组参数的成员方法进行反射
 1)需求:
  写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法
  (用普通方式调完后,要明白为什么要用反射方式去调用?)
  2)问题:
  启动java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),
  通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,
  整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,因为jdk1.5
  要兼容jdk1.4的语法,所以当把一个字符数组作为参数传递给invoke方法时,javac会按jdk1.4
  的语法进行处理。不能使用代码:mainMethod.invoke(null,new String[]{"xxx"});
  3)解决办法:
  <1>mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});//即封装成一个参数
  <2>mainMethod.invoke(null,(Object)new String[]{"xxx"});//这样做等于告诉编译器说这是一个参数
                  //编译器就不分把参数当数组看待,也不会把数组打散成若干个参数了.

3.代码演示:

 

[java] view plaincopy
  1. import java.lang.reflect.Constructor;  
  2. import java.lang.reflect.Field;  
  3. import java.lang.reflect.Method;  
  4.   
  5. public class ReflectTest {  
  6.  public static void main(String[] args)throws Exception  
  7.  {  
  8.   //TestArguments.main(new String[]{"111","222","333"});  
  9.   String startingClassName = args[0];  
  10.   Method mainMethod = Class.forName(startingClassName).getMethod("main",String[].class);  
  11.   //mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}});  
  12.   mainMethod.invoke(null, (Object)new String[]{"111","222","333"});  
  13.  }  
  14. }  
  15.   
  16. class TestArguments  
  17. {  
  18.  public static void main(String[] args)  
  19.  {  
  20.   for(String arg : args)  
  21.   {  
  22.    System.out.println(arg);  
  23.   }  
  24.     
  25.  }  
  26. }  

六.数组的反射与Object关系


1.数组反射
1)具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
  2)代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class.
  3)基本类型的一维数组可以被告当作Object类型使用,不能当作Object[]类型使用,
  非基本类型的一维数组,即可以当作Object类型使用,也能当作Object[]类型使用。
2.代码演示:
 
  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());
  System.out.println(a1.getClass().getSuperclass().getName());//获取Class实例对象的父类对应的Class实例对象名称为Object
  System.out.println(a4.getClass().getSuperclass().getName());
 
  Object aobj1 = a1;
  Object aobj2 = a4;
  //Object[] aobj3 = a1;
  Object[] aobj4 = a3;
  Object[] aobj5 = a4;
 
  System.out.println(a1);
  System.out.println(a4);
  System.out.println(Arrays.asList(a1));//--->a1整体相当于一个Object类型,故打印结果为一个对象的哈希值。
  System.out.println(Arrays.asList(a4));//--->a4中相当于有三个Object类型,故打印结果为[a,b,c].

3 Java.Lang.Reflect.Array类
  1)public final class Array
     extends Object
  Array 类提供了动态创建和访问 Java 数组的方法。 Array 允许在执行 get 或 set 操作期间进行扩展转换,
  但如果发生收缩转换,则抛出 IllegalArgumentException。
  2)方法摘要
  static Object get(Object array, int index)
    返回指定数组对象中索引组件的值。
  static int getLength(Object array)
    以 int 形式返回指定数组对象的长度。
  static void set(Object array, int index, Object value)
    将指定数组对象中索引组件的值设置为指定的新值。 
4.内存泄露;
  当一个对象被存储到HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值
  的字段了,否则,对象修改后哈希值就变了,这种情况下,使用contains方法传入该对象当前
  引用作为参数无法检索到对象,这也导致无法从HashSet集合中单独删除当前对象,从而造成
  内存泄露。

 

七.用反射技术开发框架的原理


 1.反射的作用-->实现框架功能
  1)框架与框架要解决的核心问题
  <1>我做房子给用户住,用户自己安装门窗,我做的房子就是框架,用户需要使用我的框架,
    把门窗插入进我提供的框架中,框架与工具类有区别,工具类被用户的类调用,而框架
    则是调用用户提供的类。
  <2>你做的门调用锁,锁是工具,你做的门被房子调用,房子是框架,房子和锁都是别人提供的。
  2)框架要解决的核心问题:
  <1>若干年前写的框架可以调用若干年后写的程序。
  <2>写程序时无法知被调用的类名,所以在程序中无法直接new某个类的实例对象,
   而要用反射来做。


八.用类加载器的方式管理资源和配置文件


1.类 ClassLoader
  1)public abstract class ClassLoader  extends Object类加载器是负责加载类的对象。
  ClassLoader 类是一个抽象类。如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。
  一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。
  每个 Class 对象都包含一个对定义它的 ClassLoader 的引用。
2)方法摘要
   InputStream getResourceAsStream(String name)
          返回读取指定资源的输入流。
2.将文件放到源文件下,会自动将它的字节码文件搬到classPath目录下。如:将config.properties放在包cn.itcast.day1下
   未来学到的框架的配置文件都是放在classPath指定的目录下,因为它内部用的是类加载配置文件。

3.代码分析:
  1)InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
  //此例运行是将配置文件放在了源文件(src)目录下,意为:从根目录下开始查找,若不加cn/itcast/day1的话就找不到文件。

 2)InputStream ips = ReflectTest2.class.getResourceAsStream("resources/config.properties");
  //此例运行是将配置文件放在了源文件的新建包cn.itcast.day1.resources下,意为:从默认的程序路径下开始查找,即从包cn.itcast.day下找。