Java反射

       要想理解反射的原理,首先要了解什么是类型信息。Java让我们在运行时识别对象的信息,主要有2种方式:一种是传统的RTTI,它假定我们在编译时已经知道了所有的类型信息;另一种是反射机制,它允许我们在运行时发现和使用类的信息。

RTTI与反射的区别和联系

如何获得一个类的Class对象?分为两种情况:

一:如果该类在编译前就已知。也就是该类在classPath路径下。这就是RTTI(Run-Time Type Identification运行时类型检查)

1)Class c = 类名.Class
2)Class c = Class.forName(String className)

二:如果该类编译器未知,也就是在程序运行时才知道的。这就是反射

1)Class c = Class.forName(String arg[0])

为什么要有反射?
       在编译时编译器不知道某个特定类的信息,本质是编译时无法获得并打开特定类的.class文件,而是在程序运行起来时jvm才拥有该特定类的.class文件。那么,如何使用这样的文件呢?于是“反射”这个概念应运而生—提供在运行时操作.class文件的统一API。

反射与RTTI的本质区别只是检查一个类的.class文件的时机不同:
反射:.class 文件是在编译时不可获得的,所以在运行时打开和检查未知类的.class文件从而变已知。
RTTI: .class 文件是在编译时打开和检查。

Class< T >类

在这里插入图片描述
       每个类和接口都有且仅有一个Class类型的对象,Class类封装一个对象和接口运行时的状态,当装载类时,由 Java 虚拟机自动创建 Class 对象,或通过类装载器中的 defineClass 方法生成,每个类每个对象的创建都依赖于该类的Class类型对象。

  • Class类也是类的一种

  • Class类的对象内容是你创建的类的类型信息,比如你创建一个shapes类,那么,Java会生成一个内容是shapes的Class类的对象

  • Class类的对象不能像普通类一样,以 new shapes() 的方式创建,它的对象只能由JVM创建,因为这个类没有public构造函数

  • Class类的作用是运行时提供或获得某个对象的类型信息,这些信息也可用于反射。

       Class对象仅在需要的时候才会加载,并且仅加载一次。类加载器首先会检查这个类的Class对象是否已被加载过,如果尚未加载,默认的类加载器就会根据类名查找对应的.class文件。当程序创建一个对类的静态成员的引用时,也会加载这个类,static初始化是也是在类加载时进行的。一个类在 JVM 中只会有一个 Class 实例

Class类的方法

方法名说明
forName()(1)获取Class对象的一个引用,但引用的类还没有加载(该类的第一个对象没有生成)就加载了这个类。
(2)为了产生Class引用,forName()立即就进行了初始化。
Object-getClass()获取Class对象的一个引用,返回表示该对象的实际类型的Class引用。
getName()取全限定的类名(包括包名),即类的完整名字。
getSimpleName()获取类名(不包括包名)
getCanonicalName()获取全限定的类名(包括包名)
isInterface()判断Class对象是否是表示一个接口
getInterfaces()返回Class对象数组,表示Class对象所引用的类所实现的所有接口。
getSupercalss()返回Class对象,表示Class对象所引用的类所继承的直接基类。应用该方法可在运行时发现一个对象完整的继承结构。
newInstance()返回一个Oject对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。
getFields()获得某个类的所有的公共(public)的字段,包括继承自父类的所有公共字段。 类似的还有getMethods和getConstructors。
getDeclaredFields获得某个类的自己声明的字段,即包括public、private和proteced,默认但是不包括父类声明的任何字段。类似的还有getDeclaredMethods和getDeclaredConstructors。

这里列举了几个主要的API,详细的请查看java 开发文档

获得一个Class类对象

想在运行时使用类型信息,必须获取对象的Class对象的引用
第一种办法,Class类的forName()函数

public class shapes{}  
Class obj= Class.forName("shapes");

第二种办法,使用对象的getClass()函数

public class shapes{}
shapes s1=new shapes();
Class obj=s1.getClass();
Class obj1=s1.getSuperclass();//这个函数作用是获取shapes类的父类的类型

第三种办法,使用类字面常量.class,该方法最为安全可靠,程序性能更高

Class obj=String.class;
Class obj1=int.class;

注意:使用”类.class”来创建Class对象的引用时,不会自动初始化该Class对象,使用Class.forName()函数和对象的getClass()函数 JVM会自动初始化该Class对象

使用Class类的对象来生成目标类的实例

  • 生成不精确的object实例

       获取一个Class类的对象后,可以用 newInstance() 函数来生成目标类的一个实例。然而,该函数并不能直接生成目标类的实例,只能生成object类的实例

Class obj=Class.forName("shapes");
Object ShapesInstance=obj.newInstance();
  • 使用泛型Class引用生成带类型的目标实例
Class<shapes> obj=shapes.class;
shapes newShape=obj.newInstance();

因为有了类型限制,所以使用泛化Class语法的对象引用不能指向别的类。

Class obj1=int.class;
Class<Integer> obj2=int.class;
obj1=double.class;
//obj2=double.class; 这一行代码是非法的,obj2不能改指向别的类了

然而,有个灵活的用法,使得你可以用Class的对象指向基类的任何子类。

Class<? extends Number> obj=int.class;
obj=Number.class;
obj=double.class;

因此,以下语法生成的Class对象可以指向任何类。

Class<?> obj=int.class;
obj=double.class;
obj=shapes.class;
  • 当使用这种泛型语法来构建你手头有的一个Class类的对象的基类对象时,必须采用以下的特殊语法
public class shapes{}
class round extends shapes{}
Class<round> rclass=round.class;
Class<? super round> sclass= rclass.getSuperClass();
//Class<shapes> sclass=rclass.getSuperClass();

       我们明知道,round的基类就是shapes,但是却不能直接声明 Class < shapes >,必须使用特殊语法Class < ? super round >,使用IDE的快捷键会自动生成这种引用。

反射

什么是反射

       Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。而这也是Java被视为动态(或准动态,为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。)语言的一个关键性质。

       如果不知道某个对象的确切类型,RTTI可以告诉你,但是有一个前提:这个类型在编译时必须已知,这样才能使用RTTI来识别它。Class类java.lang.reflect类库一起对反射进行了支持,该类库包含FieldMethodConstructor类,这些类的对象由JVM在启动时创建,用以表示未知类里对应的成员。
这样的话就可以使用Contructor创建新的对象,用get()和set()方法获取和修改类中与Field对象关联的字段,用invoke()方法调用与Method对象关联的方法。另外,还可以调用getFields()、getMethods()和getConstructors()等许多便利的方法,以返回表示字段、方法、以及构造器对象的数组,这样,对象信息可以在运行时被完全确定下来,而在编译时不需要知道关于类的任何事情。

反射机制的相关API

  • 获取一个对象的父类与实现的接口
 Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
        // 取得父类
        Class<?> parentClass = clazz.getSuperclass();
        System.out.println("clazz的父类为:" + parentClass.getName());
        // clazz的父类为: java.lang.Object
        // 获取所有的接口
        Class<?> intes[] = clazz.getInterfaces();
        System.out.println("clazz实现的接口有:");
        for (int i = 0; i < intes.length; i++) {
            System.out.println((i + 1) + ":" + intes[i].getName());
        }
        // clazz实现的接口有:
        // 1:java.io.Serializable
  • 获取某个类中的全部构造函数
  • 通过反射机制实例化一个类的对象
