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

黑马程序员_高薪技术一(java反射)

2013-06-19 17:01 190 查看
----------- android培训java培训、java博客、java学习型技术博客、期待与您交流! --------------
 
 本章主要的知识点总结:1、了解Class类、得到字节码的三种方式以及反射的概念
                                2、Constructor类、Field类、Method类的反射应用
                                 3、数组的反射和Object的关系及数组反射的应用,hashcode的分析。
                                 4、用反射技术开发框架的原理、用类加载器的方式管理资源和配置文件
                                         

一.Class类——反射的基石



    (1)java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class.

   (2)举例

       人-->Person

       Java类-->Class

 (3)对比提问:Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,Class

      类代表Java类,它的各个实例对象又分别对应什么呢?

 ----对应各个类在内存中的字节码,例如:Person类的字节码,ArrayList类的字节码,等等。

 -----一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,

  -----不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个空间可分别用一个个

   -----的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?

(4)如何得到各个字节码对应的实例对象(Class类型)

     类名.class,         例如:Math.class

     对象.getClass(),      例如:new String().getClass()

      Class.forName("类名"),   例如:Class.forName("java.String")//查询或加载

java里面有9个预定义的Class实例对象(8个基本数据类型和void)。

     int.class
== Integer.TYPE;

int.class.isPrimitive();-----true    //判断class类是否为基本数据类型。

      int[].class.isArray();-----true   //判断Class类是否为数组类。

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

 2.class与Class的区别:

      class是指java类,java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,

  至于这个属性的值是什么,则由这个类的实例对象来确定,不同的实例对象有不同的属性值。

        java程序中的各个类,也属于同一类事物,Class类是用来描述java程序中的各个java类这一事物的,

  Class类描 述了类的名字,类的访问属性,类所属的包名,字段名称的列表,方法名称的列表等等。
 3.Class.forName("java.lang.String");中forName()的作用:

   返回字节码。有两种方式:第一,字节码已加载过。第二,字节码未被加载过,在java虚拟机中还不存在,

   则用类加载器去加载,然后缓存到虚拟机中,以后就不用再加载了。

二、反射概念  

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

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

三、Constructor类

代表某个类中的一个构造方法。

(1)得到某个类所有的构造方法:

    例子:Constructor[] contructors = Class.forName("java.lang.String").getConstructors();

 (2)得到某一个构造方法:

  例子:Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);

  //获取方法时要用到类型

(3) 创建实例对象:

  1、通常方式:String str = new String(new StringBuffer("abc"));

  2、反射方式:String str = (String)constructor.newInstance(new StringBuffer("abc"));

//调用获得的方法时要用到上面的相同类型的实例对象

(4)Class.newInstance()方法:

  1、例子:String obj = (String)Class.forName("java.lang.String").newInstance();

  2、该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。

四、Field类 



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

代码实例:

public class ReflectPoint   

{  

 private int x;  

 public int y;  

 public ReflectPoint(int x, int y) {  

  super();  

  this.x = x;  

  this.y = y;  

 }  

  

}  

  

import java.lang.reflect.Constructor;  

import java.lang.reflect.Field;  

  

public class ReflectTest   

{  

 public static void main(String[] args)throws Exception  

 {  

   

    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().getDeclaredField("x");//x 是私有变量,是看不见的,此方法则可看见,但不能用  

  fieldX.setAccessible(true);//暴力反射:可强制使用已看到但不能用的私有变量。  

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

 }  

}  

 五、Method类

                Method类代表某个类中的一个成员方法
     ( 1)得到类中的某一个方法:
                 如:Method charAt = String.class.getMethod("charAt",int.class);
     (2)调用方法:
             1、通常方式:System.out.println(str.charAt(1));
              2、反射方式:System.out.println(charAt.invoke(str,1));
               如果传递给Method对象的invoke()方法的第一个参数为null,说明该Method对象对应的是一个静态方法。

      对接收数组参数的成员方法进行反射

       问题:

         启动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"});

      解决办法:

      (1)mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});//即封装成一个参数

       (2)mainMethod.invoke(null,(Object)new String[]{"xxx"});//这样做等于告诉编译器说这是一个参数

                  //编译器就不分把参数当数组看待,也不会把数组打散成若干个参数了.

   
 代码实例:

import java.lang.reflect.Constructor;  

import java.lang.reflect.Field;  

import java.lang.reflect.Method;  

  

public class ReflectTest {  

 public static void main(String[] args)throws Exception  

 {  

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

  String startingClassName = args[0];  

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

  //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);  

  }  

    

 }  

}  

六、数组的反射与Object关系及其反射类型

1.数组反射

(1)具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。

  (2)代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class.

  (3)基本类型的一维数组可以被告当作Object类型使用,不能当作Object[]类型使用,

  非基本类型的一维数组,即可以当作Object类型使用,也能当作Object[]类型使用。

代码例子:
  
  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类型,使用JDK1.5的,故打印结果为一个对象的哈希值。

  System.out.println(Arrays.asList(a4));//--->a4中相当于有三个Object类型,使用JDK1.4的,故打印结果为[a,b,c]。



Java.Lang.Reflect.Array类

     public final class Array extends Object

       Array 类提供了动态创建和访问 Java 数组的方法。 Array 允许在执行 get 或 set 操作期间进行扩展转换,

  但如果发生收缩转换,则抛出 IllegalArgumentException。 

  代码例子:

Object obj=null;

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));

}

}

   hash集合中hashcode()方法及内存泄漏

         当一个对象被存储到HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值

  的字段了,否则,对象修改后哈希值就变了,这种情况下,使用contains方法传入该对象当前

  引用作为参数无法检索到对象,这也导致无法从HashSet集合中单独删除当前对象,从而造成

  内存泄露。

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

 1.反射的作用-->实现框架功能

 ( 1)框架与框架要解决的核心问题

  我做房子给用户住,用户自己安装门窗,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中,框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。

   你做的门调用锁,锁是工具,你做的门被房子调用,房子是框架,房子和锁都是别人提供的。

  (2)框架要解决的核心问题:

       若干年前写的框架可以调用若干年后写的程序。

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

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

1.类 ClassLoader

  public abstract class ClassLoader  extends Object类加载器是负责加载类的对象。

  ClassLoader 类是一个抽象类。如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。

  一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。 

  每个 Class 对象都包含一个对定义它的 ClassLoader 的引用。

   InputStream getResourceAsStream(String name) 

          返回读取指定资源的输入流。 

将文件放到源文件下,会自动将它的字节码文件搬到classPath目录下。

代码实例

  (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下找。

     (3)InputStream
ips = ReflectTest2.class.getResourceAsStream("/cn/itcast/day1/resources/config.properties");

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