本篇文章分为以下几个部分:

1.认识反射

2.反射的源头(Class类)

3.利用反射操作构造方法

4.利用反射调用类中的方法

5.反射中的invoke方法

6.利用反射调用类中的属性

反射在我们普通程序开发中基本使用不到,但是在我们底层的程序设计中使用特别广泛,例如代理模式、工厂模式等一些设计模式,包括我们使用的开发工具以及各大开源框架底层都使用到了反射的原理。所以掌握了Java的反射机制对我们理解各大开源框架都有很好的帮助。

1.认识反射

反射,从这个“反”字可以看出与我们平时正常的使用逻辑肯定不一样,那么到底什么地方不一样了?想要了解“反”,就得先了解一下“正”的概念。

在正常情况下,如果要使用一个类,必须要经过以下几个步骤:

(1)使用important导入类所在的包(类:java.lang.Class)

(2)通过关键字new进行类对象实例化(构造方法:java.lang.reflect.Constructor)

(3)产生对象可以使用“对象.属性”进行类中属性的调用(属性:java.lang.reflect.Field)

(4)通过“对象.方法()”调用类中的方法(方法:java.lang.reflect.Method)

括号中的红色字体是每个步骤对应反射中使用到的类,如果现在不了解,可以先不用管,后面会一一介绍,这里是为了方便进行比较。

在反射中,使用一个类并不需要导入类的所在包,只要知道类的完整路径就可以知道该类中的所有信息。

反射不需要有明确的类型对象,所有的对象都使用Object表示。可以直接用Object的与反射机制的混合调用类中的方法。

2.反射的源头(Class类)

在认识反射机制之前,必须要介绍一下Class类,Class类是整个反射操作的源头,该类的定义如下:

public final class Class<T>
extends Object
implements Serializable, GenericDeclaration, Type, AnnotatedElement

Class类的实例表示正在运行的Java应用程序中的类和接口。

如果要想使用Class类进行操作,就必须首先产生Class类这个对象,一共有三种方法:

(1)Object类中提供了一个返回Class类的方法,定义如下:

public static Class<?> forName(String className)
throws ClassNotFoundException

在程序开发过程中,使用第二种方法比较多。但是在程序框架设计中,都是使用第三种方法,也就是反射机制用到的方法。

class类实例化对象:

Class类如果使用forName()方法之后,就可以调用Class类中newInstance()无参构造函数方法进行操作,该方法定义如下:

public T newInstance()
throws InstantiationException,
IllegalAccessException

该方法表示创建此Class对象所表示的类的一个新实例。

具体实例如下:

class Student {

	public Student() {
System.out.println("Student类的构造方法");
} @Override
public String toString() {
return "Student类的toString方法";
}
} public class ReflectDemo { public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.iflytek.Student");
// 相当于关键字实例化对象,Object obj = new Student();
Object obj = cls.newInstance();
System.out.println(obj);
} }

输出结果为:

Student类的构造方法
Student类的toString方法

通过上面的实例可以看出,调用newInstace()方法时,程序会默认调用Student类的无参构造方法,并且获取到了Student类的实例化对象,可以调用Student类里面的方法属性。

3.利用反射操作构造方法

在Class类中有两个方法可以获取类中的构造方法,分别是:

获取类中所有的构造方法:

public Constructor<?>[] getConstructors()
throws SecurityException

获取类中指定的构造方法:

public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException,
SecurityException

这两个方法返回的都是反射包(java.lang.reflect.*)中的Constructor类,该类提供了单个构造方法的信息以及对它的访问权限。

下面以获取String类中的所有构造方法为例,代码如下:

public class ReflectStringDemo {
public static void main(String[] args) throws Exception{
Class<?> cls = Class.forName("java.lang.String");
//获取所有构造函数
Constructor<?>[] cons = cls.getConstructors();
//循环打印
for (int i = 0; i < cons.length; i++) {
System.out.println(cons[i]);
}
}
}

打印结果:

public java.lang.String(byte[])
public java.lang.String(byte[],int,int)
public java.lang.String(byte[],java.nio.charset.Charset)
public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException
public java.lang.String(byte[],int,int,java.nio.charset.Charset)
public java.lang.String(java.lang.StringBuilder)
public java.lang.String(java.lang.StringBuffer)
public java.lang.String(int[],int,int)
public java.lang.String(char[],int,int)
public java.lang.String(char[])
public java.lang.String(java.lang.String)
public java.lang.String()
public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException
public java.lang.String(byte[],int)
public java.lang.String(byte[],int,int,int)

上面实例获取了String类中的所有构造方法,包括构造方法中的参数、异常等。

