黑马程序员——Java中的反射技术

来源:互联网 发布:steam上巫师三mac 编辑:程序博客网 时间:2024/04/28 17:40

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

反射的基石——Class

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

Class描述了哪些方面的信息呢?

类的名字   类的访问属性    类所属于的包名      字段名称的列表   方法名称的列表等,             

 

使用Class  那么怎么给Class类赋值呢?

 

其中的什么是字节码呢?

将一个类编译成class文件后放在硬盘上,就会生成一些二进制代码,要把这些二进制文件加载到内存中来,然后才可以创建一个一个的对象,首先要把字节码加载到类成员中来,在用字节码赋值出一个一个对象来

 

每一个字节码就是一个Class类的实例对象

Person p1=new Person();

 

Class cls1=Person.class;   //字节码1;其中Person.class就表示Person类在内存中的那个字节码

Class cls2=字节码2

 

Class.forName()的作用?就是返回字节码

返回的方式有两种:

1,这份字节码曾经被加载过,已经待在java虚拟机中了,就可以直接返回了

2java虚拟机中还没有这份字节码,则用类加载器去加载,把加载进来的字节码,缓存到虚拟机中,以后要得到这个字节码就不用在加载了

 

得到字节码的方式有三种:

1,类名.class  例如:System.class

2,对象.getClass()  例如:new Date().getClass();  //有一个对象,这个对象一定是字节码创建出来的

3Class.forName(“类名”) 例如:Class.forName(“java.util.Date”)   //使用静态方法去加载

 

在反射中主要用到的是第三种

 

isPrimitive()   判断是否是原始类型   

 

数组类型的Class实例对象

Class.isArray()   判断是否是数组

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

什么是反射?

反射就是把Java类中的各种成分映射成相应的java类。例如:一个java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个java类来表示,

 

一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。

 

使用反射技术,应该导入的包是java.reflect.*;

反射会导致程序的性能严重下降

Constructor类:

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

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

 例子:Constructor constructors[]=Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);

 

得到某一个构造方法:

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

也可写成

Constructor constructor=String.class.getConstructor(StringBuffer.class);

StringBuffer.class //获得方法时要用到的类型

 

创建实例对象:

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

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

new StringBuffer(“abc”)//调用获得的方法时,要用到上面相同类型的实例对象

 

Class.newInstance()方法:

例子:String obj=(String)Class.forName(“java.lang.String”).newInstance();

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

该方法内部用到了缓存机制来保存默认构造方法的实例对象

 

使用反射的构造方法的简单示例: 

import java.lang.reflect.*;

publicclass reflect {

   publicstaticvoid main(String[] args)throws Exception

   {     

   Constructor constructor=String.class.getConstructor(StringBuffer.class);

  

   String str=(String)constructor.newInstance(new StringBuffer("abc"));

  

   System.out.println(str.charAt(1));

   }

}

Field

Field类代表某个类中的一个成员变量

生成构造方法:

Alt+Shift+s   ——Generate Constructor using Fileds….

定义一个类

publicclass reflectClass {

   private intx;

   public inty;

   public reflectClass(int x,int y) {

      super();

      this.x = x;

      this.y = y;

   }

}

 

总结:

Field类代表一个字节码里面的一个变量,不代表某个对象身上的变量

 

Field fieldY=pt1.getClass().getField("y"); 因为y变量在类中是public类型的,所以在获取变量的时候可以用getField("y")

 

Field fieldX=pt1.getClass().getDeclaredField("x");

因为x变量在类中的是private类型的,所以在获取变量的时候必须要用getDeclaredField("x")

fieldX.setAccessible(true);//这个就是所谓的暴力反射,让变量的值强制可以去访问

 

例子将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改成“a”

 

自定义的类:

package itheima;

publicclass reflectClass {

   private intx;

   public int y;

   public Stringstr1="ball";

   public Stringstr2="basketball";

   public Stringstr3="itcast";

   public reflectClass(int x,int y) {

      super();

      this.x = x;

      this.y = y;

   }

  

   publicString toString()

   {

      returnstr1+"::"+str2+"::"+str3;   

   }

} 

主函数类:

   import java.lang.reflect.*;

   publicclass reflect {

    publicstaticvoid main(String[] args)throws Exception

              {

                reflectClasspt1=newreflectClass(3, 5);

              changeStringValue(pt1);

               System.out.println(pt1);

   }

   private staticvoid changeStringValue(Object obj)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);           

         }     

   }

}

 

总结:

1Field[] fields=obj.getClass().getFields();这句话是要获取类中所有的成员变量

2)使用增加for循环遍历所有的成员变量for(Field field:fields)

3if(field.getType()==String.class)这是应该使用==,因为是同一个字节码,这句话是用来判断变量的类型是否是String类型,如果是String类型的变量,就将其中的‘b’替换成‘a’。

4String oldValue=(String)field.get(obj);获取field对象中的值

5String newValue=oldValue.replace('b','a');b替换成a

6field.set(obj, newValue);将对象身上的值更改,obj---pt1 obj对象身上的field进行更改值,将其值设置为newValue

 

Method类:

Method类代表某个类中的一个成员方法

得到类中的某个方法:

例子: Method charAt=Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);

调用的方法:

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

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

如果传递给Method对象的invoke()方法的一个参数为null,这有着什么样的意义呢?  说明该Method对象对应的是一个静态方法!!

用反射的方式得到字节码中的方法,在拿这个方法去作用到某个对象  invoke是调用前面的方法  invoke表示方法对象上的方法

 

用反射方式执行某个类中的main方法

      String start=args[0];

      Method method=Class.forName(start).getMethod("main",String.class);

      method.invoke(null,(Object)new String[]{"111","222","333"});

 

