美文网首页我爱编程
一起来学习Java的知识:反射

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

作者: __y | 来源:发表于2018-05-27 21:40 被阅读57次

    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
    • 第一种方式:我们知道了每个类都有一个隐式的静态方法
    • 第二种方式:我们通过实例对象的getClass可以获得实例对象的类类型
    • 第三种方式:要注意的是要写全称,包括包名等

    动态加载类

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

    • 代表了类的类类型,还代表了动态加载类
    • 编译时刻加载类是静态加载类,运行时刻加载类是动态加载类
      我们在写程序的时候用的ide可能不太明显,下面我们用记事本的方式来看看什么是动态什么是静态
    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.总结

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

    相关文章

      网友评论

        本文标题:一起来学习Java的知识:反射

        本文链接:https://www.haomeiwen.com/subject/lmomjftx.html