Java基础之 反射是什么?
什么是反射,反射能干嘛?
反射是:指程序可以访问、检测和修改它本身状态或行为的一种能力
反射是一种能力,所以给的定义就是说明了它能干嘛。
我们平时用反射主要做:
- 获取类型的相关信息
- 动态调用方法
- 动态构造对象
- 从程序集中获得类型。
获取类型的相关信息
反射的核心Type类,Type对象提供的属性和方法可以获取对象的一切信息,如:方法、字段、属性、事件...等等。
我们获取已加载程序集中类型的Type对象的几种方法:(以StringBuilder 类型为例)
- 直接使用typeof操作符 Type T1 = typeof(StringBuilder);
- 通过类型实例 Type T2 = new StringBuilder().GetType();
- 通过Type类的静态方法 Type T3 = Type.GetType("System.IO.Stream");
不管使用那种,我们最终得到的结果都是一样的。
那么我们通过Type又能得到些什么信息呢?
获取类型本身信息(命名空间名、全名、是否是抽象、是否是类、、、等等)
var T1 = typeof(StringBuilder);
Console.WriteLine("命名空间名称:" + T1.Namespace);
Console.WriteLine("直接基类型:" + T1.BaseType);
Console.WriteLine("全名:" + T1.FullName);
Console.WriteLine("是抽象类型:" + T1.IsAbstract);
Console.WriteLine("是类:" + T1.IsClass);
//.....等等

获取类型成员信息(通过Tyep中的方法GetMembers)
Type T1 = typeof(TClass);
var Mets = T1.GetMembers();//获取Type对象的所有公有成员
foreach (var m in Mets)
{
Console.WriteLine("【" + m.MemberType.ToString()+ "】:" + m.Name);
// m.MemberType 是成员类型
}

MemberType所能包含的成员类型有哪些呢?如:(可以自己可以F12进去看看)

注意:其中MemberInfo的属性DeclaringType返回的是这个属性定义的类型,而ReflectedType返回的是获取这个属性的对象类型。
如:
Type T2 = typeof(TClass);
var Mets = T2.GetMembers();//获取所有公共成员(返回值是MemberInfo类型集合)
foreach (var m in Mets)
{
if (m.Name=="Equals")
{
Console.WriteLine("【" + m.MemberType.ToString() + "】:" + m.Name);
// m.MemberType 是成员类型 // m.DeclaringType;//获取申明该成员的类
// m.ReflectedType;//获取用于获取 MemberInfo 的此实例的类对象。
}
}
T2中的Equals,我们知道这个方式是在Objec中定义的,在TClass中调用的,所以:

我们发现获取Type对象的成员大多都是以 isxxx、Getxxx、Getxxxs格式的。
isxxx格式的基本上都是判断是否是某类型。
Getxxx和Getxxxs都是放回某类型和某类型集合。其中主要的类型有:
//FieldInfo封装了关于字段的所有信息 (通过Tyep对象的GetFields或GetField方法)
//PropertyInfo类型,封装了类型的属性信息;(通过Type对象的GetProperties或GetProperty方法)
//ConstructorInfo类型,封装了类型的构造函数信息; (..........)
//MethodInfo类型,封装了类型的方法信息; (........) //MemberInfo类型,封装了类型的所有公共成员;(**就是我们上面说的GetMembers方法**)
//EventInfo类型,封装了类型的事件信息;(.......)
//ParameterInfo类型,封装了方法和构造函数的参数信息;(........)
它们都在 System.Reflection 命名空间下,其每个isxxx、Getxxx、Getxxxs的细节实例用法就不一一演示了。和上面的GetMembers用法区别不大。
动态调用方法
首先定义个类:
public class TClass
{
public void fun(string str)
{
Console.WriteLine("我是fun方法,我被调用了。" + str);
}
public void fun2()
{
Console.WriteLine("我是fun2方法,我被调用了。");
} public static void fun3()
{
Console.WriteLine("我是fun3静态方法,我被调用了");
}
}
调用方式一(使用InvokeMember调用方法)
调用带参实例方法fun
Type T1 = typeof(TClass);
T1.InvokeMember("fun", BindingFlags.InvokeMethod, null, new TClass(), new string[] { "test" });

