反射是Java中的一个重要内容,反射可以实现Java在编码过程中的动态性,可以更加灵活编写代码,但是反射使用不当会造成很高的资源消耗。学习反射,可以更好理解Java面向对象的核心概念,不断深入和了解,真正理解“世间万物皆对象”
文章介绍
本文先回介绍Java在创建对象的一个基本过程,深入到底层,来引出发射的概念,探讨反射机制的具体实现和反射机制的效率,最后讲解如何提高反射机制的效率
创建对象基本过程
●Java代码在计算机中经历的三个阶段:如下图所示
上图解析:首先计算机CPU只会从内存中读取数据,不会从硬盘中读取数据。在Java代码写好之后,经过编译器进行编译,形成.class字节码文件,虚拟机使用类加载器(ClassLoader)将类从硬盘加载到内存当中,形成Class类对象(我到这里理解了原来一个类也可以看作一个对象)其中有成员变量,构造方法,还有成员方法等等,他会加载到内存中的一个方法堆区,最后到达运行阶段从方法堆区中读取类的信息。
●创建对象时的内存结构
当类加载到方法区中之后(这个就是类的模板,根据类的模板来创建对象,就像是一面镜子),在栈中创建对象的引用,在堆中创建user对象,将创建好的对象的地址赋给栈中对象的引用等
反射介绍
●概念:动态获取类的信息以及动态区调用对象方法,这就是反射机制(我理解的反射就是操作类对象,通过Class对象,来操作创建好的类,拿到这个类中的所有东西)
●Class这个对象就像一面镜子,透过这个镜子可以看到类的结构,所以,我们形象的称之为:反射。 因此,“Class 对象”是反射机制的核心
反射的具体实现
●获取Class对象的三种方式
通过GetClass()方法、通过.class静态属性、通过Class类中的静态方法forName()
Class cs=users.getClass();
Class cs=Users.class;
Class cs=Class.forName("javareflect.Users");
类对象获取之后,我们就可以通过类对象来对操作某个类中的一些东西了,具体操作如下:(看完方法再看这一句话:总结以下,大同小异,只要方法中带”Declared“的,可以忽视私有,任意的都可以得到,而没有Declared的只可以获得公共的,有”s“的都是获得全部,不带s的都是获得具体的单个,)
●获取类的构造方法
●获取类的成员变量
●获取类的方法
获取到类的方法之后,最重要的还是使用,用invoke()方法就可以
●获取类的一些其他信息
比如:获取类名,获取包名、获取超类(直接父类)、获取该类是实现的所有接口等等,和上面列举的大同小异,可以参照具体API文档进行学习,举一反三,这里就不一 一列举。
反射应用案例
应用说明:根据给定的方法名顺序来决定方法的执行顺序
先创建一个方法类
package javareflect;
public class ReflectTest {
public void method1(){
System.out.println("method1...");
}
public void method2(){
System.out.println("method2...");
}
public void method3(){
System.out.println("method3...");
}
}
创建测试类
package javareflect;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception{
ReflectTest rf=new ReflectTest();
if (args!=null&args.length>0){
//获取ReflectTest的Class对象
Class c=ReflectTest.class;
//通过反射获得ReflectTest下的所有方法
Method[] methods=c.getMethods();
//通过遍历来对主方法中参数和Methods数组中方法的匹配
for (String str:args){
for (int i=0;i<methods.length;i++){
if (str.equalsIgnoreCase(methods[i].getName())){
methods[i].invoke(rf);
break;
}
}
}
}else {
rf.method1();
rf.method2();
rf.method3();
}
}
}
在主方法中有个参数String类型的数组,名字叫args,如果我们不设置,他是空的,不传递参数,但是如果我们传递了参数,我们可以根据参数参数的顺序来决定方法执行的顺序
下图就是在主方法中传入参数:
运行截图
不在主方法中添加参数:
在主方法中传入参数:
反射机制的效率
●由于 Java 反射是要解析字节码,将内存中的对象进行解析,包括了一些动态类型,而JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多!
●反射机制的效率测试
首先创建Users类
package javareflect;
public class Users {
private String name;
public int age;
private void method(){
System.out.println("method");
}
public Users() {
}
public Users(String name, int age) {
this.name = name;
this.age = age;
}
public Users(String name) {
this.name = name;
}
private Users(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
我们对Users类中的setAge方法进行一亿次的调用来感受,通过反射创建对象执行的效率和通过new创建对象执行的效率
package javareflect;
import java.lang.reflect.Method;
public class Test2 {
public static void main(String[] args)throws Exception {
//通过反射方式执行的效率
Class c=Users.class;
long Reflectstart=System.currentTimeMillis();
Users user=(Users) c.newInstance();
Method method=c.getMethod("setAge", int.class);
for (int i=0;i<100000000;i++){
method.invoke(user,18);
}
long Reflectend=System.currentTimeMillis();
//通过普通方式执行的效率
long start=System.currentTimeMillis();
Users users=new Users();
for (int i=0;i<100000000;i++){
users.setAge(18);
}
long end=System.currentTimeMillis();
System.out.println("使用反射的时间"+(Reflectend-Reflectstart));
System.out.println("不使用反射的时间"+(end-start));
}
}
运行结果
提高反射机制的效率
●首先先介绍setAccessible()方法:setAccessible 是启用和禁用访问安全检查的开关。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检
查;默认值为 false。虽然之前我们可以获得类中的一些私有属性和方法,但是我们不可以操作他们,因为我们没有关闭访问性检查。
●如果我们在上一个效率测试的案例中加入忽略安全性检查,我们再看运行结果
总结
●Java 反射机制使得 Java具有了动态性
●优点:更灵活,更开放
●缺点:降低了效率,增加了代码维护困难
以上就是我现阶段对Java反射机制的了解和所得,如果有理解不对的地方,还望大家多多指教,之后有更加深入的学习,我会及时的进行补充,希望我和大家一起共同进步!