反射

1.反射机制介绍_Class 对象获取

1.1 反射机制是 Java 的动态性之一

动态语言:在程序运行时,可以改变程序的结构或变量的类型。

举例:JavaScript

 Java 代码

典型的动态语言”Python、ruby、JavaScrip

  •        C,C++,Java 不是动态语言,但具有一定的动态性,可以称为”准动态语言”,具备类似动态语言的特性。传一块代码来动态的执行,动态的处理,Java 也能做,可以利用反射来实现类似的功能。Java的动态性让编程变得更加的灵活,功能就更加的强大。

1.2 反射机制


程序在运行的过程中加载一些“只知道相关名字”的类,以下代码,在程序运行时加载 User 类。

一个类被加载后,JVM 会创建一个对应类的 Class 对象,
类的整个结构信息会被放到 Class 对象中。
这个 Class 对象就像镜子一样,通过这面镜子,可以得到
对应类的全部信息

1.3 反射机制的常见作用

  • 1) 动态的加载类、动态的获取类的信息(属性,方法,构造
  • 器)
  • 2) 动态构造对象
  • 3) 动态调用类和对象的任意方法、构造器
  • 4) 动态调用和处理属性
  • 5) 获取泛型信息
  • 6) 处理注解

1.4 获取 Class 对象的方式

  • 1) 通过字节码文件
  • 2) 对象的 getClass()方法
  • 3) Class 类的静态方法 forName(….)
package com.bjsxt.entity;

public class User {
    //类的属性
    private int userId;
    private String userName;
    private String password;
    //公有的取值,赋值方法
    public int getUserId() {
        return userId;
    }
    public void setUserId(int userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    //构造方法
    public User() {
        // TODO Auto-generated constructor stub
    }
    public User(int userId, String userName, String password) {
        super();
        this.userId = userId;
        this.userName = userName;
        this.password = password;
    }
    
    
}
View Code
package com.bjsxt.test;

import com.bjsxt.entity.User;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        System.out.println(int.class);
        System.out.println(void.class);
        int []arrA=new int[10];
        int[] arrB=new int[30];
        /**维数相同和类型相同的数组共享同一个Class对象*/
        System.out.println(arrA.getClass()==arrB.getClass());
        
        /**同一个类的N多对象,共享同一个Class对象*/
        User u1=new User();
        User u2=new User();
        System.out.println(u1.getClass()==u2.getClass());
        
        
        /**获取Class对象的三种方式*/
        //(1)通过对象的getClass()方法获取
        Class c1=u1.getClass();
        //(2)通过字节码文件获取
        Class c2=User.class;
        //(3)通过Class类的静态方法获取
        Class c3=Class.forName("com.bjsxt.entity.User");
        
        System.out.println((c1==c2)+"	"+(c1==c3));
        
        
    }
}
View Code

2.反射机制动态操作_方法_属性_构造器

2.1 获取类的名字

2.2 获得类的属性

公共的指的是public类型的

2.3 获得类的方法

 

2.4 获得构造方法

    

JAVA 反射机制中,Field的getModifiers()方法返回int类型值表示该字段的修饰符。

其中,该修饰符是java.lang.reflect.Modifier的静态属性。

对应表如下:

PUBLIC: 1
PRIVATE: 2
PROTECTED: 4
STATIC: 8
FINAL: 16
SYNCHRONIZED: 32
VOLATILE: 64
TRANSIENT: 128
NATIVE: 256
INTERFACE: 512
ABSTRACT: 1024
STRICT: 2048

2.5 动态的操作属性、方法、构造方法

package reflect;