调用无参实例方法fun2
Type T1 = typeof(TClass);
T1.InvokeMember("fun2", BindingFlags.InvokeMethod, null, new TClass(), null);
调用静态方法
Type T1 = typeof(TClass);
T1.InvokeMember("fun3", BindingFlags.InvokeMethod, null, T1, null);
我们发现了一个问题当我们调用实例方法的时候需要传实例对象过去。(有人会说,都实例对象了,我还要你动态掉调用个屁啊。有种情况,在我们实例了对象后,仍不确定应该调用那个方法时可以只有使用。然后有人有说了,那如果实例对象我也不确定呢?那我们下面会分析连实例对象也给动态了。那接着完下看吧。)
我们来说下这几个参数的意思吧。
第一个:要被动态调用的方法名。
第二个:是一个枚举,表示是调用一个方法
第三个:是Binder,传的是null,使用默认值。
第四个:传如实例对象(调用实例方法时)或者Type对象(调用静态方法时)。
第五个:要传给被调用发的参数数组。
调用方式二(使用MethodInfo.Invoke调用方法)
Type T1 = typeof(TClass);
T1.GetMethod("fun", BindingFlags.Instance | BindingFlags.Public).Invoke(new TClass(), new string[] { "testfun1" });
T1.GetMethod("fun2", BindingFlags.Instance | BindingFlags.Public).Invoke(new TClass(), null);
T1.GetMethod("fun3", BindingFlags.Static | BindingFlags.Public).Invoke(T1, null);

使用其实和上面的方式一区别不大。
真正的全动态调用
上面的两种方式,在编写代码的时候总是要先确定了已知的对象名和方法名。那么我们在不知道对象和方法名的时候是否也可以调用呢?答案是肯定的,实现如下:
Console.WriteLine("请输入对象类名");
string className = Console.ReadLine();
Console.WriteLine("请输入要执行的方法名");
string funName = Console.ReadLine();
Type T1 = Type.GetType(className);
ConstructorInfo ci = T1.GetConstructors()[0]; //获取构造函数
var obj = ci.Invoke(null);//实例化构造函数
T1.InvokeMember(funName, BindingFlags.InvokeMethod, null, obj, null);
当然,这个代码只能只是fun2,因为上面的传参写死了。(你也可以自己稍微修改下,就可以执行fun、fun2、fun3了)
效果如下:(对象名和方法名都是手动输入的)

动态构造对象
我们先定义一个对象:
public class TClass
{
public TClass()
{
Console.WriteLine("构造函数被执行了。。");
}
public TClass(string str)
{
Console.WriteLine("有参构造函数被执行了。。" + str);
}
}
动态构造对象
//动态构造对象,方式一
Assembly asm = Assembly.GetExecutingAssembly();
TClass obj = (TClass)asm.CreateInstance("net.tclass", true);//true:不区分大小写 //动态构造对象,方式二
ObjectHandle handler = Activator.CreateInstance(null, " net.TClass");//null:当前程序集
obj = (TClass)handler.Unwrap(); //动态构造对象,方式三(构造有参构造函数)
Assembly asm2 = Assembly.GetExecutingAssembly();
obj = (TClass)asm2.CreateInstance("net.tclass", true, BindingFlags.Default, null, new string[] { "test" }, null, null);//true:不区分大小写
执行效果图:

获取和修改属性
var obj = new TClass();
obj.name = "张三";
Type type = typeof(TClass);
//获取属性
var Name = type.InvokeMember("name", BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance, null,
obj, new object[] { }) as string;
Console.WriteLine(obj.name);
//设置属性
type.InvokeMember("name", BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance, null,
obj, new object[] { "新属性(李四)" });
Console.WriteLine(obj.name); //=====================
PropertyInfo[] pros = type.GetProperties(---);//
PropertyInfo pro = null;
var value = pro.GetValue(type);//获取值

