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. 对于Spring中AOP,DI,IoC概念的理解

    IOC IoC(inversion of Control),控制反转.就好像敏捷开发和SCRUM一样,不是什么技术,而是一种方法论,一种工程化的思想.使用IoC的思想意味着你将设计好的对象交给容器控制 ...

  2. URL的命名和反向解析

    1. 分组 url(r'^del_publisher/(\d+)', views.del_publisher), 匹配到参数,按照位置参数的方式传递给视图函数 视图函数需要定义形参接收变量 2. 命名 ...

  3. 绝对是全网最好的Splay 入门详解——洛谷P3369&BZOJ3224: Tyvj 1728 普通平衡树 包教包会

    平衡树是什么东西想必我就不用说太多了吧. 百度百科: 一个月之前的某天晚上,yuli巨佬为我们初步讲解了Splay,当时接触到了平衡树里的旋转等各种骚操作,感觉非常厉害.而第二天我调Splay的模板竟 ...

  4. Json串排序

    最近遇到个很烦人的问题.我现在做的业务有一部分是把之前app服务端的.net接口转译java接口.但是有些之前的接口,一个接口干上十几件事情,返回的json串长达五六几百行.着实看的就让人头大.但是如 ...

  5. 关于在记事本写入"\n"不显示换行的原因

    Linux系统下直接使用  "\n"  即可换行 windows下需要使用   "\r\n"

  6. 【朝花夕拾】Android自定义View篇之(十)TouchSlop及VelocityTracker

    前言 在Android事件中,有几个比较基本的概念和知识点需要掌握.比如,表示最小移动阈值的TouchSlop,追踪事件速度的VelocityTracker,用于检测手势的GestureDetecto ...

  7. Skyline WEB端开发5——添加标签后移动

    针对于标签或者模型,在skyline上可以进行移动.可以让一个模型可以像无人机似的飞行,或者描述从一个点到另一个点的飞行轨迹. 话不多说,直接上干货. 第一步 添加标签 参考网址:https://ww ...

  8. [Haoi2016]放棋子 题解

    4563: [Haoi2016]放棋子 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 440  Solved: 285[Submit][Status] ...

  9. [译]Vulkan教程(33)多重采样

    [译]Vulkan教程(33)多重采样 Multisampling 多重采样 Introduction 入门 Our program can now load multiple levels of d ...

  10. ES6 let const 关键字

    ECMAScript 和 JavaScript的关系? 前者是后者的规格,后者是前者的实现. 符合ECMAScript 规格的还有 Flash 中的AcionScript 和 TypeScript. ...