Java 类型信息详解和反射机制


到目前为止,我们已知的 RTTI 类型包括:

  1. 传统的类型转换,如多态

  2. 代表对象类型的 Class 对象

RTTI 在 Java 中还有第三种形式,那就是关键字 instanceof,它返回一个布尔值,告诉我们对象是不是某个特定类型的实例,可以用提问的方式使用它

if(x instanceof Dog) {

((Dog)x).bark();

}

Java 还提供了 Class.isInstance() 方法动态检测对象类型,例如

0 instance of String // 编译报错

String.class.isInstance(0) // 可以通过编译

反射

如果你不知道对象的确切类型,RTTI 会告诉你,但是有一个限制:必须在编译时知道类型,才能使用 RTTI 检测它。换句话说,编译器必须知道你使用的所有类

看上去这并不是什么特别大的限制,但假设你引用了一个不在程序空间中的对象,比如你从磁盘文件或网络连接中获得大量的字节,并被告知这些字节代表一个类,那该怎么办呢?

类 Class 支持反射的概念,java.lang.reflect 库中支持类 Field、Method、Constructor(每一个都实现了 Member 接口),这些类型的对象由 JVM 运行时创建,以表示未知类中的对应成员。通常我们不会直接使用反射,但反射可以用来支持其他 Java 特性,例如对象序列化等

Field 代表类的成员变量(成员变量也称为类的属性),Class 类中定义了如下方法用来获取 Field 对象

| 方法 | 用途 |

| — | — |

| getField(String name) | 获得某个公有的属性对象 |

| getFields() | 获得所有公有的属性对象 |

| getDeclaredField(String name) | 获得某个属性对象 |

| getDeclaredFields() | 获得所有属性对象 |

Field 类定义了如下方法设置成员变量的信息

| 方法 | 用途 |

| — | — |

| equals(Object obj) | 属性与 obj 相等则返回 true |

| get(Object obj) | 获得 obj 中对应的属性值 |

| set(Object obj, Object value) | 设置 obj 中对应属性值 |

Method 代表类的方法,Class 类中定义了如下方法用来获取 Method 对象

| 方法 | 用途 |

| — | — |

| getMethod(String name, Class…<?> parameterTypes) | 获得该类某个公有的方法 |

| getMethods() | 获得该类所有公有的方法 |

| getDeclaredMethod(String name, Class…<?> parameterTypes) | 获得该类某个方法 |

| getDeclaredMethods() | 获得该类所有方法 |

Method 类定义了如下方法对方法进行调用

| 方法 | 用途 |

| — | — |

| invoke(Object obj, Object… args) | 传递 object 对象及参数调用该对象对应的方法 |

Constructor 代表类的构造器,Class 类中定义了如下方法用来获取 Constructor 对象

| 方法 | 用途 |

| — | — |

| getConstructor(Class…<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |

| getConstructors() | 获得该类的所有公有构造方法 |

| getDeclaredConstructor(Class…<?> parameterTypes) | 获得该类中与参数类型匹配的构造方法 |

| getDeclaredConstructors() | 获得该类所有构造方法 |

Constructor 代表类的构造方法

| 方法 | 用途 |

| — | — |

| newInstance(Object… initargs) | 根据传递的参数创建类的对象 |

除了成员变量、方法和构造器以外,反射还能获取其他更多的信息,例如注解等,具体可查阅 Java API

反射的强大威力大家已经看到了,通过反射我们甚至可以获取到一些“本不应该获取”的信息,例如程序员为了降低耦合,往往会使用接口来隔离组件,但反射却可以轻易破解

public interface A {

void f();

}

class B implements A {

public void f() {}

public void g() {}

}

public class InterfaceViolation {

public static void main(String[] args) {

A a = new B();

a.f();

// a.g(); // 编译错误

if (a instanceof B) {

B b = (B) a;

b.g();

}

}

}

通过使用 RTTI,我们发现 a 是用 B 实现的,只要将其转型为 B,我们就可以调用不在 A 中的方法。如果你不希望客户端开发者这样做,那该如何解决呢?一种解决方案是直接声明为实际类型,另一种则是让实现类只具有包访问权限,这样包外部的客户端就看不到实现类了

除了这个以外,通过反射可以获得所有成员信息,包括 private 的,通常这种违反访问权限的操作并不是十恶不赦的,也许还可以帮助你解决某些特定类型的问题

动态代理


代理是基本的设计模式之一,一个对象封装真实对象,代替真实对象提供其他不同的操作,这些操作通常涉及到与真实对象的通信,因此代理通常充当中间对象。下面是一个简单的静态代理的示例:

interface Interface {

void doSomething();

}

class RealObject implements Interface {

@Override

public void doSomething() {

System.out.println(“doSomething”);

}

}

class SimpleProxy implements Interface {

private Interface proxied;

SimpleProxy(Interface proxied) {

this.proxied = proxied;

}

@Override

public void doSomething() {

System.out.println(“SimpleProxy doSomething”);

proxied.doSomething();

}

}

class SimpleProxyDemo {

public static void consumer(Interface iface) {

iface.doSomething();

}

public static void main(String[] args) {

consumer(new RealObject());

consumer(new SimpleProxy(new RealObject()));

}

}

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值