Java反射机制

1Class类:Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class

     例如:

       众多的人可以用Person来表示。

       众多的Java类可以用Class来表示。

 

2、Class类的功能(作用):

   Java中可以通过一个实例化对象得到一个类的完整信息。例如:该类的类名、成员变量、一般方法、构造方法、该类所属的包…等等一系列的信息。

 

3、Class类的实例化对象是什么?

      Person类代表人,它的实例化对象就是像“张三、李四、王五…”这样的一个个具体的人。

      Class类的的实例化对象就是对应各个类在内存中的字节码。例如:Person类的字节码、String类的字节码…等等。

 

4、Java中的字节码对象是什么类型?Class类型)

       当一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容也是不同的,这一个个的内存空间可以分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型就是Class。

 

5、获取类的字节码对应的实例对象的3中方式:

     ①、类名.class   例如:System.class;

     ②、对象.getClass()     例如:newDate().getClass();

     ③、Class.forName(“完整类名”)      例如:Class.forName(“java.lang.String”);

 

6、9种预定义的Class实例对象:

8大原始数据类型(基本数据类型) + void

即:boolean/byte/short/int/long/float/double/char/void

 

9种预定义的Class实例对象另一种表示形式:

即:Boolean.TYPE、Byte.TYPE、Short.TYPE、Integer.TYPE、Long.TYPE、Float.TYPE、Double.TYPE、Character.TYPE、Void.TYPE

 

代码示例:

packagecn.itcast.day1;
 
public class ReflectTest {
      public static void main(String[]args) throws Exception {
         String str1 = "abc";
         Class cls1 = str1.getClass();
         Class cls2 = String.class;
         Class cls3 = Class.forName("java.lang.String");
     
         System.out.println(cls1==cls2);   //true
         System.out.println(cls1==cls3);  //true
     
         System.out.println(cls1.isPrimitive());  //false
         System.out.println(int.class.isPrimitive()); //true
         System.out.println(int.class == Integer.class);  //false
         System.out.println(int.class == Integer.TYPE);  //true
         System.out.println(int[].class.isPrimitive());  //false  数组也是一种类型,但不是原始数据类型
         System.out.println(int[].class.isArray());  //true
      }
}


总结:只要是在源程序中出现的类型,都有各自的Class实例对象。例如:intint[]void…等等。

 


二、反射(Reflect)

1、什么是反射?

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

     反射的作用就是:实现框架功能。

  

     例如:一个Java类中,用一个Class类的对象来表示一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,轮胎等等也是一个个的类。表示Java类的Class类中显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们分别是Field、Method、Contructor、Package等等。

  

注意:反射不是JDK1.5新特性。

 

2、构造方法的反射应用 — Constructor类

Constructor类代表某个类中的一个(或所有的)构造函数。

 

获取某个类中构造方法的Class类方法:

Constructor<?>[] getConstructors():获取某个类中所有的构造方法。

例:

//得到String类中的所有的构造方法。

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

     

for(int x=0; x<constructors.length; x++){

       System.out.println(constructors[x].toString());

}

 

Constructor<T>getConstructor(Class<?>... parameterTypes):获取指定的类中的某个指定的构造方法。

例:

//得到String类中指定的构造方法。

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

System.out.println(constructor.toString());

 

3、创建实例对象的方式:

普通方式:String str = new String(new StringBuffer(“abc”));

反射方式:String str = (String)constructor.newInstance(newStringBuffer(“abc”));

 

       注意:如果想要通过Class类本身实例化其它类的对象,则可以使用newInstance()方法,但是必须要保证被实例化的类中存在一个无参的构造方法。


Ps:反射会导致程序性能严重下降。

 

代码示例:

package cn.itcast.day1;
 
import java.lang.reflect.Constructor;
 
public class ReflectTest {
       public static void main(String[]args) throws Exception {
       //new String(new StringBuffer("abc"));
       Constructor consutuctor1 = String.class.getConstructor(StringBuffer.class);
       String str2 = (String)consutuctor1.newInstance(new StringBuffer("abc"));
       System.out.println(str2.charAt(2));    //c
      }
}


4、成员变量的反射—Field类

Field类代表某个类中的一个(或所有的)成员变量。

 

获取某个类中成员变量的Class类的方法:

Field getField(String name)根据某个类的成员变量名获取Field类的对象。

Field[] getFields()获取某个类中所有的成员变量。

Field getDeclaredField(String name)根据成员变量名获取某个类中已声明的成员变量。

