反射入门
什么是反射机制?
在方法区存在这么一些对象,叫做类对象,他们表述了我们写的所有的类,当我们new对象时会根据这些类对象,并调用其构造方法为我们创建实例
首先大家应该先了解两个概念,编译期和运行期,编译期就是编译器帮你把源代码翻译成机器能识别的代码,比如编译器把java代码编译成jvm识别的字节码文件,而运行期指的是将可执行文件交给操作系统去执行。JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为ava语言的反射机制
筒单的说一个类有多个组成部分,例如:成员变量,方法,构造方法等。反射就是加载类,并解剖出类的各个组成部分。
Java反射机制主要提供了以下功能:
- 在运行时判断任意个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象成员的变量和方法;
- #ff531a生成动态代理
- 在运行时处理注解
- 在运行时获取泛型信息
反射相关的主要API
API 名称 | 代表含义 |
---|---|
Java.lang.class | 代表一个类 |
java.lang.reflect.Method | 代表类的方法 |
java.lang.reflect.Field | 代表类的成员变量 |
java.lang.reflect.Constructor | 代表类的构造器 |
new和反射创建有什么区别呢?
new: 静态编译,在编译期就将模块编译进来,执行该字节码文件,所有的模块都被加载;
**反射:动态编译,**编译期没有加载,等到模块被调用时才加载;
获取CLASS类对象的方法
反射机制可以获取任意类实例化对象(等价于new)
1.利用Object类中提供的getClass()方法获取实例化对象
类名称 对象名称=new 类名称();
Class<?> 另一个对象名称=对象名称.getClass(); Member member=new Member(); Class<?> memberclass=member.getClass();
2.使用”类.class“形式获取指定类或接口的Class实例化对象。
Class<?> 对象名称=类名称.class; Class<?> memberclass=Member.class;
3.使用Class类内部提供的forName()方法根据类的完整名称获取实例化对象
Class 对象名称=Class.forName(“包名+类名”);
Class memberclass=Class.forName(“包名+类名”);
4.通过类的加载器ClassLoader classLoader = this.getClass().getClassLoader();
String className = “java.util.commons”;
Class claz = classLoader.loadClass(className);
T和Class以及Class<?>的理解
单独的T 代表一个类型
而 Class和Class<?>代表这个类型所对应的类 Class在实例化的时候,T要替换成具体类Class<?>它是个通配泛型,?可以代表任何类型
反射Class类实例化对象
Java中什么叫做实例化
class A {
int i;
}
//这里A就是一个类而对象就是一个类的具体的某一个,如:A a1 = new A();A a2 = new A();a1 a2都是对象而创建对象的过程就叫实例化因此有时候我们也将对象叫做一个类的实例。
//通过Class对象的方法getDeclaredConstructor()中的newInstance()获取实例化对象(通过反射来创建对象)前提:类型中必须有默认的构造方法。
package com.zsq;
class Member{
public Member() {
System.out.println("【构造方法】实例化Member类对象。");
}
@Override
public String toString() {
return "【toString覆写】";
}
}
public class JavaReflectDemo {
public static void main(String[] args) throws Exception{
//获取Class类实例化对象
Class<?> clazz= Class.forName("com.zsq.Member");
//JDK1.9后提倡使用实例化对象
// Object obj=clazz.newInstance();JDK1.9前可以使用
Object obj=clazz.getDeclaredConstructor().newInstance();
//对象输出
System.out.println(obj);
}
}
本程序在获取 Member类实例化对象时并没有使用关键字new,而是基于反射机制实现了对象实例化,即按照此类结构只要设置了正确的类名称,字符串就可以自动调用无参构造方法指定类的实例化对象。
本程序中使用的反射实例化方式为 clazz.getDeclaredConstructor().newInstance(),这段代码的核心意义在于:获取指定类提供的无参构造方法并进行对象实例化,得到的对象是一个Object对象。,但是需要注意的是,这类操作是从JDK1.9后提倡使用的,而在JDK1.9前可以直接使用Class类内部提的 **new Instance()得到对象类型已经弃用(只能调用无参构造函数)**方实例化对象.
反射获取类结构信息
在反射机制的处理之中不仅仅只是一个实例化对象的处理操作,更多的情况下还有类的组成结构操作(父类、父接口、包、属性、方法)。
变量和类型 | 方法 | 描述 |
---|---|---|
软件包 | getPackage() | 获取此类的包。 |
类<? super T> | getSuperclass() | 获取继承父类 |
类<?>[] | getInterfaces() | 返回由此对象表示的类或接口直接实现的接口。 |
//定义父接口
interface IMessageService {
public void send();
}
//定义父接口
interface IChannelService {
public boolean connect();
}
//定义抽象类
abstract class AbstractBase {
}
class Mail extends AbstractBase implements IMessageService, IChannelService {
//信息发送
@Override
public void send() {
if (this.connect()) {
System.out.println("【信息发送】");
}
}
//获取连接状态
@Override
public boolean connect() {
return true;
}
}
public class JavaReflectDemo {
public static void main(String[] args) {
//获取指定类的Class对象
Class<?> cls = Mail.class;
//获取指定类的包定义
Package pack = cls.getPackage();
//获取包名称
System.out.println(pack.getName());
//获取父类对象
Class<?> parent = cls.getSuperclass();
//父类信息
System.out.println(parent.getName());
//父类信息
System.out.println(parent.getSuperclass().getName());
//获取接口信息
Class<?>[] clazz = cls.getInterfaces();
for (Class<?> temp : clazz) {
System.out.println(temp.getName());
}
}
}
运行结果:
反射调用构造方法
构造方法是类的重要组成部分,也是实例化对象时必须调用的方法,在Class类可以通过下表获取构造方法的相关信息
变量和类型 | 方法 | 描述 |
---|---|---|
Constructor<?>[] | getDeclaredConstructors() | 返回构造器对象的数组,获取指定类中的所有构造函数。 |
Constructor | getDeclaredConstructor(类<?>… parameterTypes) | 返回一个构造器对象,获取指定参数类型所表示的类或接口的指定构造函数。 |
Constructor<?>[] | getConstructors() | 返回一个包含构造器对象的数组,获取类中所有的public权限的构造函数。 |
Constructor | getConstructor(类<?>… parameterTypes) | 返回一个包含构造器对象的数组,获取指定参数类型并且访问权限为public的构造方法 |
使用Class类获取的所有构造方法都是通过java.lang.reflect.Constructor类的对象来表示。它常有的方法有如下表
构造器Constructor类常用的方法
变量和类型 | 方法 | 描述 |
---|---|---|
T | newInstance(Object… initargs) | 调用构造方法传入指定参数进行对象实例化 |
String | getName() | 获取构造方法名称(类,接口,数组类,基本类型或void) |
Type[] | getGenericInterfaces() | 获取接口的泛型类型 |
Type | getGenericSuperclass() | 得到父类的泛型类型 |
类<?>[] | getInterfaces() | 返回由此对象表示的类或接口直接实现的接口。 |
A | getAnnotation(类 annotationClass) | 获取全部生命的注解 |
T | newInstance() | **已过时。**可以根据传入的参数,调用 任意构造构造函数。 |
package com.zsq;
import java.lang.reflect.Constructor;
class Mail{
private String msg;
public Mail() {
}
public Mail(String msg) {
this.msg=msg;
System.out.println("【构造方法】调用Mail类单参构造方法,实例化对象");
}
@Override
public String toString() {
return "【toString()覆写】消息内容:"+this.msg;
}
}
public class JavaReflectDemo {
public static void main(String[] args)throws Exception {
//获取指定类的Class对象
Class<?> cls = Mail.class;
//获取该类全部public权限的构造方法
Constructor<?>[] constructors=cls.getConstructors();
for(Constructor<?>cons:constructors){
System.out.println(cons);
}
//获取单参构造方法并且参数类型为String的构造方法对象实例
Constructor<?>cons=cls.getDeclaredConstructor(String.class);
//调用单参构造实例化对象
Object object=cons.newInstance("嘻嘻");
/* Class<?> cls = Mail.class;
Constructor<?>cons=cls.getDeclaredConstructor(String.class);
Object object=cons.newInstance("嘻嘻");
这三句话相当于Mail mail=new Mail("嘻嘻")
*/
System.out.println(object);
}
}
反射调用方法
每个类都有不同的功能,所有的功能都可以通过方法进行定义。在Java中除了通过具体的实例化对象实现方法调用外(即new ),也可以利用反射基于实例化对象的形式实现方法调用。在 Class类中提供如下表所示的方法获取类中的方法信息。
变量和类型 | 方法 | 描述 |
---|---|---|
Method[] | getDeclaredMethods() | 获取一个类中所有定义的方法(包括public,protected,default(package)访问和私有方法但不包括继承的方法) |
Method | getDeclaredMethod(String name, 类<?>… parameterTypes) | 获取一个类中指定名称与指定参数类型的方法 |
Method[] | getMethods() | 获取一个类中所有public类型的方法 |
Method | getMethod(String name, 类<?>… parameterTypes) | 获取类中指定名称与指定参数类型的public方法 |
通过 Class类获取的每一个方法信息都使用 Java. lang. reflect. Method类实例描述,通过该类实例可以获取方法的相关信息,也可以实现方法的反射调用。 Method类常用方法如下表所示。
变量和类型 | 方法 | 描述 |
---|---|---|
Object | invoke(Object obj, Object… args) | 方法调用,obj -从中调用底层方法的对象(简单的说就是调用谁的方法用谁的对象)。args - 用于方法调用的参数。等价于“实例化对象.方法“ |
类<?> | getReturnType() | args - 用于方法调用的参数获取方法返回值类型 |
String | getName() | 获取构造方法名称 |
Type[] | getGenericParameterTypes() | 获取构造方法的参数类型 |
Type[] | getGenericExceptionTypes() | 获取构造方法抛出的异常类型 |
int | getParameterCount() | 获取构造方法的参数个数 |
T | getAnnotation(类 annotationClass) | 获取全部声明的Annotation |
package com.zsq;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
class Member{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class JavaReflectDemo {
public static void main(String[] args)throws Exception {
//获取指定类的Class对象
Class<?> cls = Member.class;
//设置内容
String value="大胆刁民";
//获取所有无参构造方法
Constructor<?>cons=cls.getDeclaredConstructor();
//调用无参构造并实例化对象
Object object=cons.newInstance();
//反射调用方法需要明确知道方法的名称以及方法的参数类型
String setMethodName="setName"; //方法名称
//获取指定名称以及类型的方法
Method setMethod=cls.getDeclaredMethod(setMethodName,String.class);
//等价于对象。对象.setName(value)
setMethod.invoke(object,value);
String getMethodName="getName"; //方法名称
//获取指定的方法
Method getMethod=cls.getDeclaredMethod(getMethodName);
//等价于 System.out.println(对象.getName)
System.out.println(getMethod.invoke(object));
}
}
反射调用成员属性
字段(field),通常叫做“类成员”,或 "类成员变量”,有时也叫“域”,理解为“数据成员”,用来承载数据的
Class获取成员属性操作的方法
变量和类型 | 方法 | 描述 |
---|---|---|
Field[] | getDeclaredFields() | 获取本类全部成员信息 |
Field | getDeclaredField(String name) | 获取指定成员属性信息 |
Field[] | getFields() | 获取父类中全部public成员信息 |
Field | getField(String name) | 获取父类中定义的全部public成员信息 |
反射中成员通过java.lang.reflect.Field实例描述,Field类常用方法如下:
变量和类型 | 方法 | 描述 |
---|---|---|
类<?> | getType() | 获取成员属性类型 |
void | set(Object obj, Object value) | 设置成员属性内容 obj - 应修改其字段的对象,value -用于领域的新值 obj被修改 |
Object | get(Object obj) | 获取成员属性内容 |
int | getModifiers) | 获取成员属性修饰符 |
void | setAccessible(boolean flag) | 设置成员属性可见性,让我们在用反射时访问私有变量 |
package com.zsq;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
class Member {
private String name;
}
public class JavaReflectDemo {
public static void main(String[] args) throws Exception {
//获取指定类的Class对象
Class<?> cls = Member.class;
//获取无参构造方法的构造方法对象实例
Constructor<?> cons=cls.getDeclaredConstructor();
//调用无参构造实例化对象
Object object=cons.newInstance();
//获取指定名称成员属性
Field nameField=cls.getDeclaredField("name");
/*取消封装性
* 本程序直接进行 Member类中name成员属性的操作,由于name属性使用 private封装,所以在进
行属性内容设置相取得前需要使用 setAccessible(true)方法设置其为可见 */
nameField.setAccessible(true); //设置属性内容
nameField.set(object,"还不下跪"); //获取属性内容
System.out.println(nameField.get(object));
// 本程序直接进行 Member类中name成员属性的操作,由于name属性使用 private封装,所以在进行属性内容设置相取得前需要使用 setAccessible (true) 方法设置其为可见.
}
}
在实际项目开发中很少直接通过反射来进行成员属性操作,而一般都会通过相应的setr、getr方法来操成员属性,所以以上的代码只作为Field类的使用特点进行说明。但是需要注意的是,Field类中获取成员属性类型的 get Type()方法在实际开发中使用较多,可以通过其来确定属性类型。