您的位置:首页 > 编程语言 > Java开发

开发中要知道的Java反射机制要点

2017-12-06 10:36 85 查看
1.什么是反射?

正常面向对象是是通过类,对象,然后来操作属性方法。而反射是反过来,通过大Class类提供的相关的如Field,Method,Constructor等API来用对象反向操作属性,对象,类的相关信息。所以JAVA的反射可以理解为通过大Class相关属性来操作对象相关的问题。
反射的主要内容
 * 1 Java反射机制概述
 * 2 理解Class类并获取Class类的实例(掌握)
 * 3 类的加载与ClassLoader的理解
 * 4 通过反射创建运行时类的对象 (掌握)
 * 5 通过反射获取运行时类的完整结构:所有的属性、方法、构造器、父类、接口、注解、父类的泛型、
 * 6 通过反射调用运行时类的指定属性、指定方法、指定的构造器等 (掌握)
 * 7 反射的应用:动态代理
反射主要功能如:
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理

2. 反射的实现(反射的主要体验是用Class类来实现的)

第一步:先获取运行时类,即获取Class的实例。
       //三种方法可以获取Class的实例,即运行时类
        //以Person类为例,知道运行时类。直接调用类的静态属性:.class
      1. Class cls = Person.class
        //调用Class类的静态方法:forName() ----使用频率最高
      2. Class cls = Class.forName("com.cy.exe1.Person");//用大Class提供的forName方法。
        //通过运行时类的对象,调用getClass()
      3.Class cls = p.getClass();  //p是Person的对象
         //通过类的加载器ClassLoader实现 (了解)
      4.ClassLoader classLoader = this.getClass().getClassLoader();
        Class clazz3 = classLoader.loadClass("com.robot.java.Person");

第二步:再创建运行类的对象
         方法1. Class cls = Class.forName("com.cy.exe1.Person");//获取运行类
                 //创建运行类对象,通过调用Class里的newInstanc方法,返回值默认是Objcet类型,如果用
                //Person来接收的话,要用强转符,要么获取运行类时用泛型指定类型。
                Object obj = cls.newInstance(); 或者Person p = (Person)cls.newInstance;

             注意:创建类的对象:调用Class对象的newInstance()方法
                1)运行类必须有一个无参数的构造器。
                2)类的构造器的访问权限需要足够(private抛异常)。
         方法2. ①通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器
                //getConstructor主要区别是只能调用公有的,前者者可以调用全部的,同理后面方法属性也是。
                 Construtor cons = cls.getConstructor(String.class,int.class);

              ②向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
                  Object obj = cons.newInstance("babe",13);//返回值默认Object,也可强转Person.
                 Person p = (Person)cons.newInstance("babe",13);
                 //这里的p不是Class的对象,而是Class的对象cls的对象,相当于Class对象的对象。所以p是不能直接调用Class里的方法的,它只是cls的操作对象。

第三步:使用反射,主要是通过Class提供的Field,Method,Constructor,来操作对象获取相关信息。
            即:通过反射获取运行时类的完整结构Field,Method,Interface,Constructor,SuperClass,Annotation
              Class cls = Person.class; //获取Class类的实例
              Person p = (Person)cls.newInstance;//创建Class实例的对象
           1.对于属性而言,反射通过调用Class类中的Field来获取运行类对象的属性操作。(因为运行类时Class的实例)
               //调用属性的步骤:
              // Field  field = p.getname  错误,因为p不是Class的对象,cls才是。
               //1.调用getDeclaredField(String fieldName);
              Field field = cls.getDeclaredField("name");
             //2.保证当前的属性是可访问的:setAccessible(true)
             field.setAccessible(true);//设置为可见为true,才能可见私有的
             //3.对于属性的赋值、获取
             field.set(p, "lili");
             System.out.println(field); //输出为:private java.lang.String com.cyg.exe1.Person.name
             System.out.println(p);//重写了tostring后,Person [name=lili, age=13, Num=null]
             String pName = (String) field.get(p); //get(Object obj),输出lili.

           2.对于方法而言,反射通过调用Class类中的Method来获取运行类对象的方法操作
               //1.getDeclaredMethod()获取指定方法名,指定形参列表的方法,只能获取当前运行时
                 类中声明的所有的方法。(不包含父类中的方法)。
             Method m = clazz.getDeclaredMethod("play",String.class,int.class);
                //2.setAccessable(true):保证此方法可访问
             m.setAccessible(true);
                //3.调用此方法.如下的invoke()方法的返回值即为invoke()调用者对应的方法的返回值。
                 //如果invoke()调用者对应的方法没有返回值,此invoke()返回null.
               Object returnValue = m.invoke(p, "张三",1001);
          3.Constructor等的指定调用。

