黑马程序员-java 高新技术 反射机制

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------ 

 

对于反射的理解,从以前开始学习编程到工作,这么长的时间,通过平时的总结外加这次的学习,这终于算是理清了思路,首先我先说下为什么要使用反射?

       java中,我们通常要是使用一个类里面的方法、属性和构造函数的时候,首先思考的就是要获取这个类。通常的方法是:只要知道是那个类,只要获取它的对象就可以,对它内容进行操作了。问题就出来了,你要是不知道这个类或者不确定是那个类呢?那么你该怎么处理?或者你修改别人的架包里的某个私有成员变量的初始值等等,一些关于不确定某个类的内容时。你该怎么办?

       针对这些问题,Java提供了一种反射机制,那解决上面的问题。

       什么是反射机制呢?

              就是对于任意类都可以调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能,简单的来说:就是将Java类中的各种成分映射成相应的java类,然后通过映射效果进行操作。

       注意:反射机制破坏了java的封装机制,并且使用的时候,效率很低,不适合广泛使用。

 

       其实呢,反射就是通过操作的Class类来达到映射效果。

       Class类:Java程序中的各个java类属于同一类事物,描述这类事物的java类名就是ClassClass类描述了类的名字、访问属性、类所属的包名、字段名称的列表和方法名称的列表等信息。

       当一个类被类加载器加载到内存中,会开辟一处内存空间,用来存储这个类的字节码文件信息,也就是Class类的实例对象。

那么,怎么获取各个字节码对应的实例对象(Class类型)?

 

Java提供了三种方法,如下:

.类名.class

例如:Class strClass=String.class;

.对象.getClass()

例如:Class cls=new String().getClass();

.Class.forName(“类字符串信息”);

              例如:Class.forName("java.util.Date");

              Class.forName()的作用:

返回对应类的字节码文件信息,并且返回的时候,有两种情况:

1.这个类的字节码信息曾经被加载过,已经在java虚拟机中,直接返回。

2.虚拟机中没有这个类的字节码信息,用类加载器加载。加载进的字节码信息缓存在虚拟机中,再返回。

Java反射中,九个预定义类型的实例对象(Class类型)由基本数据类型(bytecharshortintlongfloatdoubleboolean)和关键字void组成,其中需要注意的是基本类型的字节码获取方式只有一种就是类名.class。例如int.classvoid.class

publicclass ReflectTest {

    //先抛出异常,不做处理

    publicstaticvoid main(String[] args) throws ClassNotFoundException{

       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

       

       //isPrimitive()判读是不是基本类型

       System.out.println(cls1.isPrimitive()); // false

       System.out.println(int.class.isPrimitive()); // true

       System.out.println(int.class == Integer.class); // false

       //Integer.TYPE 获取它的数据类型

       System.out.println(int.class == Integer.TYPE); // true

 

       System.out.println(int[].class.isPrimitive()); // false

       //isArray() 判断是不是数组

       System.out.println(int[].class.isArray()); // true

    }

} 

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

一、通过反射获取类的构造函数

A.      得到某个类的指定(public)构造函数Connstructor<T> getConstructor(Class<?>... parameter types);

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

B.      得到某个类的(public)所有构造函数Connstructor<T>[]   getConstructors();

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

 

这里需要注意的是:A和B都只能获取非private的构造函数

那么要是获取private的构造函数,我们可以是使用getDeclaredConstructor或getDeclaredConstructosr,这两方法和构造器的访问级别无关,可以直接访问。

1. Connstructor<T> getDeclaredConstructor(Class<?>... parameter types); //返回此Class对象所表示的类的指定方法,与方法的访问级别无关。

2. Connstructor<T>[] getDeclaredConstructosr();//返回此Class对象所表示的类的全部方法,与方法的访问级别无关。

 

那么,怎么创建反射实例化对象呢?

Java中提供了两种方式:

(1)、使用Class对象的newInstance()方法来创建该Class对象对应类的实例,这种方式要求该Class对象的对应类有默认的构造函数,而执行newInstance()方法时,实际上是利用默认的构造函数来创建类的实例。

 

获取指定类的默认构造函数,步骤:

(1)获取该类的Class对象。

(2) Class对象的newInstance()方法来创建该类的实例化对象。

例如:

// 创建对象的方法

public Object createObject(String _className) throws InstantiationException, IllegalAccessException,  

            ClassNotFoundException {  

    // 根据字符串来获取对应的Class对象

    Class<?> _class = Class.forName(_className);

    // 使用Class对象的默认构造函数创建实例

    return _class.newInstance();  

}

(2)、先使用Class对象获取指定的Constructor对象,在调用Constructor对象的newInstance()方法来创建该Class对象对应的实例。通过这种方式可以选择使用某个类的指定构造函数来创建实例。

获取指定构造方法,步骤:

(1)获取该类的Class对象。

(2)利用Class对象的对应的getConstructor()或者getDeclaredConstructor()方法来获取指定的构造函数。

