1. 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——反射:运行时的类信息的更多相关文章

  1. Java 反射 —— 运行时的类型信息

    1. 反射机制的由来 RTTI 机制可以告知某个对象的确切类型,但有一个前提,该类型在编译时必须已知(编译器在编译时打开和检查 .class 文件以获取类型信息).似乎是个很宽松的限制,但假如你获取了 ...

  2. java 查看运行时某个类文件所在jar的位置

    在一些大型项目中,项目所依赖的库可能比较到,有时候也会出现库冲突的情况,曾经遇到过一种情况:一个第三方云存储提供了一个sdk,这个sdk本身依赖httpclient相关的包,然而对方却把httpcli ...

  3. Java如何在运行时识别类型信息?

    在日常的学习工作当中,有一些知识是我们在读书的时候就能够习得:但有一些知识不是的,需要在实践的时候才能得到真知——这或许就是王阳明提倡的“知行合一”. 在Java中,并不是所有的类型信息都能在编译阶段 ...

  4. Java虚拟机运行时栈帧结构--《深入理解Java虚拟机》学习笔记及个人理解(二)

    Java虚拟机运行时栈帧结构(周志明书上P237页) 栈帧是什么? 栈帧是一种数据结构,用于虚拟机进行方法的调用和执行. 栈帧是虚拟机栈的栈元素,也就是入栈和出栈的一个单元. 2018.1.2更新(在 ...

  5. 《深入理解Java虚拟机》(二)Java虚拟机运行时数据区

    Java虚拟机运行时数据区 详解 2.1 概述 本文参考的是周志明的 <深入理解Java虚拟机>第二章 ,为了整理思路,简单记录一下,方便后期查阅. 2.2 运行时数据区域 Java虚拟机 ...

  6. Java虚拟机运行时数据区

    运行时数据区程序计数器Java虚拟机栈本地方法栈Java堆(GC堆)方法区运行时常量池 运行时数据区 Java虚拟机在运行Java程序时,会将它所管理的内存划分为若干个内存区域.这些数据区域有各自的用 ...

  7. java程序运行时内存分配详解

    java程序运行时内存分配详解 这篇文章主要介绍了java程序运行时内存分配详解 ,需要的朋友可以参考下   一. 基本概念 每运行一个java程序会产生一个java进程,每个java进程可能包含一个 ...

  8. Java程序运行时内存划分

    1.Java程序跨平台运行的原因 主要原因是:各种平台的JVM和字节码文件 Java源程序--具体平台的机器代码文件---被编译器翻译成平台无关的Class文件,又用特定JVM运行字节码文件,JVM在 ...

  9. 关于Java虚拟机运行时数据区域的总结

    Java虚拟机运行时数据区域 程序计数器(Program Counter) 程序计数器作为一个概念模型,这个是用来指示下一条需要执行的字节码指令在哪. Java的多线程实际上是通过线程轮转做到的,如果 ...

随机推荐

  1. scrapy 发post请求

    可以使用 yield scrapy.FormRequest(url, formdata, callback)方法发送POST请求. 如果希望程序执行一开始就发送POST请求,可以重写Spider类的s ...

  2. django基础知识之视图:

    视图 视图接受Web请求并且返回Web响应 视图就是一个python函数,被定义在views.py中 响应可以是一张网页的HTML内容,一个重定向,一个404错误等等 响应处理过程如下图:

  3. JAVA面试题 启动线程是start()还是run()?为什么?

    面试官:请问启动线程是start()还是run()方法,能谈谈吗? 应聘者:start()方法 当用start()开始一个线程后,线程就进入就绪状态,使线程所代表的虚拟处理机处于可运行状态,这意味着它 ...

  4. Event StoryLine Corpus 论文阅读

    Event StoryLine Corpus 论文阅读 本文是对 Caselli T, Vossen P. The event storyline corpus: A new benchmark fo ...

  5. RDBMS与数据库之间的关系

    什么是数据库? 数据库就是用来存放数据的仓库,它是一种特殊的文件. 什么是关系型数据库? 关系型数据库就是指建立在关系模型基础上的数据库,通常由多张表组成,这些表之间存在一定的关系. 什么是RDBMS ...

  6. 20190716 NOIP模拟测试4 考试反思

    总分 127分 满分300 第一题 礼物 10分 一道期望题,看起来挺简单,但对于概率与期望这一块我还不怎么会,花了一个小时调他,最后只QJ了一下10%的测试点 第二题 通讯 90分 显然的缩点求解, ...

  7. 第三章.定制专属的kali

    1.更新升级 • apt-get update • apt-get upgrade • apt-get dis-upgrade   2.根据个人喜好需求安装软件包 • 库 • Apt-get命令 • ...

  8. golang开发:类库篇(四)配置文件解析器goconfig的使用

    为什么要使用goconfig解析配置文件 目前各语言框架对配置文件书写基本都差不多,基本都是首先配置一些基础变量,基本变量里面有环境的配置,然后通过环境变量去获取该环境下的变量.例如,生产环境跟测试环 ...

  9. 个人永久性免费-Excel催化剂功能第101波-批量替换功能(增加正则及高性能替换能力)

    数据处理无小事,正如没有人活在真空理想环境一下,在数据分析过程中,也没有那么真空理想化的数据源可以使用,数据处理占据数据分析的80%的时间,每一个小小的改善,获益都良多.Excel查找替换,有其局限性 ...

  10. python面向过程编程小程序- 模拟超市收银系统

    6.16自我总结 功能介绍 程序功能介绍: 商品信息再读取修改买卖均已xlsx格式 且生成购物记录也按/用户名/购买时间.xlsx格式生成 账号密码输入错误三次按照时间进行冻结 用户信息已json格式 ...