1 反射概述
问题:我们平时书写在eclipse中的Java程序是如何运行的呢?
1)首先将 .java 源文件编译为class类文件;
2)编译后的类文件是存在硬盘中的,那么我们运行需要在内存中看到效果,那么类文件是如何被加载到内存中的呢,就是jvm通过类加载器ClassLoader把硬盘中的class文件加载到内存中,这样就可以使用这个类中的成员变量和方法了。而被加载到内存中这个class文件就会变成一个Class类的对象。
反射要依赖于Class类。
由于Class表示类文件的字节码文件对象,类字节码文件就是在描述一个类,描述类的成员变量、成员函数和构造函数。
而反射就是从一个类的字节码文件中拿到成员变量、成员函数和构造函数。要想从一个类中拿东西必须拿到这个类的字节码文件对象,所以反射依赖于Class,因此我们在学习反射之前先了解下Class。
1.1 Class类介绍
在Java中使用类来描述所有的事物,而这些描述完的所有程序,在编译完之后统一都会生成各自class文件。
在Java中class文件是所有源代码(.java 文件)程序编译后统一的结果。class文件是一类可以被JVM直接执行的文件。class文件在Java世界中就是存在的一类事物。
Java使用Class类来描述和封装class文件这类事物。class文件也叫作字节码文件。
关于Class描述字节码文件如下图所示:
说明:
1)Java中使用Class类表示某个class文件;
2)任何一个class文件都是Class这个类的一个实例对象;
Class的API描述:
说明:
1)Class类它可以表示Java中的任何内容;
2)Java中使用Class类表示硬盘上的某个.class文件,启动JVM就会把文件加载到内存中,占用一片空间,称为一个字节码文件对象,这个对象就是Class类的一个实例。不同的类,有自己的字节码文件对象,这些对象都是Class类的实例;
3)Class类对于.class文件中所有的内容,都会认为是Class对象;
说明:在Class类中专门提供了几个获取成员变量 、成员函数 、构造函数的函数。
Field getField(String name) 表示获取类中的成员变量的对象;
Method getMethod() 表示获取类中的成员函数的对象;
Constructor getConstructor() 表示获取类中的构造函数的对象;
1.2 获取Class的三种方式
因为反射技术是通过Class对象来实现把一个类进行解剖的,所以需要先了解怎么样才可以获取到Class对象。
需求:演示获取Class的三种方式:
1)获取Class对象的第一种方式:使用类的class属性直接获取:类名.class。
说明:在任何的一个类中都有一个静态成员变量class,可以直接获取到class文件所属的Class对象。
2)获取Class对象的第二种方式:在Object类中,有个getClass方法,就可以获取到任何一个对象对应的Class对象。对象.getClass()。
3)获取Class对象的第三种方式:在Class类中有个静态的方法:static ClassforName(String className),根据类的名称获取类的Class对象。
说明:这里的参数className必须是类的全名(就是带有包名的全名称)。如:Class.forName("java.lang.String");
补充:上述三种方式可以获得Class对象,获得完Class对象就可以获取类的基本信息:
获取类的基本信息:
String getName() 获取类的名称,就是获得包名.类名
String getSimpleName() 获取类的简称 类名
代码演示如下所示:
/*
* 演示:获取Class的三种方式
* 方式1:类名.class
* 方式2:对象.getClass()
* 方式3:static Class forName(String className)根据类的名称获取类的Class对象
* 注意:这里的className必须是类的全名!
*
* 获取类的基本信息:
* StringgetName()获取类的名称 包名.类名
* StringgetSimpleName()获取类的简称 类名
*/
publicclass ClassDemo1{
publicstaticvoidmain(String[]args) throwsClassNotFoundException {
// 方式1:类名.class
Class clazz = String.class;
// 输出clazz全名就是包名.类名 java.lang.String
System.out.println(clazz.getName());
// 输出clazz精简名字就是类名 String
System.out.println(clazz.getSimpleName());
// 方式2:对象.getClass()
Class clazz2 ="柳岩".getClass();
// 输出clazz2全名就是包名.类名 java.lang.String
System.out.println(clazz2.getName());
// 输出clazz2精简名字就是类名 String
System.out.println(clazz2.getSimpleName());
// 方式3:static Class forName(String className)根据类的名称获取类的Class对象
// Class clazz3 =Class.forName("String");//这里必须是类的全名 包名.类名,否则报java.lang.ClassNotFoundException
Class clazz3 = Class.forName("java.lang.String");
// 输出clazz3全名就是包名.类名 java.lang.String
System.out.println(clazz3.getName());
// 输出clazz3精简名字就是类名 String
System.out.println(clazz3.getSimpleName());
//说明对于String类来说在内存中只有一个String.class文件
System.out.println(clazz==clazz2);//true
System.out.println(clazz==clazz3);//true
System.out.println(clazz2==clazz3);//true
}
}
总结:上述三种方式都是用来获取Class对象的,那么在开发中一般使用哪种获取方式呢?
在开发中我们会使用第三种获取方式。
说明:第三种的好处就是加载一个类却不需要知道这个类是什么,通过第一种方式获取前提是必须得先知道类名,然后才能通过类名.class获取。而第二种方式必须知道对象才能通过对象.getClass()来获取。
而第三种不需要知道类名或者对象就可以直接获取,或者可以这样理解,我们在真实开发中,类的名字是不知道的,都是通过IO流来读取配置文件读取回来的。
也就是说我们读取配置文件的时候根据key来获取String类型的value,这样就可以把String类型的value作为forName(StringclassName)函数的参数,而不需要在程序代码中体现出类名字样就可以获得Class对象了。
补充:三种获取Class类对象的区别进一步说明:
代码演示如下所示:
class Person
{
static{
System.out.println("我是static代码块");
}
Person()
{
System.out.println("Person..构造函数");
}
{
System.out.println("构造代码块");
}
}
publicclass ClassDemo1{
publicstaticvoidmain(String[]args) throwsClassNotFoundException {
method_2();
}
publicstaticvoid method_2()throws ClassNotFoundException {
// 方式1:类名.class
/*
* cn.itcast.sh.clazz.demo.Person
* Person
*/
Class clazz1 = Person.class;
System.out.println(clazz1.getName());
System.out.println(clazz1.getSimpleName());
// 方式2:对象.getClass()
/*
* 我是static代码块
* 构造代码块
* Person..构造函数
* cn.itcast.sh.clazz.demo.Person
* Person
*/
Class clazz2 =new Person().getClass();
System.out.println(clazz2.getName());
System.out.println(clazz2.getSimpleName());
// //方式3:static Class forName(String className)根据类的名称获取类的Class对象
/*
* 我是static代码块
* cn.itcast.sh.clazz.demo.Person
* Person
*/
Class clazz3 = Class.forName("cn.itcast.sh.clazz.demo.Person");
System.out.println(clazz3.getName());
System.out.println(clazz3.getSimpleName());
//
// System.out.println(clazz1==clazz2);//true
// System.out.println(clazz1==clazz3);//true
// System.out.println(clazz2==clazz3);//true
}
根据结果可以发现,三种生成的Class对象一样的。并且程序只打印一次“我是static代码块”。
说明:
1)类名.class 。JVM使用类加载器, 将类装入内存(前提是:类还没有装入内存),不做类的初始化工作,就是不执行任何代码,包括静态代码块。返回Class的对象。
2)Class.forName("类名字符串") (注:类名字符串是包名+类名)。 说明:装入类,并做类的静态初始化,就是执行和类有关的代码,就是执行static修饰的内容。返回Class的对象。
3)实例对象.getClass()。 说明:对类进行静态初始化、非静态初始化;返回Class的对象。
4)我们知道,静态的方法属性初始化,是在加载类的时候初始化。而非静态方法属性初始化,是new类实例对象的时候加载。因此,这段程序说明,三种方式生成Class对象,其实只有一个Class对象。在生成Class对象的时候,首先判断内存中是否已经加载。
5)所以,生成Class对象的过程其实可以按照如下理解:
当我们编写一个新的Java类时,JVM就会帮我们编译成class对象,存放在同名的.class文件中。在运行时,当需要生成这个类的对象,JVM就会检查此类是否已经加载到内存中。若是没有加载,则把.class文件加载到内存中。若是加载,则根据class文件生成实例对象。
1.3 反射的概念
到目前为止我们已经了解了如何获得Class,那么接下来我们就会使用反射技术来剖析一个class文件。
那么到底什么是反射呢?
反射就是通过一个类的Class对象把类中的各种成员映射成对应的Java类。一个类中的:成员变量、构造函数、成员方法都有对应的Java类:Field、Contructor、Method; 就比如:一个汽车是一个大类,汽车中的发动机、轮胎等等都可以是一个个小的类。
一个类的Class对象可以获取其所有成员的信息,比如一个方法的名称、修饰符、参数类型、返回值等等信息封装成一个描述方法的类(Method)中。
换句话说反射通过Class类的对象可以获取一个类中的成员,比如函数,保存在Method类中。然后通过Method类的对象来获取一个成员函数的名称、修饰符、参数类型、返回值等等信息。
一个类中的所有成员,都可以通过Class对象获得,并封装为对应的对象。我们拿到这些对象以后,有什么用?怎么用?这正是我们学习反射的要点!
1.4 预定义对象
通过查阅Class的API发现8种基本数据类型、void关键字和数组也表示为Class的对象,我们把8种基本数据类型、void叫做预定义对象,一共有9种。
说明:
1)基本数据类型要保存在内存中,那么他们也会有自己的字节码,获取的Class对象中保存的就是他们本身,因为基本数据类型不属于任何包下的。
如:Class c =int.class;对象c中保存的就是int。
2)对于函数返回值类型void,代表的是没有返回值类型,那么他也是一种数据类型,表示没有类型,那么输出对象也是void本身。
如:Class c1 = void.class;对象c1中保存的就是void。
3)通过包装类中的TYPE字段可以获得一个包装类的对应基本类型。
4)数组也是一个对象,那么既然是对象也会有自己的类,只是我们没有见过数组的类,可以按照如下方式获取数组对应的类:
如:Class c3 =int[].class;对象c3中保存的就是数组的类名[I。
如果想知道一个Class是否是一个数组,我们可以使用Class的对象c3调用Class类中的isArray()函数,是,返回true,否则返回false。
代码演示如下:
/*
* 演示:9种预定义对象:
* 包含:8种基本数据类型以及void
*/
publicclass ClassDemo2{
publicstaticvoidmain(String[]args) {
// 获取int类型的Class对象
Class c = int.class;
System.out.println(c);//int
// 获取void类型的Class对象
Class c1 = void.class;
System.out.println(c1);// void
Class type = Integer.TYPE;
System.out.println(type);//int
System.out.println(type ==c);// true
// 获取int[]类型的Class对象
Class c2 = int[].class;
System.out.println(c2);// class [I
//判断Class对象中保存的是否是数组 true 是数组 false 不是数组
System.out.println(c2.isArray());//true
System.out.println(boolean.class);//boolean
}
}
总结:任何的一种数据都具有自己的字节码。
2 使用反射获取一个类中的构造函数
2.1 使用Class类中的newInstance()函数创建某个类的对象
通过上述办法已经获取到了Class对象,即就是获取到某个类的class文件。现在我们不使用Java中的new关键字来创建这个class文件所描述的那个事物的真实对象,而通过Class这个对象,动态的创建这个类的真实对象。
在Class类中有一个newInstance()方法,可以动态的创建出当前这个class文件的真实对象。
该方法如下所示:
代码演示如下所示:
Person类:
/*
* 描述人
*/
publicclass Person {
//属性
privateStringname;
privateintage;
publicPerson(Stringname, intage) {
this.name =name;
this.age =age;
}
publicPerson() {
}
}
测试类:
/*
* 使用Class类中的newInstance()函数获取某个class文件中的真实对象
*/
publicclassReflectDemo1 {
publicstaticvoidmain(String[]args) throws Exception {
// 获取Class类的对象
Class clazz = Class.forName("cn.itcast.sh.reflect.demo.Person");
//使用对象调用函数创建Person类的对象
Person p = (Person)clazz.newInstance();
/*
* 上述两行代码等同于
*Person p= new Person();
*注意:这里是使用Person类中的无参构造函数在创建对象,所以要求Person类中必须具备无参构造函数,
*否则就会报 java.lang.InstantiationException:不能实例化异常
*/
System.out.println(p);//cn.itcast.sh.reflect.demo.Person@5a20d10a
//利用无参构造函数创建对象
String s = String.class.newInstance();
System.out.println(s.length());//0
}
}
注意:使用Class类中的newInstance()方法的时候,要求class文件中必须有空参数并且不能是private修饰的构造函数。如果没有,那么使用newInstance()方法就会报异常。
2.2 使用反射获取一个类中的所有构造函数(包括有参数和私有的构造函数)(Constructor类)
之前,我们都是使用Class类中的newInstance()调用无参的构造函数来创建对象的,对于有参数的构造方法无法实现调用并创建这个类的对象,所以sun公司专门给我们提供了如何获取一个类中的所有构造函数。
说明:
1)使用Class类的对象即字节码对象可以获取class文件中的所有构造函数,具体应该借助Class类中的如下函数:
Constructor[] getConstructors() 获取所有公共的构造函数
Constructor[]getDeclaredConstructors() 获取所有构造函数 包括私有的
ConstructorgetConstructor(Class ... parameterTypes)根据参数列表获取指定的公共的构造函数
说明:由于这里需要Class类的对象,所以在给参数的时候,直接使用实参类型的类获取Class对象即可。
举例:假设需要获取String类型的构造函数,那么这里直接使用String.class作为getConstructor(Class ... parameterTypes)的参数。
ConstructorgetDeclaredConstructor(Class ... parameterTypes)根据参数列表获取指定的构造函数包括私有的
2)获取到构造函数对象之后,就可以使用获取的构造函数对象创建某个类的真实对象。我们通过反射已经获取到构造函数,查阅Constructor类中的描述,发现Constructor类中的newInstance(Object... initargs) 方法,这个方法可以动态的创建出这个构造函数对象所表示的那个类的真实对象。
说明: Object...initargs 创建对象的时候需要传递的真实的数据,就是构造函数所需要的实际参数。
代码如下所示:
需求:获取File类的构造函数,并使用获取的构造函数创建对象。
D:\\test\\out.txt 作为newInstance函数的参数,创建对象之后并获取绝对路径。
/*
* 演示:反射获取构造函数。
* Constructor[] getConstructors()获取所有公共的构造函数
* Constructor[]getDeclaredConstructors()获取所有构造函数 包括私有的
* Constructor getConstructor(Class ... parameterTypes)根据参数列表获取指定的公共的构造函数
* 说明:由于这里需要Class类的对象,所以在给参数的时候,直接使用实参类型的类获取Class对象即可
* 举例:假设需要获取String类型的构造函数,那么这里直接使用String.class作为getConstructor(Class ...parameterTypes)的参数
* Constructor getDeclaredConstructor(Class ... parameterTypes)根据参数列表获取指定的构造函数 包括私有的
*
* 获取到构造函数对象之后,就可以使用获取的构造函数对象创建某个类的真实对象。我们通过反射已经获取到构造函数,
* 查阅Constructor类中的描述,发现Constructor类中的newInstance(Object...initargs) 方法,
* 说明: Object... initargs 创建对象的时候需要传递的 * 这个方法可以动态的创建出这个构造函数对象所表示的那个类的真实对象。
真实的数据,就是构造函数所需要的实际参数。
*/
publicclassReflectDemo2 {
publicstaticvoidmain(String[]args) throws Exception {
// 获取字节码文件对象
Class clazz=File.class;
//Constructor[] getConstructors() 获取所有公共的构造函数
Constructor[] cons =clazz.getConstructors();
for (Constructorcon : cons) {
/*
* public java.io.File(java.lang.String,java.lang.String)
* publicjava.io.File(java.lang.String)
* publicjava.io.File(java.io.File,java.lang.String)
* publicjava.io.File(java.net.URI)
*/
System.out.println(con);
}
System.out.println("--------------");
//Constructor[] getDeclaredConstructors() 获取所有构造函数
Constructor[] constructors =clazz.getDeclaredConstructors();
for (Constructorconstructor :constructors) {
/*
* public java.io.File(java.lang.String,java.lang.String)
* publicjava.io.File(java.lang.String)
* privatejava.io.File(java.lang.String,java.io.File)
* publicjava.io.File(java.io.File,java.lang.String)
* publicjava.io.File(java.net.URI)
* privatejava.io.File(java.lang.String,int)
*/
System.out.println(constructor);
}
System.out.println("--------------");
//需求:我要获取File(Stringpathname)
//Constructor getConstructor(Class ... parameterTypes)根据参数列表获取指定的公共的构造函数
Constructor constructor =clazz.getConstructor(String.class);
//public java.io.File(java.lang.String)
System.out.println(constructor);
//需求:获取privateFile(String child, File parent)
//Constructor getDeclaredConstructor(Class ... parameterTypes)根据参数列表获取指定的构造函数 包括私有的
System.out.println("--------------");
//private java.io.File(java.lang.String,java.io.File)
Constructor constructor2 =clazz.getDeclaredConstructor(String.class,File.class);
System.out.println(constructor2);
//使用获得的构造函数创建对象newInstance(Object...initargs)
File f = (File) constructor.newInstance("D:\\test\\out.txt");
System.out.println(f.getAbsolutePath());//D:\test\out.txt
}
}
注意:通过Class类中的Constructor[] getDeclaredConstructors()和Constructor getDeclaredConstructor(Class ... parameterTypes)函数可以获得类中的所有的构造函数,包括私有的构造函数,但是私有的构造函数我们在其他类中是无法使用的,如果要想使用必须强制取消Java对私有成员的权限检测或者可以理解暴力访问。
需求:使用反射技术获得File类中的私有构造函数 private File(String child, File parent)并创建File类的对象获得指定路径的绝对路径。
注:String child=”柳岩.jpg”,File parent=new File(“D:\\test”)
代码实现如下所示:
publicstaticvoid main(String[]args) throwsException {
// 获取字节码文件对象
Class clazz=File.class;
/*
* 需求:使用反射技术获得File类中的私有构造函数 private File(String child,File parent)
* 并创建File类的对象获得指定路径的绝对路径。
* 注:String child="柳岩.jpg",File parent=new File("D:\\test")
*/
Constructor constructor2 =clazz.getDeclaredConstructor(String.class,File.class);
//创建File类的对象使用获得的构造函数创建对象 newInstance(Object...initargs)
File f2 = (File) constructor2.newInstance("柳岩.jpg",new File("D:\\test"));
System.out.println(f2.getAbsolutePath());
}
上述代码发生了如下异常:
IllegalAccesssException异常是在没有访问权限时,就会引该异常。
解决上述异常,我们必须强制取消Java对私有成员的权限检测或者可以理解暴力访问,需要使用Constructor的父类AccessibleObject类中的函数:
AccessibleObject类如下所示:
使用如下函数即可:
上述函数表示强制取消Java对私有成员的权限检测。
或者可以理解暴力对私有成员进行访问。
改进的代码如下所示:
/*
* 需求:使用反射技术获得File类中的私有构造函数 private File(String child,File parent)
* 并创建File类的对象获得指定路径的绝对路径。
* 注:String child="柳岩.jpg",File parent=new File("D:\\test")
*/
Constructor constructor2 =clazz.getDeclaredConstructor(String.class,File.class);
//对于上述的构造函数是私有的,我们不能直接访问,只能暴力访问。
constructor2.setAccessible(true);
//创建File类的对象使用获得的构造函数创建对象 newInstance(Object...initargs)
File f2 = (File) constructor2.newInstance("柳岩.jpg",new File("D:\\test"));
System.out.println(f2.getAbsolutePath());//D:\test\柳岩.jpg
小结:
当要访问Class对象中的私有的构造或成员时,需要使用getDeclaredXxxx()函数:
Xxxx表示:Constructor、Field、Method。
在访问Class对象中的私有的构造函数或成员时,需要取消java语言的默认访问权限检查
setAccessible(boolean) true表示强制取消Java对私有成员的权限检测。 false表示不会取消Java对私有成员的权限检测。
3 反射获取成员变量(Field)
需求:演示:反射获取成员变量。
Field[]getFields() 获取所有公共的成员变量
Field[]getDeclaredFields() 获取所有成员变量 包括私有成员变量
FieldgetField(String name) 根据变量名获取指定的公共成员变量
FieldgetDeclaredField(String name)根据变量名获取指定的成员变量 包括私有成员变量
问题:拿到字段能干嘛?
使用Field 类中的函数获取或修改字段的值:
get(Objectobj)获取指定对象上当前字段的值。
set(Objectobj,Object value) 将obj对象上此 Field 表示的字段设置为指定的值
获取字段类型:
ClassgetType() 获取字段的数据类型的Class对象
需求:反射获取Person类中的name、age、address属性值并修改其值。
代码演示如下所示:
/*
* 演示:反射获取成员变量
* Field[]getFields()获取所有公共的成员变量
* Field[]getDeclaredFields()获取所有成员变量
* FieldgetField(String name)根据变量名获取指定的公共成员变量
* FieldgetDeclaredField(String name)根据变量名获取指定的成员变量
* 问题:拿到字段能干嘛?
* 使用Field 类中的函数获取或修改字段的值:
* get(Objectobj)获取指定对象上当前字段的值。
* set(Objectobj,Object value) 将obj对象上此 Field 表示的字段设置为指定的值
* 获取字段类型:
* ClassgetType()获取字段的数据类型的Class对象
*/
publicclassReflectDemo3 {
publicstaticvoidmain(String[]args) throws Exception {
//创建Person类的对象
Person p = new Person("柳岩",19,"上海");
//根据对象p获得Class类的对象
Class clazz =p.getClass();
//Field[] getFields() 获取所有公共的成员变量
/*
* public intcn.itcast.sh.reflect.demo.Person.age
* public java.lang.Stringcn.itcast.sh.reflect.demo.Person.address
*/
Field[] fields = clazz.getFields();
for (Fieldfield : fields) {
System.out.println(field);
}
System.out.println("------------------");
//Field[] getDeclaredFields() 获取所有成员变量
/*
* private java.lang.Stringcn.itcast.sh.reflect.demo.Person.name
* public intcn.itcast.sh.reflect.demo.Person.age
* public java.lang.Stringcn.itcast.sh.reflect.demo.Person.address
*/
Field[] fields2 = clazz.getDeclaredFields();
for (Fieldfield : fields2) {
System.out.println(field);
}
System.out.println("------------------");
//Field getField(String name) 根据变量名获取指定的公共成员变量
//获取age
Field f = clazz.getField("age");
System.out.println(f);//publicintcn.itcast.sh.reflect.demo.Person.age
System.out.println("------------------");
//Field getDeclaredField(String name)根据变量名获取指定的成员变量
Field f2 = clazz.getDeclaredField("name");
//private java.lang.String cn.itcast.sh.reflect.demo.Person.name
System.out.println(f2);
//get(Object obj)获取指定对象上当前字段的值。
intage = (int)f.get(p);
System.out.println(age);//19
//set(Object obj,Object value) 将obj对象上此 Field 表示的字段设置为指定的值
//需求,将年龄19改为18
f.set(p, 18);
intage1 = (int)f.get(p);
System.out.println(age1);//18
System.out.println("------------------");
//暴力修改
f2.setAccessible(true);
f2.set(p,"锁哥");
String name=(String)f2.get(p);
System.out.println(name);//锁哥
//获取字段类型
Class type =f.getType();
System.out.println(type);//int
}
}
4 反射获取成员方法(Method)
4.1 反射公开的非静态的成员方法
说明:
1)在Class类中提供的getMethod方法上接收一个String name,name表示的是需要反射的那个方法的名字。
因为在一个类中可以有多个不同名的方法。在反射的时候需要指定这个方法的名字,同时在一个类中还可能出现方法的重载,这时还需要指定具体反射的是哪个方法参数类型。
2)Method[] getMethods() 获取所有公共的成员方法。包括父类的公共方法。
3)让反射到的一个方法运行,需要使用Method类中的invoke方法 :
Object invoke(Object obj,Object... args)
invoke方法中的第一个参数 Object obj:表示的是当前需要调用这个方法的那个对象
invoke方法中的第二个参数Object... args:
表示的是真正需要运行的某个类中被反射的那个方法需要接收的真实参数
在调用Method类中的invoke方法的时候,其实底层是在运行被反射的那个方法,
既然是某个方法在运行,那么方法运行完之后可能会有返回值。
举例:需求:我们想通过反射技术获得Person类中的setName()函数,并让其执行。
/*
* 举例:需求:我们想通过反射技术获得Person类中的setName()函数,并让其执行。
* Method getMethod(String name, Class<?>... parameterTypes)
* 返回一个 Method 对象,它反映此Class 对象所表示的类或接口的指定公共成员方法。
* Method[] getMethods()获取所有的公共方法 包括父类的公共方法
*
* Object invoke(Objectobj, Object... args) 表示让反射到的一个方法运行
* obj:要执行哪个对象的方法
* args:方法需要的实际参数
*/
publicclass ReflectDemo{
publicstaticvoidmain(String[]args) throws Exception {
//获取Class对象
Class clazz = Class.forName("cn.itcast.sh.reflect.demo.Person");
/*
* 反射成员方法:
* public void setName(String name)
* 类中的非静态的成员方法,需要对象调用,我们反射到方法之后,最后肯定是要运行这个方法
* 这时肯定还是需要对象的
*
* Method getMethod(String name,Class<?>... parameterTypes)
* String name 反射的方法的名字
* Class<?>... parameterTypes 反射的方法接受的参数类型
*/
Method method = clazz.getMethod("setName", String.class);
//通过非反射的方式执行setName函数
/*
* Person p = new Person();
* p.setName("赵四");
* System.out.println(p.getName());//赵四
*/
//通过反射的方式执行setName函数
/*
* 让反射到的一个方法运行,需要使用Method类中的invoke方法
*
* Object invoke(Object obj, Object... args)
*
* invoke方法中的第一个参数Object obj:表示的是当前需要调用这个方法的那个对象
* invoke方法中的第二个参数Object...args:
* 表示的是真正需要运行的某个类中被反射的那个方法需要接收的真实参数
* 在调用Method类中的invoke方法的时候,其实底层是在运行被反射的那个方法,
* 既然是某个方法在运行,那么方法运行完之后可能会有返回值
*/
//获取此时clazz所表示Person类的一个新的对象
// Object obj = clazz.newInstance();
Person p = (Person)clazz.newInstance();
//执行setName函数 这句代码就是在调用反射到Person类中的setName方法
Object obj2 = method.invoke(p,"赵四");
System.out.println(p);//cn.itcast.sh.reflect.demo.Person@7f21c5df
System.out.println(obj2);//null
System.out.println(p.getName());//赵四
System.out.println("=====================================");
//Method[] getMethods() 获取所有的公共方法 包括父类的公共方法
Method[] methods =clazz.getMethods();
for (Methodm : methods) {
System.out.println(m);
}
}
}
总结:
反射过程如下图所示:
4.2 反射类中的私有,静态的,不需要参数的,且有返回值的方法
Method[] getDeclaredMethods() 获取所有本类自己声明过的成员方法。不包括父类中的方法。
Method getDeclaredMethod(String name,Class ... parameterTypes)获取某个方法。
案例:需求:反射Person类中的私有的 静态的成员方法。private static void say()。
在Person类中添加如下函数:
private static void say()
{
System.out.println("say....");
}
说明:
1)如果在反射的时候,反射的方法不需要接受参数的时候,在反射时参数的类型可以书写成null,由于第二个参数是可变参数,我们也可以什么都不写;
2)由于方法是私有的,这时需要取消Java的权限检查,method.setAccessible(true);
如果不取消权限检查会报异常:
3)调用invoke方法,让被反射的方法运行, 由于这个方法say是静态的,运行的时候是不需要对象的。这时在调用invoke方法的时候,对象书写null,由于不需要参数,真实的参数也书写成null。由于第二个参数是可变参数,我们也可以什么都不写。
具体代码如下所示:
/*
* 案例:需求:反射Person类中的私有的 静态的成员方法。private static voidsay()。
* Method[] getDeclaredMethods()获取所有本类自己声明过的成员方法。不包括父类中的方法
* Method getDeclaredMethod(String name,Class... parameterTypes)获取某个方法。
*/
publicclassReflectDemo1 {
publicstaticvoidmain(String[]args) throws Exception {
//调用自定义函数
demo();
}
//MethodgetDeclaredMethod(String name,Class ... parameterTypes)获取某个方法。
//案例:需求:反射Person类中的私有的 静态的成员方法。private static void say()。
publicstaticvoid demo()throws Exception {
//获取Class对象
Class clazz = Class.forName("cn.itcast.sh.reflect.demo.Person");
//Method[] getDeclaredMethods() 获取所有本类自己声明过的成员方法。不包括父类中的方法
//获取所有的方法
Method[] methods =clazz.getDeclaredMethods();
for (Methodm : methods) {
System.out.println(m);
}
/*
* 反射方法say
* 反射的方法不需要接受参数的时候,在反射时参数的类型可以书写成null
* 由于第二个参数是可变参数,我们也可以什么都不写。
*/
// Method method =clazz.getDeclaredMethod("say", null);
Method method = clazz.getDeclaredMethod("say");
/*
* 由于方法是私有的,这时需要取消Java的权限检查
*/
method.setAccessible(true);
/*
* 调用invoke方法,让被反射的方法运行
* 由于这个方法是静态的,运行的时候是不需要对象的
* 这时在调用invoke方法的时候,对象书写null,由于不需要参数,真实的参数也书写成null
* 由于第二个参数是可变参数,我们也可以什么都不写
*/
// method.invoke(null, null);
Object obj = method.invoke(null);
System.out.println(obj);
}
}
代码运行结果如下所示: