当我们使用eclipse等IDE工具的时候,代码可以自动补全,这其中的原理是什么呢? 有没有什么方式在不改变Test类的情况下访问如下的私有变量呢?
public class Test { private int age; private String name; }下面将通过反射知识的学习进行以上问题的解决。 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。 有所了解的可直接看文末代码。
Java作为一门面向对象的语言,一切都是对象(除了八个基本数据类型),因此类也是一种对象。系统中所有的类都是java.lang.Class的实例。 当程序主动使用某个类时,如果类尚未被加载到内存中,系统会对该类进行加载、连接、初始化操作。 类的加载由类加载器完成,类加载器通常由JVM提供。 通过使用不同的可加载器,可以从不同来源加载类的二进制数据,通常由如下几种: - 从本地文件系统加载class文件,最常见的一种方式; - 从JAR包加载class文件,比较常见; - 通过网络加载class文件; - 把一个Java源文件动态编译,并执行加载。
当类别加载之后,系统位置生成一个对应的Class对象,接着会进入连接阶段:把类的二进制数据合并到JRE中。分为三个阶段: - 验证:检验被加载的类是否有正确的内部结果; - 准备:负责为类的类变量分配内存,并设置默认初始值; - 解析:将类的二进制数据中的符号引用替换成直接引用。
类的初始化阶段,虚拟机负责对类进行初始化,主要是对类变量进行初始化。类变量制定初始化值有两种方式:①声明类变量是指定初始值;②使用静态初始化块为类变量制定初始化值。 JVM初始化类包含以下几个步骤: - 假如这个类还没有被加载和连接,则程序先加载并连接该类; - 假如该类的直接父类尚未被初始化,则先初始化其直接父类; - 假如类中有初始化语句,则系统一次执行这些初始化语句。 类初始化时机 当Java程序首次通过以下六种方式使用某个类或接口时,系统会初始化该类或接口: - 创建类的实例。包括:使用new操作符创建实例;通过反射创建实例;通过反序列化创建实例(?)。 - 调用某个类的类方法。 - 访问某个类或接口的类变量,或为该类变量赋值。 - 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。 - 初始化某个类的子类, - 直接使用java.exe命令运行某个主类。
类加载器负责将.class文件加载到内存中,并为之生成java.lang.Class对象。 当JVM启动时,会形成由三个类加载器组成的初始类加载器层次结构。 - Bootstrap ClassLoader:根类加载器。负责加载Java核心类。 - Extension ClassLoader:扩展类加载器。负责加载JRE扩展目录(C:\Program Files\Java\jre1.8.0_131\lib\ext)中JAR包的类。 - System ClassLoader:系统类加载器。负责在JVM启动时加载来自java命令的-classpath选项、java.class.path系统属性,或CLASSPATH环境变量所指定的JAR包和类路径。
Java程序中许多对象在运行时会出现两种类型:编译时类型和运行时类型。如
Person person = new Student();以上代码将会生成变量person,该变量的编译时类型为Person,运行时类型为Student。程序在运行时发现对象和类的真实信息,有如下两种方式:
在编译时和运行时完全知道类型的具体信息。可以先使用instanceof运算符进行判断,再利用强制类型转换将其转换成其运行时类型的变量。如下列: Object obj = "arvin"; if (obj instanceof String) { String str = (String) obj; } 编译时无法预知该对象和类可能属于哪些类,程序只能依靠运行时信息发现类的真实信息,则需要使用反射。类在加载后。系统回味该类生成一个对象的Class对象,通过该对象可以访问JVM中的这个类。在Java程序中有如下三种方式获取Class对象: - 使用Class类的forName(String clazzName)静态方法。传入的clazzName参数值必须使某个类的全限定类名(包括完整包名)。 - 调用某个类的class属性来获取该类对应的Class对象。如:Person.class将会返回Person类对象的Class对象。 - 调用某个对象的getClass()方法。 对于第一种方式和第二种方式都是直接根据类返回所对应的Class对象,第二种方式有如下两个优势: - 代码更安全。代码在编译阶段就可以检查需要刚问的Class对象是否存在。 - 程序性能更好。因为无需调用方法。 因而大部分时候使用第二种方式获取指定类的Class对象。但如果程序只能获得一个字符串,则只能使用第一种方式。 一旦获得某个类所对应的Class对象之后,程序就可以调用Class对象的方法来获得该对象的信息。
Class类提供了大量的实例方法来获取该Class对象所对应类的详细信息。详细信息可查阅API文档,下面仅列出常用方法。
通过反射生成对象有两种方式 - 使用Class对象的newInstance()方法来创建该Class对象对应类的实例,要求该Class对象的对应类有默认构造器。 - 使用Class对象获取制定Constructor对象,在调用Constructor对象的newInstance()方法创建Class对象的对应类的实例,该方式可选择使用制定的构造器来创建实例。
控制台输出结果
年龄是:0 姓名是:zhangsan