public class User {
    //类的属性
    private int userId;
    private String userName;
    private String password;
    //公有的取值,赋值方法
    public int getUserId() {
        return userId;
    }
    public void setUserId(int userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    //构造方法
    public User() {
        // TODO Auto-generated constructor stub
    }
    public User(int userId, String userName, String password) {
        super();
        this.userId = userId;
        this.userName = userName;
        this.password = password;
    }
    
    
}
View Code
package reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, NoSuchMethodException {
        String path="reflect.User";
        //(1)获取类的名称
        Class c=Class.forName(path);
        System.out.println("类的全名称:"+c.getName());
        System.out.println("类的名称:"+c.getSimpleName());
        //(2)获取父类的class对象
        Class cSuper=c.getSuperclass();
        System.out.println(cSuper.getName());
        System.out.println(cSuper.getSimpleName());
        //获取类的属性信息
        //Field f=c.getField("userId");//只能获取公共属性
        //System.out.println(f);
        Field[] fields=c.getFields();//只能获取公共属性
        System.out.println(fields.length);
        
        Field[] fields2=c.getDeclaredFields();
        //System.out.println(fields2.length);
         for (Field field : fields2) {
            //System.out.println(field);//调用了tostring方法
            System.out.println(field.getModifiers()+"	"+field.getType()+"	"+field.getName());
                    
            
            
        }
        //获取类的方法信息
         Method[] methods=c.getDeclaredMethods();//本类中的公共方法对象
         System.out.println(methods.length);
         for(Method method:methods){
             //System.out.println(method);
             System.out.println("访问权限:"+method.getModifiers());
             System.out.println("返回值类型:"+method.getReturnType());
             System.out.println("方法的名称:"+method.getName());
             //方法的参数
             Class<?>[] cPara = method.getParameterTypes();
             for (Class c1 : cPara) {
                System.out.println(c1.getTypeName()+"	");
            }
             System.out.println("
-----------");
             
         }
         
        //获取类的构造器
         Constructor[] cons = c.getConstructors();
         for(Constructor constructor:cons){
             System.out.println(constructor);
         }
         //获取指定的构造方法
         Constructor con = c.getConstructor(null);
         
         System.out.println(con);
         Constructor con2=c.getConstructor(int.class,String.class,String.class);
         System.out.println(con2);
         
         
    }
}
View Code
package reflect;

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

public class Test2 {
  public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
    //获取class类的对象
      Class c=Class.forName("reflect.User");
      //(1)得到无参构造方法的对象
      Constructor cons = c.getConstructor(null);
      //通过无参构造方法的对象,创建User类的对象
    User user =(User) cons.newInstance();
      
    
    //(2)动态操作属性
    Field field=c.getDeclaredField("userId");
    field.setAccessible(true);//这个属性不需要做安全检查了,可以直接访问
    field.set(user, 1001);
    System.out.println("取出userid这个属性的值"+field.get(user));//通过反射直接取值
    
    //(3)动态操作方法
    Method m = c.getDeclaredMethod("setUserName", String.class);
    //执行setUserName这个方法
      m.invoke(user, "张三");
      Method m2=c.getDeclaredMethod("getUserName", null);
      System.out.println(m2.invoke(user));
      
}
}
View Code

3.提高反射效率

  • 反射机制对程序的运行在性能上有一定的影响,速度慢

3.1 如何提高反射的性能

1) 通过 setAccessible 提高性能

  • a) setAccessible 启用和禁用访问安全检查的开关,值为true 则指示反射的对象在使用时应该取消 Java 语言访问检查,值为 false 则指示反射的对象不实施 Java 语言访问检查,并不是为 true 就能访问为 false 就不能访问
  • b) 禁止安全检查,可以提高反射的运行速度
package reflect;

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

public class TestAccessbile {

    public static void test01(){
        //User u=new User();
        Object obj=new Object();
        long startTime=System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            obj.hashCode();
        }
        long endTime=System.currentTimeMillis();
        System.out.println("调用普通方法,循环执行一亿次"+(endTime-startTime)+"ms");
    }
    public static void test02() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        Object obj=new Object();
        Class c=obj.getClass();
        //获取指定的方法
        Method m=c.getDeclaredMethod("hashCode",null);
        long startTime=System.currentTimeMillis();
        for(int i=0;i<1000000000;i++){
            //执行这个方法
            m.invoke(obj, null);
            
        }
        long endTime=System.currentTimeMillis();
        System.out.println("调用反射,循环执行一亿次"+(endTime-startTime)+"ms");
    }
    public static void test03() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        Object obj=new Object();
        Class c=obj.getClass();
        //获取指定的方法
        Method m=c.getDeclaredMethod("hashCode",null);
        m.setAccessible(true);//不执行安全检查
        long startTime=System.currentTimeMillis();
        for(int i=0;i<1000000000;i++){
            //执行这个方法
            m.invoke(obj, null);
            
        }
        long endTime=System.currentTimeMillis();
        System.out.println("调用反射,不启用安全检查循环执行一亿次"+(endTime-startTime)+"ms");
    }
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        test01();
        test02();
        test03();
    }

}
View Code

4.反射操作泛型

4.1 泛型

      Java 中的泛型仅仅是给编译器 javac 使用的,确保数据的安全性和免去强制类型转换的麻烦,但是一旦编译完成,所有与泛型有关的类型全部擦除。使用泛型直接读取泛型,是读取不到的,因为反射是操作加载以后的类的。

4.2Java 新增的数据类型