从程序集中获得类型
取得当前代码所在程序集(使用GetExecutingAssembly)
Assembly ass = Assembly.GetExecutingAssembly();
Console.WriteLine("当前所在程序集名:"+ass.ManifestModule.Name);
Console.WriteLine("当前所在程序集路径:"+ass.Location);

通过反射加载程序集并创建程序中的类型对象
从程序集中获得类型,这个应该是我们平时用得比较多。如我们所谓的依赖注入和控制反转(这个主题将在下篇博文进行分析)就用到了通过反射从程序集中获取类型。
首先我们还是看看怎么从程序集中获得类型吧。我们可以使用Assembly类型提供的静态方法LoadFrom()或Load(),如:
Assembly asm = Assembly.LoadFrom("Demo.dll");
Assembly asm = Assembly.Load("Demo");
区别:
Assembly asm = Assembly.LoadFrom("net.exe");//需要加后缀,可以指定路径,如下面的
Assembly asm1 = Assembly.LoadFrom(@"C:\01文件\05Svn\BlogsCode\Blogs\Blogs.Web\bin\Blogs.BLL.dll");
Assembly asm2 = Assembly.Load("Blogs.BLL");//无需加后缀,不可以指定路径
//Assembly asm3 = Assembly.Load(@"C:\01文件\05Svn\BlogsCode\Blogs\Blogs.Web\bin\Blogs.BLL");//这里会报错
//使用Load可以加载当前程序bin目录行下的程序集或者系统程序集
//这里TClass可以是一个接口,那么可以在外面的dll任意实现了。
TClass obj = (TClass)asm2.CreateInstance("net.tclass", true);//true:不区分大小写
obj.fun();//***调用动态加载的dll中的方法***
这样带来的功能是非常强大的。如 我们在没有引用程序集的情况下,也可以使用到程序外的程序集。我们还可以根据不同情况引用不同的程序集。我们甚至还可以通过配置文件来直接配置代码运行时应该加载哪个dll,运行哪个dll中的哪个实现方法。(下篇在讲依赖注入的时候会讲到,同学们继续关注哦~)
从上所知,反射不是某一个概念,而是一类操作的统称。或者说是某些能力的统称。 感觉不好回答反射到底是什么,只能说反射能干什么。它能动态创建对象、动态调用对象方法、动态读取和设置属性和字段、它能动态加载程序外的dll。总的感觉就是大多数都是跟“动态”扯上了关系。
补充:跨程序集反射
如果我们反射A.dll,而A.dll中引用了B.dll,那么在assembly.GetTypes(); //运行到这个地方会弹出如下错误描述
“未处理 System.Reflection.ReflectionTypeLoadException Message="无法加载一个或多个请求的类型。有关更多信息,请检索LoaderExceptions属性。”
这种情况可以
Assembly assembly = Assembly.LoadFrom("A.dll") ;
Type type = assembly.GetType("xxx.myclassname") ; //传入对应的需要反射的类型 而不能GetTypes。且,应用程序需要应用A.dll锁依赖的B.dll。
Java基础之 反射是什么?的更多相关文章
- 黑马程序员:Java基础总结----反射
黑马程序员:Java基础总结 反射 ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! 反射 反射的基石:Class类 Class类代表Java类,它的各个实例对象又分别 ...
- Java基础之一反射
反射是框架设计的灵魂 (使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码)) 一.反射的概述 JAVA反射机制是在运行状态中,对于任意一个类,都能够 ...
- Java基础之—反射
反射是框架设计的灵魂 (使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码)) 一.反射的概述 JAVA反射机制是在运行状态中,对于任意一个类,都能够 ...
- JAVA基础知识|反射
一.理解反射 1.1.基础概念 反射:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为ja ...
- java基础之反射机制
一.概念 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为jav ...
- java基础(十一 )-----反射——Java高级开发必须懂的
本文我们通过一个实际的例子来演示反射在编程中的应用,可能之前大家对反射的学习,仅仅是停留在概念层面,不知道反射究竟应用在哪,所以是一头雾水.相信通过这篇教程,会让你对反射有一个更深层次的认知. 概念 ...
- java基础之反射---重要
java反射: 反射是框架设计的灵魂 (使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码)): 1:获取Class字节码文件对象的三种方式: /** ...
- 【Java基础】反射和注解
前言 在Java中,反射机制和注解机制一直是一个很重要的概念,那么他们其中的原理是怎么样呢,我们不仅仅需要会使用,更要知其然而之所以然. 目录 反射机制 反射如何使用 注解定义 注解机制原理 注解如何 ...
- java基础篇---反射机制
一.JAVA是动态语言吗? 一般而言,说到动态言,都是指在程序运行时允许改变程序结构或者变量类型,从这个观点看,JAVA和C++一样,都不是动态语言. 但JAVA它却有着一个非常突出的动态相关机制:反 ...
- Java 基础总结--反射的基本操作
一.反射的概念 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为 ...
随机推荐
- BZOJ_1662_[Usaco2006 Nov]Round Numbers 圆环数_数位DP
BZOJ_1662_[Usaco2006 Nov]Round Numbers 圆环数_数位DP Description 正如你所知,奶牛们没有手指以至于不能玩“石头剪刀布”来任意地决定例如谁先挤奶的顺 ...
- 【源码安装】Heartbeat3.0.9
1.概述1.1 关于Heartbeat1.2 本篇博客实践环境2. 部署基础环境2.1 通过YUM安装依赖环境2.2 创建Heartbeat用户和组3. 编译安装3.1下载源码包3.2 编译安装3.2 ...
- Java解析表达式
需求 思路 总结 需求 指定一个String表达式,表达式符合给出的运算符规范,比如:2!=2 and 2>=3 or 4<=4,计算出表达式的结果(true or false). 支持的 ...
- 大数据技术之_19_Spark学习_03_Spark SQL 应用解析 + Spark SQL 概述、解析 、数据源、实战 + 执行 Spark SQL 查询 + JDBC/ODBC 服务器
第1章 Spark SQL 概述1.1 什么是 Spark SQL1.2 RDD vs DataFrames vs DataSet1.2.1 RDD1.2.2 DataFrame1.2.3 DataS ...
- XiaomiPushDemo【小米推送集成,基于V3.6.12版本】
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 这个Demo只是记录小米推送的集成,不能运行. 使用步骤 一.项目组织结构图 注意事项: 1. 导入类文件后需要change包名以 ...
- 真win10官方原版ISO下载方法
最近装新机器,计划装个双系统,但是新硬件用不了Win7,只好改装Win10.经过数遍尝试,发现网上很多打着官方原版旗号的ISO以及各种装机软件,或多或少都捆绑了一些"流氓"软件,这 ...
- Http通讯Util
目录 HttpUtil类 HttpUtil类 import java.io.BufferedReader; import java.io.IOException; import java.io.Inp ...
- 用系统为centos6的主机,搭建PXE服务器,实现批量安装centos6,7系统
1. iptables -F setenforce 0 临时关掉selinux,清掉防火墙 永久生效更改配置文件:vim /etc/sysconfig/selinux chkconfig iptabl ...
- [目录]搭建一个简单的WebGIS应用程序
“如果一件事情超过自己的能力,自己很难达到,那就像是婴儿跳高,不但没有好处,反而拔苗助长”. 4月份时报名参加了2018年ESRI杯GIS应用开发比赛,到前几天提交了作品.作品很简单,没有那么多复杂深 ...
- PostgreSQL 10.7 linux 主从配置
PostgreSQL 10.7 主从安装 硬件环境 云服务商:华为云 Linux: CentOS7.1 工具:Xshell Xftp IP:114.115.251.168 Port: 5432 543 ...