获取所有构造方法看上去并不难,如果想要进行指定构造方法的调用,则必须关注Constructor类,使用newInstance()方法进行实例。

public T newInstance(Object... initargs)
throws InstantiationException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException

获取指定有参构造方法并且实例化对象实例:

import java.lang.reflect.Constructor;

class Student2 {
private String name; private Integer age; public Student2(String name, Integer age) {
this.name = name;
this.age = age;
} @Override
public String toString() {
return "name:" + this.name + ";age:" + this.age;
}
} public class ReflectConstructorDemo { public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.iflytek.Student2");
Constructor<?> con = cls.getConstructor(String.class, Integer.class);
// 这里就相当于Object obj = new Student2("马小超",20);
Object obj = con.newInstance("马小超", 20);
System.out.println(obj);
} }

打印结果:

name:马小超;age:20

通过上面可以看出,如果要实例化一个对象,使用无参构造方法比有参构造方法简单的多,使用无参直接调用newInstance()方法,使用有参则先获取有参构造方法,再通过Constructor中的newInstance()方法,并用指定的初始化参数初始化改实例。很多框架中的底层代码默认都是使用无参构造方法来实例化对象,所以在简单Java类开发中都要明确给出无参构造方法。

4.利用反射调用类中的方法 获取类中的方法可以分为两大类,每个大类中又可以分为两小类,风别是:

获取包括父类集成而来的方法:

获取全部方法:

public Method[] getMethods()
throws SecurityException

获取指定方法:

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

获取本类中定义的方法:

获取全部方法:

public Method[] getDeclaredMethods()
throws SecurityException

获取指定方法:

public Method getDeclaredMethod(String name,
Class<?>... parameterTypes)
throws NoSuchMethodException,
SecurityException

实例:

import java.lang.reflect.Method;

class Student3{
public void fun(){}; public void talk(){};
} public class ReflectMethodStuDemo {
public static void main(String[] args) throws ClassNotFoundException{
Class<?> cls = Class.forName("com.iflytek.Student3");
//获取本类中定义的方法
Method[] method = cls.getDeclaredMethods();
//循环打印
for (int i = 0; i < method.length; i++) {
System.out.println(method[i]);
}
}
}

打印结果:

public void com.iflytek.Student3.fun()
public void com.iflytek.Student3.talk()

如果把上述代码中的getDeclaredMethods()换成getMethods(),打印出来的结果如下:

public void com.iflytek.Student3.fun()
public void com.iflytek.Student3.talk()
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

使用getMethods()方法时,不仅获取到了Student3类中的方法,Object类中的所有方法也被获取到。

上面的程序是直接调用了Method类中的toString()方法输出的,输出格式并不是很理想,没有异常等相关信息。如果有需要,我们也可以自己整理拼接方法输出。

需要用到Method类中的如下几种方法:

getModifiers():以整数形式返回此 Method 对象所表示方法的 Java 语言修饰符。

getReturnType():返回一个 Class 对象,该对象描述了此Method 对象所表示的方法的正式返回类型。

getName():以 String 形式返回此 Method 对象表示的方法名称。

getParameterTypes():按照声明顺序返回 Class 对象的数组,这些对象描述了此Method 对象所表示的方法的形参类型。

getExceptionTypes():返回 Class 对象的数组,这些对象描述了声明将此Method 对象表示的底层方法抛出的异常类型。

在这需要注意的是,利用getModifiers()获取修饰符并不是简单的输出public、static等,而是以整数形式返回所表示的方法的Java语言修饰符。可借助Modifier类的toString()方法来完成。

自己手动拼接输出类中的方法信息的具体代码如下:

import java.lang.reflect.Method;
import java.lang.reflect.Modifier; interface Message {
public void get();
} class Student1 implements Message { public void fun() {
} public void print() {
} public void get() {
}
} public class ReflectMethodDemo { public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.iflytek.Student1");
Method[] me = cls.getMethods();
for (int i = 0; i < me.length; i++) {
// 此时用了method的toString方法输出,如果有需要,用户也可以自己拼凑输出
// System.out.println(me[i]);
// 取得修饰符
System.out.print(Modifier.toString(me[i].getModifiers()) + " ");
// 取得返回值类型
System.out.print(me[i].getReturnType().getSimpleName() + " ");
// 取得方法名称
System.out.print(me[i].getName() + "(");
// 取得方法参数
Class<?> params[] = me[i].getParameterTypes();
if (params.length > 0) {
for (int j = 0; j < params.length; j++) {
System.out.print(params[j].getSimpleName() + " arg-" + j);
if (j < params.length - 1) {
System.out.print(", ");
}
}
}
System.out.print(") ");
// 取得异常
Class<?>[] exp = me[i].getExceptionTypes();
if (exp.length > 0) {
System.out.print("throws ");
for (int j = 0; j < exp.length; j++) {
System.out.print(exp[j].getSimpleName());
if (j < exp.length - 1) {
System.out.println(", ");
}
}
}
System.out.println("{}");
System.out.println();
}
}
}

