您的位置:首页 > 编程语言 > Java开发

JAVA技术发展——你不知道的J2SE(二)

2016-04-28 10:50 405 查看
Reflection-反射是J2SE1.1就已经提出了,但当时仅支持Introspection自省。在1.2之后开始逐渐成熟,spirng、hibernate等成熟框架都大量使用java反射技术实现。
在面向对象设计思想中,使用类这一概念表述一类具有相同属性的对象;而这些属性值具体是什么,由该类的每个实例化对象来确定,每个对象可以有不同的属性值。反之,这些是否属于同一类事务,java使用Class来描述类的访问属性、包名、字段名。学习反射,首先就要了解这个首字母大写的-Class
[b]一、Class—字节码[/b]
在源程序中,实例化某个类时,先将类编译成.class形成二进制代码,然后把二进制代码加载到java内存中,用于创建对象。这个二进制代码就是Class类的字节码(Byte-code)。当使用到多个类时,内存中就会加载多个不同的字节码,占用内存空间。字节码跟类是对应的,不同字节码在jvm中的内容以类的类型区分。
有了对象实例,通过obj.getClass()方法也可以获得这个对象在内存中的字节码,得到字节码方可得到这个对象所对应的类。反之,知道了这个类也可以获得实例对象。
[b]二、一个New并不能解决问题[/b]
那么问题来了,有些人可能会说,干嘛那么麻烦都个大圈,我直接new
一个类,不就可以轻松创建这个实例吗?
这就涉及到java对象创建的两个编译方式:静态编译和动态编译
静态编译:在编译时确定对象,绑定对象即可通过编译。

动态编译:运行时确定类型,绑定对象。
静态编译使用new关键字就可以获取创建对象属性方法,但在动态编译时就行不通了。反射正是实现了动态创建对象和编译。通过反射,传入类名,形成字节码,加载到jvm中创建实例,然后获取实例信息,最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
简言之,在程序还不知道要调用哪些类,创建哪些实例时,代码是无法将某个具体的类以new的方式实例化的。正如你还没结婚,没办法先给自己未来的小孩买各种女式婴儿用品,万一他是个男孩儿呢?使用反射的目的即在于:传入一个无论是什么类,通过反射都可一获取到这个类对应对象的属性、方法等信息。
联想一下Spring 等框架,框架设计者并不知道我们会创建哪些类,那为什么把bean网配置文件里一放,class属性一写,它就可以直接使用这个类的可用属性和公共方法了呢。这就是典型的反射应用。
[b]三、反射具体怎么用?[/b]
通过反射,只要知道类名,就可以获取该类方法、属性、构造方法等信息,然后通过java
API对这些方法进行操作,对属性赋值等。下面介绍几种常用的利用反射机制实现获取类信息类型;

构造方法(Constructor)

1)、获取类的构造方法 Class.forName(“java.lang.String”).getConstructors();//得到String类的所有构造方法

Class.forName(“java.lang.String”).getConstructor(“StringBuffer.class”);//得到某一个类的构造方法

2)、使用构造方法创建对象实例:

通常:String str=new String(new StringBuffer(“aaa”))

反射:Constructor
c=Class.forName("java.lang.String").getDeclaredConstructor(StringBuffer.class);//获取StringBuffer类的方法构造器

String s=(String)c.newInstance(new StringBuffer("adf"));//创建名为adf的StringBuffer对象实例,强转成String类型输出

System.out.println(s.toString()); //adf

获取变量(Field)

例如创建一个坐标类Point,有xy两个成员变量。

1)、使用反射获取类的成员变量

System.out.println(Class.forName("java.lang.reflect.Array").getDeclaredFields()); //获取所有声明过的成员变量

System.out.println(Class.forName("java.lang.reflect.Array").getFields());//无法获取private 私有成员变量

2)、通过set、get方法对该对象属性进行数据操作

Field day=Class.forName("it.webservice.mobile.WeekDay").getField("day");//noSuchFieldEcxeption

day.setAccessible(true);

System.out.println(day.get(day));

使用反射获取变量在框架中使用频繁,以Spring为例,在Spring配置文件中写入类的包名.类名,添加property属性,程序启动时自动扫描配置文件,把类实例化,同时反射获取property属性信息就知道这个类中声明了多少个字段,再通过javaBean自省get/set的方式为对象取值赋值。使用非常广泛。

获取方法(Method)

1)、获取方法

Class.forName("java.lang.reflect.Array").getMethods();//返回一个method[]数组

2)、可以使用invoke方法调用该方法

Method[] methods=Class.forName("java.lang.reflect.Array").getMethods();

3)、某个类main方法的获取与调用

System.out.println(Class.forName("it.webservice.mobile.WeekDay").getMethod("main", String[].class));//方法名,方法传入参数类型

Class.forName("it.webservice.mobile.WeekDay").getMethod("main", String[].class).invoke(null, (Object)new String[]{"jdkaj"});

注意在获取类的main方法时,main方法的参数类型为String集合,在调用main方法时,注意第一个参数为null表示main的静态方法属性;第二个参数传入一个string数组,并强转成一个Object对象类型。因为如果传入普通数组,使用反射会把数组打散成若干个参数,所以转成一个Object对象传入invoke使得main传入正确参数。对于数组的反射应用会在下一篇博文中谈到。

四、反射在框架中的具体应用

在具体框架中一般都会结合配置文件的使用,把可变的传入类类型以配置文件配置的形式进行配置。简单的代码实现流程如下:

public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException {
InputStream input=new FileInputStream("config.properties");//加载config配置文件
Properties prop=new Properties();
prop.load(input);
input.close();
String className=prop.getProperty("className");//获取配置文件中className值:java.util.ArrayList
Collection cl=(Collection)Class.forName(className).newInstance();//实例化ArrayList

System.out.println(cl.toString());
}


通过查看Spring mvc 框架的部分源码,很容易发现这种加载配置文件-利用反射将类实例化的影子。例如在Spring
MVC核心过滤器 dispatcher中,通过三级继承,父类实现ApplicationContextAware接口,而这个接口仅提供了一个方法

public interface ApplicationContextAware extends Aware {

/**
* Set the ApplicationContext that this object runs in.
* Normally this call will be used to initialize the object.
* <p>Invoked after population of normal bean properties but before an init callback such
* as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
* or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
* {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
* {@link MessageSourceAware}, if applicable.
* @param applicationContext the ApplicationContext object to be used by this object
* @throws ApplicationContextException in case of context initialization errors
* @throws BeansException if thrown by application context methods
* @see org.springframework.beans.factory.BeanInitializationException
*/
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}


从这个方法漫长的注释不难看出,这个接口主要用于提供配置文件初始化时,调用初始化对象时使用的,也就是说,传入applicationContext参数,底层肯定跟上面代码的逻辑八九不离十,加载配置,同时利用反射初始化配置中的normal Bean 和properties。

一通百通,搞懂这些原理,可能确实在平时的编程中会相对比较少用到反射、委托等,但做平台、做框架的毋庸置疑,百分之百会使用到这些技术提高灵活扩展性。因为目前大多都是在用框架,毕竟金字塔顶端的也只是那么少部分。但如果遇到系统瓶颈了,考虑使用这些核心技术来解决问题的那一天就不远了。视野决定高度。

下一篇会介绍数组的反射应用,另外侧重介绍ArrayList 和HashSet在存储和使用上的区别与联系。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: