Java反射机制
一、什么是反射机制
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
二、反射机制的作用
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成动态代理
三. 反射机制的优点与缺点
反射机制的优点:可以实现动态创建对象和编译,体现出很大的灵活性(特别是在J2EE的开发中它的灵活性就表现的十分明显)。通过反射机制我们可以获得类的各种内容,进行了反编译。对于JAVA这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。
反射机制的缺点:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此Java反射机制只应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。使用反射会模糊程序内部逻辑:程序员希望在代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。
四. 反射机制的示例
1.通过一个对象获得完整的包名和类名
所有类的对象其实都是Class的实例。
1 package Reflect;
2
3 class Demo{
4 //other codes...
5 }
6
7 class hello{
8 public static void main(String[] args) {
9 Demo demo=new Demo();
10 System.out.println(demo.getClass().getName());
11 }
12 }
13 //【运行结果】:Reflect.Demo
2.实例化Class类对象
1 package Reflect;
2
3 class Demo{
4 //other codes...
5 }
6
7 class hello{
8 public static void main(String[] args) {
9 Class<?> demo1=null;
10 Class<?> demo2=null;
11 Class<?> demo3=null;
12 try{
13 //一般尽量采用这种形式
14 demo1=Class.forName("Reflect.Demo");
15 }catch(Exception e){
16 e.printStackTrace();
17 }
18 demo2=new Demo().getClass();
19 demo3=Demo.class;
20
21 System.out.println("类名称 "+demo1.getName());
22 System.out.println("类名称 "+demo2.getName());
23 System.out.println("类名称 "+demo3.getName());
24 }
25 }
26 //【运行结果】:
27 //类名称 Reflect.Demo
28 //类名称 Reflect.Demo
29 //类名称 Reflect.Demo
3.通过Class实例化其他类的对象
1 package Reflect;
2
3 class Person{
4 public String getName() {
5 return name;
6 }
7 public void setName(String name) {
8 this.name = name;
9 }
10 public int getAge() {
11 return age;
12 }
13 public void setAge(int age) {
14 this.age = age;
15 }
16 @Override
17 public String toString(){
18 return "["+this.name+" "+this.age+"]";
19 }
20 private String name;
21 private int age;
22 }
23
24 class hello{
25 public static void main(String[] args) {
26 Class<?> demo=null;
27 try{
28 demo=Class.forName("Reflect.Person");
29 }catch (Exception e) {
30 e.printStackTrace();
31 }
32 Person per=null;
33 try {
34 per=(Person)demo.newInstance();
35 } catch (InstantiationException e) {
36 // TODO Auto-generated catch block
37 e.printStackTrace();
38 } catch (IllegalAccessException e) {
39 // TODO Auto-generated catch block
40 e.printStackTrace();
41 }
42 per.setName("Rollen");
43 per.setAge(20);
44 System.out.println(per);
45 }
46 }
47 //【运行结果】:
48 //[Rollen 20]
但是注意一下,当我们把Person中的默认的无参构造函数取消的时候,比如自己定义只定义一个有参数的构造函数之后,会出现错误:
比如定义了一个构造函数:
1 public Person(String name, int age) {
2 this.age=age;
3 this.name=name;
4 }
然后继续运行上面的程序,会出现:
java.lang.InstantiationException: Reflect.Person
at java.lang.Class.newInstance0(Class.java:340)
at java.lang.Class.newInstance(Class.java:308)
at Reflect.hello.main(hello.java:39)
Exception in thread "main" java.lang.NullPointerException
at Reflect.hello.main(hello.java:47)
所以大家以后再编写使用Class实例化其他类的对象的时候,一定要自己定义无参的构造函数。
4.通过Class调用其他类中的构造函数 (也可以通过这种方式通过Class创建其他类的对象)
1 package Reflect;
2
3 import java.lang.reflect.Constructor;
4
5 class Person{
6 public Person() {
7
8 }
9 public Person(String name){
10 this.name=name;
11 }
12 public Person(int age){
13 this.age=age;
14 }
15 public Person(String name, int age) {
16 this.age=age;
17 this.name=name;
18 }
19 public String getName() {
20 return name;
21 }
22 public int getAge() {
23 return age;
24 }
25 @Override
26 public String toString(){
27 return "["+this.name+" "+this.age+"]";
28 }
29 private String name;
30 private int age;
31 }
32
33 class hello{
34 public static void main(String[] args) {
35 Class<?> demo=null;
36 try{
37 demo=Class.forName("Reflect.Person");
38 }catch (Exception e) {
39 e.printStackTrace();
40 }
41 Person per1=null;
42 Person per2=null;
43 Person per3=null;
44 Person per4=null;
45 //取得全部的构造函数
46 Constructor<?> cons[]=demo.getConstructors();
47 try{
48 per1=(Person)cons[0].newInstance();
49 per2=(Person)cons[1].newInstance("Rollen");
50 per3=(Person)cons[2].newInstance(20);
51 per4=(Person)cons[3].newInstance("Rollen",20);
52 }catch(Exception e){
53 e.printStackTrace();
54 }
55 System.out.println(per1);
56 System.out.println(per2);
57 System.out.println(per3);
58 System.out.println(per4);
59 }
60 }
61 //【运行结果】:
62 //[null 0]
63 //[Rollen 0]
64 //[null 20]
65 //[Rollen 20]
5.返回一个类实现的接口
1 package Reflect;
2
3 interface China{
4 public static final String name="Rollen";
5 public static int age=20;
6 public void sayChina();
7 public void sayHello(String name, int age);
8 }
9
10 class Person implements China{
11 public Person() {
12
13 }
14 public Person(String sex){
15 this.sex=sex;
16 }
17 public String getSex() {
18 return sex;
19 }
20 public void setSex(String sex) {
21 this.sex = sex;
22 }
23 @Override
24 public void sayChina(){
25 System.out.println("hello ,china");
26 }
27 @Override
28 public void sayHello(String name, int age){
29 System.out.println(name+" "+age);
30 }
31 private String sex;
32 }
33
34 class hello{
35 public static void main(String[] args) {
36 Class<?> demo=null;
37 try{
38 demo=Class.forName("Reflect.Person");
39 }catch (Exception e) {
40 e.printStackTrace();
41 }
42 //保存所有的接口
43 Class<?> intes[]=demo.getInterfaces();
44 for (int i = 0; i < intes.length; i++) {
45 System.out.println("实现的接口 "+intes[i].getName());
46 }
47 }
48 }
49 //【运行结果】:
50 //实现的接口 Reflect.China
(以下几个例子,都会用到这个例子的Person类,所以为节省篇幅,此处不再粘贴Person的代码部分,只粘贴主类hello的代码)
6.取得其他类中的父类
1 class hello{
2 public static void main(String[] args) {
3 Class<?> demo=null;
4 try{
5 demo=Class.forName("Reflect.Person");
6 }catch (Exception e) {
7 e.printStackTrace();
8 }
9 //取得父类
10 Class<?> temp=demo.getSuperclass();
11 System.out.println("继承的父类为: "+temp.getName());
12 }
13 }
14 //【运行结果】
15 //继承的父类为: java.lang.Object
7.获得其他类中的全部构造函数
1 //这个例子需要在程序开头添加import java.lang.reflect.*;
2 class hello{
3 public static void main(String[] args) {
4 Class<?> demo=null;
5 try{
6 demo=Class.forName("Reflect.Person");
7 }catch (Exception e) {
8 e.printStackTrace();
9 }
10 Constructor<?>cons[]=demo.getConstructors();
11 for (int i = 0; i < cons.length; i++) {
12 System.out.println("构造方法: "+cons[i]);
13 }
14 }
15 }
16 //【运行结果】:
17 //构造方法: public Reflect.Person()
18 //构造方法: public Reflect.Person(java.lang.String)
1 class hello{
2 public static void main(String[] args) {
3 Class<?> demo=null;
4 try{
5 demo=Class.forName("Reflect.Person");
6 }catch (Exception e) {
7 e.printStackTrace();
8 }
9 Constructor<?>cons[]=demo.getConstructors();
10 for (int i = 0; i < cons.length; i++) {
11 Class<?> p[]=cons[i].getParameterTypes();
12 System.out.print("构造方法: ");
13 int mo=cons[i].getModifiers();
14 System.out.print(Modifier.toString(mo)+" ");
15 System.out.print(cons[i].getName());
16 System.out.print("(");
17 for(int j=0;j<p.length;++j){
18 System.out.print(p[j].getName()+" arg"+i);
19 if(j<p.length-1){
20 System.out.print(",");
21 }
22 }
23 System.out.println("){}");
24 }
25 }
26 }
27 //【运行结果】:
28 //构造方法: public Reflect.Person(){}
29 //构造方法: public Reflect.Person(java.lang.String arg1){}
8.取得其他类的全部属性,将这些整理在一起,也就是通过class取得一个类的全部框架
1 class hello {
2 public static void main(String[] args) {
3 Class<?> demo = null;
4 try {
5 demo = Class.forName("Reflect.Person");
6 } catch (Exception e) {
7 e.printStackTrace();
8 }
9 System.out.println("===============本类属性========================");
10 // 取得本类的全部属性
11 Field[] field = demo.getDeclaredFields();
12 for (int i = 0; i < field.length; i++) {
13 // 权限修饰符
14 int mo = field[i].getModifiers();
15 String priv = Modifier.toString(mo);
16 // 属性类型
17 Class<?> type = field[i].getType();
18 System.out.println(priv + " " + type.getName() + " "
19 + field[i].getName() + ";");
20 }
21 System.out.println("===============实现的接口或者父类的属性========================");
22 // 取得实现的接口或者父类的属性
23 Field[] filed1 = demo.getFields();
24 for (int j = 0; j < filed1.length; j++) {
25 // 权限修饰符
26 int mo = filed1[j].getModifiers();
27 String priv = Modifier.toString(mo);
28 // 属性类型
29 Class<?> type = filed1[j].getType();
30 System.out.println(priv + " " + type.getName() + " "
31 + filed1[j].getName() + ";");
32 }
33 }
34 }
35 //【运行结果】:
36 //===============本类属性========================
37 //private java.lang.String sex;
38 //===============实现的接口或者父类的属性========================
39 //public static final java.lang.String name;
40 //public static final int age;
9.通过反射调用其他类中的方法
1 class hello {
2 public static void main(String[] args) {
3 Class<?> demo = null;
4 try {
5 demo = Class.forName("Reflect.Person");
6 } catch (Exception e) {
7 e.printStackTrace();
8 }
9 try{
10 //调用Person类中的sayChina方法
11 Method method=demo.getMethod("sayChina");
12 method.invoke(demo.newInstance());
13 //调用Person的sayHello方法
14 method=demo.getMethod("sayHello", String.class,int.class);
15 method.invoke(demo.newInstance(),"Rollen",20);
16 }catch (Exception e) {
17 e.printStackTrace();
18 }
19 }
20 }
21 //【运行结果】:
22 //hello ,china
23 //Rollen 20
10.调用其他类的set和get方法
1 class hello {
2 public static void main(String[] args) {
3 Class<?> demo = null;
4 Object obj=null;
5 try {
6 demo = Class.forName("Reflect.Person");
7 } catch (Exception e) {
8 e.printStackTrace();
9 }
10 try{
11 obj=demo.newInstance();
12 }catch (Exception e) {
13 e.printStackTrace();
14 }
15 setter(obj,"Sex","男",String.class);
16 getter(obj,"Sex");
17 }
18
19 /**
20 * @param obj 操作的对象
21 * @param att 操作的属性
22 * */
23 public static void getter(Object obj, String att) {
24 try {
25 Method method = obj.getClass().getMethod("get" + att);
26 System.out.println(method.invoke(obj));
27 } catch (Exception e) {
28 e.printStackTrace();
29 }
30 }
31
32 /**
33 * @param obj 操作的对象
34 * @param att 操作的属性
35 * @param value 设置的值
36 * @param type 参数的属性
37 * */
38 public static void setter(Object obj, String att, Object value,
39 Class<?> type) {
40 try {
41 Method method = obj.getClass().getMethod("set" + att, type);
42 method.invoke(obj, value);
43 } catch (Exception e) {
44 e.printStackTrace();
45 }
46 }
47 }// end class
48 //【运行结果】:
49 //男
11.通过反射操作属性
1 class hello {
2 public static void main(String[] args) throws Exception {
3 Class<?> demo = null;
4 Object obj = null;
5
6 demo = Class.forName("Reflect.Person");
7 obj = demo.newInstance();
8
9 Field field = demo.getDeclaredField("sex");
10 field.setAccessible(true);
11 field.set(obj, "男");
12 System.out.println(field.get(obj));
13 }
14 }// end class
12.通过反射取得并修改数组的信息
1 import java.lang.reflect.*;
2
3 class hello{
4 public static void main(String[] args) {
5 int[] temp={1,2,3,4,5};
6 Class<?>demo=temp.getClass().getComponentType();
7 System.out.println("数组类型: "+demo.getName());
8 System.out.println("数组长度 "+Array.getLength(temp));
9 System.out.println("数组的第一个元素: "+Array.get(temp, 0));
10 Array.set(temp, 0, 100);
11 System.out.println("修改之后数组第一个元素为: "+Array.get(temp, 0));
12 }
13 }
14 //【运行结果】:
15 //数组类型: int
16 //数组长度 5
17 //数组的第一个元素: 1
18 //修改之后数组第一个元素为: 100
13.通过反射修改数组大小
1 class hello{
2 public static void main(String[] args) {
3 int[] temp={1,2,3,4,5,6,7,8,9};
4 int[] newTemp=(int[])arrayInc(temp,15);
5 print(newTemp);
6 System.out.println("=====================");
7 String[] atr={"a","b","c"};
8 String[] str1=(String[])arrayInc(atr,8);
9 print(str1);
10 }
11 /**
12 * 修改数组大小
13 * */
14 public static Object arrayInc(Object obj,int len){
15 Class<?>arr=obj.getClass().getComponentType();
16 Object newArr=Array.newInstance(arr, len);
17 int co=Array.getLength(obj);
18 System.arraycopy(obj, 0, newArr, 0, co);
19 return newArr;
20 }
21 /**
22 * 打印
23 * */
24 public static void print(Object obj){
25 Class<?>c=obj.getClass();
26 if(!c.isArray()){
27 return;
28 }
29 System.out.println("数组长度为: "+Array.getLength(obj));
30 for (int i = 0; i < Array.getLength(obj); i++) {
31 System.out.print(Array.get(obj, i)+" ");
32 }
33 }
34 }
35 //【运行结果】:
36 //数组长度为: 15
37 //1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 =====================
38 //数组长度为: 8
39 //a b c null null null null null
14.动态代理
首先来看看如何获得类加载器:
1 class test{
2
3 }
4 class hello{
5 public static void main(String[] args) {
6 test t=new test();
7 System.out.println("类加载器 "+t.getClass().getClassLoader().getClass().getName());
8 }
9 }
10 //【程序输出】:
11 //类加载器 sun.misc.Launcher$AppClassLoader
其实在java中有三种类类加载器。
1)Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。
2)Extension ClassLoader 用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类
3)AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。
如果想要完成动态代理,首先需要定义一个InvocationHandler接口的子类,已完成代理的具体操作。
1 package Reflect;
2
3 import java.lang.reflect.*;
4
5 //定义项目接口
6 interface Subject {
7 public String say(String name, int age);
8 }
9
10 // 定义真实项目
11 class RealSubject implements Subject {
12 @Override
13 public String say(String name, int age) {
14 return name + " " + age;
15 }
16 }
17
18 class MyInvocationHandler implements InvocationHandler {
19 private Object obj = null;
20
21 public Object bind(Object obj) {
22 this.obj = obj;
23 return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
24 .getClass().getInterfaces(), this);
25 }
26
27 @Override
28 public Object invoke(Object proxy, Method method, Object[] args)
29 throws Throwable {
30 Object temp = method.invoke(this.obj, args);
31 return temp;
32 }
33 }
34
35 class hello {
36 public static void main(String[] args) {
37 MyInvocationHandler demo = new MyInvocationHandler();
38 Subject sub = (Subject) demo.bind(new RealSubject());
39 String info = sub.say("Rollen", 20);
40 System.out.println(info);
41 }
42 }
43 //【运行结果】:
44 //Rollen 20
类的生命周期
在一个类编译完成之后,下一步就需要开始使用类,如果要使用一个类,肯定离不开JVM。在程序执行中JVM通过装载,链接,初始化这3个步骤完成。
类的装载是通过类加载器完成的,加载器将.class文件的二进制文件装入JVM的方法区,并且在堆区创建描述这个类的java.lang.Class对象。用来封装数据。 但是同一个类只会被类装载器装载以前
链接就是把二进制数据组装为可以运行的状态。
链接分为校验,准备,解析这3个阶段:
校验 一般用来确认此二进制文件是否适合当前的JVM(版本),
准备 就是为静态成员分配内存空间。并设置默认值
解析 指的是转换常量池中的代码作为直接引用的过程,直到所有的符号引用都可以被运行程序使用(建立完整的对应关系)。
完成之后,类型也就完成了初始化,初始化之后类的对象就可以正常使用了,直到一个对象不再使用之后,将被垃圾回收。释放空间。当没有任何引用指向Class对象时就会被卸载,结束类的生命周期。