3.理解java.lang.Class类。 

 * 1.Class类是反射的源头
  * 2.java程序在经过编译(javac.exe)以后,会生成一个或多个字节码文件(.class),
  * 然后执行java.exe命令,将指定的字节码文件加载到内存中(通过JVM的类的加载器v 加载的)。加载到内存中的字节码文件本身
  * 该运行类本身就是Class的实例。
  * 3. 加载到内存中的字节码文件对应的类,就称作运行时类。比如:Person类。
  * 每一个加载到内存中的运行时类,都作为Class的实例。
  * 4. 运行时类只被加载一次。

4.Java反射全过程详解:   

  1.先获取运行时类,即获取Class的实例或者说实例化Class
  2.有了Class实例以后,可以做什么?
  应用一:可以创建对应的运行时类的对象(重点)
  应用二:获取对应的运行时类的完整的类的结构:属性、方法、构造器、包、父类、接口、泛型、注解、异常、 内部类。
  应用三:调用对应的运行时类中指定的结构(某个指定的属性、方法、构造器)(重点)调用指定属性结构

5.反射:体现java语言的动态的特性。

public Object getInstance(String className) throws Exception{
  Class clazz = Class.forName(className);
  return clazz.newInstance();
  }
}

6.创建对象的常见方式  

  1.调用构造器的方式 new
  2.直接调用静态方法:runtime.getInstance(),Calendar.getInstance();
  3.反射机制创建对象。

反射有关的API简介
1.反射实现的API简介 

  在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
–Class类:代表一个类。
–Field 类:代表类的成员变量(成员变量也称为类的属性)。
–Method类:代表类的方法。
–Constructor 类:代表类的构造方法。
–Array类:提供了动态创建数组,以及访问数组的元素的静态方法
 在java.lang.Object类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类型。

2.Class类是Reflection API 中的核心类,它有以下方法

–getName():获得类的完整名字。
–getFields():获得类的public类型的属性。
–getDeclaredFields():获得类的所有属性。
–getMethods():获得类的public类型的方法。
–getDeclaredMethods():获得类的所有方法。
-getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。
-getConstructors():获得类的public类型的构造方法。
•getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型。
•newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
•(2)通过默认构造方法创建一个新对象:
•Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
•以上代码先调用Class类的getConstructor()方法获得一个Constructor 对象,它代表默认的构造方法,然后调用Constructor对象的newInstance()方法构造一个实例。
•(3)获得对象的所有属性:
•Field fields[]=classType.getDeclaredFields();
•Class 类的getDeclaredFields()方法返回类的所有属性,包括public、protected、默认和private访问级别的属性
(4)Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,如果参数为基本类型数据,必须转换为相应的包装类型的对象。invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回
(5)Java中,无论生成某个类的多少个对象,这些对象都会对应于同一个Class对象。 要想使用反射,首先需要获得待处理类或对象所对应的Class对象。

3.案例:获取运行时类的完整结构

我们可以通过反射,获取对应的运行时类中所有的属性、方法、构造器、父类、接口、父类的泛型、包、注解、异常等。。。。
【典型题目1】
@Test
  public void test1(){
  Class clazz = Person.class;

  //获取所有的方法
  //getMethods():只能获取运行时类及其所有的父类中声明为public的方法
// Method[] methods = clazz.getMethods();
// for(Method m : methods){
// System.out.println(m);
// }

  //getDeclaredMethods():只能获取当前运行时类中声明的所有的方法。(不包含父类中的方法)
  Method[] methods = clazz.getDeclaredMethods();
  for(Method m : methods){
  System.out.println(m);
  }
  }

【典型题目2】
// 获取父类的泛型
  //功能性代码 vs 逻辑性代码
  @Test
  public void test3() {
  Class clazz = Person.class;
  Type genericSuperclass = clazz.getGenericSuperclass();
  // 强转为带具体泛型参数的Type类型
  ParameterizedType paramType = (ParameterizedType) genericSuperclass;
  // 返回泛型参数构成的数组
  Type[] typeArguments = paramType.getActualTypeArguments();
  System.out.println(((Class) typeArguments[0]).getName());
  }
  // 带泛型的父类
  @Test
  public void test2() {
  Class clazz = Person.class;
  Type genericSuperclass = clazz.getGenericSuperclass();
  System.out.println(genericSuperclass);
  }
  // 父类
  @Test
  public void test1() {
  Class clazz = Person.class;

  Class superclass = clazz.getSuperclass();
  System.out.println(superclass); }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: