反射

 

1.反射机制

 

静态VS动态语言

 

动态语言:一类在运行可以改变其结构的语言。通俗点说就是在运行时代码可以根据某些条件改变自身结构。

 

静态语言:运行时不可改变的语言就是静态语言。

 

Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。

 

反射相关的主要API

 

java.lang.Class:代表一个类

 

java.lang.reflect.Method:代表类的方法

 

java.lang.reflect.Filed:代表类的成员变量

 

java.lang.reflect.Constructor:代表类的构造器

 

 

 

2.获得反射对象

 

Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行借助于Reflection AP获得任何类的内部信息,并能直接操作任何的内部属性及方法。

 

Class c = Class.forName("java.lang.String")

 

加载完类之后,在堆内存的方法区中就产生一个Class类型的对象(一个类只有一个class对象),这个对象包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,通过这个镜子看到类的结构,所以,我们形象的称之为:反射。

 

package FanSe;

public class Test01 {
   public static void main(String[] args) throws ClassNotFoundException {
       //通过反射获得类的class对象
        Class  c1= Class.forName("FanSe.User");
       System.out.println(c1);
       Class  c2= Class.forName("FanSe.Test01");
       Class  c3= Class.forName("FanSe.Test01");
       Class  c4= Class.forName("FanSe.Test01");

       //一个类在内存中只有一个Class对象
       //一个类被加载后,类的整个结构都会被封装在Class对象中
       System.out.println(c2.hashCode());
       System.out.println(c3.hashCode());
       System.out.println(c4.hashCode());
  }
}
class User{
   private String name;
   private int id;
   private int age;

   public User() {
  }

   public String getName() {
       return name;
  }

   public void setName(String name) {
       this.name = name;
  }

   public int getId() {
       return id;
  }

   public void setId(int id) {
       this.id = id;
  }

   public int getAge() {
       return age;
  }

   public void setAge(int age) {
       this.age = age;
  }

   @Override
   public String toString() {
       return "User{" +
               "name='" + name + '\'' +
               ", id=" + id +
               ", age=" + age +
               '}';
  }
}

 

3.得到类的几种对象

 

package FanSe;

import java.lang.annotation.ElementType;

public class Test03 {
   public static void main(String[] args) {
       Class c1 =Object.class;//类
       Class c2=Comparable.class;//接口
       Class c3=String[].class;//一位数组
       Class c4=int[][].class;//二维数组
       Class c5=Override.class;//注解
       Class c6= ElementType.class;//枚举
       Class c7=Integer.class;//基本数据类型
       Class c8=void.class;//void
       Class c9=Class.class;//Class
       
       System.out.println(c1);
       System.out.println(c2);
       System.out.println(c3);
       System.out.println(c4);
       System.out.println(c5);
       System.out.println(c6);
       System.out.println(c7);
       System.out.println(c8);
       System.out.println(c9);

       //只要元素类型与维度一样,真是同一个Class
       int[] a=new int[10];
       int[] b=new int[100];
       System.out.println(a.getClass().hashCode());
       System.out.println(b.getClass().hashCode());


  }
}

 

 

 

 

4.类加载内存分析

 

类的加载过程:类的加载(Load)---》类的链接(Link)--》类的初始化(Initialize)

 

类的加载与ClassLoaderd 的理解

 

加载:将class文件字节码内容加载到内存中,并将这些静态数据转换为方发区的运行数据结构,然后生成一个代表中国类的java.lang.Class对象。

 

链接:将java类的二进制代码合并到 JVM的运行状态之中的过程。

 

初始化:

 

package FanSe;

public class Test04 {
   public static void main(String[] args) {
       A a = new A();
       System.out.println(A.m);

       /*
       1.加载到内存,会产生一个了对应Class对象
       2.创建,链接结束 m=0
       3.初始化
               <client>(){
               System.out.println("A类静态代码块初始化");
               m=300;
               m=100;
          }
             
           m=100

        */
  }
}
class A{
   static {
       System.out.println("A类静态代码块初始化");
       m=300;
  }

   static int m=100;
   public A(){
       System.out.println("A类的无参构造初始化");
  }

}

 

5.类的初始化

 

类的主动引用(一定会产生类的初始化)

 

  1. 当虚拟机启动,先初始化main方法所在的类

  2. new一个类的对象

  3. 调用类的静态成员和静态方法

  4. 使用反射

  5. 当初始化一个类,如果父类没有被初始化,则先会初始化他的父类

 

类的被动引用(不会加载类的初始化)

 

  1. 当访问一个静态域时,只有真正声明这个域类才会被初始化。

  2. 通过数组定义引用,不会触发此类的初始化。

  3. 引用常量不会触发此类的初始化(常量在链接阶段就存入了调用类的常量池中了)

 

package FanSe;

public class Test05 {
   static {
       System.out.println("main类被加载");

  }

   public static void main(String[] args) throws ClassNotFoundException {
       //1.主动引用
       //Son son =new Son();

       //反射也会产生主动引用
       //Class.forName("FanSe.Son");

       //不会产生类的引用
       //System.out.println(Son.b);
       Son[] srray = new Son[5];
       System.out.println(Son.M);

  }
}
class Father{
   static int b=2;
   static {
       System.out.println("父类被加载");
  }
}
class Son extends Father{
   static {
       System.out.println("子类加载");
  }
   static int m=100;
   static final int M=1;
}

 

6.类加载器

 

类加载器的作用是用来把类(class)装进内存的。

 

类加载的作用:将class文件字节内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后再堆中生成一个代表这个类的java.lang.Class对象,作为方法区中数据的访问入口。

 

类缓存:标准的javaSE类加载器可以按照要求查找类,但一旦某个类被加载到类加载器中,它将维持加载一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

 

7.获取类的运行时结构

 

 

 

package FanSe;

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

public class Test06 {
   public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
       Class c1 = Class.forName("FanSe.User");

       //获得类的名字
       System.out.println(c1.getName());//获得类的包名+类名
       System.out.println(c1.getSimpleName());//获得类名

       //获得类的属性
       System.out.println("********************");
       Field[] fields = c1.getFields();//只能找到public属性

       fields=c1.getDeclaredFields();//找到全部的属性
       for (Field field:fields){
           System.out.println(field);
      }
       //获得指定属性的方法
       Field declaredFields = c1.getDeclaredField("name");
       System.out.println(declaredFields);

       //获得类的方法
       System.out.println("********************");
       Method[] methods = c1.getMethods();//获得本类及其父类的全部public方法
       for (Method method:methods){
           System.out.println("正常的:"+method);
      }
       c1.getDeclaredFields();//获得本类的所有方法
       for (Method method:methods){
           System.out.println("getDeclaredFields:"+method);
      }
       //获得指定方法
       Method getName = c1.getMethod("getName", null);
       Method setName = c1.getMethod("setName", String.class);
       System.out.println(getName);
       System.out.println(setName);

       //获得指定的构造器
       Constructor[] constructors = c1.getConstructors();
       for(Constructor constructor:constructors){
           System.out.println(constructor);
      }
       constructors=c1.getDeclaredConstructors();
       for(Constructor constructor:constructors){
           System.out.println("#"+constructor);
      }

       //获得指定构造器
       Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
       System.out.println("指定:"+declaredConstructor);
  }
}

 

8.动态创建对象

 

创建类的对象:调用Class对象的newInstance()方法

 

  1. 类必须由一个无参类的构造器

  2. 类的构造器的访问权限需要足够

 

 

 

1).通过Class类的getDeclaredConstructor(Class...parmeterTypes)取得本类的指定形参类型的构造器

 

2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所有需要的各个参数

 

3)通过Constructor实例化对象

 

调用指定的方法:通过反射,调用类中的方法,通过Method类完成。

 

 

 

 

 

package FanSe;
//动态创建对象

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

public class Test08 {
   public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
       Class c1 = Class.forName("FanSe.User");

       //构造一个对象
//       User user = (User)c1.newInstance();
//       System.out.println(user);

       //通过构造器创建对象
       Constructor constructor = c1.getDeclaredConstructor(String.class,int.class,int.class);
       User user2= (User)constructor.newInstance("小明",001,18);
       System.out.println(user2);

       //通过反射调用方法
       User user3 = (User)c1.newInstance();
       //通过反射获取一个方法
       Method setName = c1.getDeclaredMethod("setName", String.class);
       //invoke:激活的意思
       //(对象,“方法是值”)
       setName.invoke(user3,"狂神");
       System.out.println(user3.getName());

       //通过反射操作属性
       User user4 = (User)c1.newInstance();
       Field name = c1.getDeclaredField("name");
       //不能直接操作私有属性,我们需要关闭程序的安全监测,属性或方法的setAccessible(true)
       name.setAccessible(true);
       name.set(user4,"狂神2");
       System.out.println(user4.getName());

  }
}

 

9.性能对比分析

 

package FanSe;

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

public class Test09 {

   //普通方式调用
   public static void test01(){
       User user = new User();

       long startTime = System.currentTimeMillis();
       for (int i = 0; i < 1000000000; i++) {
           user.getName();
      }
       long endTime = System.currentTimeMillis();
       System.out.println("普通方法执行10亿次:"+(endTime-startTime)+"ms");

  }
   //反射方式调用
   public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
       User user = new User();
       Class c1= user.getClass();

       Method getName = c1.getDeclaredMethod("getName",null);

       long startTime = System.currentTimeMillis();

       for (int i = 0; i < 1000000000; i++) {
           getName.invoke(user,null);
      }
       long endTime = System.currentTimeMillis();
       System.out.println("反射方法执行10亿次:"+(endTime-startTime)+"ms");

  }
   //反射方式调用 关闭监测
   public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
       User user = new User();
       Class c1= user.getClass();

       Method getName = c1.getDeclaredMethod("getName",null);
       getName.setAccessible(true);
       long startTime = System.currentTimeMillis();

       for (int i = 0; i < 1000000000; i++) {
           getName.invoke(user,null);
      }
       long endTime = System.currentTimeMillis();
       System.out.println("关闭监测方法执行10亿次:"+(endTime-startTime)+"ms");

  }

   public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
       test01();
       test02();
       test03();
  }
}

 

10.获取泛型信息

 

package FanSe;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class Test10 {

   public void test01(Map<String,User> map, List<User> list){
       System.out.println("test01");
  }

   public Map<String,User> test02(){
       System.out.println("test02");
       return null;
  }

   public static void main(String[] args) throws NoSuchMethodException {
       Method method = Test10.class.getMethod("test01", Map.class, List.class);
       Type[] genericParameterTypes = method.getGenericParameterTypes();
       for (Type genericParameterType:genericParameterTypes) {
           System.out.println("a" + genericParameterType);
           if (genericParameterType instanceof ParameterizedType) {
               Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
               for (Type actualTypeArgument : actualTypeArguments) {
                   System.out.println(actualTypeArgument);
              }

          } }

           method = Test10.class.getMethod("test02", null);
           Type genericReturnType = method.getGenericReturnType();
           if (genericReturnType instanceof ParameterizedType) {
               Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
               for (Type actualTypeArgument : actualTypeArguments) {
                   System.out.println(actualTypeArgument);
              }
          }

      }
}

 

11.获取注解信息

 

ORM:Object relationship Mapping ->对象关系映射

 

package FanSe;

import java.lang.annotation.*;
import java.lang.reflect.Field;

public class Test11 {
   public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
       Class c1 = Class.forName("FanSe.Student2");
       //通过反射获得注解
       Annotation[] annotations = c1.getAnnotations();
       for (Annotation annotation:annotations){
           System.out.println(annotation);
      }

       //获得注解的value的值
       TableZheng tablezheng = (TableZheng)c1.getAnnotation(TableZheng.class);
       String value = tablezheng.value();
       System.out.println(value);

       //获得类指定注解
       Field f = c1.getDeclaredField("id");
       Fieldzheng annotation = f.getAnnotation(Fieldzheng.class);
       System.out.println(annotation.columnName());
       System.out.println(annotation.length());
       System.out.println(annotation.type());
  }

}
@TableZheng("db_student")
class Student2{
   @Fieldzheng(columnName = "db_id",type = "int",length = 10)
   private int id;
   @Fieldzheng(columnName = "db_age",type = "int",length = 10)
   private int age;
   @Fieldzheng(columnName = "db_name",type = "varchar",length = 3)
   private String name;

   public Student2() {
  }

   public Student2(int id, int age, String name) {
       this.id = id;
       this.age = age;
       this.name = name;
  }

   public int getId() {
       return id;
  }

   public void setId(int id) {
       this.id = id;
  }

   public int getAge() {
       return age;
  }

   public void setAge(int age) {
       this.age = age;
  }

   public String getName() {
       return name;
  }

   public void setName(String name) {
       this.name = name;
  }

   @Override
   public String toString() {
       return "Student2{" +
               "id=" + id +
               ", age=" + age +
               ", name='" + name + '\'' +
               '}';
  }

}


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableZheng{
       String value();
}
//属性注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldzheng{
   String columnName();
   String type();
   int length();
}

 

 

 

 

posted @ 2021-08-02 09:53  清钦  阅读(23)  评论(0)    收藏  举报