详解Java反射机制

一、什么是JAVA的反射机制

Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的类的内部信息,包括其方法(诸如public, static 等)、父类(例如Object)、实现接口(例如Cloneable),也包括属性和方法的所有信息,并可于运行时改变属性值或调用方法。

Java反射机制容许程序在运行时加载、探知、使用编译期间完全未知的类。Java可以加载一个运行时才得知名称的类,获得其完整结构。

简言之,JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

二、JDK中提供的Reflection API

Java反射相关的API在包java.lang.reflect中,JDK 1.6.0的reflect包如下图:

这里写图片描述

该类下的几个主要类和接口
这里写图片描述

三、JAVA反射机制提供了什么功能

=========================================

Java反射机制提供如下功能(用途):

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时判段任意一个类所具有的成员变量和方法

在运行时调用任一个对象的方法

在运行时创建新类对象

=======================================

一个对象的创建分三步完成:

1、加载类到内存;
2、静态资源初始化;
3、创建对象;

一个类对应一个字节码文件*.class,反射研究的就是字节码文件;一个字节码文件中主要包含类的成员变量、方法、构造方法;通过该字节码文件可以获取该类的相关信息。同一个字节码文件可以构建多个对象;

//类的字节码文件
MyA a1=new MyA();
//获得该类的字节码文件
Class clazz=a1.getClass();

对象类型有字节码文件,基础的数据类型也有相应的字节码文件。

int.class
void.class

在使用Java的反射功能时,基本首先都要获取类的Class字节码对象,再通过Class对象获取其他的对象。

1、获取类的Class字节码对象

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。获取类的Class对象有多种方式:

这里写图片描述

2、获取类的属性

可以通过反射机制得到某个类的某个属性,然后可以改变对应于这个类的某个实例的该属性值。JAVA 的Class<T>类提供了几个方法获取类的属性。

public Field getField(String name) 

返回某类一个public修饰的成员变量(public,包括父类中定义);

public Field[] getFields() 

返回一个数组,数组中包含某类下的public修饰的所有成员变量(public,包括父类中定义);

public Field  getDeclaredField(String name) 

返回某类一个private修饰的成员变量(private,不包括父类中定义);

public Field[] getDeclaredFields() 

返回一个数组,数组中包含某类下的所有private修饰的成员变量(private,不包括父类中定义);

3、获取类的方法

通过反射机制得到某个类的某个方法,然后可以调用对应于这个类的某个实例的该方法,Class<T>类提供了几个方法获取类的方法。

public Method  getMethod(String name,Class<?>... parameterTypes) 

通过方法名和方法的入参得到一个某类下的public修饰的Method方法对象(public,包括父类中定义);

public Method[]  getMethods() 

得到某类下的public修饰的所有Method方法对象(public,包括父类定义的)

public MethodgetDeclaredMethod(Stringname,Class<?>... parameterTypes)

通过方法名和方法的入参得到一个某类下的private修饰的Method方法对象(private,不包括父类中定义);;

public Method[] getDeclaredMethods() 

得到某类下的private修饰的所有Method方法对象(private,不包括父类定义的)

通过反射获得类的方法(public)

MyA m1=new MyA();

Class clazz=m1.getClass();

//方法名,入参
clazz.getMethod("setAge",int.class);

//获得方法名之后,如何调用该方法(对象,入参)
setAge.invoke(m1,23);//调用m1下的该方法

m1.getAge();//23

注意:
setAge.invoke(null,23);

当前类下的不依赖对象的方法(静态方法)要被执行;

4、获取类的Constructor构造方法

通过反射机制得到某个类的构造器,然后可以调用该构造器创建该类的一个实例Class<T> 类提供了几个方法获取类的构造器。

public Constructor<T> getConstructor(Class<?>... parameterTypes)

通过构造方法的入参返回一个有相应入参的 Constructor 对象(public,包括父类中定义);

public   Constructor<?>[]    getConstructors() 

返回一个数组,该数组中包含该类下的所有构造方法(public,包括父类中定义);