// 第一种方法,实例化默认构造方法,调用set赋值
       User user = (User) class1.newInstance();
       user.setAge(20);
       user.setName("Rollen");
       System.out.println(user);
       // 结果 User [age=20, name=Rollen]
       // 第二种方法 取得全部的构造函数 使用构造函数赋值
       Constructor<?> cons[] = class1.getConstructors();
       // 查看每个构造方法需要的参数
       for (int i = 0; i < cons.length; i++) {
           Class<?> clazzs[] = cons[i].getParameterTypes();
           System.out.print("cons[" + i + "] (");
           for (int j = 0; j < clazzs.length; j++) {
               if (j == clazzs.length - 1)
                   System.out.print(clazzs[j].getName());
               else
                   System.out.print(clazzs[j].getName() + ",");
           }
           System.out.println(")");
       }
       // 结果
       //cons[0] (int,java.lang.String)
       //cons[1] (java.lang.String)
       // cons[2] ()
       user = (User) cons[0].newInstance(21, "Rollen");
       System.out.println(user);
       // 结果 User [age=0, name=Rollen]
       user = (User) cons[1].newInstance("jame");
       System.out.println(user);
       // 结果 User [age=20, name=Rollen] 
  • 获取某个类的全部属性
Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
       System.out.println("===============本类属性===============");
       // 取得本类的全部属性
       Field[] field = clazz.getDeclaredFields();
       for (int i = 0; i < field.length; i++) {
           // 权限修饰符
           int mo = field[i].getModifiers();
           String priv = Modifier.toString(mo);
           // 属性类型
           Class<?> type = field[i].getType();
           System.out.println(priv + " " + type.getName() + " " + field[i].getName() + ";");
       }
        
       System.out.println("==========实现的接口或者父类的属性==========");
       // 取得实现的接口或者父类的属性
       Field[] filed1 = clazz.getFields();
       for (int j = 0; j < filed1.length; j++) {
           // 权限修饰符
           int mo = filed1[j].getModifiers();
           String priv = Modifier.toString(mo);
           // 属性类型
           Class<?> type = filed1[j].getType();
           System.out.println(priv + " " + type.getName() + " " + filed1[j].getName() + ";");
       }
  • 获取某个类的全部方法
    Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
       Method method[] = clazz.getMethods();
       for (int i = 0; i < method.length; ++i) {
           Class<?> returnType = method[i].getReturnType();
           Class<?> para[] = method[i].getParameterTypes();
           int temp = method[i].getModifiers();
           System.out.print(Modifier.toString(temp) + " ");
           System.out.print(returnType.getName() + "  ");
           System.out.print(method[i].getName() + " ");
           System.out.print("(");
           for (int j = 0; j < para.length; ++j) {
               System.out.print(para[j].getName() + " " + "arg" + j);
               if (j < para.length - 1) {
                   System.out.print(",");
               }
           }
           Class<?> exce[] = method[i].getExceptionTypes();
           if (exce.length > 0) {
               System.out.print(") throws ");
               for (int k = 0; k < exce.length; ++k) {
                   System.out.print(exce[k].getName() + " ");
                   if (k < exce.length - 1) {
                       System.out.print(",");
                   }
               }
           } else {
               System.out.print(")");
           }
           System.out.println();
       }
  • 通过反射机制调用某个类的方法
  public static void main(String[] args) throws Exception {
       Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
       // 调用TestReflect类中的reflect1方法
       Method method = clazz.getMethod("reflect1");
       method.invoke(clazz.newInstance());
       // Java 反射机制 - 调用某个类的方法1.
       // 调用TestReflect的reflect2方法
       method = clazz.getMethod("reflect2", int.class, String.class);
       method.invoke(clazz.newInstance(), 20, "张三");
       // Java 反射机制 - 调用某个类的方法2.
       // age -> 20. name -> 张三
   }
   public void reflect1() {
       System.out.println("Java 反射机制 - 调用某个类的方法1.");
   }
   public void reflect2(int age, String name) {
       System.out.println("Java 反射机制 - 调用某个类的方法2.");
       System.out.println("age -> " + age + ". name -> " + name);
   }
  • 通过反射机制操作某个类的属性
package net.xsoftlab.baike;
import java.lang.reflect.Field;
public class TestReflect {
   private String proprety = null;
   public static void main(String[] args) throws Exception {
       Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
       Object obj = clazz.newInstance();
       // 可以直接对 private 的属性赋值
       Field field = clazz.getDeclaredField("proprety");
       field.setAccessible(true);
       field.set(obj, "Java反射机制");
       System.out.println(field.get(obj));
   }
}

反射能做什么

       我们知道反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!

反射的常见作用有:

  • 动态加载类,动态获取类的信息(属性、方法、构造器
  • 动态构造对象
  • 动态调用类和对象的任意方法、构造器
  • 动态调用和处理属性
  • 生成动态代理
  • 获取泛型信息
  • 处理注解
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值