zoukankan      html  css  js  c++  java
  • 8.2(java学习笔记)反射

    一、基础知识

      对象是表示或封装一些数据,一个类被加载后JVM会创建一个对应该类的Class对象,

      类的整个结构信息会被放在对应的对象中,通过这个对象我们可以获取改类的全部信息,

      而这些操作称为反射。

    二、反射基本操作

      2.1获取对象类

      上面说了每一个类在加载时会创建一个对应该类的Class对象,这个对象中存放着这个类相对应的信息。我们通过反射可以对这个类进行操作。

      那么首先我们要获取这个类对应的Class对象,

      我们可以通过Class.forName(path);也可以直接调用该对象getClass()方法。

      

    User类

    public class User {
        private String userName;
        private int age;
        private String sex;
        private String pass;
        
        public User() {
            
        }
    
        public User(String userName, int age, String sex, String pass) {
            super();
            this.userName = userName;
            this.age = age;
            this.sex = sex;
            this.pass = pass;
        }
        
        public String getUserName() {
            return userName;
        }
        public void setUserName(String userName) {
            this.userName = userName;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public String getSex() {
            return sex;
        }
        public void setSex(String sex) {
            this.sex = sex;
        }
        public String getPass() {
            return pass;
        }
        public void setPass(String pass) {
            this.pass = pass;
        }
        
    }
    public class TestGetInfo {
        public static void main(String[] args) throws ClassNotFoundException{
            User u = new User();
            Class u1 = u.getClass();//对应对象.getClass()获取类对象
            Class u2 = Class.forName("GetClassInfo.User");//Class.forName("包名+类名")获取对象
            System.out.println(u2.getName()+"
    "+u1.getName());//getName是获取完整路径名
         System.out.println(u1.getSimpleName());//只获取类名 } }
    运行结果:
    GetClassInfo.User
    GetClassInfo.User
    User
      

      

      2.2获取属性名称

      Filed[] getFields()//获取该类对象所代表类中所有属性,只能获取public修饰的属性,不能获取private修饰的属性。

      Filed[] getDeclaredFields();//获取该类对象所代表类中所有属性和方法,包括private修饰的属性。

      Filed getDeclaredFields(String name);//获取指定属性

      

    import java.lang.reflect.Field;
    
    public class TestGetInfo {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException{
            User u = new User();
            Class u1 = Class.forName("GetClassInfo.User");
            Field[] f1 = u1.getDeclaredFields();
            Field[] f2 = u1.getFields();//只能访问public修饰的属性
            Field f3 = u1.getDeclaredField("age");//如果用getField("age")获取会出错,无法访问私有属性
            for(Field temp:f1){
                System.out.println("f1:"+temp);
            }
            for(Field temp:f2){
                System.out.println("f2:"+temp);
            }
            System.out.println("f3:" + f3);
        }
    }
    运行结果:
    f1:private java.lang.String GetClassInfo.User.userName
    f1:private int GetClassInfo.User.age
    f1:private java.lang.String GetClassInfo.User.sex
    f1:private java.lang.String GetClassInfo.User.pass
    f3:private int GetClassInfo.User.age

      

      2.3获取方法

      Method[] getDeclaredMethods();//获取该类所有方法

      //获取指定方法,name为方法名,paremeterTypes为参数类型对应的Class对象

      方法可能有重名的情况,这时需要方法名加参数类型确定具体方法。

      Method[] getDeclaredMethod(String name, Class<?>... parameterTypes);

      

    import java.lang.reflect.Method;
    
    public class TestGetInfo {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{
            User u = new User();
            Class<User> u1 = (Class<User>) Class.forName("GetClassInfo.User");
            Method[] m = u1.getDeclaredMethods();
            Method m1 = null;
            try {
                 m1 = u1.getDeclaredMethod("setAge",int.class);//如果对应类中除了setAget(int age)还有setAge(){}方法,将参数设置为null获取的是setAge()方法
            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            for(Method temp:m){
                System.out.println("m:" + temp);
            }
            System.out.println("m1:" + m1);
        }
    }
    运行结果:
    m:public void GetClassInfo.User.setAge(int)
    m:public void GetClassInfo.User.setSex(java.lang.String)
    m:public void GetClassInfo.User.setPass(java.lang.String)
    m:public java.lang.String GetClassInfo.User.getSex()
    m:public java.lang.String GetClassInfo.User.getPass()
    m:public int GetClassInfo.User.getAge()
    m:public void GetClassInfo.User.setUserName(java.lang.String)
    m:public java.lang.String GetClassInfo.User.getUserName()
    m1:public void GetClassInfo.User.setAge(int)

    由于类中方法都是public修饰的所以也可以用getMethod等方法获取。

      2.4获取构造器

      Constructor[] getDeclaredConstructors();//获取所有构造器

      Constructor<T> getConstructor(Class<?>... parameterTypes);//获取指定参数的构造器

      

    public class TestGetInfo {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{
            User u = new User();
            Class<User> u1 = (Class<User>) Class.forName("GetClassInfo.User");
            Constructor<User>[] con = (Constructor<User>[]) u1.getDeclaredConstructors();//获取所有构造器
            Constructor<User> con1 = null;
            try {
                con1 = u1.getDeclaredConstructor(String.class,int.class,String.class,String.class);//获取指定构造器
            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            for(Constructor<User> temp:con){
                System.out.println("con:" + temp);
            }
            System.out.println("con1:"+con1);
        }
    }
    运行结果:
    con:public GetClassInfo.User()
    con:public GetClassInfo.User(java.lang.String,int,java.lang.String,java.lang.String)
    con1:public GetClassInfo.User(java.lang.String,int,java.lang.String,java.lang.String)

       2.5反射构造对象

          2.5.1调用无参构造方法构造对象

            1)、获取对应类对象

            2)、调用newInstance方法实例化对象(此时调用的是该类的无参构造进行实例化)

            

    import java.lang.reflect.Constructor;
    
    public class TestGetInfo {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{
            User u = new User();
            Class<User> u1 = (Class<User>) Class.forName("GetClassInfo.User");
            User user = u1.newInstance();
            user.setAge(19);
            System.out.println(user.getAge());
        }
    }
    运行结果:
    19

          2.5.2调用指定有参构造方法构造对象

            1)、获取对应有参构造方法

            2)、通过获取的有参构造方法构造对象

        

    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    
    public class TestGetInfo {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{
            User u = new User();
            Class<User> u1 = (Class<User>) Class.forName("GetClassInfo.User");
            Constructor<User> con1 = null;
            User user = null;
            try {
                //获取对应构造器
                con1 = u1.getDeclaredConstructor(String.class,int.class,String.class,String.class);
                //通过构造器实例化对象
                user = con1.newInstance("hcf",19,"man","123455");
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("姓名:" + user.getUserName()+ "年龄" + user.getAge());
        }
    }
    运行结果:
    姓名:hcf年龄19

      2.6反射调用方法

        1)、获取对应方法

        2)、通过方法对象的invoke()方法调用

        Object invoke(Object obj, Object... args);//将该方法作用于obj对象,参数为args...

        

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    
    public class TestGetInfo {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{
            User u = new User();
            Class<User> u1 = (Class<User>) Class.forName("GetClassInfo.User");
            User user = u1.newInstance();
            Method m = null;
            try {
                //获取setAge方法
                 m = u1.getDeclaredMethod("setAge", int.class);
                 //调用setAget方法,作用对象是user,参数为19
                 m.invoke(user, 19);
            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(user.getAge());
        }
    }
    运行结果:
    19

       2.7反射操作属性

        1)、获取指定属性

        2)、调用void set(Object obj, Object value)方法设置属性值

         ps:如果属性是private修饰的则需要添加setAccessible(true);//表示不做访问检查,直接访问。

        

    import java.lang.reflect.Field;
    
    
    public class TestGetInfo {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{
            User u = new User();
            Class<User> u1 = (Class<User>) Class.forName("GetClassInfo.User");//获取对应类对象
            User user = u1.newInstance();//通过反射实例化对象
            Field f = u1.getDeclaredField("age");
            f.setAccessible(true);//true表示反射对象在使用时不进行访问检查
            f.set(user, 20);
            System.out.println(user.getAge());
        }
    }
    运行结果:
    20

      使用反射可以完成平常无法完成的一些操作,但反射会造成程序效率低下。

      下面举个例子看下使用反射与不使用反射直接的差距:

    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.Date;
    
    
    public class TestGetInfo {
        public static void test1(){
            Date start = new Date();
            User u = new User();
            for(int i = 0; i < 1000000000L; i++){
                u.getAge();
            }
            Date end = new Date();
            System.out.println("不使用反射直接调用所消耗时间:" + (end.getTime() - start.getTime()));
        }
        
        public static void test2(){
            Date start = new Date();
            User u = new User();
            Class<User> clazz = (Class<User>) u.getClass();
            User user = null;
            Method m = null;
            try {
                user = clazz.newInstance();
                m = clazz.getDeclaredMethod("getAge",null);
                for(int i = 0; i < 1000000000L; i++){
                    m.invoke(user, null);
                }
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Date end = new Date();
            System.out.println("使用反射调用所消耗时间(做访问检查):" + (end.getTime() - start.getTime()));
        }
        
        public static void test3(){
            Date start = new Date();
            User u = new User();
            Class<User> clazz = (Class<User>) u.getClass();
            User user = null;
            Method m = null;
            try {
                user = clazz.newInstance();
                m = clazz.getDeclaredMethod("getAge",null);
                m.setAccessible(true);//设置为true不做访问检查
                for(int i = 0; i < 1000000000L; i++){
                    m.invoke(user, null);
                }
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Date end = new Date();
            System.out.println("使用反射调用所消耗时间(不做访问检查):" + (end.getTime() - start.getTime()));
        }
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{
            test1();
            test2();
            test3();
        }
    }
    运行结果:
    不使用反射直接调用所消耗时间:607
    使用反射调用所消耗时间(做访问检查):2529
    使用反射调用所消耗时间(不做访问检查):1967

    我们可以看到使用反射后效率非常低,但是如果设置不进行访问检查效率会有一定的提高。

    如果某一段反射进行频繁的操作,设置不进行安全检查,效率会有较大提升。

       3.反射机制读取注解

        到这里我们就可以结合前面的注解,将一个类和SQL表关联起来(ORM对象关系映射)。

        首先我把一个类看做是一个表中一行数据的集合,那么我们要为这个类和对应的表关联起来。

        其次我们还需要类中的属性与表中的属性关联起来。

        

        例如我有这样一张表,其中id为主键会自动设置,regTime数据库会自动设置这两个不需要我们负责。

        那么就剩下username 和pwd了

        我们可以建一个User类,其中有username和pwd。

        再为这个类设置一个注解主要标识表名,为类中属性也设置一个注解主要标识列名和类型。

        这样我们创建的类就和这个表对应起来了。

        注解:

          1.@TableName   

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(value={ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)//可以被反射读取
    public @interface TableName {
        String value();
    }

        

        2.@FieldName

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface FieldName {
        String cloName();
        String type();
    }

        

        User类  

    @TableName(value="t_user")//为类添加注解
    public class User {
        //为属性添加注解
        @FieldName(cloName = "username", type = "varchar")
        private String userName;
        @FieldName(cloName = "pwd", type = "varchar")
        private String pass;
        public User() {
            
        }
    
        public User(String userName, int age, String sex, String pass) {
            super();
            this.userName = userName;
            this.pass = pass;
        }
        
        public String getUserName() {
            return userName;
        }
        public void setUserName(String userName) {
            this.userName = userName;
        }
        
        public String getPass() {
            return pass;
        }
        public void setPass(String pass) {
            this.pass = pass;
        }    
    }

      

        读取注解,建立SQL插入语句:

        读取注解需要用到:public <T extends Annotation> T getAnnotation(Class<T> annotationClass)获取注解;

        annotationClass为注解对应的类对象(注解名.Class),返回类型是注解类型。

        例如调用的是xxx.getAnnotation(TableName.class),那么返回的就是TableName类型,即对应的注解,通过这个就可以获取注解中具体的值。 

        要想获取类的注解,要先获取类,同样获取属性的注解要想获取属性。  

    import java.lang.annotation.Annotation;
    import java.lang.reflect.AnnotatedType;
    import java.lang.reflect.Field;
    
    
    public class TestGetInfo {
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
            Class  clazz = Class.forName("GetClassInfo.User");
            User user = (User)clazz.newInstance();//实例化对象
            user.setUserName("gcmh");//设置信息
            user.setPass("123456");
            Field fName = null;
            Field fPass = null;
            FieldName annoFieldName = null;//属性对应的注解类型
            FieldName annoFieldPass = null;//属性对应的注解类型
            TableName tableName = null;//类对应的注解类型
            try {
                //获取类的注解
                tableName = (TableName) clazz.getAnnotation(TableName.class);
                fName = clazz.getDeclaredField("userName");//获取Name属性
                annoFieldName = fName.getAnnotation(FieldName.class);//获取Name属性的注解
                fPass = clazz.getDeclaredField("pass");//获取pass属性
                annoFieldPass = fPass.getAnnotation(FieldName.class);//获取pass属性的注解
            } catch (NoSuchFieldException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            String sqlName = "testjdbc";//数据库名
            //拼凑SQL插入语句
            String sqlInsert = "INSERT INTO" +" `" + sqlName +"`." +
                                "`"+ tableName.value() + "`" +"(`" +
                                annoFieldName.cloName()+ "`,`" + 
                                annoFieldPass.cloName() + "`)Value("+
                                "'"+user.getUserName()+"','" + user.getPass()+"');";
            System.out.println(sqlInsert);//输出插入语句
        }
    }
    运行结果:
    INSERT INTO `testjdbc`.`t_user`(`username`,`pwd`)Value('gcmh','123456');

    在SQL中执行这个语句即可插入一个名称为gcmh密码为 123456的用户。

      

  • 相关阅读:
    find命令
    shell编程基础
    grep命令
    awk命令
    结对项目之需求分析与原型模型设计
    使用Git进行代码管理的心得
    软件工程的实践项目的自我目标
    第五次作业——团队项目——需求规格说明书
    调研android开发环境的发展演变
    结对编程总结
  • 原文地址:https://www.cnblogs.com/huang-changfan/p/10055286.html
Copyright © 2011-2022 走看看