黑马程序员--反射

来源:互联网 发布:隋文帝杨坚 知乎 编辑:程序博客网 时间:2024/04/30 03:47

----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------

 

1.反射的基石->Class 类

Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类得字节码,不同的类得字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型。

得到各个字节码对象的实例对象(Class 类型)有下面三种方法:

类名.class,例如,System.class

对象.getClass(),例如,new Date().getClass()

Class.forName("类名"),例如,Class.forName("java.util.Date");

例:String str = "abc";
       Class cls1 = String.class;
       Class cls2 = str.getClass();
       Class cls3 = Class.forName("java.lang.String");
       System.out.println(cls1); //代表一份字节码
       System.out.println(cls2);
       System.out.println(cls3);

打印的结果都为 class java.lang.String,

       System.out.println(cls1.isPrimitive());  //是否是原始类型(boolean,byte,char,short,int,long,float,double)(String 是一个类,返回false)
       System.out.println(int.class.isPrimitive());
       System.out.println(int.class == Integer.class);
       System.out.println(Integer.TYPE); //打印出Integer包装类的基本类型(int)
       System.out.println(int.class == Integer.TYPE); //Integer.TYPE 代表的是包装的那个基本类型的字节码(和int.class 代表的字节码一样)
       System.out.println(int[].class.isPrimitive());
       System.out.println(int[].class.isArray()); //判断是否是数组类型的class实例对象

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

 

2.反射

反射就是把Java类中的各种成分映射成相应的Java类。表示Java类得Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Constructor、Package等等。

2.1 Constructor类

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

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

Constructor[] constructors = Class.forName("java.lang.String").getConstrutors();

得到某一个构造方法:

Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);    //获得指定的构造函数(传入的参数为StringBuffer)

创建实例对象:

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

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

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

Class.newInstance() 方法:

例子:String obj = (String) Class.forName("java.lang.String").newInstance();    //该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象

2.2 Field 类

Field 类代表某个类中的一个成员变量。看下面的例子:

先创建一个类

public class ReflectPoint {
 
  private int x;
  public int y;

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

}

  ReflectPoint pt1 = new ReflectPoint(3,5);
  Field fieldY = pt1.getClass().getField("y");
  System.out.println(fieldY.get(pt1)); //取出变量在某个对象上的值(结果为5)

注意:fieldY不是对象身上的变量,而是类上,要用它去取某个对象对应的值

 /* Field fieldX = pt1.getClass().getField("x");
  System.out.println(fieldX.get(pt1));  */
  //这种方法只能获取public类型的变量,x 为private类型,所以用这种方法,达不到效果
  
  Field fieldX = pt1.getClass().getDeclaredField("x"); //获取所有声明过的变量
  fieldX.setAccessible(true); //如果不设置成可以访问的话,就算fieldX中有所有声明过的变量,但是你取不到fieldX里面的值(暴力反射)
  System.out.println(fieldX.get(pt1));

通过上面这种方法,可以访问类中的私有类型(private) 的变量。

2.3 Method 类

Method代表某个类中的一个成员方法。
得到类中的某一个方法:

Method  methodCharAt = String.class.getClass("CharAt",int.class);

调用方法:

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

反射方式:System.out.println(CharAt.invoke(str,1));

如果传递给Method对象的invoke()方法的一个参数为null,说明该Method对象对应的是一个静态方法!

 

3.数组的反射

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

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

③基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用,非基本类型的一维数组,既可以当做Object类型使用,又可以当作Object[]类型使用。

例:int[] a1 = new int[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()); //属于同一个字节码(返回为true)
        System.out.println(a1.getClass().getName()); //结果为 [I ([表示数组,I表示int类型)
        System.out.println(a1.getClass().getSuperclass().getName()); //获取父类(java.lang.Object)
        System.out.println(a4.getClass().getSuperclass().getName());

int a = new int[] {1,2,3}

System.out.println(Arrays.asList(a1));    //打印的结果是1,2,3(作用是依次打印出数组的每个元素)

 

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

set中的数据对象没有顺序且不可以重复;List中的数据对象有顺序且可以重复。看下面的例子:

  Collection collections  = new ArrayList();
 // Collection<ReflectPoint> collections  = new HashSet<ReflectPoint>();
  ReflectPoint pt1 = new ReflectPoint(3,3);
  ReflectPoint pt2 = new ReflectPoint(5,5);
  ReflectPoint pt3 = new ReflectPoint(3,3);
  collections.add(pt1);
  collections.add(pt2);
  collections.add(pt3);
  collections.add(pt1);
 // System.out.println(collections.size()); //结果为4(List中的数据对象有顺序且可以重复)
  System.out.println(collections.size()); //结果为3(Set中的数据对象没有顺序且不可以重复)

如果实现了下面的方法:

@Override
  public int hashCode() {
   final int prime = 31;
   int result = 1;
   result = prime * result + x;
   result = prime * result + y;
   return result;
  }

  @Override
  public boolean equals(Object obj) {
   if (this == obj)
    return true;
   if (obj == null)
    return false;
   if (getClass() != obj.getClass())
    return false;
   ReflectPoint other = (ReflectPoint) obj;
   if (x != other.x)
    return false;
   if (y != other.y)
    return false;
   return true;
  }

  public String toString() {
   return str1 + ":" + str2 + ":" + str3;
  }

打印的结果是2。(pt1 和 pt2 是同一个对象)

用反射和配置文件来管理

创建一个config.properties 文件,根据该文件路径的不同,有下面不同的写法:

// InputStream is = new FileInputStream("config.properties");
 // InputStream is = ReflectTestAgain.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");

InputStream is = ReflectTestAgain.class.getResourceAsStream("config.properties"); //相对路径(与类在同一个包下)
Properties props = new Properties();
props.load(is);
is.close();
String className = props.getProperty("className");
Collection collections  = (Collection) Class.forName(className).newInstance();

反射最主要的应用就是框架,在以后的学习中,会慢慢体会到……

----------------------- android培训、java培训、java学习型技术博客、期待与您交流! ----------------------

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

原创粉丝点击