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()+"\n"+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的用户。

  

猜你喜欢

转载自www.cnblogs.com/huang-changfan/p/10055286.html