我爱编程

一起来学习Java的知识:反射

2018-05-27  本文已影响57人  __y

1.Class类的使用

什么是Class类呢?

我们知道在面向对象的思想中,万物都是对象。那么我们写的类就是java.lang.Class类的实例对象;这个对象我们称为该类的类类型

Class表示的方法

package com.test.reflect;
class Foo{

}
public class ClassDemo1 {
    public static void main(String[] args) {
        //获得Foo的实例
        Foo foo1 = new Foo();
        //Class类的实例对象的表示方法,有三种该表示方法

        //第一种方式
        Class c1 = Foo.class;

        //第二种方式
        Class c2 = foo1.getClass();
        //c1 ,c2表示了Foo类的类类型(class Type)
        System.out.println(c1 == c2);
        //第三种方式
        try {
            Class c3 = Class.forName("com.test.reflect.Foo");
            System.out.println(c2 == c3);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        try {
            //实例化Foo的对象
            Foo f2 = (Foo) c1.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}


image.png

动态加载类

我们可以看到上面第三种方式Class.forName("类的全称");

public class Office {
    public static void main(String[] args) {
        if("Word".equals("args[0]")) {
            Word word = new Word();
            w.start();
        }
        if("Excel".equals(args[0])) {
            Excel excel = new Excel();
            e.start();
        }
    }
}

编译提示:

image.png
这个时候我们想一想,我们这时候如果加入一个Word类,他还是会提示Excel不存在;
这就证明了new创建对象是静态加载类实在编译的时候就加载了所需要的类,如果没有的话就会报错,但在实际应用中,有时候我们想的是需要什么就加载什么,这个时候我们就可以通过动态加载的方式解决该问题。
我们接下来改造一下
public class Office {
    public static void main(String[] args) {
        try {
            Class c = Class.forName(args[0]);
            OfficeAble officeAble = null;
            officeAble = (OfficeAble) c.newInstance();
            officeAble.start();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}


现在不报错了


image.png

再来改造一下Word类,让他更适用

interface OfficeAble {
    public  void start();

}

public class Word implements OfficeAble{
    public  void start() {
        System.out.println("Word---start");
    }
}

结果:


image.png

定义一个接口的话,可以使代码更具有扩展性,当我们想增加更多地Office的应用的时候比如Excel的时候直接实现接口,实现动态加载。
以后我们实现的时候,功能性的类尽可能使用动态加载

基本数据类型

基本的数据类型有类类型吗?答案是有的

package com.test.reflect;

public class ClassDemo2 {
    public static void main(String[] args) {
        Class c1 = int.class;
        Class c2 = double.class;
        Class c3 = void.class;
        Class c4 = String.class;
        //获得类类型的名字
        System.out.println(c1.getName());
        System.out.println(c2.getName());
        System.out.println(c3.getName());
        System.out.println(c4.getName());
        //简称,不包含包名
        System.out.println(c4.getSimpleName());
    }
}

image.png

2.方法的反射

创建一个工具类

package com.test.reflect;

import java.lang.reflect.Method;

public class ClassUtil {
    /**
     * 打印类的信息,包括类的信息,类的成员函数,类的成员变量
     * @param object 该类对象的所属类信息
     */
    public static  void printClassMessage(Object object) {
        //获取类的类类型
        Class c = object.getClass();//该类的子类的类类型,c就是该子类的类类型
        //获取类的名称
        System.out.println(c.getName());
        /**
         * 方法对象
         * 获取该类的方法,一个成员方法就是一个Method对象
         * getMethods()获取所有的public的函数,包括父类继承而来的
         * getDeclaredMethodes()获取的所有该类自己声明的方法,不问访问权限
         */
        Method[] methods = c.getMethods();
        for (Method method : methods) {
            //得到方法的返回值类型的类类型:比如如果返回的是Siring 则返回的是String.class
            Class returnType = method.getReturnType();
            //得到方法的返回类类型的名字
            System.out.print(returnType.getName() + " ");
            //得到方法的名字
            System.out.print(method.getName() + "(");
            //获取参数类型,得到的是参数列表的类型的类类型比如 如果参数类型是Int 则得到的Int.class
            Class[] parameterTypes = method.getParameterTypes();
            for (Class parameterType : parameterTypes) {
                System.out.print(parameterType.getName() + ",");
            }
            System.out.println(")");
        }
    }
}

测试类

package com.test.reflect;

public class TestClient {
    public static void main(String[] args) {
        String str = "Hello";
        ClassUtil.printClassMessage(str);
    }
}

结果:


image.png

可以到结果我们可以获取的方法的类型,名字,参数列表等信息

3.成员变量的反射

在ClassUtil类中加一个方法

 public static  void getFieldMessage(Object object) {
        Class c = object.getClass();
        Field[] fs = c.getDeclaredFields();
        for (Field field : fs) {
            //得到成员变量的类型的类类型
            Class fieldType = field.getType();
            //得到成员变量类型的名字
            String typeName = fieldType.getTypeName();
            //得到成员变量的名称
            String fieldName = field.getName();
            System.out.println(typeName + " " + fieldName);
        }
    }
package com.test.reflect;

public class TestClient {
    public static void main(String[] args) {
        String str = "Hello";
        //ClassUtil.printClassMessage(str);
        ClassUtil.getFieldMessage(str);
    }
}

结果:


image.png

4.构造函数的反射

public static void getConMessage(Object object) {
        Class c = object.getClass();
        Constructor[] constructors= c.getDeclaredConstructors();
        for (Constructor cs: constructors ) {
            //获得构造方法的名字
            System.out.println(cs.getName() + "(");
            //获取构造方法的参数列表
            Class[] parameterTypes = cs.getParameterTypes();
            for (Class parameterType:
                    parameterTypes) {
                System.out.print(parameterType.getName()+ ",");
            }
            System.out.println(")");
        }

    }
package com.test.reflect;

public class TestClient {
    public static void main(String[] args) {
        String str = "Hello";
        //ClassUtil.printClassMessage(str);
        //ClassUtil.getFieldMessage(str);
        ClassUtil.getConMessage(str);
    }
}

结果:


image.png

5.方法的反射

如何获取某个方法呢?我们需要方法的名称和方法的参数列表才能知道是哪个方法

package com.test.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class A {
    public void print(int a, int b) {
        System.out.println( a + b);
    }

    public void print(String a, String b) {
        System.out.println(a.toUpperCase() + "," + b.toLowerCase());
    }
}
public class MethodDemo {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        A a1 = new A();
        Class c = a1.getClass();
        //获取方法
        try {
            Method m = c.getMethod("print",int.class,int.class);
            //方法的反射操作
            //方法如果没有返回值则返回null,没有则是Null
            m.invoke(a1,new Object[]{10,20});
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

image.png

6.总结

我们可以看到利用反射的机制可以写出很多通用性的代码,在运行的时候获取变量,构造方法,调用对象的方法等。这种对于编写框架等是非常有用的,但是对于写应用程序是很脆弱的。因为编译器很难帮我们发现程序中的错误,因此只有在运行的时候才发现错误并导致异常。

上一篇下一篇

猜你喜欢

热点阅读