- 在讲java反射机制之前,先了解下静态语言和动态语言.
- 动态语言:
是一类在运行时可以改变其结构的语言.在运行时代码可以根据某些条件改变自身结构.例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其结构上的改变.
比如动态语言有: Python、PHP、C#、JavaScript、Object-C - 静态语言:
和动态语言相对,在运行时结构不可变的语言.
比如静态语言有:Java、C、C++. - 虽然Java不是动态语言,但是Java可以被称之为“准动态语言”,原因就在于Java的反射机制可以使得开发者获得类似动态语言的特性.
-
反射
反射(Reflection)是Java被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API获取类的所有内部信息,并能直接操作对象的所有内部属性及方法.
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象,也就是.class二进制文件),这个对象包含了完整的类的结构信息.我们可以通过这个对象看到类的结构.这个对象就像一面镜子,透过这个镜子看到类的结构,所以形象的称之为:反射.
正常方式:
引入需要的“包类”名称(import) -> 通过new实例化 -> 取得实例化对象
反射方式:
实例化对象 -> getClass()方法 -> 得到完整的“包类”名称.
注: getClass()方法这只是得到Class对象的其中一种方式. -
Java反射的优缺点
优点:
可以实现动态创建对象和编译,体现出很大的灵活性
缺点:
对性能有影响.使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求.这类操作总是慢于直接执行相同的操作. -
获取Class对象的几种方式
- 通过类的class属性获取
Class c = Person.class;
- 通过类的实例的getClass()方法获取
Class c = person.getClass();
- 通过Class类的静态方法forName()获取
注: 全类名=包名+类名Class c = Class.forName("com.kenai.Person");
- 内置基本数据类型可以通过类名.TYPE获取
Class c = Integer.TYPE;
- 利用ClassLoader类加载器获取
-
通过Class对象可以获取什么信息?
可以获取该类的参数(包括private)、方法、构造函数、父类信息等等 -
哪些类型可以有Class对象
- 类: 外部类、成员内部类、静态内部类、局部内部类、匿名内部类
- 接口
- 数组
- 枚举
- 注解@interface
- 基本数据类型
- void
- class对象是在什么时候生成的呢?
在类加载的时候生成.
这就涉及到类的加载过程:
- 加载
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,然后生成一个代表该类的java.lang.class对象. - 链接(将类的二进制数据合并到JRE中).分为以下三步:
- 验证: 加载的类信息是否符合JVM规范
- 准备: 为类的静态变量分配内存,并将其初始化为默认值
- 解析: 将常量池中的符号引用(常量名)替换成直接引用(地址)
- 初始化
为静态变量和静态代码执行初始化工作,将静态变量值设置为真正的初始值. - 注: 类加载的时候只为静态变量和静态代码块执行初始化工作,非静态初始化块在每个对象生成时都会被执行一次
- 什么时候会发生类的初始化呢?(发生类的主动引用)
- 当虚拟机启动时,初始化main方法所在类
- new一个类
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则会先初始化其父类.
- 什么时候不会发生类的初始化?(类的被动引用)
- 当访问一个静态域,只有真正声明这个域的类才会被初始化.
比如当通过子类引用父类的静态变量,会导致父类的初始化,而不是导致子类的初始化. - 通过数组定义类引用,不会触发此类的初始化.
- 引用final常量不会触发此类的初始化(常量在链接阶段就存入diao)
- 反射中invoke的使用
public class User{
private String name;
private Int age;
public void setName(String name){
this.name = name
}
public String getName(){
return this.name;
}
}
public class test{
public static void main(String[] args){
// 获取User的Class对象,参数为类的全路径
Class c = Class.forName("...User")
// 获取User实例对象
User user = (User)c.new Instance();
// 通过反射获取一个方法
Method setName = c.getDeclaredMethod("setName", String.Class);
setName.invoke(user, "zlj");
System.out.println(user.getName);
// 通过反射操作属性
Field name = c.getDeclaredField("name");
// 想要获取私有的属性/方法,必须通过setAccessible()方法关闭程序的安全检测
name.setAccessible(true);
// 为属性name的set方法赋值
name.set(user, "wangwu");
System.out.println(user.getName);
}
}