• Java反射机制——学习总结


    前几天上REST课,因为涉及到Java的反射机制,之前看过一直没有用过,有些遗忘了,周末找了些资料来重新学习,现在总结一下,加深印象。

    什么是反射机制?

    参考百度百科对java反射机制的定义:

    “JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。”

    我们通过一些例子,更好理解反射机制。

    Class类

    我们知道Java是一门面向对象语言,在Java的世界里,万物皆为对象,比如我们有一个Person类:

    public class Person {
    
    }

    我们创建一个Person类的实例,Person person = new Person();  那么这个person,就是Person类的实例对象。

    那么既然万物都为对象,所以类也是对象。

    类是什么的对象呢?类是Class类的对象,表示方式有三种:

    //第一种,任何一个类都有一个隐含的静态成员变量class
    Class c1 = Person.class;
    
    //第二种,已经知道该类的对象,通过getClass()获得
    Class c2 = person.getClass();
    
    //第三种,Class类的forName()方法
    Class c3 = Class.forName("Person");
    
    //这里,c1,c2,c3都是Class类的实例,我们称c1, c2 ,c3为Person类的类类型
    //不难看出,c1 == c2结果是true, c2 == c3结果也是true

    通过类的类类型,我们经常会用到的方法就是newInstance()方法,通过该方法可以创建该类的实例:

    Person personA = new Person(); //直接new一个实例
    Person personB = Person.class.newInstance(); //通过newInstance()方法获得Person的实例
    
    //在学习JAVAEE时候,newInstance()方法我们最常见于获取数据库驱动
    Class.forName("com.mysql.jdbc.Driver").newInstance();
    
    //需要注意的是,在使用newInstance()方法的前提是该类必须要有无参构造方法

    动态加载类:

    编译时刻加载类称为静态加载,运行时刻加载类称为动态加载,使用new方法新建实例即为静态加载类,在编译时候就要加载全部类。这里我们举一个例子:

    为了更好的区分编译和运行,我们不适用IDE工具,而使用记事本来实现这个例子:

    //我们举女娲造人的例子,创建一个CreatePerson类
    public class CreatePerson {
        public static void main(String[] args) {
            if(args[0].equalsIgnoreCase("man")) {  //如果从命令行传入的参数为man 则创建Man的实例并调用say()方法
                new Man().say();
            }
            if(args[0].equalsIgnoreCase("woman")) {  //如果从命令行传入的参数为woman 则创建Woman的实例并调用say()方法
                new Woman().say();
            }
        }
    }
    
    //CreatePerson类用到了2个类,分别是Man和Woman。
    //但是我们现在只创建Man类
    public class Man {
        public void say() {
            System.out.println("I am a man !");
        }
    }

    我们在CMD中编译CreatePerson,看看会发生什么:

    提示我们找不到Woman这个类。这就是静态加载,在编译时刻需要加载全部类。那么问题来了,如果我要写一个程序,里面有100个功能,这100个功能分别由100个类实现,那么一旦缺少一个类,这整个程序是不是就不能用了。

    为了解决这个问题,我们可以使用动态加载。我们把上面这个例子改一下:

    //创建一个Person接口
    public interface Person {
        void say();
    }
    
    //修改Man类,继承Person接口
    public class Man implements Person{
        public void say() {
            System.out.println("I am a man !");
        }
    }
    
    //修改CreatePerson类,实现动态加载类
    public class CreatePerson{
        public static void main(String[] args) {
            
            try{
                //动态加载类
                Class c = Class.forName(args[0]);
                //通过类的类类型,创建该类实例对象
                Person person = (Person)c.newInstance();
                person.say();
                
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
    }

    这个时候,我们再进行编译:

    没有报错了,那么我们继续把Man编译,再运行一下:

    如果我们在命令行中输入的参数是Woman呢?

     

    抛出ClassNotFoundException异常。因为我们并没有创建Woman类,所以如果以后需要创建Woman类实例,我们只需要新建一个Woman类,并实现Person接口就行了。

    通过反射机制,获得类的信息

     在Java反射机制的定义中有:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。下面是一个实例:

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    public class ClassUtil {
        /**
         * 获取成员函数的信息
         */
        public static void getClassMethodMessage(Object obj) {
            Class c = obj.getClass();
            //获取类的名称
            //System.out.println(c.getName());
            
            /**
             * 
             * Method类,方法对象
             *getMethods方法获得所有public的方法,包括继承而来
             *getDeclaredMethods是获取该类自己的声明的方法
             */
            Method[] ms = c.getMethods();
            for(int i = 0; i < ms.length; i++) {
                //得到方法的返回值类型的类类型
                Class returnType = ms[i].getReturnType();
                System.out.print(returnType.getName() + " ");
                //得到方法的名称
                System.out.print(ms[i].getName() + "(");
                //获取参数类型
                Class[] paramTypes = ms[i].getParameterTypes();
                for(Class class1:paramTypes) {
                    System.out.print(class1.getName() + ",");
                }
                System.out.println(")");
            }
            
            
        }
    
    
    
        public static void getFieldMessage(Object obj) {
            Class c = obj.getClass();
            
            /**
             * 成员变量也是对象
             * java.lang.reflect.Field
             * 
             */
            Field[] fs = c.getDeclaredFields();
            for(Field field:fs) {
                //得到成员变量类型的类类型
                Class fieldType = field.getType();
                String typeName = fieldType.getName();
                //得到成员变量的名称
                String fieldName = field.getName();
                System.out.println(typeName + " " + fieldName);
                
            }
        }
        
        public static void printConMessage(Object obj) {
            Class c = obj.getClass();
            /**
             * 构造函数也是对象
             * 
             */
            Constructor[] cs = c.getConstructors();
            for (Constructor constructor : cs) {
                System.out.print(constructor.getName() + "(");
                //获取构造函数参数列表------>参数列表的参数类型
                Class[] paramType = constructor.getParameterTypes();
                for (Class class1 : paramType) {
                    System.out.print(class1.getName() + ",");
                }
                System.out.println(")");
            }
            
        }
        
        public static void main(String[] args) {
            String s = "hello";
            getFieldMessage(s);
            ClassUtil.printConMessage(s);
            getClassMethodMessage(s);
        }
    }

    方法的反射操作

    可以通过方法的反射操作实现方法的调用:

    import java.lang.reflect.Method;
    
    public class MethodDemo1 {
        public static void main(String[] args) {
            //要获取print(int, int)
            //要获取类的方法就要获取类的信息,获取类的信息就要获取类的类类型
            A a1 = new A();
            Class c = a1.getClass();
            //2,获取方法 名称和参数列表
            //getMethod获取的是public的方法
            try {
                Method m = c.getDeclaredMethod("print", int.class,int.class);
                
                //方法的反射操作
                //a1.print(10, 20);方法的反射操作,用m来进行方法调用和前者效果一致
                Object obj = m.invoke(a1, 10,20);//如果方法有返回值返回值,没有就null
                
                
            } catch (Exception e) {
                
                e.printStackTrace();
            }
        }
        
        
    }
    
    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.toUpperCase());
        }
    }

    以上就是我对Java反射机制的学习和理解,如果有错误,望指出,以便及时更正!谢谢!

  • 相关阅读:
    oracle 用户 权限
    oracle move 释放 表空间
    oracle 视图 参数
    js 、c# 编码解码
    Oracle实体化视图
    简单工厂VS工厂方法
    面向对象——(9)封装和接口
    写给明天的软件工程师——(转)
    设计模式简介
    设计模式的六大原则
  • 原文地址:https://www.cnblogs.com/zhuangcy4567/p/5972947.html
Copyright © 2020-2023  润新知