Java反射机制

    |     2015年3月30日   |   Java面向对象高级特性   |     0 条评论   |    1209

Java 反射指:运行时对任意类都能查出全部属性和方法,对任意对象都能调用任意方法和属性。动态拿到这些信息、动态调用对象方法,就是语言层面的反射机制。

它依赖运行时由类加载器装入的 Class 对象:编译期看不到的信息都记在这份元数据里,程序才能在启动后按需装载、查看、生成对象并改写信息。远程方法调用、容器取出组件,都靠这一套。

一、反射背景:编译时与运行时

先把几个容易混的词分清。编译是把源程序翻成机器能执行的形式;在 Java 里就是把 .java 翻成 .class 字节码。这个过程叫编译时。运行时则是类加载器装入字节码,交给 JVM 解释执行。

编译型语言一次全部翻成二进制再跑,效率高、依赖编译器、跨平台弱;解释型语言翻一句执行一句,效率低、依赖解释器、跨平台强。Java 是既编译又解释。

类加载器负责把编译好的字节码送到校验器做安全检查,通过后再解释执行。运行时动态加载就是:真正用到一个类(有实例、调了静态成员)时才装它。通过 Class 对象在运行时操作类信息,像照镜子——所以叫反射。

面向对象里万事皆对象。类本身也是对象:它是 java.lang.Class 的实例。静态成员、基本类型不是普通对象,但对应的 Class 对象仍然存在。

二、三种拿到 Class 对象的方式

反射的入口永远是 Class。三种写法:

1.调用对象的getClass方法,返回该对象的Class对象; Class c=Object.getClass();

2.Class.forName(“类的名字”);  Class c=Class.forName (“…”);

3.Class c=类名.class;  Class c=String.class;

第一种走实例的 getClass();第二种 Class.forName 按全名动态装载(最常见);第三种 类名.class 在编译期就绑定字面量。

方式 写法 典型场景
实例方法 obj.getClass() 手里已有对象
全名装载 Class.forName("pkg.Name") 配置驱动、工厂、框架
类字面量 String.class 编译期已知目标类型

三、reflect 包里的三类钥匙

java.lang.reflect 提供三把钥匙:

  • Method:方法信息,以及调用接口。
  • Constructor:构造器信息,以及创建实例的接口。
  • Field:域(字段)信息,以及读写接口。

四、创建对象、调方法、取字段

用 Class 创建实例(无参构造):

Class c= Class.forName("类的全名");



Object o=c.newInstance();



Test1 t = Test1.class.newInstance();

查这个类属于哪个包:

Class c=Test1.class;



Package p;



p=c.getPackage();

按参数类型拿到方法再 invoke。先 newInstance 得到被访问对象,再准备参数类型数组和实参数组:

Class c = Class.forName(“类的全名”);//获得访问类的对象



Object obj=c.newInstance();//获得被访问类的实例对象



Class[] cs=new Class[]{String.class};//设置参数类型



Method ff=c.getMethod(“setName”,cs);//得到参数匹配的方法



Object[] values=new Object[]{“你好”};//设置需要传递的值 ff.invoke(obj,values);//映射



ff=c.getMethod("getName");



Object uuu= ff.invoke(obj);

动态调用带参静态方法时,invoke 的第一个实参可以是 Class 本身(静态方法不绑实例):

Class c = Class.forName("类的全名");



Class[] cs=new Class[]{String.class};



Method m=c.getMethod("setName",cs);



Object[] o=new Object[]{"aaa"};



m.invoke(c,o);

一次拿出全部属性、构造器和函数(含私有、静态):

Field[] ff = c.getDeclaredFields(); //无论静态还实体还私有皆可得到属性



Constructor[] ccc= c.getDeclaredConstructors(); //得到构造



Method[] mm=c.getDeclaredMethods();//无论静态还实体还私有皆可得到函数

按参数类型拿到构造器再 newInstance:

Class c = Class.forName(“类的全名");



Class[] cs=new Class[]{String.class};



Constructor cc= c.getConstructor(cs);



Object[] o=new Object[]{"ssssssssssssss"};



cc.newInstance(o);

getMethods() 含父类成员方法;getDeclaredMethods() / getDeclaredFields() 只看本类声明:

Method[] methods = classObj1.getMethods(); //获取所有成员方法包含 父类成员方法

Method[] methods = calze.getDeclaredMethods(); //获取类里所有成员方法

Field[] fields = claze.getDeclaredFields();//得到所有属性

Constructor[] constructors = claze.getConstructors();//得到所有构造方法

五、反射的优缺点

优点:提高灵活性和扩展性,降低耦合,程序不必提前写死目标类,就能创建和控制任意类的对象。

缺点:(1)性能——反射本质是解释执行,字段和方法访问远慢于直接代码,适合框架,普通业务不建议铺开。(2)可读性——绕过源码直接改结构,内部逻辑变糊,维护成本高,代码也更绕。

六、用反射实现工厂模式

工厂按类的全名 Class.forName 再 newInstance,调用方只依赖接口 Fruit,新增苹果/橘子不必改工厂分支:

package com.scxh.java.factory;

interface Fruit { //水果接口

    public void eat() //吃水果

}

class Apple implements Fruit{ //定义苹果

    public void eat() {

       System.out.println("**吃苹果。");

    }

}

class Orange implements Fruit{

    public void eat() {

        System.out.println("**吃橘子。");

    }

}

class Factory{

   public static Fruit getInstance(String className){

      Fruit fruit = null

      try{

         fruit = (Fruit) Class.forName(className).newInstance()

      }catch(Exception e ){

         e.printStackTrace()

       }

      return fruit

   }

}

public class FactoryDemo{

   public static void main(String args[]){ //通过工厂类取得接口实例,传入完整的包.类名称

      Fruit f = Factory.getInstance("com.scxh.java.factorydemo.Apple")

         if(f!=null){ //判断是否取得接口实例

            f.eat()

         }

      }

}

七、反射工具类:打印指定类的成员

下面工具一次性打印指定类的属性、构造方法和成员方法(示例输出见方法注释)。作者标识沿用原文 zhou.sir。

/**

* 反射机制实现: 打印指定类的属性、方法,构造方法

*

*

* @author zhou.sir

*

*/



public class ReflectUtil {

/**



输出结果:

String name

int age

int id

String sex

Student()

Student(String int int String )

String toString()

void playeGame()

void makeFriend()

void learn()



*/

public static void main(String[] args) throws ClassNotFoundException {

showDeclaredParameter(getClaze());

showConstruction(getClaze());

showDeclaredMethods(getClaze());

}

/**

* 获取类对象

* @throws ClassNotFoundException

*/

public static Class<?> getClaze() throws ClassNotFoundException{

Class<?> classObj1 = Student.class;

// Class classObj2 = new Dog().getClass();

// Class classObj3 = Class.forName("com.scxh.java1601.object.ex01.test.Student");



// Log.d(classObj1.getName());



return classObj1;

}



/**

* 显示成员方法头

* @param calze

*/

public static void showDeclaredMethods(Class calze){

// Method[] methods = classObj1.getMethods(); //获取所有成员方法包含 父类成员方法

Method[] methods = calze.getDeclaredMethods(); //获取类里所有成员方法

for (int i = 0, n = methods.length; i < n; i++) {

String methodName = methods[i].getName();

Class returnType = methods[i].getReturnType();

Class[] parameterType = methods[i].getParameterTypes();



Log.v(subString(returnType.getName()) + " " + methodName + "(");

for (Class p : parameterType) {

Log.v(subString(p.getName()) + " ");

}

Log.d(")");

}

}



public static void showDeclaredParameter(Class claze){

Field[] fields = claze.getDeclaredFields();//得到所有属性

for(Field f : fields){

String fieldName = f.getName();

Class parameterType = f.getType();



String ptName = subString(parameterType.getName());

Log.d(ptName+ "\t"+fieldName);

}

}



public static void showConstruction(Class claze){

Constructor[] constructors = claze.getConstructors();//得到所有构造方法

for(Constructor c : constructors){

String constructorName = c.getName();



Class[] parameterType = c.getParameterTypes();

Log.v(subString(constructorName) + "(");

for (Class p : parameterType) {

Log.v(subString(p.getName()) + " ");

}

Log.d(")");

}

}



public static String subString(String str){

String ptName = str;

if(ptName.contains(".")){

int lastIndex = ptName.lastIndexOf(".");

ptName = ptName.substring(lastIndex+1);

}

return ptName;

}



}

一句话总结:反射就是运行时的 Class 对象:用它创建实例、调方法、读字段;灵活,但慢,也更难维护。

转载请注明来源:Java反射机制
本文链接地址:https://ai.zhousir.top/?p=103
回复 取消