Field[] getDeclaredFields()获取某个类中已声明的所有的成员变量。

  

问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?

类只有一个,而该类的实例对象有多个,如果是与对象关联,那关联的是哪个对象呢?所以字段fieldX 代表的是x的定义,而不是具体的x变量。

  

代码示例1

ReflectPoint.java

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


ReflectTest.java

package cn.itcast.day1;
 
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");
         System.out.println(fieldY.get(pt1));         //5
      }
}

       结论:Field类的对象对应的不是一个类的对象上的变量,而是对应该类上面的成员变量。因为要用fieldY去取某个类的对象对应的值。

 

代码示例2

ReflectTest.java

package cn.itcast.day1;
 
import java.lang.reflect.Field;
 
public class ReflectTest {
      public static void main(String[]args) throws Exception {
         ReflectPoint pt1 = new ReflectPoint(3,5);
         Field fieldX =pt1.getClass().getField("x");
         System.out.println(fieldX.get(pt1));
      }
}


       注意:如果一个类的成员变量被private所修饰,则通过getField()方法获取Field类对象时会在编译阶段出现NoSuchFieldException异常。

       getField()方法只能获取到可见的(即非私有)成员变量。

 

异常提示如下:

Exception in thread "main"java.lang.NoSuchFieldException: x

at java.lang.Class.getField(Class.java:1690)

atcn.itcast.day1.ReflectTest.main(ReflectTest.java:45)

  

 

解决方式:将getField()方法换成getDeclaredField()方法

代码示例3

ReflectTest.java

package cn.itcast.day1;
 
import java.lang.reflect.Field;
 
public class ReflectTest {
      public static void main(String[]args) throws Exception {
         ReflectPoint pt1 = new ReflectPoint(3,5);
         Field fieldX = pt1.getClass().getDeclaredField("x");
         System.out.println(fieldX.get(pt1));
      }
}


编译结果:由于被private所修饰,Field类的对象无法访问ReflectPoint类中的成员变量x,所以会出现以下异常:

Exception in thread "main"java.lang.IllegalAccessException: Class cn.itcast.day1.ReflectTest can not access amember of class cn.itcast.day1.ReflectPoint with modifiers "private"

atsun.reflect.Reflection.ensureMemberAccess(Reflection.java:101)

atjava.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:295)

atjava.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:287)

atjava.lang.reflect.Field.get(Field.java:384)

atcn.itcast.day1.ReflectTest.main(ReflectTest.java:46)

 

       注意:getDeclaredField()方法可以获取到类中声明过的成员方法,不管是可见的还是不可见的(即不管是被private修饰的还是被其它修饰符所修饰的)

 

解决方式:通过Field类继承来的setAccessible(booleanflag)方法强制获取到被private修饰的成员变量的值。此方式被称为“暴力反射”。

 

代码示例4

ReflectTest.java
package cn.itcast.day1;
 
import java.lang.reflect.Field;
 
public class ReflectTest {
      public static void main(String[]args) throws Exception {
         ReflectPoint pt1 = new ReflectPoint(3,5);
         Field fieldX = pt1.getClass().get DeclaredField("x");
         fieldX.setAccessible(true);
         System.out.println(fieldX.get(pt1));  //3
      }
}

 

 

需求:使用反射的方式将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"

代码示例5

ReflectPoint.java

package cn.itcast.day1;
 
public class ReflectPoint {
      public String str1 = "ball";
      public String str2 = "basketball";
      public String str3 = "itcast";
 
      @Override
      public String toString(){
         return "str1="+str1+",str2="+str2+",str3="+str3;
      }
}


ReflectTest.java

package cn.itcast.day1;
 
import java.lang.reflect.Field;
 
public class ReflectTest {
      public static void main(String[]args) throws Exception {
         changeStringValue(pt1);
         System.out.println(pt1.toString());
      }
 
      private static void changeStringValue(Objectobj) throws Exception {
         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');
               field.set(obj,newValue);
            }
         }
     }
}

运行结果:str1=aall, str2=aasketaall, str3=itcast

 

5、成员方法的反射—Method类

Method类代表某个类中的一个(或者所有的)成员方法。

 

获取某个类中成员变量的Class类的方法:

MethodgetMethod(String name, Class<?>... parameterTypes):获取指定类中的某一个成员方法。

例:

Method charAt =Class.forName("java.lang.String").getMethod("charAt",int.class);

Method[] getMethods():获取某个类中的所有的成员方法。

Method getDeclaredMethod(String name,Class<?>... parameterTypes):获取指定类当中一个已声明的成员方法。

Method[] getDeclaredMethods():获取某个类中已声明的所有的成员方法。

Object invoke(Object obj, Object... args):通过反射调用类中的方法。


例:调用方法的方式

普通调用方式:System.out.println(str.charAt(1));

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

 

       注意:如果传递给Method对象的invoke()方法的第一个参数为null(也就是说该方法不是通过一个对象来调用的,则必定是通过类名来调用),说明该Method对象对应的是一个静态方法。

 

代码示例:

ReflectTest.java

package cn.itcast.day1;
 
import java.lang.reflect.Method;
 
public class ReflectTest {
      public static void main(String[]args) throws Exception {
         //普通调用方式:str1.charAt(1);
         //反射调用方式:
         Method methodCharAt =String.class.getMethod("charAt", int.class);
         System.out.println(methodCharAt.invoke(str1,1));  //b
     
         //jdk1.4中的语法调用。
         System.out.println(methodCharAt.invoke(str1,new Object[]{2}));  //c
      }
}

 

6、框架的概念以及用反射技术开发框架的原理

     反射的作用:实现框架功能。

  

①、框架与框架要解决的核心问题:

      框架就是已经实现好的类,用户可以自定义类提供给框架使用,加快开发效率。因为在写程序时无法知道要被调用的类名,所以在程序中无法直接new某个类的实例对象,而需要使用反射的方式来做。

     

      框架和工具类的区别:

      工具类是被用户的类调用,而框架则是调用用户提供的类。

  

②、框架在现实生活中的理解举例:

      框架的理解:房地产开发商建造房子给用户住,由用户自己安装门窗和电器,则房地产开发商建造的房子就是框架,用户需要使用框架,则需要把门窗插入到已经开发好的框架当中去。

     

      框架要解决的核心问题:程序开发人员在写框架的时候,可能某个用户还在上小学,还不会写程序。而程序开发人员已经写好的框架程序怎样才能调用还在上小学的这个用户将来写的类(门窗)呢?

 

③、配置属性文件/配置文件

      属性文件:后缀名为.properties的文件,里面的内容以键值对的形式存放。

     

      配置步骤:选中当前项目JavaEnhance2,然后依次点击“File → New → File → 输入文件名config.properties → 点击Finish完成”。



创建完成属性文件之后,就可以在当前项目JavaEnhance2的根目录下看到刚刚新建好的config.properties文件了  

 

然后编辑config.properties属性文件,点击下方的“Source”,然后就可以在文本编辑区域里面写入键值对形式的内容。


代码示例:

ReflectTest2.java

package cn.itcast.day1;
 
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
 
public class ReflectTest2 {
public static void main(String[]args) throws Exception {
         InputStream ips = new FileInputStream("config.properties");
         Properties props = new Properties();
         props.load(ips);
         ips.close();
     
         String className = (String)props.get("className");
         Collection collections = (Collection)Class.forName(className).newInstance();
     
         //ReflectPoint类中覆写了equals()方法和hashCode()方法。
         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());  //3
      }
}

当把config.properties属性文件当中的键值对改成HashSet集合的时候,则程序输出的结果为2。

 

7、用类加载器的方式管理资源文件和属性(配置)文件

     首先,将config.properties属性文件移动到当前工程的src目录下的cn.itcast.day1的根目录下。

      

代码示例:

ReflectTest2.java

package cn.itcast.day1;
 
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
 
public class ReflectTest2 {
   public static void main(String[] args) throws Exception {
      //方式一:采用类加载器进行加载,使用相对路径的方式
      //InputStream ips = ReflectTest.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
     
      //方式二:利用Class方式进行加载,使用相对路径的方式
      // InputStream ips =  ReflectTest.class.getResourceAsStream("config.properties");
     
      //方式三:利用Class方式进行加载,使用绝对路径的方式
      InputStream ips = ReflectTest.class.getResourceAsStream("/cn/itcast/day1/config.properties");
     
      Properties props = new Properties();
      props.load(ips);
      ips.close();
     
      String className = (String)props.get("className");
      Collection collections = Collection)Class.forName(className).newInstance();
     
      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());
   }
}

       注意:将.properties属性文件放到工程的src目录下,则eclipse会自动将其拷贝到该工程的bin目录下存放.class文件的目录当中。

 

 



 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值