打印结果:

public void get() {}

public void print() {}

public void fun() {}

public final void wait(long arg-0, int arg-1) throws InterruptedException{}

public final native void wait(long arg-0) throws InterruptedException{}

public final void wait() throws InterruptedException{}

public boolean equals(Object arg-0) {}

public String toString() {}

public native int hashCode() {}

public final native Class getClass() {}

public final native void notify() {}

public final native void notifyAll() {}

通过打印的结果来看,手动拼接的没有直接获取的简单粗暴,看起来比较舒服,该有的都有,自己想要什么结果就可以拼接什么结果。

上面的代码一般在编写开发工具中实现,随笔提示就是此类代码的。上面所说的内容在在我们开发过程中是很少用到,那么肯定有人不明白,既然开发过程中用不到,为什么要说这么多了?

前面的知识都是让大家对Java的反射机制有个更好的认识,以及反射的基本使用方法。前面的算是热身,接下来要给大家介绍一个反射中比较重要的方法。

5.反射中的invoke方法

调用(invoke):对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。

public Object invoke(Object obj,
Object... args)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException

该方法的英文名字是invoke,中文名称就叫“调用”,该方法在Method类中,Method类本身就代表一个方法,当Method类中的对象调用invoke方法时,就相当于调用了Method对象所代表的方法,方法里面传入对应的参数,实现动态调用方法。这里可能比较难理解,看一下一个简单的实例:

import java.lang.reflect.Method;

class Student4 {
private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
} public class ReflectInvokeDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.iflytek.Student4");
// 实例化对象
Object obj = cls.newInstance();
//获取setName()方法
Method setNameMethod = cls.getMethod("setName", String.class);
//获取getName()方法
Method getNameMethod = cls.getMethod("getName");
//调用setName()方法,相当于 对象.setName("马小超");
setNameMethod.invoke(obj, "马小超");
//调用getName()方法并输出
System.out.println(getNameMethod.invoke(obj));
}
}

上面的例子简单的实现了invoke的用法,在动态代理中和Spring框架中都用到了invoke方法,可以实现方法的动态调用,所以在程序设计时使用很广泛。

6.利用反射调用类中的属性

调用类中的属性和调用类中的方法差不多,也是分为两大类,每个大类里面分为两小类,如下:

获取包括继承而来的属性:

获取全部属性:

public Field[] getFields()
throws SecurityException

获取指定属性:

public Field getField(String name)
throws NoSuchFieldException,
SecurityException

获取本类定义的属性:

获取全部属性:

public Field[] getDeclaredFields()
throws SecurityException

获取指定的属性:

public Field getDeclaredField(String name)
throws NoSuchFieldException,
SecurityException

利用反射获取类中的属性,是不提倡使用的,因为违背了面向对象的封装特性。 在Field类中定义了进行属性调用的方法:

设置属性内容:

public void set(Object obj,
Object value)
throws IllegalArgumentException,
IllegalAccessException

获取属性类容:

public Object get(Object obj)
throws IllegalArgumentException,
IllegalAccessException

实例:

import java.lang.reflect.Field;

class Student5 {

	private String name;

}

public class ReflectFiledDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.iflytek.Student5");
//实例化
Object obj = cls.newInstance();
//获取属性
Field nameField = cls.getDeclaredField("name");
//取消访问检查
<strong>nameField.setAccessible(true);</strong>
//给属性赋值
nameField.set(obj, "马小超");
//获取属性值并输出
System.out.println(nameField.get(obj));
}
}

大家在阅读上面代码时会看到有一行代码被加粗了,为什么了?原因是Student类中的name属性是private属性的,不对外开放,如果Field类直接访问该属性,会报权限错误。

在Construction,Method,Field三个类中有一个共同的父类AccessibleObject,定义了取消封装的操作:setAccessible(Boolean flag)

public void setAccessible(boolean flag)
throws SecurityException

该方法默认的是参数是false,表示反射的对象应该实施 Java 语言访问检查。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。

Java反射机制的基本概念及使用就这么多了,相信大家对反射已经不会感到很陌生了,后面我会介绍一下反射机制在工厂模式、代理模式以及Spring框架中的使用,让大家对反射有更深入的理解。

