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

黑马程序员_JAVA基础加强——反射

2013-11-20 23:57 477 查看
---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流!
----------------------
--什么是反射?

想要知道反射的概念,要先明白java类的作用是什么?java类就是用于描述一类事物的共性,这些事物包括现实中存在的事物,也包括虚拟的一些事物。把具有相同属性和行为的一类事物抽象化,就形成了类。那么,java中的所有类是不是也有一些共性的内容呢?我们知道,一个类是由多个部分组成的:成员变量、成员方法和构造方法等。那么我们是不是也可以把这些共性的内容抽取成一个类呢?在java中,使用Class这个类来描述java中所有的类。

在java中,所有的类都是Class的一个实例对象,它们的内容不同,但是,它们的特征相同,譬如,都有方法,有字段,有父类,有包。那么,它们的实例对象又是什么呢?学java基础的时候我们知道,每个类在被加载进内存时,都会先把它们对应的.class字节码加载进内存,那么,这份字节码就是Class的实例对象。那么,反射就是将类的字节码加载进内存,而后剖析出这个类的各个组成部分,包括构造函数、成员变量和方法等。这些信息用相应类的实例对象来表示,它们是Constructor、Field和Method等。
--加载类
了解了反射的概念后,我们就先创建一个类:Person。加载该类的字节码,然后通过反射技术剖析这个类的组成部分。
package cn.itcast.reflect;

import java.io.InputStream;
import java.util.List;
/**
* 人类
* @author 中关村阿旺
*
*/
public class Person {

public int age=44;   //年龄(public修饰)
private static String address;   //住址(private static修饰)

public Person(){
System.out.println("person");
}

//构造方法,private修饰
private Person(List list){
System.out.println("list");
}

public void aa1(){
System.out.println("aa1");
}

private void aa1(InputStream in){
System.out.println(in);
}

//方法,static修饰
public static void aa1(int num){
System.out.println(num);
}

//main方法,static修饰,有点特殊
public static void main(String[] args){
System.out.println("main");
}
}


  加载Person类就是将硬盘上的Person类的字节码文件内容加载到内存里,并封装成Class对象。
 
加载类有三种方式:

  第一种方式:通过Class类的静态方法forName()加载类,该方法接收一个参数,字符串类型的完整类名称。

  例如:Class clazz=Class.forName("cn.itcast.reflect.Person");

  第二种方式:通过实例化类的对象加载该类。因为实例化对象的过程是先要加载该类到内存中,然后才会初始化对象。

  通过对象的getClass()方法获得Class对象,也就是该类的字节码。

  例如:Class clazz1=new Person().getClass();

  第三种方式:直接将该类的字节码加载进内存中。

  例如:Class clazz2=Person.class;
  以上三种方式,我们使用哪种方式都可以,类已经进内存了,就可以用反射技术剖析类的组成部分了。
--反射类的构造函数
 在java中Constructor类提供关于类的单个构造方法的信息以及对它的访问权限。
 例如:反射构造函数:public Person(){}
 通过Class类的getConstructor()方法可以得到一个Constructor对象。该方法接收一个参数,就是你反射的那个构造方法的参数的类型(字节码)。

 因为我们反射的是无参的构造函数,所以传入null值,得到构造函数对象。

 代码:Constructor c=clazz.getConstructor(null);

 此构造函数对象中有一个方法:newInstance(),可以返回我们反射的类的实例对象。该方法接收的参数是实际传入构造方法的参数。

 我们反射的是Person类,所以用Person对象接收。由于newInstance()方法返回类型是Object,所以需要强转。

 代码:Person p=(Person)c.newInstance(null);
 用得到的这个对象,我们可以获取该对象中封装的信息。

 System.out.println(p.age);        //返回44
 上面反射的是public修饰符修饰的构造函数,我们也可以反射非public修饰符修饰的构造函数。
 例如:反射构造函数:private Person(List list){}
 由于此构造函数反射的是private修饰的,所以可以通过Class类的getDeclaredConstructor()方法获取构造函数对象。getDeclaredConstructor()方法可以反
 射在Person类中所有声明(定义)过的构造方法,包括private修饰的。
 由于此构造函数有参数,我们可以传入此构造函数参数的字节码即可。
 代码:Constructor c=clazz.getDeclaredConstructor(List.class);
 由于private修饰的成员只能在本类中被访问,但是,暴力反射可以在让其在本类外被访问。
 所谓暴力反射,就是将那些在反射的类中不是public修饰的成员方法、属性、构造方法等,强行打开它们的访问权限,使其可以在本类外访问。

 代码:c.setAccessible(true);这就是暴力反射
 代码:Person p=(Person) c.newInstance(new ArrayList());

 System.out.println(p.age);   //返回44
 
 注:另一种使用反射创建对象的方法,不是反射构造函数,而是直接使用Class类的newInstance()方法。这是因为java为了方便通过反射技术创建类的实例而添加的方法。
 例如:Person p=(Person) clazz.newInstance();

           System.out.println(p.age);   //返回44
--反射类的方法
 在java中Method类提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。
1.反射类中的普通方法
 通过Class类的getMethod()方法得到Method对象,该方法接收两个参数,第一个指明需要反射的方法名称,第二个指明需要反射的方法的参数类型。
 例如:反射类的方法:public void aa1(){}

 代码:Method m=clazz.getMethod("aa1", null);

 使用Method对象的invoke()方法可以调用反射的方法,需要传入对象名(指明反射的是哪个对象的方法)和参数类型。

 代码:m.invoke(p, null);          //返回aa1
 上面的代码反射的是public修饰的方法,想要访问非public修饰的方法可以通过Class类的getDeclaredMethod()方法访问在Person类中定义(声明)的所有方法。
 例如:反射类的方法:private void aa1(InputStream in){}
 由于要反射的此方法有参数,那么在第二个参数中传入该参数的字节码即可。

 代码:Method m=clazz.getDeclaredMethod("aa1", InputStream.class);

 m.setAccessible(true);  //暴力反射

 代码:m.invoke(p, new FileInputStream("e:\\黑马\\黑马视频笔记\\033java基础加强\\课堂笔记1.txt"));    //返回java.io.FileInputStream@150bd4d
 注:如果反射的方法是一个静态方法,比如:public static void aa1(int num){}
 那么在用反射技术得到Method对象后,调用invoke()方法传入对象时,可以传入null。
 代码:m.invoke(null, 56);    //返回56
 2.反射类中的main方法
 反射类中的main方法:public static void main(String[] args)
 有两种调用方式:
 第一种方式:
  代码:Method m=clazz.getMethod("main", String[].class);

  由于main方法也是静态方法,所以可以不用传入对象。
  代码:m.invoke(null, new Object[]{new String[]{"aaa","bbb"}});    //返回main

  那么之所以这样写,是因为JDK1.4当中没有可变参数这一特性,它使用的是Object类型的数组作为参数,调用invoke()方法时,会把数组中的每一个元素当做一个参数来看待,也就是会把数组拆开。JDK1.5虽然有了可变参数这一新特性,但是它兼容JDK1.4,所以,它也会把可变参数当做是一个数组进行拆封。这样一拆,就成了两个字符串对象了,就与main方法的参数类型不一致了,所以会出现参数类型不匹配异常。为了解决这一异常,需要把数组再一次封装成数组,这样在进行拆封时,得到的仍然是一个数组,刚好符合main方法的参数类型。

  第二种方式:

  强制让java虚拟机把参数类型当做是一个Object类型的参数,而不是一个Object类型的数组。这样就不会进行拆封,而里面实际存放的是字符串数组,所以仍然与main方法的参数类型一致。

  代码:m.invoke(null, (Object)new String[]{"rrrr","dddd"});    //返回main
--反射类的字段
  在java中Field类提供有关类或接口的单个字段的信息,以及对它的动态访问权限。
  通过Class类的getField()方法可以得到Field对象,该方法接收一个参数,即需要反射的字段的名称。
  例如:反射类的字段:public int age=44;
  代码:Field f=clazz.getField("age");

  可以根据Field类对象的get()方法得到该字段的值,该方法接收一个参数,指明需要反射的那个对象的字段。
  代码:int num=(Integer) f.get(p);  //由于该方法返回值为Object类型,而age字段为int,所以需要强转。

  代码:System.out.println(num);     //返回44

  注:由于Person类文件可能不是我们自己编写的,所以不知道字段的类型,我们可以通过Field类对象的getType()方法得到该字段所属类型的字节码,根据字节码判断该字段的类型。

  代码:Object value=f.get(p);

  代码:Class type=f.getType();   //获得指定字段的类型,返回字节码对象

  由于每种类型的字节码只存在一份,所以用“==”判断比较高效,当然equals()方法也可以。

  if(type == int.class){

       int newValue=(Integer) value;   //自动拆箱

       System.out.println(newValue);  //自动装箱

  }
  上面的代码反射的是public修饰的字段,那么反射非public修饰的字段需要使用Class类的getDeclaredField()方法,该方法可以反射在Person类中定义(声明)过的所有字段。
  例如:反射类的字段:private static String address;
  代码:Field f=clazz.getDeclaredField("address");

  代码:f.setAccessible(true);   //由于反射的是private修饰的字段,所以需要暴力反射。

  该字段没有手动赋予值,可以通过Field类对象的set()方法写入指定的值。该方法接收两个参数,一个是需要反射的类的对象,一个是赋予该字段的实际值。由于所要获取的字段是静态的,不必创建该类的对象,所以传入null。

  代码:f.set(null, "北京市");
  如果我们不知道该字段的类型,同样可以通过getType()方法得到。

  代码:Object value=f.get(null);

  代码:Class type=f.getType();

  if(type == String.class){

       String newValue=(String)value;

       System.out.println(newValue);    //返回北京市

  }
---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a
href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------
详细请查看:<a href="http://edu.csdn.net" target="blank">http://edu.csdn.net</a>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: