黑马程序员—Java中的反射机制

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

 

1.什么是反射机制?  

        简单的来说,反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息。

 

反射机制的优点与缺点:  

        为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念,  

       静态编译:在编译时确定类型,绑定对象,即通过。  

       动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。  

       一句话,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发。

它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。

 

Class类和Class类实例

 

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

对比提问:众多的人用一个什么类表示?众多的Java类用一个什么类表示?

人  Person

Java类 Class

对比提问: Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,Class类代表Java类,它的各个实例对象又分别对应什么呢?

对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等;

一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的;

用类来描述对象,类:描述数据的结构

用元数据来描述Class,MetaData(元数据):描述数据结构的结构;

反射就是得到元数据的行为;

 

 

备注:一个类在虚拟机中只有一份字节码;

 

2、获得Class对象

 

如何得到各个字节码对应的实例对象?

每个类被加载后,系统会为该类生成对应的Class对象,通过Class对象可以访问到JVM中的这个类,

3种方式:

1、调用某个类的class属性获取Class对象,如Date.class会返回Date类对应的Class对象(其实就是得到一个类的一份字节码文件);

2、使用Class类的forName(StringclassName)静态方法,className表示全限定名;如String的全限定名:java.lang.String;

3、调用某个对象的getClass()方法。该方法属于Object类;

Class<?> clz = new Date().getClass();

 

 

3、九个预定义Class对象

 

基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void通过class属性也表示为 Class 对象;

Class类中boolean isPrimitive() :判定指定的 Class 对象是否表示一个基本类型。

包装类和Void类的静态TYPE字段;

Integer.TYPE == int.class ;         

Integer.class == int.class;             

 数组类型的Class实例对象:

Class<String[]> clz = String[].class;

数组的Class对象如何比较是否相等? 数组的维数和数组的类型;

Class类中 boolean isArray() :判定此 Class 对象是否表示一个数组类型。

 

public class PreClassDemo2 {
       public static voidmain(String[] args) {
             
              Class<?>in = int.class;
              System.out.println(in);//int
              Class<?>in2 = Integer.class;
              //包装类都有一个常量TYPE,用来表示其基本数据类型的字节码
              Class<?>in3 = Integer.TYPE;
             
              System.out.println(in2);//class java.lang.Integer
              System.out.println(in3);//int
              System.out.println(in3 == in);//true 包装类都有一个常量TYPE,用来表示其基本数据类型的字节码,所以这里会相等!
              System.out.println(in3 == in2);//false
              Class<String[]>s = String [].class;
              Class<int[]> i = int [].class;
              //System.out.println(i==s);//编译根本就通过不了,一个是int,一个是String
       }
       //这两个自定义的方法是可以的,一个int,一个Integer//包装类与基本数据类型的字节码是不一样的
       public void show(int i){}
       public void show(Integer i){}
}
 
4、利用Class获取类的属性信息
 
package junereflect624;
 
import java.lang.reflect.Modifier;
 
class A { 
}
interface B{
}
interface C{
}
 
public class BaseDemo3 extends A implements B,C{
      
       //内部类
       public class C{}
       public interface D{}
       public static voidmain(String[] args) {
              //类可以,接口也可以
              Class<BaseDemo3>c = BaseDemo3.class;
              System.out.println(c);//classjunereflect624.BaseDemo3
 
              //得到包名
              System.out.println(c.getPackage());//packagejunereflect624
 
              //得到全限定名
              System.out.println(c.getName());//junereflect624.BaseDemo3
             
              //得到类的简称
              System.out.println(c.getSimpleName());//BaseDemo3
             
              //得到父类
              /**
               *Class<? super T> getSuperclass() 此处super表示下限
                    返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。
               */
              System.out.println(c.getSuperclass().getSimpleName());//A,先获取父类,再获取父类的简称
             
              //得到接口
              System.out.println(c.getInterfaces());//[Ljava.lang.Class;@1b60280
              Class[]arr = c.getInterfaces();
              for (Class cla : arr) {
                     System.out.println(cla);//interfacejunereflect624.B   interfacejunereflect624.C
              }
             
              //获得public修饰的类
              /**
               * Class<?>[] getClasses()
                               返回一个包含某些 Class 对象的数组,这些对象表示属于此 Class 对象所表示的类的成员的所有公共类和接口。 (如果内部类前面没有加上public的话那么得不到!)
               */
              Class[]cl = c.getClasses();
              System.out.println(cl.length);//在内部类没有加上public修饰的时候长度为0,加上就是2(获取的是公共的)
              for (Class class1 : cl) {
                     System.out.println(class1);
              }
             
              //获得修饰符
              int i = c.getModifiers();
              System.out.println(i);//常量值1表示public
              System.out.println(Modifier.toString(i));//直接打印出public
       }
}
 


5利用反射创建对象

 

创建对象:

1、使用Class对象的newInstance()方法创建该Class对象的实例,此时该Class对象必须要有无参数的构造方法

2、使用Class对象获取指定的Constructor对象,再调用Constructor的newInstance()方法创建对象类的实例,此时可以选择使用某个构造方法。如果这个构造方法被私有化起来,那么必须先申请访问,将可以访问设置为true;

 

复杂点的:更强大的第二种:

 

使用指定构造方法来创建对象:

获取该类的Class对象。

利用Class对象的getConstructor()方法来获取指定的构造方法。

调用Constructor的newInstance()方法创建对象。

 

AccessibleObject对象的setAccessible(boolean flag)方法,当flag为true的时候,就会忽略访问权限(可访问私有的成员)。

其子类有Field,Method, Constructor;

若要访问对象private的成员?

在调用之前使用setAccessible(true),

       Xxxx = getDeclaredXxxx();//才能得到私有的类字段.

 

总结步骤:

 

1.        获取该类的Class对象。

2.        利用Class对象的getConstructor()方法来获取指定的构造方法。

3.        申请访问(设置为可访问)

4.        调用Constructor(构造方法)的newInstance()方法创建对象

 

6使用反射调用方法

 

每个Method的对象对应一个具体的底层方法。获得Method对象后,程序可以使用Method里面的invoke方法来执行该底层方法。

Object invoke(Object obj,Object ...args):obj表示调用底层方法的对象,后面的args表示传递的实际参数。

如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为null,想想为什么?

如果底层方法所需的形参个数为0,则所提供的 args 数组长度可以为 0 或 null。

不写,null,或 new Object[]{}

若底层方法返回的是数组类型,invoke方法返回的不是底层方法的值,而是底层方法的返回类型;

 

 

 

7使用反射调用可变参数方法

 

使用反射操作对象-调用可变参数方法

 

要把可变参数都当做是其对应的数组类型参数;

如 show(XX...is)作为show(XX[]is)调用;

 

若可变参数元素类型是引用类型:

JDK内部接收到参数之后,会自动拆包取出参数再分配给该底层方法,为此我们需要把这个数组实参先包装成一个Object对象或把实际参数作为一个Object一维数组的元素再传递。

 

若可变参数元素类型是基本类型:

JDK内部接收到参数之后,不会拆包,所以可以不必再封装.不过封装了也不会错.所以建议,不管基本类型还是引用类型都使用Object[]封装一层,保证无误.


8使用反射操作字段

 

Field提供两组方法操作字段:

xxx getXxx(Object obj):获取obj对象该Field的字段值,此处的xxx表示8个基本数据类型。若该字段的类型是引用数据类型则使用,Object get(Object obj);

void setXxx(Object obj,xxx val):将obj对象的该Field字段设置成val值,此处的xxx表示8个基本数据类型。若该字段的类型是引用数据类型则使用,void set(Object obj, Object value);

 

package junereflect624;

//获取字符,并且赋值,然后再取出来(对应的去查看api,比如这个是Field,别的比如Constructor,Method)

步骤:

1.获取类

2.获取字段

3.赋值(set(c.newInstance(),””));{如果为私有的话设置可接受}

 

9反射和泛型-反射来获取泛型信息

 

通过指定对应的Class对象,程序可以获得该类里面所有的Field,不管该Field使用private 方法public。获得Field对象后都可以使用getType()来获取其类型。

Class<?> type = f.getType();//获得字段的类型

但此方法只对普通Field有效,若该Field有泛型修饰,则不能准确得到该Field的泛型参数,如Map<String,Integer>;

为了获得指定Field的泛型类型,我们采用:

Type gType = f.getGenericType();得到泛型类型

然后将Type对象强转为ParameterizedType,其表示增加泛型后的类型

Type getRawType()//返回被泛型限制的类型;

Type[] getActualTypeArguments()//返回泛型参数类型;

 

利用反射来获取泛型的类型(泛型信息)

步骤:

1.获取当前类

2.获取目标字段

3.获取包含泛型类型的类型 getGenericType()

4.强转至子类ParameterizedType 因为Type没有任何对应的方法

5.获得泛型真正的类型 getActualTypeArguments()

 

 

 

 

------- android培训java培训、期待与您交流! ----------详细请查看:http://edu.csdn.net

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值