今天课上学习了反射的一部分知识,反射机制是面向对象编程的一个重要的机制,这种思想值得我们深入学习和使用,于是课下我查找了好多相关知识的文档学习和总结,写下了这篇文章与大家分享,如果有不对的地方,我欣然接受大家指出的错误,并加以纠正。
在学习反射之前首先介绍Class类:
(1)如果你使用Java,那么你应该知道Java中有一个Class类。Class类本身表示Java对象的类型,我们可以通过一个Object对象的getClass方法取得一个对象的类型,此函数返回的就是一个Class类
(2)Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
(3)基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象
(4)重要方法:forName(String className)返回与带有给定字符串名的类或接口相关联的 Class 对象。
(5)Class类是整个Java反射机制的源头。一切关于Java反射的故事,都从Class类开始,因此,要想使用Java反射,我们首先得到Class类的对象。
(6)如下列出几种得到Class类的方法:
1.getClass()
package day0801;
public class reflection {
public static void main(String[] args) {
String str="abc";
Class c1=str.getClass();
System.out.println(c1);
}
}
输出结果:class java.lang.String
2.getSuperClass()
package day0801;
import java.awt.Button;
public class reflection {
public static void main(String[] args) {
Button b=new Button();
Class c2=b.getClass();
Class c3=c2.getSuperclass();
System.out.println(c2);
System.out.println(c3);
}
}
输出结果:class java.awt.Button
class java.awt.Component
(Component是Button的父类)
3.(static)forName()
package day0801;
public class reflection {
public static void main(String[] args) throws ClassNotFoundException {
//使用forName时需指定参数:包名.类名
Class c1 = Class.forName ("java.lang.String");
Class c2 = Class.forName ("java.awt.Button");
Class c3 = Class.forName ("java.util.LinkedList$Entry");
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
}
}
输出结果:class java.lang.String
class java.awt.Button
class java.util.LinkedList$Entry
4.语法.class
package day0801;
public class reflection {
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = String.class;
Class c2 = java.awt.Button.class;
Class c4 = int.class;
Class c5 = int[].class;
System.out.println(c1);
System.out.println(c2);
System.out.println(c4);
System.out.println(c5);
}
}
输出结果:
class java.lang.String
class java.awt.Button
int
class [I
5.使用原始包装类的TYPE语法
package day0801;
public class reflection {
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Boolean.TYPE;
Class c2 = Byte.TYPE;
Class c3 = Character.TYPE;
Class c4 = Short.TYPE;
Class c5 = Integer.TYPE;
Class c6 = Long.TYPE;
Class c7 = Float.TYPE;
Class c8 = Double.TYPE;
Class c9 = Void.TYPE;
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
}
}
输出结果:
boolean
byte
char
short
int
long
float
double
void
类中三个重要的信息
如果要对一个类的信息重要性进行排名的话,那么这三个信息理应获得前三的名次。它们分别是:构造函数、成员函数、成员变量。
1.构造方法
Java反射机制能够得到构造函数信息实在应该是一件令人惊喜的事情。正因为此,反射机制实质上才拥有了孵化生命的能力。换句话言之,我们可以通过反射机制,动态地创建新的对象。
获取构造函数的方法有以下几个:
Constructor getConstructor(Class[] params)
Constructor[] getConstructors()
Constructor getDeclaredConstructor(Class[] params)
Constructor[] getDeclaredConstructors()
接下来我们通过对这四个函数的两种不同方式的分类来进行讲解:
(1)按照构造函数的确定性分:
如果我们在Java反射时指定构造函数的参数而确定地返回我们需要的那个“唯一”的构造函数,则用getConstructor(Class[] params) 和getDeclaredConstructor(Class[] params方式。
相反,如果我们不清楚每个构造函数的参数表,或者我们出于某种目的需要获取所有的构造函数的信息,那么我们就不需要明确指定参数表,而这时返回的就应该是构造函数数组,因为构造函数很可能不止一个。getConstructors()和getDeclaredConstructors()就是这种方式。
(2)按照构造函数的访问权限分:
在设计类的时候,我们往往有一些构造函数需要声明为“private”、“protect”或者“default”,目的是为了不让外部的类调用此构造函数生成对象。于是,基于访问权限的不同,我们可以将构造函数分为public和非public两种。
getConstructor(Class[] params) 和getConstructors()仅仅可以获取到public的构造函数,而getDeclaredConstructor(Class[] params) 和getDeclaredConstructors()则能获取所有(包括public和非public)的构造函数。
2.成员函数
如果构造函数类比为对象的诞生过程的话,成员函数无疑可以类比为对象的生命行为过程。成员函数的调用执行才是绝大多数对象存在的证据和意义。Java反射机制允许获取成员函数(或者说成员方法)的信息,也就是说,反射机制能够帮助对象践行生命意义。通俗地说,Java反射能使对象完成其相应的功能。
和获取构造函数的方法类似,获取成员函数的方法有以下一些:
Method getMethod(String name, Class[] params)
Method[] getMethods()
Method getDeclaredMethod(String name, Class[] params)
Method[] getDeclaredMethods()
(1)其中需要注意,String name参数,需要写入方法名。
(2)关于访问权限和确定性的问题,和构造函数基本一致。
3.成员属性
从内存的角度来说,构造函数和成员函数都仅仅是Java对象的行为或过程,而成员变量则是真正构成对象本身的细胞和血肉。简单的说,就是成员变量占用的空间之和几乎就是对象占用的所有内存空间。
获取成员变量的方法与上面两种方法类似,具体如下:
Field getField(String name)
Field[] getFields()
Field getDeclaredField(String name)
Field[] getDeclaredFields()
(1)其中,String name参数,需要写入变量名。
(2)关于访问权限和确定性的问题,与前面两例基本一致。
java的反射机制初步
Java反射机制是Java语言被视为准动态语言的关键性质。Java反射机制的核心就是允许在运行时通过Java Reflection APIs来取得已知名字的class类的相关信息(包括上面说的构造函数、成员函数、成员变量),动态地生成此类,并调用其方法或修改其域(甚至是本身声明为private的域或方法)。
下面,让我们就分别了解一下这三个重要信息的获取方式。
使用下面这几个重要的方法:
(1)Constructor的方法:
newInstance(Object initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
(2)Method的方法:
invoke(Object obj, Object args)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
说明:
我们能很好理解此函数的第二个参数args,它代表这个方法所需要接收的参数。也许大家对第一个参数receiver还存在疑惑之处。这得从编程语言的发展历程讲起。
如果你关注几种主流编程语言的起源,那么你能有这样的印象:C从汇编而来,C++从C而来,而Java从C/C++而来。有这样一种印象就足够了。从这样的发展史我们可以看出,C++和Java这两种面向对象的编程语言都是从面向过程的C语言基础上发展而来的。OOP是一种思想,它本身与编程语言无关。也就是说,我们用C也能写出面向对象的程序,这也是C++和Java能够以C为基础的根本所在。然而,C无法实现类似object.method()这种表现形式,因为C语言的结构体中并不支持函数定义。那么我们用C实现OOP的时候,如何调用对象的方法呢?
本质上说,object.method()这种调用方式是为了表明具体method()的调用对象。而invoke(Object receiver, Object... args)的第一个参数正是指明调用对象。在C++中,object.method()其实是有隐含参数的,那就是object对象的指针,method原型的第一个参数其实是this指针,于是原型为method(void* this)。
另外需要注意的是,如果某个方法是另外需要注意的是,如果某个方法是Java类的静态方法,那么Object receiver参数可以传入null,因为静态方法不从属于对象。
(3)Field的方法:
get(Object obj)
返回指定对象上此 Field 表示的字段的值。
set(Object obj,Object value)
将指定对象变量上此 Field 对象表示的字段设置为指定的新值。