深入理解java:1.1.1. 反射机制
反射
到底什么是反射(Reflection)呢?
反射有时候也被称为内省(Introspection),事实上,反射,就是一种内省的方式,
Java不允许在运行时改变程序结构或类型变量的结构,但它允许在运行时去探知、加载、调用在编译期完全未知的class,可以在运行时加载该class,生成实例对象(instance object),调用method,或对field赋值。
这种类似于“看透”了class的特性被称为反射(Reflection),我们可以将反射直接理解为:可以看到自己在水中的倒影,这种操作与直接操作源代码效果相同,但灵活性高得多。
反射概念,反射的应用场景
反射的应用场景:如果我们在程序运行时,一个对象想要检视自己所拥有的成员属性,该如何操作?
如果我们想要在运行期获得某个类Class的信息如它的属性、构造方法、一般方法 后再考虑是否创建它的对象,这种情况该怎么办呢?
这就需要用到反射!
.Java文件在编译后会变成.class文件,
这就像是个镜面,本身是.java,在镜中是.class,他们其实是一样的;
那么同理,我们看到镜子的反射是.class,就能通过反编译,了解到.java文件的本来面目。即为反射。
官方给出的概念:反射是java语言的一个特性,它允程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。
例如它允许一个java的类获取它所有的成员变量和方法并且显示出来。
反射机制的作用:
1,反编译:.class-->.java
2,通过反射机制访问java对象的属性,方法,构造方法,甚至其父类,接口等。
获取类的三种方式
- //第一种方式:
- Class c1 = Class.forName(User);
- //第二种方式:
- //java中每个类型都有class 属性.
- Class c2 = User.class
- //第三种方式:
- //java语言中任何一个java对象都有getClass 方法
- User u = new User();
- Class c3 = u.getClass(); //c3是运行时类 (e的运行时类是User)
创建对象方法
- Class c =Class.forName("User");
- //创建此Class 对象所表示的类的一个新实例
- Object o = c.newInstance(); //调用了User的无参数构造方法.
获取类属性
- //获取整个类
- Class c = Class.forName("java.lang.Integer");
- //获取所有的属性
- Field[] fs = c.getDeclaredFields();
获取方法
getDeclaredMethods()
...
反射的缺点:
反射机制给予Java开发很大的灵活性,但反射机制本身也有缺点,代表性的缺陷就是反射的性能,
一般来说,通过反射调用方法的效率比直接调用的效率要至少慢一倍以上。
关于性能的问题,可以参考这篇博客http://blog.csdn.net/l_serein/article/details/6219897
优点:
a、Java的反射机制就是增加程序的灵活性,避免将程序写死到代码里。
例如: 实例化一个 person()对象, 不使用反射,需要new person(); 如果想变成实例化其他类,那么必须修改源代码,并重新编译。
使用反射: class.forName("person").newInstance();
而且这个类描述可以写到配置文件中,如 **.xml, 这样如果想实例化其他类,只要修改配置文件的"类描述"就可以了,不需要重新修改代码并编译。
在JavaWeb中加载数据库驱动时会用到。
b、增加程序的灵活性。
例如:struts中。请求的派发控制。
当请求来到时。struts通过查询配置文件。找到该请求对应的action方法。然后通过反射实例化action。并调用响应method。
如果不适用反射,那么你就只能写死到代码里了。
反射一般在框架中使用较多。
因为框架要适用更多的情况。对灵活性要求较高。
反射与设计模式
反射的一个很重要的作用,就是在设计模式中的应用,包括在工厂模式和代理模式中的应用。
工厂模式和代理模式又在各种框架(如Strust2,Spring)中广泛应用。
先来看看,如果不用反射的时候,的工厂模式吧:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
/** * @author Rollen-Holt 设计模式之 工厂模式 */interface fruit{ public abstract void eat();}class Apple implements fruit{ public void eat(){ System.out.println("Apple"); }}class Orange implements fruit{ public void eat(){ System.out.println("Orange"); }}// 构造工厂类// 也就是说以后如果我们在添加其他的实例的时候只需要修改工厂类就行了class Factory{ public static fruit getInstance(String fruitName){ fruit f=null; if("Apple".equals(fruitName)){ f=new Apple(); } if("Orange".equals(fruitName)){ f=new Orange(); } return f; }}class hello{ public static void main(String[] a){ fruit f=Factory.getInstance("Orange"); f.eat(); }} |
这样,当我们在添加一个子类的时候,就需要修改工厂类了。如加一个梨的子类时,需要修改工厂类。
如果我们添加太多的子类的时候,改的就会很多。
现在我们看看利用反射机制:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package Reflect;interface fruit{ public abstract void eat();}class Apple implements fruit{ public void eat(){ System.out.println("Apple"); }}class Orange implements fruit{ public void eat(){ System.out.println("Orange"); }}class Factory{ public static fruit getInstance(String ClassName){ fruit f=null; try{ f=(fruit)Class.forName(ClassName).newInstance(); }catch (Exception e) { e.printStackTrace(); } return f; }}class hello{ public static void main(String[] a){ fruit f=Factory.getInstance("Reflect.Apple"); if(f!=null){ f.eat(); } }} |
我们传给工厂类的参数是子类的全名,现在就算我们添加任意多个子类的时候,工厂类就不需要修改。
上面的代码虽然可以通过反射取得接口的实例,但是需要传入完整的包和类名。
而且用户也无法知道一个接口有多少个可以使用的子类,所以我们通过属性文件的形式配置所需要的子类。
下面我们来看看: 结合属性文件的工厂模式,可以称之为 动态工厂模式。
首先创建一个fruit.properties的资源文件,
内容为:
|
1
2
|
apple=Reflect.Appleorange=Reflect.Orange |
然后编写主类代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
package Reflect;import java.io.*;import java.util.*;interface fruit{ public abstract void eat();}class Apple implements fruit{ public void eat(){ System.out.println("Apple"); }}class Orange implements fruit{ public void eat(){ System.out.println("Orange"); }}//操作属性文件类class init{ public static Properties getPro() throws FileNotFoundException, IOException{ Properties pro=new Properties(); File f=new File("fruit.properties"); if(f.exists()){ pro.load(new FileInputStream(f)); }else{ pro.setProperty("apple", "Reflect.Apple"); pro.setProperty("orange", "Reflect.Orange"); pro.store(new FileOutputStream(f), "FRUIT CLASS"); } return pro; }}class Factory{ public static fruit getInstance(String ClassName){ fruit f=null; try{ f=(fruit)Class.forName(ClassName).newInstance(); }catch (Exception e) { e.printStackTrace(); } return f; }}class hello{ public static void main(String[] a) throws FileNotFoundException, IOException{ Properties pro=init.getPro(); fruit f=Factory.getInstance(pro.getProperty("apple")); if(f!=null){ f.eat(); } }} |
【运行结果】:Apple
动态代理模式
接下来我们来看一下代理。
代理可以帮助我们进行很好的封装,使底层的代码能够有效的隐藏起来。
我们先来看看静态代理吧。
public class Main2 {
//这里传入的是接口类型的对象,方便向上转型,实现多态
public static void consumer(ProxyInterface pi){
pi.say();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
consumer(new ProxyObject());
}
}
//代理接口
interface ProxyInterface{
public void say();
}
//被代理者
class RealObject implements ProxyInterface{
//实现接口方法
@Override
public void say() {
// TODO Auto-generated method stub
System.out.println("say");
}
}
//代理者
class ProxyObject implements ProxyInterface{
@Override
public void say() {
// TODO Auto-generated method stub
//dosomething for example
System.out.println("hello proxy");
new RealObject().say();
System.out.println("this is method end");
}
}
output:
hello proxy
say
this is method end
这就是静态代理,理解这个应该不难。
下面我们再来看看动态代理
import java.lang.reflect.*; public class Main {
static void customer(ProxyInterface pi){
pi.say();
}
public static void main(String[] args){
RealObject real = new RealObject();
//当运行时(动态)才创建代理类
ProxyInterface proxy = (ProxyInterface)Proxy.newProxyInstance(real.getClass().getClassLoader(), real.getClass().getInterfaces(), new ProxyObject(real));
customer(proxy);
}
}
interface ProxyInterface{
void say();
}
//被代理类
class RealObject implements ProxyInterface{
public void say(){
System.out.println("i'm talking");
}
}
//代理类,实现InvocationHandler 接口
class ProxyObject implements InvocationHandler{
private Object proxied = null;
public ProxyObject(){
}
public ProxyObject(Object proxied){
this.proxied = proxied;
}
public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
System.out.println("hello");
return arg1.invoke(proxied, arg2);
};
}
可以看到动态代理的代理类是实现了一个InvocationHandler的接口,
我们通过reflect.Proxy的类的newProxyInstance方法就可以得到这个接口的实例,然后再来作为参数传递进去,
这里每一个在代理类上处理的东西也会被重定向到调用处理器上。
至于动态代理和静态代理的区别,
即动态代理是动态的创建代理和动态的处理方法的,这也是反射的一个重要体现之处。
深入理解java:1.1.1. 反射机制的更多相关文章
- 《深入理解 Java 虚拟机》学习 -- 类加载机制
<深入理解 Java 虚拟机>学习 -- 类加载机制 1. 概述 虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的 J ...
- Java 核心类库之反射机制
1:什么是反射机制? 2:反射机制它可以做什么呢? 3:反射机制对应的API又是什么? 1):通过反射机制来获取一个对象的全限定名称(完整包名),和类名: 2):实例化Class对象 3):获取对象的 ...
- 《深入理解Java虚拟机》虚拟机类加载机制
上节学习回顾 上一节,我们深入到类文件去了解其结构细节,也大概对类文件的编写规则略知一二了,解析来我们就得学习这个类文件是如何被加载到Java虚拟机的,看看有什么引人入胜的奥秘. 本节学习重点 大部分 ...
- JAVA(五)反射机制/Annotation
成鹏致远 | lcw.cnblog.com |2014-02-04 反射机制 1.认识Class类 在正常情况下,必须知道一个类的完整路径之后才可以实例化对象,但是在 java中也允许通过一个对象来找 ...
- 深入理解Java虚拟机:虚拟机类加载机制
目录 7.1 概述 7.2 类加载的时机 类的生命周期 5种情况需要"初始化" 7.3 类加载的过程 1.加载 2.验证 3.准备 4.解析 5.初始化 7.4 类加载器 类与类加 ...
- java的RTTI和反射机制
RTTI,即Run-Time Type Identification,运行时类型识别.RTTI能在运行时就能够自动识别每个编译时已知的类型. 很多时候需要进行向上转型,比如Base类派生出Derive ...
- java.lang.Class<T> -- 反射机制
1反射机制是什么 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为jav ...
- Java进阶之reflection(反射机制)——反射概念与基础
反射机制是Java动态性之一,而说到动态性首先得了解动态语言.那么何为动态语言? 一.动态语言 动态语言,是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结构上的变化.比如常见 ...
- 在JAVA中,关于反射机制的讨论
一.什么是反射机制 简单的来说,反射机制指的是程序在运行时能够获取自身的信息.在java中,只要给定类的名字, 那么就可以通过反射机制来获得类的所有信息. 二.哪里用到反射机制 ...
- java工厂类与反射机制
java 简单工厂类 2012-04-22 15:44:07| 分类: java | 标签:java工厂类 简单工厂类 |举报|字号 订阅 简单工厂模式需要由以下角色组成: 接口 ...
随机推荐
- SSM框架中IoC、DI与AOP的理解
框架封装了普通项目中程序员需要重复书写的代码和调用过程,就比如说在传统的jsp项目中,我们的controller接收到前端的请求然后程序员就需要去开发Dao层,里面还涉及数据库的连接和存储过程的代码, ...
- QT中获取选中的radioButton的两种方法
QT中要获取radioButton组中被选中的那个按钮,可以采用两种如下两种办法进行: 方法一:采用对象名称进行获取 代码: 1 QRadioButton* pbtn = qobject_cast&l ...
- null 的应用
它是 Oracle 中非常特殊的一种类型.它表示不确定,表示没有值.并且它能转化成所有的类型. 向数据库中插入空字符串时,oracle 会把它自动转化为 null 类型.所以,在查询空字符的时候: s ...
- 微信小程序评价页面
<!--服务评价--> <view class="service"> <!--评分--> <view class='score'> ...
- python主流框架简介和Django框架的使用
目录 一.手撸简易web框架 二.动静态网页 1. 静态网页 2. 动态网页 三.jinja2模板语法 1. jinja2的作用 四.python主流web框架 1. django 3. tornad ...
- uwsgi 的启动、停止、重启
## 一.概念释义### WSGI WSGI 是一个Web服务器(如nginx)与应用服务器(如uWSGI)通信的一种规范(协议).官方定义是,the Python Web Server Gatewa ...
- 【leetcode】1186. Maximum Subarray Sum with One Deletion
题目如下: Given an array of integers, return the maximum sum for a non-empty subarray (contiguous elemen ...
- shell练习--PAT题目1008:数组元素循环右移问题 (失败案例,运行超时)
一个数组A中存有N(>)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(≥)个位置,即将A中的数据由(A0A1⋯AN−1)变换为(AN−M⋯AN−1A ...
- MySQL5.7数据转移至SQL Server详解
本文链接:https://blog.csdn.net/qq_37308779/article/details/80679358一.安装MySQL ODBC驱动为MySQL安装Connector/ODB ...
- caffe 安装
安装caffe 拉取镜像 nvidia/cuda:9.0-cudnn7-devel-centos7 1,换源 安装https://blog.csdn.net/tuomen5867/article/de ...