为了通过反射操作这些类型以迎合实际开发的需要

  • 1) ParameterizedType: 表 示 一 种 参 数 化 的 类 型 ,比 如Collection<String>,可以获取 String 信息
  • 2) GenericArrayType:泛型数组类型
  • 3) TypeVariable:各种类型变量的公共父接口
  • 4) WildcardType:代表一种通配符类型表达式,比如? extends Number,? super Integer(Wildcard 是一个单词,就是通配符)
    • package com.bjsxt.entity;
      
      public class User {
      
      }
      View Code
      package com.bjsxt.generice;
      
      import java.lang.reflect.InvocationTargetException;
      import java.lang.reflect.Method;
      import java.util.ArrayList;
      import java.util.List;
      
      public class Test {
          public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
              List<String> alist=new ArrayList<String>();
              Class c=alist.getClass();//得到Class对象
              Method m=c.getDeclaredMethod("add", Object.class);
              //执行添加方法
              m.invoke(alist, 123);
              //输出集合中元素的个数
              System.out.println(alist.size());
              
          }
      }
      View Code
      package com.bjsxt.generice;
      
      import java.lang.reflect.Method;
      import java.lang.reflect.ParameterizedType;
      import java.lang.reflect.Type;
      import java.util.List;
      import java.util.Map;
      
      import com.bjsxt.entity.User;
      
      public class TestGeneric {
          public void test01(Map<String,User> map,List<User> list,String s){
              System.out.println("TestGeneric.test01()");
          }
          public Map<Integer,User> test02(){
              System.out.println("TestGeneric.test02()");
              return null;
          }
          public String test03(){
              System.out.println("TestGeneric.test03()");
              return null;
          }
          public static void main(String[] args) throws NoSuchMethodException, SecurityException {
              //获取test01方法的泛型参数信息
              Class c=TestGeneric.class;
              Method test01=c.getMethod("test01", Map.class,List.class,String.class);
              
              //获取带泛型参数的类型
              Type [] tytes=test01.getGenericParameterTypes();
              System.out.println(tytes.length);
              for (Type type : tytes) {
                  //System.out.println("#"+type);
                  if (type instanceof ParameterizedType) {
                      Type[] genericType= ((ParameterizedType) type).getActualTypeArguments();
                      //遍历每一个泛型参数中泛型的类型  
                      for (Type genType : genericType) {
                          System.out.println("泛型类型:"+genType);
                      }
                      System.out.println("
      --------------------------");
                  }
              }
              
              System.out.println("
      ----------------------------
      ");
              //获取test02方法返回值的泛型信息
              Method m2=c.getMethod("test02", null);
              Type returnType=m2.getGenericReturnType();
              //判断是否带有泛型
              if(returnType instanceof ParameterizedType){
                  Type [] types=((ParameterizedType) returnType).getActualTypeArguments();
                  for (Type type : types) {
                      System.out.println("返回值的泛型类型:"+type);
                  }
              }
              
              System.out.println("
      ------------------------------
      ");
              Method m3=c.getMethod("test03", null);
              Type returnType3=m3.getGenericReturnType();
              //System.out.println(returnType3);
              System.out.println(returnType3 instanceof ParameterizedType);
          }
          
      }
      View Code

5.注解

5.1 注解的作用

  • 1) 不是程序本身,可以对程序作出解释。(这一点跟注释没什么区别)
  • 2) 可以被其他程序(比如:编译器等)读取。(注解信息处理流程,是注解和注释的重大区别,如果没有注解信息处理流程,则注解毫无意义)


5.2 注解的格式

  • 1) 注解是以”@注释名”在代码中存在,还可以添加一些参数值,例如@SuppressWarnings(value=”unchecked”)。


5.3 注解在哪里使用

  • 1) 可以附加在 package,class,method,field 等上面,相当于给它们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元素的访问。


5.4 内置的注解

  • 1) @Override :标识方法是重写的方法
  • 2) @Deprecated :标识的方法不建议使用
  • 3) @SuppressWarnings:用来抑制编译时的警告信息
  • @SuppressWarinings 需要提供参数才能正常使用,这些参数都是已经定义好的,我们只需要选择就可以了。

举例:

  • @SuppressWarnings("unchecked")
  • @SuppressWarnings(value={"unchecked","deprecation"}
  • package com.bjsxt.annotation;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    public class TestAnnotation {
        @Override
        public String toString() {
            // TODO Auto-generated method stub
            return super.toString();
        }
        
        @Deprecated
        public void show(){ 
            Date d=new Date();
            System.out.println(d.getYear());
        }
        @SuppressWarnings("all")
        public void method(){
            List list=new ArrayList();
        }
        @SuppressWarnings(value={"unchecked","path"})
        public static void main(String[] args) {
            new TestAnnotation().show();
        }
    }
    View Code

6.自定义注解

6.1 自定义注解的语法

使 用 @interface 定 义 自 定 义 注 解 时 , 自 动 继 承 了java.lang.annotation.Annotation 接口

  • 1) @interface 用来声明一个注解
  • 2) 其中的每一个方法实际上是声明了一个配置参数

  • a) 方法的名称就是参数的名称
  • b) 返回值类型就是参数类型(返回值类型只能是基本类型、Class、String、enum)
  • c) 可以通过 default 来声明参数的默认值
  • d) 如果只有一个成员,一般参数名为 value