class TestArguments

{

   publicstaticvoid main(String[] args)

   {    

      for(String arg:args)

      System.out.println(arg);

   }

}

数组的反射:

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

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

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

Arrays.asList()方法处理int[]和String[]时的差异

Array工具类用于完成对数组的反射操作

 

总结:

如果使用数组的反射,那么每个数组都属于同一个字节码,但是必须具有相同的数据类型,以及具有相同的维度

如果对象想要得到字节码,必须使用方法getClass();

想要获取对象的字节码的名字,必须使用方法getName();  因为是数组,所以在显示名字的时候,如果是int类型,则显示“[I

这有个图:

 

 

在定义数组的时候,如果后面指定了值,前面就不能指定数组的个数

例如:

   Int[] a1=new int[3]{1,2,3}; 这种方式是错的

   Int[] a1=new int[]{1,2,3};  这种方式是正确的

 

所有的类都有父类,除了Object

 

Int[][] a3=new int[2][3];

Int[] a1=new int[3];

String[] a4=new String[3];

 

Object[]  aObj3=a1;  这种方式不能用,因为基本类型的一维数组,不能转换成Object[] 数组的,因为这个数组中装的是int,不是Object

 

Object[]  aObj4=a3;   a3是二维数组,就相当于里面装入了int类型的一维数组,因为这个数组中装的是一维数组,一维数组是Object

 

Object[]  aObj5=a4;  这个数组中装的是String类型    String也是Object类型

Arrays.asList()方法处理int[]和String[]时的差异:

String不属于基本数据类型

Int[] a1=new int[3];   这个可以理解为,有一个数组,这个数组中装的是int类型,int是基本数据类型,不是Object

Arrays.asList(Object[]  obj); 

System.out.println(Arrays.asList(a1));   asList方法接收的是Object类型的数组,因为a1int[]类型的数组,在向方法中传值的时候,Object[]这个数组认为和这个对象对不上,因为传入的值是int,基本数据类型,不能处理,jdk1.5版本可以处理

String[] a4=new String[3];这个可以理解为,有一个数组这个数组中装的是String类型,String类型是Object

System.out.println(Arrays.asList(a4));  因为a4String类型的数组,所以可以将数组中的值打印出来

思考题:怎么得到数组中元素的类型呢?

不能获取整个数组的类型,只能获取整个数组中某个元素的类型

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

Object a1=new Object[]{“a”,1,2};  //可以使用a[0].getClass().getName()来获取某个元素的类型

 

ArrayListHashSet的比较及HashCode的分析

ArrayListHashSet的区别:

ArrayList是一个有顺序的集合,有顺序的集合就相当于一个数组,在将对象放入集合中的时候,是将对象的引用放入进去,是按照依次的顺序先后放进去,如果一个对象有多个引用的时候,也是依次放入集合中的,可以明确的说取出第几个

(这里的顺序指的是位置的顺序)

 

HashSet是在向里面存入对象的引用的时候,先查看集合中有没有这个对象是否相等,如如果有这个对象,则不向里面存入

 

面试题:

HashCode方法的作用:

哈希算法来提高从集合中查找元素的效率,这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希吗分组,每组分对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储在哪个区域了。

当有一个对象的时候,先算出它属于哪一个区域,假如属于第五个区域,那么就不依次去找,只在第五区域找是否有与它相等的对象即可,这样就提高了程序的效率

 

要想hashCode的方法有价值,前提是对象必须存储在哈希集合中才有价值

 

ReflectPoint pt1=new ReflectPoint(3,3);

ReflectPoint p2=new ReflectPoint(4,4);

ReflectPoint pt3=new ReflectPoint(3,3);

如果没有实现hashCode方法为什么长度就等于3呢?虽然pt1pt3这两个对象用equals比较是相等的,但是算出来的hashCodept1pt3都是按照自己的内存地址去算的,这两个本来相同的对象,分别存在了不同的区域中,所以被放入到了集合中

为了让相等的对象放在相同的区域中,那么就应该是如果两个对象的equals方法相等,那么两个对象的hashCode方法也应该相等

如果集合不存在HashSet中,那么就没有必要使用hashCode方法了

 

提示:当一个对象被存储进HashSet集合中以后,就不能拿修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也讲返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露

 

所谓的内存泄露是我这个对象不要用了,但还一直占用内存的空间,没有被释放,这就叫内存泄露

 

反射的作用——>实现框架功能

 

框架与框架要解决的核心问题

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

 

框架要解决的核心问题:

 

我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样调用到你以后写的类(门窗)呢?

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

 

框架和工具类的区别:

这两个类都是别人写的,一个是你调用人家,一个是人家调用你

框架是框架在调用用户的类

工具类是用户在调用工具类

 

例子:

config.properties类中:

className=java.util.HashSet

 

reflectSet类中:

package itheima;

import java.util.*;

import java.io.*;

importjava.lang.reflect.*;

publicclass reflectSet {

 

   publicstaticvoid main(String[] args)throws Exception{

 

      InputStream in=new FileInputStream("config.properties");

 

      Properties prop=new Properties();

     

      prop.load(in);

     

      in.close();

     

      //getProperty()这个方法是获取config.properties文件中的属性

      String className=prop.getProperty("className");

      Collection a1=(Collection)Class.forName(className).newInstance();

     

      reflectClass pt1=new reflectClass(3, 3);

      reflectClass pt2=new reflectClass(5, 5);

      reflectClass pt3=new reflectClass(3, 3);

     

      a1.add(pt1);

      a1.add(pt2);

      a1.add(pt3);

      a1.add(pt1);

      System.out.println(a1.size());

     

   }

 

}

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

0 0
原创粉丝点击