一、关于反射需要了解的一些基本概念
1、java类:Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,
至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同的实例对象有不同的属性值。
2、类的生命周期
在一个类编译完成之后,下一步就需要开始使用类,如果要使用一个类,肯定离不开JVM。在程序执行中JVM通过装载,链接,初始化这3个步骤完成。
类的装载是通过类加载器完成的,加载器将.class文件的二进制文件装入JVM的方法区,并且在堆区创建描述这个类的java.lang.Class对象。用来封装数据。
但是同一个类只会被类装载器装载一次。链接就是把二进制数据组装为可以运行的状态。
链接分为校验,准备,解析这3个阶段
校验一般用来确认此二进制文件是否适合当前的JVM(版本),
准备就是为静态成员分配内存空间,。并设置默认值
解析指的是转换常量池中的代码作为直接引用的过程,直到所有的符号引用都可以被运行程序使用(建立完整的对应关系)
完成之后,类型也就完成了初始化,初始化之后类的对象就可以正常使用了,直到一个对象不再使用之后,将被垃圾回收。释放空间。
当没有任何引用指向Class对象时就会被卸载,结束类的生命周期。
3、反射:由java类的基本概念,我们可以知道,java程序中各个java类属于同一类事物,而描述这类事物的java类名就是Class。
说白了,反射就是把java类中的各个成分映射成相应的java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:
成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。
表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,
它们是Field、Method、Contructor、Package等等。
4、内存中的思考:一个类被加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同类的字节码是不同的,所以他们在内存中
的内容也是不同的,这一个个的空间分别用一个个的对象来表示,这些对象具有相同的类型,这个类型就是Class.这些字节码只被装载一次。
二、对于反射的具体实现
1、在进入反射的代码使用时,我们需要先了解一下,jdk为反射提供了那些类与方法
在jdk中,主要由一下类来实现Java的反射机制,这些类都位于java.lang.reflect包中
Class类:代表一个类(类的模板)
Field类:代表类的成员变量(即类的属性)
Method类:代表类的方法。
Constructor类:代表类的构造方法
Arrays类:动态创建数组,以及访问数组元素的静态方法(数组的排序sort(),数组中的最大值getMax()等)
其中Class类是Reflaction API的核心类。
类中的方法和和具体的作用
a.获得一个类的类模板
Class clazz = 对象名.getClass();
Class clazz = Class.forName(包名+类名);这是我们经常用到的,比如说在jdbc中获得某个数据库的驱动器的类
Class clazz = 类名.class(方法中以Class类型作为参数时,经常用到)
b.根据类模板获取类的信息:
Field类 ---clazz.getFiled(String pname) 获得指定属性(公共字段)
Filed类----clazz.getDeclearedFiled(String pname) 获得指定属性(所有权限字段)
Filed[]----clazz.getFileds(String pname) 获得所有属性(公共字段)
Filed[]----clazz.getDeclearedFileds(String pname) 获得所有属性(所有权限字段)
通过以上方法获得的Field的类的对象是属于类的即clazz,而不是这个类所代表的的具体的对象的属性,
如果要利用反射获得对象的属性,需要用到Filed类提供的public Object get(Object obj)的方法,
返回指定对象上此 Field 表示的字段的值(obj就是要得到属性的对象)。如果该值是一个基本类型值,则自动将其包装在一个对象中。
c.获取类的方法:
Method类----clazz.getMethod(StringmethodName,class[] params) 获得指定方法(公共方法)
Method类----clazz.getDeclearedMethod(StringmethodName,class[] params) 获得指定方法(所有方法)
Method[]----clazz.getMethods(StringmethodName,class[] params) 获得所有方法(公共方法)
Method[]----clazz.getDeclearedMethods(StringmethodName,class[] params) 获得所有方法(所有权限方法)
通过以上方法获得类模板的方法并不包括类的构造方法,上面只是获得方法,如果要执行某个对象的方法,
则要用到Method类中的public Object invoke(Object obj,Object... args),在使用这个方法时有两点要注意:
1。如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。
2.如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null。
d.获取类的构造方法:
Constructors类---getConstructor(Class>... parameterTypes) 获得指定构造方法(公共方法)
Constructors[]---public Constructor>[] getConstructors() 获取所有类的构造方法(公共的)
Constructors类---public Constructor getDeclaredConstructor(Class>... parameterTypes)
Class 对象所表示的类或接口的指定构造方法
Constructors[]---public Constructor>[] getDeclaredConstructors() 获取所有类的构造方法(公共、保护、默认(包)访问和私有构造方法)
通过以上方法可以获得类的所有的构造方法,如果要创建类的具体的实例,则需要用到Constructors类中的public T newInstance(Object... initargs),
注意点:如果底层构造方法所需形参数为 0,则所提供的 initargs 数组的长度可能为 0 或 null。
2、介绍完所有的类和方法,下面就是演示案例了
-
package cn.yusheng.reflaction
-
-
-
-
public class Test {
-
public static void main(String[] args) {
-
-
System.out.println("我是Test类的main()方法");
-
-
}
-
-
}
-
-
-
package cn.yusheng.reflaction
-
-
import java.lang.reflect.Constructor;
-
import java.lang.reflect.Field;
-
import java.lang.reflect.Method;
-
import java.util.Date;
-
-
public class ReflectTest {
-
public static void main(String[] args) throws Exception {
-
-
Class cls1 = String.class;
-
Class cls2 = new String().getClass();
-
Class cls3 = Class.forName("java.lang.String");
-
System.out.println(cls1.getName()+"--"+cls2.getName()+"--"+cls3.getName());
-
-
-
-
System.out.println(Integer.TYPE == int.class);
-
-
-
-
Class cls4 = Class.forName("java.lang.String");
-
Constructor cons = cls4.getConstructor(StringBuffer.class);
-
-
String str = (String) cons.newInstance(new StringBuffer("我是通过反射出的构造方法创建的String实例"));
-
-
-
System.out.println(str);
-
-
-
-
-
Class cls5 = Class.forName("java.lang.String");
-
String str2 = (String) cls5.newInstance();
-
-
-
ReflectPoint r1 = new ReflectPoint(1, 2);
-
Class cls6 = Class.forName("ReflectPoint");
-
-
Field x = cls6.getDeclaredField("x");
-
x.setAccessible(true);
-
int x1 = (Integer)x.get(r1);
-
System.out.println(x1);
-
-
-
String str3 = "hello world";
-
Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
-
-
System.out.println("hello world中的第2个字符是:"+charAt.invoke(str3, 1));
-
-
-
Class cls7 = Class.forName("Test");
-
Method main = cls7.getMethod("main", String[].class);
-
main.invoke(cls7, (Object)new String[]{"12","13"});
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
}
-
}
-
-
-
-
class ReflectPoint{
-
private int x;
-
public int y;
-
public ReflectPoint(int x, int y) {
-
super();
-
this.x = x;
-
this.y = y;
-
}
-
-
}
动态创建和访问数组
java.lang.Array 类提供了动态创建和访问数组元素的各种静态方法
例程ArrayTester1 类的main()方法创建了一个长度为10 的字符串数组,接着把索引位置为5 的
元素设为“hello”,然后再读取索引位置为5 的元素的值
代码实现:
-
public class ArrayTester1 {
-
public static void main(String args[]) throws Exception {
-
Class classType = Class.forName("java.lang.String");
-
-
Object array = Array.newInstance(classType, 10);
-
-
Array.set(array, 5, "hello");
-
-
String s = (String) Array.get(array, 5);
-
System.out.println(s);
-
}
-
-
}
三、总结与思考
反射的优缺点:
为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念,
静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多
态的应用,有以降低类之间的藕合性。
一句话,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中
它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编
译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如
这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能
的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功
能。
它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它
满足我们的要求。这类操作总是慢于只直接执行相同的操作。
写到这,关于反射的知识算是总节的差不多了,还好这次写博客用了2个小时,算是有点进步吧。其实对于javaSE的
东西我以前都是看过的,如今只是复习,进度算是蛮快的,但这样边写博客边复习,却让我发现自己还有好多的知识点
掌握的不牢固,还需要加深,不过还有时间,慢慢来,静下心来,赶不上第一批录取,就力争第二批吧,只要我努力,
我相信一定会通过的。
阅读(321) | 评论(0) | 转发(0) |