反射与内省


3、反射;java.lang.reflect;


就是把java类中的各成员映射成对应的类的实例对象,然后再进行操作;
使用反射的前提就是所反射的目标对象开始并没有;是后来出现的;即目的就是为了调用后来出现的类等;


Class类,描述类(类型)这种事物的一个类;具体的实体对象就是某一种类型,即其具体的对象是对应的类的字节码文件;表示为***.class;

反射的使用,会使的程序性能降低,因为要缓存很多的成员,以便备用,而单独一个类使用不需要这么多缓存;反射,他是可以一个类,搞定无数个类;


(1)获取Class实例对象;  throw ClassNotFoundException

获取某个类的字节码对象有两种方法;
Object的方法 :getClass();
Class的静态方法:Class.forName("类的完整全名字符串");
第二种方法的作用:获取类的字节码对象,如果没有加载,则先加载类文件,再返回字节码对象;
还有一种方法就是直接写出来;
Class<? super T> getSuperclass()  获取父类类字节码对象;

九个预定义的Class的实例对象:
八个基本数据类型和void的字节码对象;描述的是对应的类的Class对象的实例化;

获取:字节码对象对应的类名的字符串表示形式:
getName();
int getModifiers() 
toString();获取字节码对象的字符串表示形式;有特有固定的格式;

判断:
isPrimitive(),是否是基本类型;
包装类中定一个常量TYPE,定义的是包装类型的基本类型的字节码对象;
int.class==Integer.TYPE // true;  void.class == void.TYPE;
int[].class.isArray();
任何数据类型都有对应的字节码对象;
isEnum();

(2)获取构造函数并用构造函数新建实例对象;

步骤:获取字节码文件:getClass()/Class.forname(String classname) ——> 获取构造函数 :getConstructor()/getConstructors()(获取所有的构造函数的数组)
——> T newInstance(Object... initargs) ;
 
Type[] getGenericParameterTypes()  / Class<?>[] getParameterTypes() 获取构造方法中的参数类型; 

当然也可以通过Class类当中的方法来新建实例;T newInstance()    获得无参的实例对象;     throw  illegalAccessExceptio ,instantiationException
底层也是调用的上面的方法;只是简化了中间的过程;
getName();

(3)成员属性字段Field的反射;

获取类型的实例对象  ——> 获取字段:getField() / getFields()——> Object get(Object obj) ;
getDeclaredFields();


String getName()  
Class<?> getDeclaringClass() // 和getClass()效果一样;  可获取私有化的内部类;
  Class<?> getType() 
int getModifiers()  

【常用方法】
Object get(Object obj)  
void set(Object obj, Object value) // 给成员属性重新赋值;

还可以具体类型设置具体类型的值;
  void setChar(Object obj, char c) 
  void setInt(Object obj, int i)  
  没有设置字符串的;

【重点】
暴力反射;
Field getDeclaredField(String name) 
Field[] getDeclaredFields()  
void setAccessible(boolean flag) // 父类方法,强制可视;然后再启用set方法就可以修改赋值;父类java.lang.reflect.AccessibleObject


  (4)成员方法的反射:   throw  NoSuchMethodException
  getMethod();——> Object invoke(Object obj, Object... args)  ;
  getMethods();getDeclaredMethod();
  invoke(Object,Object...)
 
  【易忽略】
  invoke(null,Object...args);指的是调用静态方法;
 
  【注意】
  因为在1.5版本之前,多个同类型参数都是用数组表示,这里有可能调用的方法的某参数类型就是数组类型,如主函数,所以jvm容易判错;
  故需要做一点处理;一般处理方式有二:
  对所传数组外面再加一层数组,让所传数组成为一个元素;
  第二种就是直接在数组外面用Object进行强转,告诉java这就只是一个Object对象;无需拆装;
   
  【注意】,Class类中的getMethod(Class),以及getConstructor(Class)方法中的参数都是Class对象;
 
  (5)数组的反射:
 
  【易错】
  具有相同元素类型和相同数组维度的数组为同一类型,即对应的class字节码对象是同一个;
 
  【易错】
数组的父类是Object,基本数据类型没有父类,即他们是没有父类的;
int[] a = new int[3];
Object obj = a ;正确;
Object [] ob= a; 错误; 这是为什么Arrays.asList(T...)不能用int【】传值的原因;结果会不同;

Arrays中的asList(),按照1.4版本的,往里面传入类类型的数组即可将数组转换为集合;
而1.5版本可以直接输入同一种类型的多个数据值即可;当然1.5是兼容1.4版本的;
在这里分析研究Arrays.asList()方法处理int[]和String[]时的差异,
以及Arrays.deepToString()方法不能处理int[],但能处理String[]的原因。


java.lang.reflect.Array;
static Object get(Object array, int index)  获取指定脚标的元素;
static int getLength(Object array)  获取数组长度;
 
  附:
在hash表数据结构的集合中,要实现equals()方法返回为true,就必须要先保证hashCode()返回值必须相等;
  因为hash表数据结构的集合中,为了优化程序性能,通常按照hashCode()返回的值来将数据分为不同的区域,这样操作数据效率更高;这就是hash数据结构的特点,也是hashCode()方法使用比较重要的地方;
  但是在这种情况下要注意的是:对于根据hashCode()方法来比较元素时,不要随意的更改参与hashCode()方法的变量值,因为随意更改后,对应的对象就无法进行删除操作,就会造成内存泄漏现象;
  所谓内存泄漏,是指这个对象或者底层资源不需要用了,但是一直在占用内存空间,没有被释放;

(6)反射的作用  ———— 实现框架功能; 
在一个类没有出现,就已经把它调用起来了,这就是框架;
还有一个概念就是工具;调用别的类就是调用的工具;

