黑马-------反射

本文详细解读Java反射的基础概念,包括Class类的作用、反射的定义与应用,特别是构造方法、成员变量、方法的反射操作。通过实例代码,展示了如何使用反射API获取和操作类的组成部分,以及在实际开发中的应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

一.透彻分析反射的基础_Class类


反射的基石---->Class类
1.java程序中的各个JAVA类属于同一类事物,描述这类事物的JAVA类名就是Class
2.对比提问:众多的人用一个什么类表示?众多的JAVA类用一个什么类表示 ?
3.对比提问:Person类代表人, 它的实例对象就是张三,李四这样的一个个具体的人,Class类代表JAVA类,
它的各个实现对象又分别对应什么呢?
 对应各个类在内存中的字节码,例如 :Person类的字节码,ArrayList类的字节码,等等
 一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,
不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来
表示,这些对象显然具有相同的类型,这个类型是什么呢?
4.如何得到各个字节码对应的实例对象(Class类型)
(1)类名.class,例如:System.class
(2)对象.getClass(),例如:new Date().getClass();
(3)Class.forName("类名"),例如:Class.forName("java.util.Date");
例如:String str1="abc";
 Class cls1=str1.getClass();
 Class cls2=String.class();
 Class cls3=Class.forName("java.lang.String");
5.九个预定义Class实例对象
 参看 Class.isPrimitive方法的帮助//是否是原始类型
 Int.class==Integer.TYPE
 数组类型的Class实例对象
 Class.isArray();
总之,只要是在源程序出现的类型,都有各自的Class实例对象,例如:int[], void
八个基本数据类型:
boolean,byte,char,short,
int ,long,float,double
Class class=void.class
一共九个

 

6.JAVA类用于描述一类事物的共性 ,该类事物有什么属性,没有什么属性,至于这个属性的值是什么 ,则是由这个类的实例对象
来确定的,不同的实例对象有不同的属性值 ,JAVA程序中的各个JAVA类,它们是否属于同一类事物呢?是不是可以用一个类来描述这
类事物呢?这个类的名字就是Class,要注意与小写的class关键字的区别,Class类描述了哪些方面的信息呢?类的名字
,类的访问属性,类所属于的包名,字段名称的列表 ,方法名称的列表,等等,学习反射,首先就要明白Class这个类
7.写如下代码进行对比理解 :
/*
 Person p1=new Person("zhangsan");
 Person p2=new Person("lisi");
 Class cla1=Person.class;//字节码1
 Class cla2=Math.class;//字节码2
 Class.forName("java.lang.String");//forName()的作用是返回字节码

 

*/

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

 

 

构造方法的反射应用:

 

Constructor类:
1.Constructor类代表某个类中的一个构造方法
2.得到某个类所有的构造方法:
例子:Constructor[] constructors=Class.forName("java.lang.String").getConstructor();
3.得到某一个构造方法:
例子:Constructor constructor=Class.forName("java.lang.String").getConstructor(StringBuffer.class);
 //获得方法时要用到类型
4.创建实例对象:
   通常方式:String str=new String(new StringBuffer("abc"));
   反射方式:String str=(String)constructor.newInstance(new StringBuffer("abc"));
  //调用获得的方法时要用到上面相同类型的实例对象
5.一个类有多个构造方法,用什么方式可以区分清楚想得到其中的哪个方法呢?根据参数的个数和类型,例如:
Class.getMethod(name.Class…args)中的args参数就代表所要获取的那个方法和各个参数的类型的列表,重点:参数类型
用什么方式表示?用Class实例对象,例如:
int.class,(int[]).class
int[] ints=new int[0];
ints.getClass();
Constructor对象代表一个构造方法,大家觉得Constructor对象上会有什么方法呢?得到名字,得到所属于的类,产生实例对象
6.讲解创建实例对象时,先应该举例说说通常情况下是怎样做的,String str=new String(new StringBuffer("abc"));在源程序中用注释
方式给出传统方式的代码,以便对比提醒程序阅读者。

 


成员变量的反射:

Fild类代表某个类中的一个成员变量
1.演示用eclipse自动生成JAVA类的构造方法
2.问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例
对象有多个,如果是与对象关联,那关联的是如个对象呢?所以字段fiedX代表的是x的定义,而不是具体的x变量
3.示例代码:
ReflectPiont point=new ReflectPoint(1,7);
Field y=Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
System.out.println(point);
//Field x=Class.forName("cn.itcast.corejava.ReflectPoint").getField("x");
Field x=Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
 x.setAccessible(true);
System.out.println(x.get(point));
作业:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改成“a”
public class ReflectPointP{
 private int x;
 public int y;
 public ReflectPoint(int x,int y)
 {
  super();
  this.x=x;
  this.y=y;


 }

 

}

成员变量反射的综合案例:


字节码用==比,
字符串用equals比


private Static void 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);


  }

 

 }

 

}

Method类
1.Method类代表某个类中的一个成员方法
2.得到类中的某一个方法
例子:Method chsrAt=Class.forName("java.lang.String").getMethod("charAt,int.class");
3.调用方法:
 通常方式:System.out.println(str.charAt(1));
 反射方式:System.out.println(charAt.invoke(str,1));
  如果传递给Method对象的invoke()方法的一个参数为null,这有着什么样的意义呢?
说明该Method对象对应的是一个静态方法
4.JDK1.4和JDK1.5的invoke方法的区别:
JDK1.5:public Objectinvoke(Object obj,Object…args)
JDK1.4:public Objectinvoke(Object obj,Object[]args)即按JDK1.4的语法
需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被
调用方法中的一个参数,所以,调用charAt方法的代码也可以用JDK1.4改写为charAt.invoke("str",new Object[]{1})形式
5.大家应通过思考和推理的方式来学习反射中的API,例如:Class.getMethod方法用于得到一个方法 ,该方法要接受什么参数呢?显然
要一个方法名,而一个同名的方法有多个重载形式,用什么方式可以区分清楚想得到重载方法系列中的哪个方法呢?根据参数的个数和类型,
例如:Class.getMethod(name,Class…args)中的args参数就代表所要获取的那个方法的各个参数的类型的列表
再强调一遍参数类型用什么来表示啊?用Class对象
//反射不能读这种非public的类。

 

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

1.目标:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
用普通方式调完后,大家要明白为什么要用反射方式去调啊?

2.问题:启动JAVA程序的main方法的参数是一个字符串数组,即public static void main(String[] args)
通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按JDK1.5的语法
整个数组是一个参数,而按JDK1.4的语法,数组中的每一个元素对应一个参数,当把一个字符串数组作为参数传递
给invoke方法时,JAVAC会到底按照哪种语法进行处理呢?JDK1.5肯定要兼容JDK1.4的语法,会按JDK1.4的语法进行处理
,即把数组打散成为若干个单独的参数,所以在级main方法传递参数时,不能使用代码
mainMethod.invoke(null,new String[]{"xxxx"}),JAVAC只把它当作JDK1.4的语法进行
理解,而不是把它当作JDK1.5的语法解释,因此会出现参数类型不对的问题
3.解决办法:
  mainMethod.invoke(null,newObject[]{new String[]{"xxx"}})
  mainMethod.invoke(null,(Object)new String[]{"xxx"});编译器会作特殊处理,编译时不把参数当作数组看待,也就不会将数组打散也若干个参数了


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


数组的反射:
1.具有相同维数和元素类型的数组属于现一个类型,即具有相同的Class实例对象
2.代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class
3.基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用,非基本类型的一维数组,
既可以当做Object类型使用,又可以当做Object[]类型使用,
4.Arrays.asList()方法处理int[]和String[]时的差异。
5.Array工具类用于完成对数组的反射操作
6.思考题:怎么得到数组的元素类型?
==============================
ArrayList_HashSet的比较及Hashcode分析:


反射的作用---->实现框架功能
框架的概念及用反射技术开发框架的原理:
1.框架与框架要解决的核心问题:
 我做房子卖给用户住,由用户自己安装门窗和空调,我做的的房子就是框架,
用户需要使用我的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用 ,而框架则是
调用用户提供的类
2.框架要解决的核心问题:

(1)我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序 呢?我写的
框架程序怎样调用到你以后写的类(门窗)呢?
(2)因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象
了,而要用反射方式来做
3.综合案例:
先直接用new语句创建ArrayList和HashSet的实例对象,演示用eclipse自动生成
ReflectPoint类的equals和hashCode方法,比较两个集合的运行结果差异

然后改为采用配置文件加反射的方式创建ArrayList和HashSet的实例对象,
比较观察运行结果差异

引入了elipse对资源文件的管理方式的讲解

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

程序中不处理异常,而是main方法声明抛出异常,便于大家可以集中看主要的关键代码

Class类也提供getResourceAsStream方法的比喻:如果你每次都找我给你商店买可乐,
那我还不如直接向你买可乐,即直接提供一个买可乐的方法给你。


=======
小补充:

hashCode方法与HashSet类
    如果想查找一个集合中是否包含有某个对象 ,大概的程序代码怎么写呢?你通常是逐一
取出每个元素与要查找的对象进行比较,当发现某个元素与要查找的对象进行equals方法
比较的结果相等时,则停止继续查找并返回肯定的信息,否则返回否定的信息。如果一个集合中有很多个
元素,比如有一万个元素,并且没有包含要查找的对象时,则意味着你的程序需要从该集合中取出一万个元素进行
逐一比较才能得到结论,有人发明了一种哈希算法来提高从集合中查找元素的效率 ,这种方式将集合分成若干个存储区域
每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象
应该存储在哪个区域。
   HashSet就是采用哈希算法存取对象的集合,它内部采用对某个数字n进行取余的方式对哈希码进行分组和划分对象在存储区域
.Object类中定义了一个hashCode()方法获得该对象的哈希码,然后根据哈希码找到相应的存储区域,最后取出该存储区域内的每个元素
与该对象进行equals()方法比较,这样不用遍历集合中的所有元素就可以得到结论,可见,HahSet集合具有很好的对象检索性能 ,但是
HashSet集合存储对象的效率相对要低些,因为向HashSet集合中添加一个对象时,要先计算出对象的哈希码和根据这个哈希码确定对象在集合中
的存入位置
 为了保证一个类的实例对象能在HashSet正常存储,要求这个类的两个实例对象用equals()方法比较的结果相等时,它们的哈希码也必须
相等。也就是说,如果obj1.equals(obj2)方法比较的结果为true,那么以下表达式的结果也要为true
obj1.hashCod()==obj2.hashCode()
 如果一个类的hashCode()方法没有遵循上述要求,那么,当这个类的两个实例对象用equals()方法比较的结果相等时,它们本来应该无法被
同时存储进Set类集合中,但是,如果将它们存储进HashSet集合中时,由于它们的HahsCode()方法的返回值不同,第二个对象首先按哈希码计算可能会被放
进与第一个对象不同的区域中,这样,它就不可能与第一个对象进行equals方法比较了,也就可能被存储进HashSet集合中了,Object类中的hashCode()方法不能满足对象
被存入到HashSet中的要求,国为它的返回值是通过对象的内存地址推算出来的,同一个对象在程序运行期间的任何时候返回的哈希值都是不变的,所以,只要是两个不同的实例对象
,即使它们的equals方法比较结果相等,它们默认的hashCode()方法的返回值是不同的。
 只有类的实例对象要被采用哈希算法进行存储和检索时,这个类才需要按要求覆盖hashCode方法,即使程序可能暂时不会用到当前类的hashCode方法,但是为它提供一个hashCode
方法也不会有什么不好,没准以后什么时候又用到这个方法了,所以通常要求hashCode方法和equals方法一并被同时覆盖
 提示

(1)通常来说,一个类的两个实例对象用equals()方法比较的结果相等时,它们的哈希码也必须相等,但么之则不成立即equals方法比较结果不相等的对象可以有相同的哈希码,或者说哈希码相同的两个对象的equals方法比较的结果可以不相等但它们的hashCode方法返回值却相等
(2)当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算
哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为参数去HashSet集合中检索对象,
也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值