1.JAVA反射机制
JAVA有一个非常突出的动态相关机制:反射(Reflection)。运用反射我们可以在运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载在运行时才得知名称的class,获悉其完整构造方法,并生成其对象实体、或对其属性设值、或唤起其成员方法。
要让Java程序能够运行,就得让Java类被Java虚拟机加载。Java类如果不被Java虚拟机加载就不能正常运行。正常情况下,我们运行的所有的程序在编译期时候就已经把那个类加载了。 Java的反射机制是在编译时并不确定是哪个类被加载了,而是在程序运行的时候才加载。使用的是在编译期并不知道的类。这样的编译特点就是java反射。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制(注意关键词:运行状态)。
反射机制主要提供的功能:
①在运行时判断任意一个对象所属的类;
②在运行时构造任意一个类的对象;
③在运行时判断任意一个类所具有的成员变量和方法;
④在运行时调用任意一个对象的方法;
反射的实质:
反射就是把Java类中的各种存在给解析成相应的Java类。要正确使用Java反射机制就得使用Class(C大写) 这个类。它是Java反射机制的起源。当一个类被加载以后,Java虚拟机就会自动产生一个Class对象。通过这个Class对象我们就能获得加载到虚拟机当中这个Class对象对应的方法、成员以及构造方法的声明和定义等信息。
反射机制的优点与缺点:
为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念:
静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,降低类之间的耦合性。
一句话,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用安装,只需要在运行时才动态的创建和编译,就可以实现该功能。它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。
2.反射的基本使用
首先来看看用反射机制和用以前的方法新建对象实例有什么不同。
以新建一个Person对象为例:
用以前的方法是:
Person p = new Person();
在内存中新建一个Person的实例,对象p对这块内存地址进行引用。
用反射机制实现 (有三种方法):
①第一种:
Class> cls=Class.forName(“com.fanshe.Person”); //forName(包名.类名)
Person p=(Person)cls.newInstance();
首先通过JVM查找并加载指定的类(上面的代码指定加载了com.fanshe包中的Person类);然后调用newInstance()方法让加载完的类在内存中创建对应的实例,并把实例赋值给p。
②第二种:
Person p = new Person();
Class> cls=p.getClass();
Person p2=(Person)cls.newInstance();
首先在内存中新建一个Person的实例,对象p对这个内存地址进行引用;然后对象p调用getClass()返回对象p所对应的Class对象;最后调用newInstance()方法让Class对象在内存中创建对应的实例,并且让p2引用实例的内存地址。
③第三种:
Class> cls=Person.Class();
Person p=(Person)cls.newInstance();
首先获取指定类型的Class对象,这里是Person;然后调用newInstance()方法让Class对象在内存中创建对应的实例,并且让p引用实例的内存地址
注意:
①cls.newInstance()方法返回的是一个泛型T,需要强转成Person类。
②cls.newInstance()默认返回的是Person类的无参数构造对象,所以被反射机制加载的类必须有无参数构造方法,否者运行会抛出异常。
可能有人会有疑问,明明直接new对象就好了,为什么非要用反射呢?代码量不是反而增加了?
其实反射的初衷不是方便你去创建一个对象,而是让你在写代码的时候可以更加灵活,降低耦合,提高代码的自适应能力.
怎么样降低耦合度,提高代码的自适应能力?
通过接口实现,但是接口如果需要用到new关键字,这时候耦合问题又会出现。
举个栗子:
public static void main(String[] args) {
HeroFacrty facrty =new HeroFacrty();
hero iroman= facrty.CreateHero(“IronMan”);
iroman.attach();
}
public hero CreateHero(String name) {
if ((name).equals(“IronMan”)) {
return new IronMan();
}
if ((name).equals(“Hulk”)) {
return new Hulk();
}
return null;
}
interface hero {
public void attach();
}
class IronMan implements hero {
@Override
public void attach() {
System.out.println(“Laser Light”);
}
}
class Hulk implements hero {
@Override
public void attach() {
System.out.println(“fist”);
}
}
假设有1000个不同Hero需要创建,那你打算写1000个 if语句来返回不同的Hero对象?
这时候用反射机制来看看:
public static void main(String[] args) {
HeroFacrty facrty = new HeroFacrty();
Hero hero=facrty.CreateHero(“test.IroMan”);
hero.attack();
}
public Hero CreateHero(String name) {
try {
Class> cls = Class.forName(name);
Hero hero = (Hero) cls.newInstance();
return hero;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
class IroMan implements Hero {
@Override
public void attack() {
System.out.println(“Laser Light”);
}
}
class Hulk implements Hero {
@Override
public void attack() {
System.out.println(“Fist”);
}
}
interface Hero {
public void attack();
}
利用反射机制进行解耦的原理就是利用反射机制"动态"的创建对象:向CreateHero()方法传入Hero类的包名.类名,通过加载指定的类,然后再实例化对象。
说完了反射机制如何生成类对象,那生成了对象以后当然要开始调用类的方法了。
在调用方法前先了解Class> cls=Class.forName(“fanshe.Person”);cls内部有哪些方法供我们使用:
①getDeclareMethods() 获取所有的方法
②getReturnType() 获取方法的返回值类型
③getParameterTypes()
获取方法的传入参数类型
④getDeclareMethod(“方法名,参数类型.class,…”)
获得特定的方法
⑤getDeclaredConstructors()
获取所有的构造方法
⑥getDeclaredConstructors(参数类型.class,…)
获取特定的构造方法
⑦getDeclaredFields
获取所有成员变量
⑧getDeclaredField(参数类型.class,…)
获取特定的成员变量
⑨getSuperclass()
获取某类的父类
⑩getInterfaces()
获取某类实现的接口
比如下面的Person类,以前我们的写法是:
public class fanshe03 {
public static void main(String[] args) {
Person person=new Person();
person.setName(“Lipt0n”);
System.out.print(person.getName);
}
}
class Person {
String name;
public Person() {
}
public Person(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
很简单,步骤分为三步:
①实例化一个Person类对象
二调用Person.setName(“name”)设置名字
③在控制台打印Person.getName()的值
现在我们用反射机制来实现上面的代码:
public static void main(String[] args) {
try {
Class> cls=Class.forName(“test.Person”); //加载Person类
Object object=(Object) cls.newInstance(); //实例化Person
Method setname=cls.getDeclaredMethod(“setName”, String.class);//获取setName()方法
setname.invoke(object, “Lipt0n”);//设置调用setName方法的对象和传入setName的值
Method getname=cls.getDeclaredMethod(“getName”);//获取getName方法
System.out.print(getname.invoke(object, null));//设置调用getName方法的对象.把值打印到控制台
} catch (Exception e) {
e.printStackTrace();
}
}



