JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
在《Thinking in java》中提出了RTTI和反射机制的概念
RTTI假定我们在编译时已经知道了所有的类型,反射允许我们在允许是发现和使用类信息。
纠结着看完了一整章也没看懂它们的区别orz。但到了知乎上的这条回答解决了疑问Java RTTI和反射的区别?
Java的官方文档中并没有RTTI的说法,有的是“Reflect”和“Reflection API”。Thinking in Java 的作者先写的Thinking in C++ ,RTTI是C++中的概念。RTTI和反射是两个不同体系在描述相同的问题。
一、Class对象
Class对象是用来创建类的所有“常规”对象的,定义一个特定类的实现。所有的类都是在对其第一次使用时,动态加载到JVM中的,当程序第一个对象的静态成员引用时,就会加载这个类,这证明构造器也是类的静态方法
一旦某个类的Class对象被加载入内存,就能用来创建这个类的所有对象。
为加载类而做的准备工作包含三步骤:
1、加载,是有类加载器执行的,将查找字节码,并从这些字节码中创建一个Class对象
2、链接,验证类中的字节码,为静态域分配存储空间,并且如果必须的话,将解析这个类创建的对其他类的所有引用
3、初始化、如果该类具有超类,则对其初始化,执行初始化和静态初始化块
初始化被延迟到了对静态方法(构造器隐身地是静态的)或者非常数静态域进行首次引用时才执行
举个例子:
class Apple{
static final int finalnum = 5;//编译器常量
static int num = 4;//只是将域设置为final或static,访问时将强制进行类的初始化
static {
System.out.println("I'm a apple");
}
}
class Banana{
static {
System.out.println("I'm a banana");
}
}
public class Test1 {
public static void main(String[] args) {
Class c = Apple.class;//仅使用.class获得对类的引用不会引发初始化
System.out.println(c.getSimpleName());
System.out.println(Apple.finalnum);
System.out.println(Apple.num);//强制进行类的初始化
try {
Class.forName("com.example.reflectTest.Banana");//包名+类名,Class.forname()立即就进行初始化
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出:
Apple
5
I'm a apple
4
I'm a banana
java 类中的静态域的初始化和静态代码块的执行只在类加载的时候执行且只执行一次,仅使用.class语法获得对类的引用不会引发初始化,而Class.forName()返回Class对象的引用。
Class的其他方法:
class Fruit{
public Fruit() {
}
@Override
public String toString() {
return "I'm a fruit.";
}
}
class Orange extends Fruit implements Interface1, Interface2, Interface3{
public Orange() {
}
@Override
public String toString() {
return "I'm a orange.";
}
}
public class Test2 {
public static void describe(Class c){
System.out.println("simpleName:" + c.getSimpleName() + "\n"
+ "is interface:" + c.isInterface() + "\n"
+ "canonical name:" + c.getCanonicalName());
}
public static void main(String[] args) {
Class class1 = null;
try {
class1 = Class.forName("com.example.reflectTest.Orange");
} catch (ClassNotFoundException e) {
System.out.println("class not found");
}
describe(class1);
for (Class face : class1.getInterfaces()) {
System.out.println(face);
}
Object object = null;
Object object2 = null;
try {
object = class1.newInstance();
object2 = class1.getSuperclass().newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
System.out.println(object);
System.out.println(object2);
}
}
输出:
simpleName:Orange
is interface:false
canonical name:com.example.reflectTest.Orange
interface com.example.reflectTest.Interface1
interface com.example.reflectTest.Interface2
interface com.example.reflectTest.Interface3
I'm a orange.
I'm a fruit.
二、泛化的Class引用
通过使用泛型语句,可以让编译器强制执行额外的类型检查
public class GenericityClass {
public static void main(String[] args) {
Class<String> class1 = String.class;
Class class2 = Integer.class;
//class1 = Double.class;//Illegal
//Class<Number> class3 = Integer.class;
//虽然Integer继承自Number,但是Integer Class 不是Number Class的子类
Class<? extends Number> class3 = Integer.class;//使用通配符"?"
class2 = Double.class;
try {
Object integer = class2.newInstance();//返回的是Object
String str = class1.newInstance();//返回的是确切的类型
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
三、instanceof,isInstance和isAssignableFrom
1、instanceof关键字,用来判断对象是否是类的实例
Integer integer = new Integer(3);
System.out.println(integer instanceof Number);//true
2、isAssignableFrom,用来判断类型间是否存在派生关系
System.out.println(Number.class.isAssignableFrom(Integer.class));//true
//自身类.class.isAssignableFrom(自身类或子类.class)
3、isInstance方法,用来判断对象是否属于某个类型的实例
System.out.println(String.class.isInstance("haha"));//true
四、动态代理
代理模式:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
举个简单的例子吧,假如要期末考试了,某学生很方啊,因为他知道他一定会挂科,于是,他找了个人替他去考试
interface Student{
void exam();
}
class realExamStudent implements Student{
@Override
public void exam() {
System.out.println("real:挂科");
}
}
class ProxyStudent implements Student{
@Override
public void exam() {
System.out.println("proxy:考试过了");
}
}
public class ProxyTest1 {
public static void examStart(Student student){
//接受的是Interface。所以它无法知道到底是Real还是Proxy
student.exam();
}
public static void main(String[] args) {
examStart(new realExamStudent());
examStart(new ProxyStudent());
}
}
结果:
real:挂科
proxy:考试过了
想要将额外的操作从“实际”对象中分离到不同的地方的时候,就很有用了。
上面的例子是一种静态代理,没有做到代码的复用,并且有时需要造很多个静态的代理类。
下面说说java中的动态代理,它不需要为每一个具体类做代理,而是使用反射的机制,通过参数和代理方法自动生成代理的代码。
class ExamStudent implements Student{
@Override
public void exam() {
System.out.println("考试");
}
}
class StudentProxyHandler implements InvocationHandler{
private Object proxied;
public StudentProxyHandler(Object proxied) {
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy:" + proxy.getClass() + "\nmethod:"
+ method + "\nargs:" + args);
if (args != null) {
for (Object object : args) {
System.out.println(object + " ");
}
}
return method.invoke(proxied, args);//将请求转发给代理对象
}
}
public class ProxyHandlerTest {
public static void examStart(Student student){
student.exam();
}
public static void main(String[] args) {
ExamStudent realStudent = new ExamStudent();
examStart(realStudent);
Student proxyStudent = (Student)Proxy.newProxyInstance(
Student.class.getClassLoader(),//类加载器
new Class[]{Student.class}, //代理实现的接口列表
new StudentProxyHandler(realStudent));//InvocationHandle接口的实现
examStart(proxyStudent);
}
}
结果:
考试
proxy:class com.sun.proxy.$Proxy0
method:public abstract void com.example.reflectTest.Student.exam()
args:null
考试
对接口的调用被重定向为对代理的调用
五、使用反射调用方法或域
通过使用反射,可以到达并调用所有的方法,甚至是private方法,也可以调用private域
class B {
private int num = 2;
private void g(){
System.out.println("g()");
}
}
public class Test4 {
public static void main(String[] args) throws Exception {
B b = new B();
Method[] methods = b.getClass().getDeclaredMethods();
for (Method method : methods) {
method.setAccessible(true);
method.invoke(b);
}
Field field = b.getClass().getDeclaredField("num");
field.setAccessible(true);
System.out.println(field.get(b));
field.setInt(b, 5);//修改private域的值
System.out.println("修改后:" + field.get(b));
}
}
输出:
g()
2
修改后:5