java反射(转)
链接:https://www.zhihu.com/question/24304289/answer/38218810
来源:知乎
著作权归作者所有,转载请联系作者获得授权。
反射之中包含了一个“反”的概念,所以要想解释反射就必须先从“正”开始解释,一般而言,当用户使用一个类的时候,应该先知道这个类,而后通过这个类产生实例化对象,但是“反”指的是通过对象找到类。
packagecn.mldn.demo;
classPerson {}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Person per = newPerson() ; // 正着操作
System.out.println(per.getClass().getName()); // 反着来
}
}
以上的代码使用了一个getClass()方法,而后就可以得到对象所在的“包.类”名称,这就属于“反”了,但是在这个“反”的操作之中有一个getClass()就作为发起一切反射操作的开端。
Person的父类是Object类,而上面所使用getClass()方法就是Object类之中所定义的方法。
·取得Class对象:public final Class<?> getClass(),反射之中的所有泛型都定义为?,返回值都是Object。
而这个getClass()方法返回的对象是Class(注意,这边跟我们定义类的修饰符class不一样,这个Class是大写,它也是一种类,就跟String,List这些类似,都是属于一个类,只不过这个类专门用于记录其他类的信息)类的对象,所以这个Class就是所有反射操作的源头。但是在讲解其真正使用之前还有一个需要先解释的问题,既然Class是所有反射操作的源头,那么这个类肯定是最为重要的,而如果要想取得这个类的实例化对象,Java中定义了三种方式:
方式一:通过Object类的getClass()方法取得,基本不用:
packagecn.mldn.demo;
class Person {}
public class TestDemo {
public static void main(String[] args) throws Exception {
Person per = new Person() ; // 正着操作
Class<?> cls = per.getClass() ; // 取得Class对象
System.out.println(cls.getName()); // 反着来
}
}
方式二:使用“类.class”取得,在日后学习Hibernate开发的时候使用
packagecn.mldn.demo;
class Person {}
public class TestDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Person.class; // 取得Class对象
System.out.println(cls.getName()); // 反着来
}
}
方式三:使用Class类内部定义的一个static方法,主要使用
·取得Class类对象:public static Class<?>
forName(String className) throws
ClassNotFoundException;
package cn.mldn.demo;
class Person {}
public class TestDemo {
public static void main(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class对象
System.out.println(cls.getName()); // 反着来
}
}
那么现在一个新的问题又来了,取得了Class类的对象有什么用处呢?对于对象的实例化操作之前一直依靠构造方法和关键字new完成,可是有了Class类对象之后,现在又提供了另外一种对象的实例化方法:
·通过反射实例化对象:public T newInstance() throws InstantiationException,
IllegalAccessException;
范例:通过反射实例化对象
package cn.mldn.demo;
class Person {
@Override
public String toString() {
return"Person Class Instance .";
}
}
public class TestDemo {
public static void main(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class对象
Object obj = cls.newInstance() ; // 实例化对象,和使用关键字new一样
Person per = (Person) obj ; // 向下转型
System.out.println(per);
}
}
那么现在可以发现,对于对象的实例化操作,除了使用关键字new之外又多了一个反射机制操作,而且这个操作要比之前使用的new复杂一些,可是有什么用?
对于程序的开发模式之前一直强调:尽量减少耦合,而减少耦合的最好做法是使用接口,但是就算使用了接口也逃不出关键字new,所以实际上new是造成耦合的关键元凶。
范例:回顾一下之前所编写的工厂设计模式
package cn.mldn.demo;
interface Fruit {
public void eat() ;
}
class Apple implements Fruit {
public void eat() {
System.out.println("吃苹果。");
};
}
class Factory {
public static Fruit getInstance(String className) {
if("apple".equals(className)){
return new Apple() ;
}
returnnull;
}
}
public class FactoryDemo {
public static void main(String[] args) {
Fruit f = Factory.getInstance("apple") ;
f.eat() ;
}
}
以上为之前所编写最简单的工厂设计模式,但是在这个工厂设计模式之中有一个最大的问题:如果现在接口的子类增加了(比如多了一个banner的子类),那么工厂类肯定需要修改,这是它所面临的最大问题,而这个最大问题造成的关键性的病因是new,那么如果说现在不使用关键字new了,变为了反射机制呢?
反射机制实例化对象的时候实际上只需要“包.类”就可以,于是根据此操作,修改工厂设计模式。
packagecn.mldn.demo;
interface Fruit {
public void eat() ;
}
class Apple implements Fruit {
public void eat() {
System.out.println("吃苹果。");
};
}
class Orange implementsFruit {
public void eat() {
System.out.println("吃橘子。");
};
}
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 ;
}
}
public class FactoryDemo {
public static void main(String[] args) {
Fruit f = Factory.getInstance("cn.mldn.demo.Orange") ;//只需要传入报名+类名
f.eat() ;
}
}
发现,这个时候即使增加了接口的子类,工厂类照样可以完成对象的实例化操作,这个才是真正的工厂类,可以应对于所有的变化。如果单独从开发角度而言,与开发者关系不大,但是对于日后学习的一些框架技术这个就是它实现的命脉,在日后的程序开发上,如果发现操作的过程之中需要传递了一个完整的“包.类”名称的时候几乎都是反射机制作用。
3.12.2 、反射的深入应用
以上只是利用了Class类作为了反射实例化对象的基本应用,但是对于一个实例化对象而言,它需要调用类之中的构造方法、普通方法、属性,而这些操作都可以通过反射机制完成。
3.12.2 .1、调用构造
使用反射机制也可以取得类之中的构造方法,这个方法在Class类之中已经明确定义了:
以下两个方法
取得一个类的全部构造:
public Constructor<?>[] getConstructors() throws
SecurityException
取得一个类的指定参数构造:
public Constructor<T> getConstructor(Class<?>...
parameterTypes) throws NoSuchMethodException, SecurityException
现在发现以上的两个方法返回的都是java.lang.reflect.Constructor类的对象。
范例:取得一个类之中的全部构造
packagecn.mldn.demo;
import java.lang.reflect.Constructor;
classPerson { // CTRL + K
publicPerson() {}
publicPerson(String name) {}
publicPerson(String name,intage) {}
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class对象
Constructor<?> cons [] = cls.getConstructors() ; // 取得全部构造
for(intx = 0; x < cons.length; x++) {
System.out.println(cons[x]);
}
}
}
验证:在之前强调的一个简单Java类必须存在一个无参构造方法
范例:观察没有无参构造的情况
packagecn.mldn.demo;
classPerson { // CTRL + K
privateString name;
privateintage;
publicPerson(String name,intage) {
this.name= name ;
this.age= age ;
}
@Override
publicString toString() {
return"Person [name="+ name+ ", age="+ age+ "]";
}
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class对象
Object obj = cls.newInstance(); // 实例化对象
System.out.println(obj);
}
}
此时程序运行的时候出现了错误提示“java.lang.InstantiationException”,因为以上的方式使用反射实例化对象时需要的是类之中要提供无参构造方法,但是现在既然没有了无参构造方法,那么就必须明确的找到一个构造方法,而后利用Constructor类之中的新方法实例化对象:
·实例化对象:public T newInstance(Object...
initargs) throws InstantiationException, IllegalAccessException,IllegalArgumentException,
InvocationTargetException
packagecn.mldn.demo;
importjava.lang.reflect.Constructor;
classPerson { // CTRL + K
privateString name;
privateintage;
publicPerson(String name,intage) {
this.name= name ;
this.age= age ;
}
@Override
publicString toString() {
return"Person [name="+ name+ ", age="+ age+ "]";
}
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class对象
// 取得指定参数类型的构造方法
Constructor<?> cons = cls.getConstructor(String.class,int.class) ;
Object obj = cons.newInstance("张三", 20); // 为构造方法传递参数
System.out.println(obj);
}
}
很明显,调用无参构造方法实例化对象要比调用有参构造的更加简单、方便,所以在日后的所有开发之中,凡是有简单Java类出现的地方,都一定要提供无参构造。
3.12.2 .2、调用普通方法
当取得了一个类实例化对象之后,下面最需要调用的肯定是类之中的方法,所以可以继续使用Class类取得一个类中所定义的方法定义:
·取得全部方法:public Method[] getMethods()
throws SecurityException;
·取得指定方法:public Method getMethod(String
name, Class<?>... parameterTypes) throws NoSuchMethodException,
SecurityException
发现以上的方法返回的都是java.lang.reflect.Method类的对象。
范例:取得一个类之中所定义的全部方法
packagecn.mldn.demo;
importjava.lang.reflect.Method;
classPerson {
privateString name;
publicvoidsetName(String name) {
this.name= name;
}
publicString getName() {
returnname;
}
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class对象
Method met [] = cls.getMethods() ; // 取得全部方法
for(intx = 0; x < met.length; x++) {
System.out.println(met[x]);
}
}
}
但是取得了Method类对象最大的作用不再于方法的列出(方法的列出都在开发工具上使用了),但是对于取得了Method类对象之后还有一个最大的功能,就是可以利用反射调用类中的方法:
·调用方法:public Object invoke(Object
obj, Object... args) throws IllegalAccessException,IllegalArgumentException,
InvocationTargetException
之前调用类中方法的时候使用的都是“对象.方法”,但是现在有了反射之后,可以直接利用Object类调用指定子类的操作方法。(同时解释一下,为什么setter、getter方法的命名要求如此严格)。
范例:利用反射调用Person类之中的setName()、getName()方法
packagecn.mldn.demo;
importjava.lang.reflect.Method;
classPerson {
privateString name;
publicvoidsetName(String name) {
this.name= name;
}
publicString getName() {
returnname;
}
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class对象
Object obj = cls.newInstance(); // 实例化对象,没有向Person转型
String attribute = "name"; // 要调用类之中的属性
Method setMet = cls.getMethod("set"+ initcap(attribute), String.class);// setName()
Method getMet = cls.getMethod("get"+ initcap(attribute));// getName()
setMet.invoke(obj, "张三") ; // 等价于:Person对象.setName("张三")
System.out.println(getMet.invoke(obj));// 等价于:Person对象.getName()
}
publicstaticString initcap(String str) {
returnstr.substring(0,1).toUpperCase().concat(str.substring(1)) ;
}
}
在日后的所有框架技术开发之中,简单Java类都是如此应用的,所以必须按照标准进行。
3.12.2 .3、调用成员
类之中最后一个组成部分就是成员(Field,也可以称为属性),如果要通过反射取得类的成员可以使用方法如下:
·取得本类的全部成员:public Field[]
getDeclaredFields() throws SecurityException;
·取得指定的成员:public Field
getDeclaredField(String name) throws NoSuchFieldException, SecurityException;
这两个方法的返回值类型是java.lang.reflect.Field类的对象,下面首先观察如何取得一个类之中的全部属性。
范例:取得一个类之中的全部属性
packagecn.mldn.demo;
importjava.lang.reflect.Field;
classPerson {
privateString name;
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class对象
Field field [] = cls.getDeclaredFields() ; // 取得全部属性
for(intx = 0; x < field.length; x++) {
System.out.println(field[x]);
}
}
}
但是找到Field实际上就找到了一个很有意思的操作,在Field类之中提供了两个方法:
·设置属性内容(类似于:对象.属性= 内容):public void set(Object obj,
Object value)
throws
IllegalArgumentException, IllegalAccessException;
·取得属性内容(类似于:对象.属性):public Object get(Object obj)
throws
IllegalArgumentException, IllegalAccessException
可是从类的开发要求而言,一直都强调类之中的属性必须封装,所以现在调用之前要想办法解除封装。
·解除封装:public void
setAccessible(boolean flag) throws SecurityException;
范例:利用反射操作类中的属性
packagecn.mldn.demo;
importjava.lang.reflect.Field;
classPerson {
privateString name;
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person"); // 取得Class对象
Object obj = cls.newInstance(); // 对象实例化属性才会分配空间
Field nameField = cls.getDeclaredField("name") ; // 找到name属性
nameField.setAccessible(true) ; // 解除封装了
nameField.set(obj, "张三") ; // Person对象.name = "张三"
System.out.println(nameField.get(obj)); // Person对象.name
}
}
java反射(转)的更多相关文章
- 第28章 java反射机制
java反射机制 1.类加载机制 1.1.jvm和类 运行Java程序:java 带有main方法的类名 之后java会启动jvm,并加载字节码(字节码就是一个类在内存空间的状态) 当调用java命令 ...
- Java反射机制
Java反射机制 一:什么事反射机制 简单地说,就是程序运行时能够通过反射的到类的所有信息,只需要获得类名,方法名,属性名. 二:为什么要用反射: 静态编译:在编译时确定类型,绑定对象,即通过 ...
- java反射(基础了解)
package cn.itcast_01; /** *Person类 */ public class Person { /** 姓名 */ private String name; ...
- java基础知识(十一)java反射机制(上)
java.lang.Class类详解 java Class类详解 一.class类 Class类是java语言定义的特定类的实现,在java中每个类都有一个相应的Class对象,以便java程序运行时 ...
- java基础知识(十一)java反射机制(下)
1.什么是反射机制? java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象都能够调用他的属性和方法,这种动态获取属性和方法的功能称为java的反射机制. ...
- java反射学习之二万能EXCEL导出
一.EXCEL导出的实现过程 假设有一个对象的集合,现在需要将此集合内的所有对象导出到EXCEL中,对象有N个属性:那么我们实现的方式是这样的: 循环这个集合,在循环集合中某个对象的所有属性,将这个对 ...
- java反射学习之一反射机制概述
一.反射机制背景概述 1.反射(reflection)是java被视为动态语言的一个关键性质 2.反射机制指的是程序在运行时能获取任何类的内部所有信息 二.反射机制实现功能概述 1.只要给定类的全名, ...
- java反射 之 反射基础
一.反射 反射:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为 ...
- java反射 cglib asm相关资料
有篇文章对java反射的调用的效率做了测试,写的比较好.猛击下面地址 http://www.blogjava.net/stone2083/archive/2010/09/15/332065.html ...
- 超详细的java反射教程
看技术博客时,看到关于java反射的博文,写的非常好.猛击下面的地址,开始java反射之旅 中文翻译地址:http://ifeve.com/java-reflection/ 英文原版地址:http:/ ...
随机推荐
- Asp.Net Core API网关Ocelot
首先,让我们简单了解下什么是API网关? API网关是一个服务器,是系统的唯一入口.从面向对象设计的角度看,它与外观模式类似.API网关封装了系统内部架构,为每个客户端提供一个定制的API.它可能还具 ...
- C#连接六类数据库的代码集
本文列出了C#连接Access.SQL Server.Oracle.MySQL.DB2和SyBase六种不同数据库的程序源码和需要注意的点. 1.C#连接Access 程序代码: ;
- DNS生效时间
http://blog.itechol.com/space-33-do-blog-id-908.html http://www.madboa.com/geek/dig/ Dig简介 Dig是一个在类U ...
- MySQL阿里云安装问题小记
1. [root@iZ2zeg11g0cdei5171kqskZ mysql]# ./scripts/mysql_install_db --user=mysql FATAL ERROR: please ...
- SSM框架通过mybatis-generator自动生成代码
一.首先eclipse配置好maven环境,并且创建好一个SSM框架的工程 二.在pom.xml中添加plugin <build> <finalName>ssm_web< ...
- 使用Javascript获取当前目录的绝对路径
转自http://heeroluo.net/Article/Detail/101 一谈到路径相关的问题,大家都会往window.location上想,确实这个对象提供了相当多的路径信息,其中常用的就包 ...
- SQL SERVER 2012 SEQUENCE
一.Sequence简介 Sequence对象对于Oracle用户来说是最熟悉不过的数据库对象了, 在SQL SERVER2012终于也可以看到这个对象了.Sequence是SQL Server201 ...
- AIO5系统中-打印样式常见问题处理
1.为什么我的报表没有数据显示? 答:请看一下报表是否有绑定数据源,有添加查询,如果这些都做了,请看下主项数据的数据源有没有绑定,这些都绑定了,就会有数据的显示. 2.为什么我做的报表分组很乱? 答: ...
- Java中net.sf.json包关于JSON与对象互转的问题
在Web开发过程中离不开数据的交互,这就需要规定交互数据的相关格式,以便数据在客户端与服务器之间进行传递.数据的格式通常有2种:1.xml:2.JSON.通常来说都是使用JSON来传递数据.本文正是介 ...
- java版Web Socket,实现消息推送
# web socket是什么? WebSocket协议是基于TCP的一种新的网络协议. 它实现了浏览器与服务器全双工(full-duplex)通信,允许服务器主动发送信息给客户端. ## 用途 实时 ...