注意事项:注解元素必须要有值。我们定义注解元素时,经
常使用空字符串,0 作为默认值。
也经常使用负数(比如-1)表示不存在的含义

6.2 元注解


元注解的作用就是负责注解其他注解。在 Java 中定义了 4
个标准的 meta-annotation 类型,它们被用来提供对其它
annotation 类型作说明
这些类型和它们所支持的类在 java.lang.annotation 包中可
以找到

  • 1) @Target
  • 2) @Retention
  • 3) @Documented
  • 4) @Inherited

6.3 @Target


作用:用于描述注解的使用范围(即被描述的注解可以用在什
么地方)

举例:
@Target(value=ElementType.TYPE)

6.4 @Retention


作用:表示需要在什么级别保存该注解信息,用于描述注解的
生命周期

package com.bjsxt.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
        String stuName() default "";
        int age() default 0;
        String [] school () default {"清华大学","北京大学"};
}
View Code
package com.bjsxt.annotation;

//@MyAnnotation 该注解只能应用到方法上
//@MyAnnotation2(value="aaaa")
@MyAnnotation2("aaa")
public class Test {
    @MyAnnotation(stuName="张小三")
    public void show(){
        
    }
    @MyAnnotation(stuName="王一一",age=23,school={"北京交通大学","北京广播学院"})
    public void method(){
        
    }
}
View Code
package com.bjsxt.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation2 {
    String value();
}
View Code

7.反射读取注解信息

7.1ORM (Object Relationship Mapping)

ORM:对象关系映射

  • 1) 类与表结构对应
  • 2) 属性和字段对应
  • 3) 对象和记录对应

使用注解完成类和表结构的映射关系


7.2 功能描述

将Java中的Student类使用第三方程序通过读取注解生成数据库中的表

7.3 实现步骤

  • 1) 编写 Student 类
  • 2) 编写注解
  • 3) 在类中使用注解
  • 4) 通过解析程序将注解读取出来 (通过框架解析)
  • 5) 拼接 SQL 语句,使用 JDBC 到数据库中执行创建表
  • package com.bjsxt.entity;
    
    import com.bjsxt.annotation.SxtField;
    import com.bjsxt.annotation.SxtTable;
    
    @SxtTable("tb_student")
    public class Student {
        //私有属性
        @SxtField(columnName="id",type="int",length=10)
        private int id;
        
        @SxtField(columnName="stuname",type="varchar",length=20)
        private String stuName;
        
        @SxtField(columnName="age",type="int",length=10)
        private int age;
        
        
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getStuName() {
            return stuName;
        }
        public void setStuName(String stuName) {
            this.stuName = stuName;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public Student(int id, String stuName, int age) {
            super();
            this.id = id;
            this.stuName = stuName;
            this.age = age;
        }
        public Student() {
            super();
        }
        
    }
    View Code
    package com.bjsxt.annotation;
    
    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 SxtField {//属性的注解
        String columnName(); //数据库中列的名称
        String type();  //数据库中列的类型
        int length();   //类型的长度
    }
    View Code
    package com.bjsxt.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.TYPE) //注解的使用范围
    @Retention(RetentionPolicy.RUNTIME) //在运行时起作用
    public @interface SxtTable {
        String value();
    }
    View Code
    package com.bjsxt.annotation.test;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Field;
    
    import com.bjsxt.annotation.SxtField;
    import com.bjsxt.annotation.SxtTable;
    
    public class Test {
        public static void main(String[] args) throws Exception {
            //(1)创建Student类的Class对象
            Class clazz=Class.forName("com.bjsxt.entity.Student");
            //(2)得到Student类的所有注解
            Annotation [] annotations=clazz.getDeclaredAnnotations();
            for (Annotation a : annotations) {
                System.out.println(a);
            }
            System.out.println("
    ----------------------------");
            //(3)获取指定的注解
            SxtTable st=(SxtTable) clazz.getDeclaredAnnotation(SxtTable.class);
            System.out.println(st);
            System.out.println("
    ----------------------------");
            
            //(4)获取属性的注解
        
            Field field=clazz.getDeclaredField("stuName");
            SxtField sf=field.getDeclaredAnnotation(SxtField.class);
            System.out.println(sf.columnName()+"--"+sf.type()+"--"+sf.length());
            
            /**拼接SQL语句  DDL ,使用JDBC在数据库中执行,创建出了一张表,tb_student,表中的列就为id,stuname,age*/
            
        }
    }
    View Code
原文地址:https://www.cnblogs.com/wq-9/p/10382791.html