public Constructor<T>  getDeclaredConstructor(Class<?>... parameterTypes)

通过构造方法的入参返回一个有相应入参的 Constructor 对象(private,不包括父类中定义);

public    Constructor<?>[]     getDeclaredConstructors()

返回一个数组,该数组中包含该类下的所有构造方法(private,不包括父类中定义);

5、创建类的实例

通过反射机制创建类的实例,有几种方法可以创建

调用无自变量的构造器

1、调用类的Class对象的newInstance方法,该方法会调用对象的默认构造器,如果没有默认构造器,会调用失败(所以,在实际应用中用到这种方法时,如果要重写一个类的带参数的构造方法,就一定要把不带参数的构造方法显式定义出来).

Class<?> classType = ExtendType.class;
Object inst = classType.newInstance();

System.out.println(inst);
输出:
Type:Default Constructor
ExtendType:Default Constructor
com.quincy.ExtendType@d80be3

2、调用默认Constructor对象的newInstance方法

Class<?> classType = ExtendType.class;
Constructor<?> constructor1 = classType.getConstructor();
Object inst = constructor1.newInstance();
System.out.println(inst);
输出:
Type:Default Constrructor
ExtendType:Default Constru
com.quincy.ExtendType@1006d756d75

调用带参数构造器

3、调用带参数Constructor对象的newInstance方法

Constructor<?> constructor2 =

classType.getDeclaredConstructor(int.class, String.class);

Object inst = constructor2.newInstance(1, "123");

System.out.println(inst);

输出:

Type:Default Constructor
ExtendType:Constructor with parameters
com.quincy.ExtendType@15e83f9

6、调用类的函数

通过反射获取类Method方法对象,调用Field的Invoke方法调用函数(方法)。

参见上面获取类的方法

7、设置/获取类的属性值

通过反射获取类的成员变量,调用成员变量的方法设置或获取值;

MyA m1=new MyA("zhangsan");
MyA m2=new MyA("lisi");

Class clazz=m1.getClass();
//获得MyA类的所有成员变量
Field[] fields=clazz.getFields();

//获得MyA下名为"name"的成员变量
Field nameField=clazz.getField("name");
以上适用于用public修饰的成员变量,如果成员变量使用private修饰的则拿不到;要用以下方法:

//nameField是一个引用(地址)
Field nameField=clazz.getDeclaredField("name");
//如果该成员变量使用private修饰的,要加下面一句
nameField.setAccessible("true");

------------------要指定获得哪一个对象上的数据
//获得m1中nameField对应的成员变量的名字
String name=nameField.get(m1);

//获得m2中nameField对应的成员变量的名字
String name=nameField.get(m2);

--------------------为m1中的成员变量设值
nameField.set(m1,"张三");//覆盖掉zhangsan 

//获得nameField对应的成员变量的类型
nameField.getType();

四、动态创建代理类

代理模式:代理模式的作用=为其他对象提供一种代理以控制对这个对象的访问。

代理模式的角色:

抽象角色:声明真实对象和代理对象的共同接口
代理角色:代理角色内部包含有真实对象的引用,从而可以操作真实对象。

真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。

动态代理:

java.lang.reflect.Proxy
Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类
InvocationHandler
是代理实例的调用处理程序实现的接口,每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。

动态Proxy是这样的一种类:

它是在运行生成的类,在生成时你必须提供一组Interface给它,然后该class就宣称它实现了这些interface。你可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

在使用动态代理类时,我们必须实现ndler接口

有关代理模式的具体了解请参看本人设计模式相关文章。

补充:

1、
`MyA a1=new MyA();

Class clazz=a1.getClass();

MyA a2=new MyA();

if(a1 == a2)…//不相等,比较的是引用(地址)

if(clazz == a2.getClass())…//相等(是由同一个字节码文

件构建的,都相当于:new MyA().getClass())
`

2、拿到一个字节码文件之后,如何判断它是一个对象类型还是普通的数据类型

//基础数据类型返回true,对象类型返回false
clazz.isPrimitive();//false

int.class.inPrimitive();//true
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值