(7)配置文件加载的两种方式: 
(i)通过用户选择路径然后获取绝对路径进行配置即可;
(ii)类加载器可以加载字节码文件,同时也可以加载配置文件;
Class类的ClassLoader getClassLoader()  获取类加载器;
java.lang.ClassLoader:
InputStream getResourceAsStream(String name)  读取指定的配置文件(含绝对路径)(只读)
任何框架都要加载配置文件,而配置文件都是放在classpath目录下; 

Class类当中有进行简化了上面的步骤:底层也是上面的步骤
InputStream getResourceAsStream(String name)  
// 这里使用相对路径和绝对路径都可以;
如果在路径前面打/就表示相对于classpath的根目录,否则就是相当当前目录;


4、内省     Introspector  
java.beans.Introspector


(1)javabean
javabean是一个特殊的java类,如果一个java类中含有getter方法和setter方法就可以当成javabean来使用;

一般的,如果某一个java类不当成javabean来操作,我们可以通过反射来进行调用其类中的属性和方法,但是这样会显得比较复杂,
所以一般对于一些特殊的类,我们就把他们定义为一个模型即javabean,然后使用特定的方法对这些类来进行操作,这样操作起来就变得相对简单很多;
也就是说,对于javabean的操作方法如内省就是有针对性的反射操作方法;即针对于特定规范的类操作的封装了很多反射操作的工具集;即内省就是一种特定的反射操作;

【来龙去脉】
(一般的只要操作起来够复杂而且用起来比较广泛的的操作,java一般都会将其进行封装,使操作起来变得更加简单;这才是内省的真正的来龙去脉)
封装的特定的操作对象就是javabean,在java中用BeanInfo进行描述;封装的操作方法就是Introspector;

【细节易错处】
如何定义一个javabean;应如下定义;
class Person {
private int x;  // 这只是一个成员字段,不能说是javabean的属性,因为这个要是私有的,故应定义为其他标识符;
public int getAge(){ return x;}
public void setAge(int age){this.x = age;}}
定义原则:成员字段应设为私有,getter和setter方法要设为公有;

注意javabean的属性是根据方法名称来的,里面自定义的成员字段是私有的,看不见的,不能称为javabean的属性;
而如果要当成java类时,属性就是要声明为age,而不是字段x;

【易忽略处】
因为javabean的属性是根据getter和setter获取的,所以命名属性时要注意,
如果第二个字母是小写,那么第一个字母就小写;

(2)javabean的内省操作;


BeanInfo接口是对javabean的描述接口;
Introspector类描述的是为通过工具学习有关受目标 Java Bean 支持的属性、事件和方法的知识提供了一个标准方法;
对于这三种信息,Introspector 将分别分析 bean 的类和超类,寻找显式或隐式信息,使用这些信息构建一个全面描述目标 bean 的 BeanInfo 对象;
简单的说,Introspector就是描述操作javabean的工具类;

类 PropertyDescriptor 描述的是javabean从getter和setter中得到的属性的特征的类;
MethodDescriptor 描述了一种特殊方法,即 Java Bean 支持从其他组件对其进行外部访问。 

javabean的主要操作思路就是获取属性描述器,然后通过属性描述器调用对应的getter和setter;

(i)javabean的简单操作;获取属性和设置属性;
步骤: 
定义一个属性描述器对象:
PropertyDescriptor(String propertyName, Class<?> beanClass);
用该属性描述器获取getter和setter : 
  getReadMethod(Object obj,String propertyName); 
  getWriteMethod(Object obj, String propertyName, Object value);
 
  (ii)javabean的复杂操作,通过内省操作;
  步骤:
  将javabean类变为BeanInfo接口的子类对象;Introspector.getBeanInfo(Class) ;
  通过BeanInfo 获取所有的属性描述器;   每一个属性对应一个属性描述器;
  这里需要调用父类的方法 getName() 获取属性的字符串名称   java.beans.FeatureDescriptor
 
  【注意:】
  需要注意一点的是,在所有的javabean操作的方法中,属性都是用字符串来表示的;  因为真正属性是不知道的,都是私有的;

【重点掌握】
(iii)使用Beanutils工具包操作javabean;
该工具包中的基本都是静态的方法;
主要使用该工具包中的BeanUtils和PropertyUtils两个类对javabean进行操作;
【关键注意】
所反射的javabean类必须要是public修饰的才行;

BeanUtils类中的方法:
BeanUtils.getProperty(Object,String); 返回值为String;
BeanUtils.setProperty(Object obj,String propertyName,String value);
【注意】
这两个方法设置的值和获取的值的类型都是String形式;在web开发中不需要再进行转换;

BeanUtils中的方法支持属性的级联操作,即可以操作属性的属性值可以很多层次的属性值;
如果用反射来操作将会非常的复杂;

BeanUtils的其他方法;
static void copyProperties(Object dest, Object orig)  将一个对象的属性拷贝到另外一个对象上;
static void copyProperty(Object bean, String name, Object value)

将javabean的属性转换成Map集合;用的也比较多;
static Map<String,String>describe(Object bean)

将Map集合转换成javabean;
static void populate(Object bean, Map<String,? extends Object> properties)

BeanUtils还可以直接操作Map集合的键值对;因为map和javabean很类似;
Map map = {name:"zhangsan",age:20};  //  java7的新特性;
BeanUtils.setProperty(map,"name","lisi");

PropertyUtils类当中的方法;
static Object getProperty(Object bean, String name);
static void setProperty(Object bean, String name, Object value);

【重点】
BeanUtils和PropertyUtils中的获取属性和设置属性的区别在于:
BeanUtils返回的类型和设置值的类型都是字符串类型;
PropertyUtils返回的类型和设置值的类型都是字符串本身的类型;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值