1. 简介反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods; 反射机制容许程序在运行时加载、探知、使用编译期间完全未知的classes。通过JAVA的反射机制,可以获得程序内部或第三方JAR包的CLASS,METHOD,属性,参数等信息。
2.JAVA动态加载类和静态加载类的区别
它们俩本质的区别在于静态加载的类的源程序在编译时期加载(必须存在),而动态加载的类在编译时期可以缺席(源程序不必存在)。
3. JAVA动态加载类和静态加载类的方式
1 ).程序在编译时执行,在执行过程中加载所有可能执行到的程序。在这种加载方式下,只要加载中一个方法出错,程序就不能运行。我们一般写程序默认的是静态加载;new创建对象的方式称作为静态加载。
2).动态加载(类唯一字节码对象); 程序在运行时调用相应方法,即使其他方法是错误的,程序依旧会执行。通过动态加载可以让程序的可延长性大大提升,对以后的维护和扩展有重要意义。
Class c1=Class.forName("XXX");
Class c2= XXX.class;
XXX u1=new XXX(); Class c3=u1.getClass();
使用当前类的ClassLoader
ClassLoader clo1 =this.getClass.getClassLoader();
Class c3=clo1.loadClass("XXX");
使用当前线程的ClassLoader
ClassLoader clo2 =Thread.currentThread().getContextClassLoader();
Class c4=clo2.loadClass("XXX");
使 用系统ClassLoader,即系统的入口点所使用的ClassLoader,(注意,system ClassLoader与根 ClassLoader并不一样。JVM下system ClassLoader通常为App ClassLoader)
ClassLoader clo3=ClassLoader.getSystemClassLoader();
Class c5=clo3.loadClass("XXX");
4.举两个实际例子说明:
1)例一
public interface IUser {
}
package reflect;
public class User implements IUser{
private String name;
private int id;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the id
*/
public int getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(int id) {
this.id = id;
}
}
public static void main (String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException{
Class c1=Class.forName("reflect.User");//每个类对应一个字节码实体对象
Class c2=User.class; //每个类对应一个字节码实体对象
User u1=new User();//构造器实例化一个对象
Class c3=u1.getClass();//该对象对应的字节码实体对象
Object o1=c1.newInstance();//该类反射出的实例
Object o2=c2.newInstance();//该类反射出的实例
System.out.print(o1==o2);//false
System.out.print(c3==c1);//true
System.out.print(c1==c2);//true
ClassLoader clo=Thread.currentThread().getContextClassLoader();
ClassLoader clos=ClassLoader.getSystemClassLoader();
Class c4=clo.loadClass("reflect.User");
Class c5=clo.loadClass("reflect.User");
System.out.println(c5==c1);//true
System.out.print(c4==c1);//true
//利用反射获取一个类的属性
Field[] fs = c1.getDeclaredFields();
for(Field field:fs){
System.out.println("获得属性的修饰符,例如public,static等等 >>"+Modifier.toString(field.getModifiers()));
System.out.println("属性的类型的名字 >>"+field.getType());
System.out.println("属性的名字 >>"+field.getName());
}
Method[] ms = c1.getDeclaredMethods();
for(Method field:ms){
System.out.println("获得方法的修饰符,例如public,static等等 >>"+Modifier.toString(field.getModifiers()));
System.out.println("方法的参数个数 >>"+field.getParameterTypes().length);
System.out.println("方法的名字 >>"+field.getName());
}
System.out.println("c1的父类>>"+c1.getSuperclass()); //c1的父类>>class java.lang.Object
Class[] cs=c1.getInterfaces();
for(Class field:cs){
System.out.println("接口的名字 >>"+field.getName()); //接口的名字 >>reflect.IUser
}
Method method=c1.getDeclaredMethod("setName", String.class);
method.invoke(o1, "guolzlijing");
Method method2=c1.getDeclaredMethod("getName", null);
System.out.println(method2.invoke(o1, null));//guolzlijing
}
2)解释关键字:
方法关键字
getDeclaredMethods()
获取所有的方法(不包括继承类)
getDeclaredMethod方法第一个参数指定一个需要调用的方法名称
第二个参数是需要调用方法的参数类型列表,是参数类型!如无参数可以指定null。
参数必须和方法中一样int和Integer,double和Double被视为不同的类型。
反射中getMethods 与 getDeclaredMethods 的区别
public Method[] getMethods()返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法。
public Method[] getDeclaredMethods()对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
当然也包括它所实现接口的方法。
getReturnType()
获得方法的反回类型
getParameterTypes()
获得方法的传入参数类型
getDeclaredMethod("方法名",参数类型.class,……)
获得特定的方法
构造方法关键字
getDeclaredConstructors()
获取所有的构造方法(不包括继承类)
getDeclaredConstructor(参数类型.class,……)
获取特定的构造方法
父类和父接口
getSuperclass()
获取某类的父类
getInterfaces()
获取某类实现的接口
Invoke
一个方法可以生成多个Method对象,但只有一个root对象,主要用于持有一个MethodAccessor对象,这个对象也可以认为一个方法只有一个,相当于是static的,因为Method的invoke是交给MethodAccessor执行的。
可以看到Method.invoke()实际上并不是自己实现的反射调用逻辑,而是委托给sun.reflect.MethodAccessor来处理。
每个实际的Java方法只有一个对应的Method对象作为root。这个root是不会暴露给用户的,而是每次在通过反射获取Method对象时新创建Method对象把root包装起来再给用户。
在第一次调用一个实际Java方法对应得Method对象的invoke()方法之前,实现调用逻辑的MethodAccessor对象还没创建;
等第一次调用时才新创建MethodAccessor并更新给root,然后调用MethodAccessor.invoke()真正完成反射调用。
AVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
3)例子二:
public interface RunBetter {
public void startPro();
}
public class Word implements RunBetter{
public void startPro() {
// TODO Auto-generated method stub
System.out.println("Word Test!");
}
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//静态加载。编译时加载,编译时就需要加载全部可能使用到的的类,一个类出错,其他类的功能都不能得到执行 如下注释将编译不通过
/*if("Word".equals(args[0])){
Word Word = new Word();
System.out.println("Word");
}
if("Excel".equals(args[0])){
Excel excel = new Excel();
System.out.println("Excel");
}
}*/
Class c1 = Class.forName(GetProperties.use_classLoador());//配置文件中读取具体实现类
//在这个代码中我们通过获得类类型newInstance。然后再执行startPro中的方法。
//为什么不将RunBetter rb = (RunBetter)c1.newInstance();写成 MainLoad rb = (MainLoad)c1.newInstance(); ???
//不利于拓展,如果有多个实现类都执行startPro方法,这样每加一个类都需要改动源代码程序;接口的形式,只需要 关注接口不关注具体实现类是哪一个;也是多态性
RunBetter rb = (RunBetter)c1.newInstance();
rb.startPro();
}
4)例子三:
public interface Attribute {
abstract void belongNature();
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Scanner sc=new Scanner(System.in);
System.out.println("请输入人群,获取属性……");
while(true){
String action=sc.nextLine();
if("exit".equals(action)){
return;
}
if(action.length()>0){
runClassLoader(action);
}
}
}
static void run(String action){
switch (action) {
case "Student":
Student student=new Student();
student.belongNature();
break;
case "Teacher":
Teacher teacher=new Teacher();
teacher.belongNature();
break;
default:
break;
}
}
static void runClassLoader(String action) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
switch (action) {
case "Student":
Class class1=Class.forName(GetProperties.use_classLoador("class_student"));
Attribute attribute=(Attribute)class1.newInstance();
attribute.belongNature();
break;
case "Teacher":
Class class2=Class.forName(GetProperties.use_classLoador("class_teacher"));
Teacher attribute2=(Teacher)class2.newInstance();
attribute2.belongNature();
break;
default:
break;
}
}