(3)调用Constructor的newInstance方法来创建该类的实例化对象。

例如:

/*

       _className:类对应的字符串信息

    _types:构造函数的参数类型

    _obj:构造函数的参数值

*/

public Object createObject(String _className, Class<?> _types, Object _obj) throws Exception {

    // 根据字符串来获取对应的Class对象

    Class<?> _class = Class.forName(_className);

    //获取Constructor类

    Constructor _ctor = _class.getConstructor(_types);

    // 使用Constructor对象的newInstance创建实例化对象

    return _ctor.newInstance(_obj);

}
二、通过反射获取类的属性

A.获取Class对象所表示的类的指定的public属性 Field getField(String name)

如:

//obj为当前对象, strName为指定的属性名称
Field[] fields = obj.getClass().getField(_strName);

B.获取Class对象所表示的类的所有public属性Field[] getFields()

如:

//obj为当前对象       

Field[] fields = obj.getClass().getFields();

这里需要注意的是:A和B都只能获取非private的属性

那么要是获取private属性,我们可以使用getDeclaredField和getDeclaredFields,这两方法和属性的访问级别无关,可以直接访问。

(1)Field getDeclaredField(String name):返回该Class对象所表示的类的指定属性,与属性的访问级别无关。

(2)Field[]   getDeclaredFields():返回该Class对象所表示的类的全部属性,与属性的访问级别无关。

实例如下:

publicclass reflectPoint {

    publicintx;

    privateinty;

    public reflectPoint(int x, int y) {

       this.x = x;

       this.y = y;

    }

}

 

publicclass reflect1 {

    publicstaticvoid main(String[] args) throws Exception {

       reflectPoint pt1 = new reflectPoint(4, 7);

       // 通过getField()方法,获取公共的字段

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

       System.out.println(fieldX.get(pt1));

       // 通过getDeclaredField(()方法,可以无视权限访问,就可以访问private属性

       Field fieldY = pt1.getClass().getDeclaredField("y");

       fieldY.setAccessible(true);// 对私有字段取消访问权限,暴力访问。

       System.out.println(fieldY.get(pt1));

    }

}
拓展知识点:

要是获取Field数据类型,我们可以使用Class<?> a=field.getType(); 进行获取,但是我要是获取像Map<String,Integer>这种泛型的参数列表里面的数据类型时,该怎么获取呢?

java.lang.reflect包里面提供了对应的类,进行解决上面的问题,实现步骤如下:

1、 使用getGenericType(),获得Field实例对象的泛型类型

2、 ParameterizedType类提供了getActualTypeArguments(),可以获取泛型类型的所有泛型参数的参数类型。

实例:

import java.lang.reflect.Field;

import java.lang.reflect.ParameterizedType;

import java.lang.reflect.Type;

import java.util.Map;

 

publicclass Test {

    private Map<String, Integer> map;

    publicstaticvoid main(String[] args) throws Exception {

       //获取map对应的Field对象

       Field field = (Test.class).getDeclaredField("map");

       //获取Field对象的泛型类型

       Type type = field.getGenericType();

       ParameterizedType pType = (ParameterizedType) type;

       Type[] tArgs = pType.getActualTypeArguments();

       for (int i = 0; i < tArgs.length; i++) {

           System.out.println("第"+i+"个参数数据类型>>>" + tArgs[i]);

       }

    }

}

打印结果是:

0个参数类型>>>class java.lang.String

1个参数类型>>>class java.lang.Integer

三、通过反射获取类的方法

A. 获取Class对象所表示的类的指定public方法 Metnod getMethod(String name,Class<?>... parameter types)

如:Method methodCharAt = String.class.getMethod("startsWith", boolean.class);

B.获取Class对象所表示的类的所有public方法 Method[] getMethods()

如:Method[] methods = Class.forName("java.lang.String").gerMethods();

这里需要注意的是:A和B都只能获取非private的方法

那么要是获取private方法,我们可以使用getDeclaredMethod和getDeclaredMethods,这两方法,这里和方法的访问级别无关,可以直接访问。

(1) Method getDeclaredMethod(String name,Class<?>... parameter types);返回此Class对象所表示的类的指定方法,与方法的访问级别无关

(2) Method[] getDeclaredMethods();返回此Class对象所表示的类的全部方法,与方法的访问级别无关

Jdk1.4jdk1.5invoke方法的区别

       JDK1.5public Object invoke(Object obj , Object...args);     //可变参数

       JDK1.4public Object invoke(Object obj , Object[] args);    //数组作为参数传递,数组中的元素和参数列表一一对应。

实例:

String str = "abc";    

Method methodcharAt = String.class.getMethod("charAt", int.class);

//在jdk1.5

methodcharAt.invoke(str,1);//b

//在jdk1.4   

//methodcharAt.invoke(str,new Object[]{2});//c 

注意:如果Method对象的invoke方法,传入的对象为null,说明Method对象对应的是一个静态方法。

 
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值