https://blog.csdn.net/mlc1218559742/article/details/52754310

Java反射机制的基本概念与使用的更多相关文章

  1. Java反射机制一 概念和简单的使用方法。

    一 概念 java反射机制属于 java动态性之一  ,指的是可以运行时加载,探知,使用编译期间完全未知的类,程序在运行状态中,可以动态的加载一个只有, 名称的类,对于任意一个已加载的类,都能够知道这 ...

  2. Java反射机制概念及应用场景

    Java的反射机制相信大家在平时的业务开发过程中应该很少使用到,但是在一些基础框架的搭建上应用非常广泛,今天简单的总结学习一下. 1. 什么是反射机制? Java反射机制是在运行状态中,对于任意一个类 ...

  3. 第28章 java反射机制

    java反射机制 1.类加载机制 1.1.jvm和类 运行Java程序:java 带有main方法的类名 之后java会启动jvm,并加载字节码(字节码就是一个类在内存空间的状态) 当调用java命令 ...

  4. java反射机制深入详解

    java反射机制深入详解  转自:http://www.cnblogs.com/hxsyl/archive/2013/03/23/2977593.html 一.概念 反射就是把Java的各种成分映射成 ...

  5. 反射——Java反射机制

    反射概述 什么是反射? ①   反射的概念是由Smith在1982年首次提出的,主要指程序可以访问.检测和修改它本身状态或行为的一种能力. ②   JAVA反射机制是在运行状态中,对应任意一个类,都能 ...

  6. [转]java反射机制

    原文地址:http://www.cnblogs.com/jqyp/archive/2012/03/29/2423112.html 一.什么是反射机制         简单的来说,反射机制指的是程序在运 ...

  7. 11.Java反射机制 哦对了,前面的序号来自随笔关于编程之路的思索第一篇

    基本概念 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法? 答案是肯定的. 这种动态获取类的信息以及动态调用对象的方法的功能来自于J ...

  8. 转!!java反射机制

    Java 反射机制 基本概念 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法? 答案是肯定的. 这种动态获取类的信息以及动态调用对象 ...

  9. java 反射机制01

    // */ // ]]>   java反射机制01 Table of Contents 1 反射机制 2 反射成员 2.1 java.lang.Class 2.2 Constructor 2.3 ...

随机推荐

  1. C#基础视频教程4.3 如何编写简单的计算器

    我们接着往下改,为了让这个计算器更加实用,我们要像官方的计算器一样可以接着计算(你算出来一笔数据之后,可以接着累加累减,我们暂时不考虑加括号,优先级之类的,因为绝大部分情况下我们打开计算器就是为了进行 ...

  2. Fireworks如何制作透明窗口PNG

    1 做好的透明PNG窗口如图所示,打开之后可以发现其实分层的PNG和Photoshop打开一个PSD文件类似. 2 但是Photoshop并不会像Fireworks一样可以编辑分层的PNG,在打开分层 ...

  3. Java并发性和多线程介绍目录

    http://ifeve.com/java-concurrency-thread-directory/

  4. Hadoop之MapReduce命令

    概述 全部的Hadoop命令都通过bin/mapred脚本调用. 在没有不论什么參数的情况下.执行mapred脚本将打印该命令描写叙述. 使用:mapred [--config confdir] CO ...

  5. ScriptableObject 对象化的运用

    http://www.cnblogs.com/oldman/articles/2409554.html using UnityEngine; using UnityEditor; using Syst ...

  6. Java开发环境配置(Win7 64位系统/server 2008)

    下面以jdk1.8.0_05版本为例: 1.在用户变量里新增变量名:JAVA_HOME 变量值:D:\Java\jdk1.8.0_05 (根据实例路径变换) 2.在用户变量里新增变量名:CLASSPA ...

  7. linux内核——TSS

    task state segment,任务状态段. 关于每个cpu对应不同TSS段的问题,如下解释: TSS段主要用在当前的任务从用户态切入内核态时去找到该任务的内核堆栈. 多核上的任务是真正的并发, ...

  8. 使用maven拆分项目

    在开发环境中,有时需要专人负责专门的模块,其他模块不需接触,这就需要将项目拆分,如下 fund_demo项目具有三个模块,现将主业务core模块单独提出另建一个项目fund_core,拆分时需要注意相 ...

  9. MySQL学习记录一

    1.MySQL join操作 left join以左表为基础,其记录会全部表示出来,而右表只显示满足搜索条件的记录.right join以右表为基础,其记录会全部显示出来,而左表只显示满足搜索条件的记 ...

  10. 使用samba实现linux与windows文件共享

                       1,安装samba                              sudo apt-get install samba                 ...