Java——反射:运行时的类信息
- RTTI的使用
如果不知道某个对象的确切类型,RTTI会告诉我们,但是有一个限制:这个类型在编译时必须已知,这样才能使用RTTI识别它,并利用这些信息做一些有用的事情。
2.什么情况下需要反射
假设你获取了一个指向某个并不在你的程序空间的对象的引用;或者,你从磁盘文件,或者网络连接中获取了一串字节,并且你被告知这些字节代表一个类,想要使用这些类就需要反射。
3.反射和RTTI的区别
当通过反射与一个未知类型的对象打交道的时候,JVM只是简单的检查这个对象,看它属于那个特定的类。然后在使用这个对象之前先加载那个类的class对象,这个.class要么从本地机器获得,要么从网络中获得。RTTI和反射的区别就是,对RTTI来说,编译器在编译时打开和检查.class文件,而对于反射来说,.class在编译时是不可获取的,是在运行的时候打开和检查.class文件。
Class类和java.lang.reflect类库一起对反射的概念进行了支持,该类库包括Field、Method以及Constructor类(每个类都实现了Member接口)。这些类型的对象是由JVM在运行时创建的,用以表示未知类里对应的成员。
一、类方法提取器
import java.lang.reflect.*;
import java.util.*;
import java.util.regex.*;
class Ex1{
public Ex1(){
}
public void fun() {
System.out.println("fun()");
}
}
public class Ex {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String name = in.nextLine();
try {
Class<?> c = Class.forName(name);
Method[] methods = c.getMethods();
Constructor[] ctors = c.getConstructors();
System.out.println("methods:");
for(Method method: methods) {
System.out.println(method.toString());
}
System.out.println();
System.out.println("Constructor:");
for(Constructor ctor:ctors) {
System.out.println(ctor.toString());
}
}catch(ClassNotFoundException e) {
System.out.println("ClassNotfound");
}
}
}
输出:
Ex1
methods:
public void Ex1.fun()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll() Constructor:
public Ex1()
Class.forName()生成的结果是编译时未知的,然后Class的getMethods()和getConstructors()分别返回Method对象的数组和Constructors对象的数组。Constructors就是构造器。
2、动态代理
代理: 代理模式(Proxy)就是为一个对象创建一个替身,用来控制对当前对象的访问。目的就是为了在不直接操作对象的前提下对对象进行访问。
interface Interface{
void doSomething();
void SomethingElse(String arg);
}
class RealObject implements Interface{ public void doSomething() {
System.out.println("doSomething1");
}
public void SomethingElse(String arg) {
System.out.println("SomethingElse1" + arg);
}
}
class SimpleProxy implements Interface{
private Interface proxied;
public SimpleProxy(Interface proxied) {
this.proxied = proxied;
} public void doSomething() {
System.out.println("doSomething2");
proxied.doSomething();
} public void SomethingElse(String arg) {
System.out.println("SomethingElse2" + arg);
proxied.SomethingElse(arg);
} }
public class Ex2 {
public static void consumer(Interface iface) {
iface.doSomething();
iface.SomethingElse("xxxx");
}
public static void main(String[] args) {
consumer(new RealObject());
consumer(new SimpleProxy(new RealObject()));
}
}
输出:
doSomething1
SomethingElse1xxxx
doSomething2
doSomething1
SomethingElse2xxxx
SomethingElse1xxxx
这里的SimpleProxy和RealObject都实现了Interface,然后在SimpleProxy中创建一个RealObject的替身,用来控制对RealObject对象的访问,这样的使用称为代理模式。
代理模式的好处就是在某些情况下,一个客户不想或者不能直接引用一个对象,代理对象就再客户端和被代理对象之间起到中介的作用。就好比你在北京租房,初来乍到,人生地不熟,找房子遍地都是中介,想找房东可没那么容易(基本算得上是找不到房东直租的)。问题来了,找不到房东直租,但房子又是一切的基础,so....走中介,房东能租房,中介也能租房,不过是你通过中介去将房东的房子租给自己。OK,这就是一个活生生的代理模式的例子,相必在外漂泊的小年轻们都感同身受吧。
这样的代理在运行之前,就确定好代理类、被代理类之间的关系,称之为静态代理。像上面的代码就是一个中介只代理一个房东,如果想增加中介代理的房东就需要额外的编写代码所以出现了动态代理
Java的动态代理可以动态地创建代理并动态地处理对代理方法的调用。动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作就是揭示调用的类型并确定相应的对策。
import java.lang.reflect.*; class DynamicProxyHandler implements InvocationHandler{ private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(proxied, args);
return null;
} } public class Ex3 {
public static void consumer(Interface iface) {
iface.doSomething();
iface.SomethingElse("xxxx");
} public static void main(String[] args) {
RealObject real = new RealObject();
SimpleProxy por = new SimpleProxy(real);
Interface proxy1 = (Interface)Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[] { Interface.class },
new DynamicProxyHandler(real));
consumer(proxy1);
System.out.println();
Interface proxy2 = (Interface)Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[] { Interface.class },
new DynamicProxyHandler(por));
consumer(proxy2); }
}
首先通过实现InvocationHandler接口创建动态代理类,实现代码运行过程中对各种真实对象的动态代理,构造器中将传入一个想要代理的类对象,在应该被实现的invoke方法中通过method.invoke(proxied, args)实现代理的类的方法
然后在main中调用静态方法Proxy.newProxyInstance()可以创建动态代理,这个方法需要得到一个类加载器,一个希望该代理实现的接口列表,以及一个InvocationHandler接口的一个实现。
输出:
doSomething1
SomethingElse1xxxx doSomething2
doSomething1
SomethingElse2xxxx
SomethingElse1xxxx
3、空对象
有时引入空对象的思想会很有用,它可以接受传递给它的所代表的对象的信息,但是返回值表示为实际上并不存在的任何“真实”对象的值,假如我们需要查询某个学生的信息,我们输入学号来进行查询,如果没有这个学生的话,我们就可以返回一个空对象。
interface Null{}
class Person{
public final String name;
public Person(String name){
this.name = name;
}
public String toString() {
return "the person is" + name;
}
public static class NullPerson extends Person implements Null{ private NullPerson() {
super("None");
}
public String toString() {
return "NullPerson";
}
}
public static final Person NULL = new NullPerson();
}
先创建一个标志接口,然后实现这个接口,并继承Person类,将name设为None,最后在创建一个静态final的NULL的Person对象。这个NULL就是一个空对象,使用是可以直接使用equals或者是==来与Person.NULL进行比较,还可以选择使用instanceof来探测泛化的NULL还是更具体的NullPerson。
5、接口与类型信息
interface A{
public void f();
}
class B implements A{ public void f() {
System.out.println("f()");
} public void g() {
System.out.println("g()");
}
}
public class Ex5 {
public static void main(String[] args) {
A a = new B();
System.out.println(a.getClass().getName());
if(a instanceof B) {
B b = (B) a;
b.g();
}
}
}
输出:
B
g()
这里B是一个实现A接口的类,在main中我们可以发现a是被当作B实现的,然后我们还可以通过转型为B从而调用g()方法。这样会试代码的耦合程度超过你的期望,最简单的方法是实现使用包访问权限,在包外部的就不能使用它。
interface A{
public void f();
}
class B implements A{ public void f() {
System.out.println("f()");
} public void g() {
System.out.println("g()");
}
} public class Ex5 {
public static A makeA() {
return new B();
}
}
这个class中只有Ex5部分是public的,这个方法将返回一个当作B实现的A,
public class Ex6 {
public static void main(String[] args) throws Exception {
A a = Ex5.makeA();
a.f();
System.out.println(a.getClass().getName());
/*
* B = (B) a
* 当试图将其向下转型为B时,则将会被禁止,因为没有任何的B类型可以用
*/
}
}
但其实通过反射还是能够调用所有的方法的:
import java.lang.reflect.Method; public class Ex6 {
public static void main(String[] args) throws Exception {
A a = Ex5.makeA();
a.f();
System.out.println(a.getClass().getName());
callEx5Method(a,"g");
}
static void callEx5Method(Object a, String MethodName) throws Exception {
Method g = a.getClass().getDeclaredMethod(MethodName);
g.setAccessible(true);
g.invoke(a);
}
}
callEx5Method方法传入一个对象,和一个String类型的MethodName(方法名),通过调用a.getClass().getDeclaredMethod(MethodName)传入方法名获得想要调用的方法,然后调用setAccessible()并传入true这样就可以通过反射访问私有方法,最后使用invoke()运行该方法。
输出:
f()
B
g()
Java——反射:运行时的类信息的更多相关文章
- Java 反射 —— 运行时的类型信息
1. 反射机制的由来 RTTI 机制可以告知某个对象的确切类型,但有一个前提,该类型在编译时必须已知(编译器在编译时打开和检查 .class 文件以获取类型信息).似乎是个很宽松的限制,但假如你获取了 ...
- java 查看运行时某个类文件所在jar的位置
在一些大型项目中,项目所依赖的库可能比较到,有时候也会出现库冲突的情况,曾经遇到过一种情况:一个第三方云存储提供了一个sdk,这个sdk本身依赖httpclient相关的包,然而对方却把httpcli ...
- Java如何在运行时识别类型信息?
在日常的学习工作当中,有一些知识是我们在读书的时候就能够习得:但有一些知识不是的,需要在实践的时候才能得到真知——这或许就是王阳明提倡的“知行合一”. 在Java中,并不是所有的类型信息都能在编译阶段 ...
- Java虚拟机运行时栈帧结构--《深入理解Java虚拟机》学习笔记及个人理解(二)
Java虚拟机运行时栈帧结构(周志明书上P237页) 栈帧是什么? 栈帧是一种数据结构,用于虚拟机进行方法的调用和执行. 栈帧是虚拟机栈的栈元素,也就是入栈和出栈的一个单元. 2018.1.2更新(在 ...
- 《深入理解Java虚拟机》(二)Java虚拟机运行时数据区
Java虚拟机运行时数据区 详解 2.1 概述 本文参考的是周志明的 <深入理解Java虚拟机>第二章 ,为了整理思路,简单记录一下,方便后期查阅. 2.2 运行时数据区域 Java虚拟机 ...
- Java虚拟机运行时数据区
运行时数据区程序计数器Java虚拟机栈本地方法栈Java堆(GC堆)方法区运行时常量池 运行时数据区 Java虚拟机在运行Java程序时,会将它所管理的内存划分为若干个内存区域.这些数据区域有各自的用 ...
- java程序运行时内存分配详解
java程序运行时内存分配详解 这篇文章主要介绍了java程序运行时内存分配详解 ,需要的朋友可以参考下 一. 基本概念 每运行一个java程序会产生一个java进程,每个java进程可能包含一个 ...
- Java程序运行时内存划分
1.Java程序跨平台运行的原因 主要原因是:各种平台的JVM和字节码文件 Java源程序--具体平台的机器代码文件---被编译器翻译成平台无关的Class文件,又用特定JVM运行字节码文件,JVM在 ...
- 关于Java虚拟机运行时数据区域的总结
Java虚拟机运行时数据区域 程序计数器(Program Counter) 程序计数器作为一个概念模型,这个是用来指示下一条需要执行的字节码指令在哪. Java的多线程实际上是通过线程轮转做到的,如果 ...
随机推荐
- 使用nexus搭建npm私服
第一步,下载nexus 下载链接: https://pan.baidu.com/s/1GOYi2M3nT4Wcy7JEYmnqdA 提取码: a9hf 第二步,解压缩 我下载的是nexus-3.16. ...
- 浅谈 Attention 机制的理解
什么是注意力机制? 注意力机制模仿了生物观察行为的内部过程,即一种将内部经验和外部感觉对齐从而增加部分区域的观察精细度的机制.例如人的视觉在处理一张图片时,会通过快速扫描全局图像,获得需要重点关注的目 ...
- Dapper学习笔记
听说有个轻量化的orm Dapper,我就去了解下.试着对Sql Server和Mysql进行增删改查,体验不错.它不如EF臃肿,也比一般的封装灵活,比如我们封装了一个映射类.利用反射,在Execut ...
- python函数知识二 动态参数、函数的注释、名称空间、函数的嵌套、global,nonlocal
6.函数的动态参数 *args,**kwargs:能接受动态的位置参数和动态的关键字参数 *args -- tuple *kwargs -- dict 动态参数优先级:位置参数 > 动态位置参数 ...
- 个人永久性免费-Excel催化剂功能第86波-人工智能之图像OCR文本识别全覆盖
在上一年中,Excel催化剂已经送上一波人工智能系列功能,鉴于部分高端用户的需求,再次给予实现了复杂的图像OCR识别,包含几乎所有日常场景,让公司个人手头的图像非结构化数据瞬间变为可进行结构化处理分析 ...
- python-if条件判断与while/for循环
条件判断if 让计算机像人一样,能判断是非对错,根据条件做一些事情. if ''' ------ if代码结构:------- if 条件: 代码体 tips:同一缩进范围内的代码被视作同一代码体,p ...
- C# backgroundwork的使用方法
引言:在 WinForms 中,有时要执行耗时的操作,在该操作未完成之前操作用户界面,会导致用户界面停止响应.解决的方法就是新开一个线程,把耗时的操作放到线程中执行,这样就可以在用户界面上进行其它操作 ...
- 实现简单的 IOC 和 AOP
1 简单的 IOC 1.1 简单的 IOC 容器实现的步骤 加载 xml 配置文件,遍历其中的标签 获取标签中的 id 和 class 属性,加载 class 属性对应的类,并创建 bean 遍历标签 ...
- python课堂整理11---函数即变量
一.前向引用 函数即是变量,在调用前要先定义好. def bar(): print('from bar') def foo(): print('from foo') bar() foo() def f ...
- tornado并发性能测试
1.带server2.0装饰器 接口访问数据库查询 并发100 平均每秒处理11.8次请求 平均响应时间6944ms 接口不做任何处理 并发100 平均每秒处理99.9次请求 平均响应时间3ms 并发 ...