反射是什么,用最简单的语句来介绍反射就是加载类并解剖类的各个组成部分,稍有java基础的人都知道,java类在内存中是以字节码的形式存在的,而java中有一个Class类表示的正是某个类的字节码对象。大家查阅javaAPI可以发现这个类有个静态方法forName,它可以接收一个字符串作为类名,返回值正是此类名所表示的类的字节码对象,至此我们完成了反射技术中的第一步:加载类,也就是说我们成功的把一个我们指定的类加载到内存并且得到它的字节码对象。
那么在java中,到底有几种加载类得到类的字节码对象的方法呢,上面我们介绍的通过Class类的静态方法forName方法是一种,那还有没有其他的方法呢,下面我们就用代码的方式把加载类的几种方法给大家一一呈现:
package com.reflection;
public class ReflectionDemo {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
//第1种:调用Class类的静态方法foName加载并返回某个类的字节码对象
Class personClass1 = Class.forName("com.reflection.Person");
//第2种:调用Person对象的getClass方法也可以返回该类的字节码对象
Class personClass2 = new Person().getClass();
//第3种:直接使用Person.class也可以将Person类加载到内存并返回Person类的字节码对象
Class personClass3 = Person.class;
}
}
加载类只是反射技术的第一步,其实反射技术的核心是在第二步,那就是解剖类的各个组成部分,大家想一想,java类有几个组成部分呢,无非就是构造函数、字段和方法,所以,所谓的解剖类的各个组成部分,无非就是解剖出类的构造函数、字段或者是方法。下面我们就一一介绍怎样解剖出的类的这三个部分。
解剖类的构造方法
构造方法在java类中是一种特殊的方法,它的作用只有一个,就是用来创造对象,同样,反射机制解剖构造方法的作用也是为了用它创造对象,我们翻阅一下javaAPI的Class类的方法列表,不难发现他就有一个getConstructor方法,它接收一系列可变参数(关于java可变参数,不熟悉的读者可以先去掌握这部分知识),返回的是一个Constructor对象,里面封装的是一个构造函数,而Constructor对象有一个方法newInstance,同样接收一系类可变参数,返回值是Object类型,此Object类型即是我们反利用解剖出的构造方法创建的新对象。getConstructor只能解剖类的public类型的构造函数,对于private类型的构造函数,需要用到Class类的另外一个函数getDeclaredConstructor,接收的参数和返回值和getConstructor一样,接下来,我们就用代码演示下怎么解剖类的各种构造函数。
被反射的类Person,为了演示解剖各种构造函数,我们给Person添加三个构造函数:
package com.reflection;
public class Person {
public Person() {
System.out.println("name");
}
public Person(String name) {
System.out.println(name);
}
private Person(String name, int age) {
System.out.println(name + age);
}
}
测试类:演示了如何解剖出类的各种构造函数
package com.reflection;
import java.lang.reflect.Constructor;
import org.junit.Test;
public class ReflectionDemo {
@Test
public void Test1() throws Exception {
Class PersonClass = Class.forName("com.reflection.Person");
//解剖出那个无参的构造函数,所以传入的参数为null
Constructor constructor = PersonClass.getConstructor(null);
//使用构造函数创建对象,因为是无参构造函数,所以传入参数为null
Person person = (Person)constructor.newInstance(null);
}
@Test
public void Test2() throws Exception {
Class PersonClass = Class.forName("com.reflection.Person");
//解剖出那个接收一个String参数的构造函数,我们使用String.class来指定参数类型为String
Constructor constructor = PersonClass.getConstructor(String.class);
//创建对象时需要传入一个String类型的参数
Person person = (Person)constructor.newInstance("person name");
}
@Test
public void Test3() throws Exception {
Class PersonClass = Class.forName("com.reflection.Person");
//解剖那个接收两个参数的private类型的构造函数,使用getDeclaredConstructor方法,同样传入的参数为String.class,int.class
Constructor constructor = PersonClass.getDeclaredConstructor(String.class,int.class);
//这句很重要,对于私有的构造方法,我们要调用Constructor的setAccessible将它的访问权限设为true,这样才可以在类的外面调用它
constructor.setAccessible(true);
//同样创建对象时需要传入一个String一个int类型的变量
Person person = (Person)constructor.newInstance("person name",23);
}
}
掌握了怎么反射类的构造方法,再来反射类的方法就很简单了,其实都差不多,下面就直接上代码了
首先给Person类加几个各种各样的方法
package com.reflection;
public class Person {
public Person() {
System.out.println("person");
}
public Person(String name) {
System.out.println(name);
}
private Person(String name, int age) {
System.out.println(name + age);
}
public void grow() {
System.out.println("growing......");
}
public void grow(String name) {
System.out.println(name + "is growing......");
}
public int howOld() {
return 23;
}
private void sex() {
System.out.println("I am a boy");
}
public static void sport() {
System.out.println("I like run");
}
}
测试类:反射Person类中各种各样的方法
package com.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.junit.Test;
public class ReflectionDemo1 {
Person person = new Person();
@Test
public void Test1() throws Exception {
Class PersonClass = Class.forName("com.reflection.Person");
//第一个参数为要反射的方法的名字,第二个参数为可变参数,接收的为该方法要接收的参数类型
Method method = PersonClass.getMethod("grow", null);
//调用Method类的invoke方法来执行该方法,执行的时候需要接收一个对象来做为方法执行的主体,第二个可变参数才是方法要传入的一系列参数
method.invoke(person, null);
}
@Test
public void Test2() throws Exception {
Class PersonClass = Class.forName("com.reflection.Person");
Method method = PersonClass.getMethod("grow", String.class);
method.invoke(person, "xiaohei");
}
@Test
public void Test3() throws Exception {
Class PersonClass = Class.forName("com.reflection.Person");
Method method = PersonClass.getMethod("howOld", null);
//用一个Integer来接收方法的返回值,然后转成int类型
int age = (Integer)method.invoke(person, null);
System.out.println(age);
}
@Test
public void Test4() throws Exception {
Class PersonClass = Class.forName("com.reflection.Person");
Method method = PersonClass.getDeclaredMethod("sex", null);
method.setAccessible(true);
method.invoke(person, null);
}
@Test
public void Test5() throws Exception {
Class PersonClass = Class.forName("com.reflection.Person");
Method method = PersonClass.getDeclaredMethod("sport", null);
method.setAccessible(true);
//反射的静态方法,不必指定执行主体,所以第一个参数可为null,当然指定也可
method.invoke(null, null);
}
}
其实在方法的反射上,有一个方法的反射和其他方法是不大一样的,那就是main方法,到底有什么不一样,大家可以自己去网上查查,这里不赘述。
解剖类的字段
和反射方法差不多,只是Field类有自己独特的方法,直接上代码
给Person类加了几个字段
package com.reflection;
public class Person {
public String name = "xiaohei";
private int age = 23;
private static String sex = "male";
public Person() {
System.out.println("person");
}
public Person(String name) {
System.out.println(name);
}
private Person(String name, int age) {
System.out.println(name + age);
}
public void grow() {
System.out.println("growing......");
}
public void grow(String name) {
System.out.println(name + "is growing......");
}
public int howOld() {
return 23;
}
private void sex() {
System.out.println("I am a boy");
}
public static void sport() {
System.out.println("I like run");
}
}
测试类:演示反射各种各样的字段
package com.reflection;
import java.lang.reflect.Field;
import org.junit.Test;
public class ReflectionDemo2 {
Person person = new Person();
@Test
public void Test1() throws Exception {
Class PersonClass = Class.forName("com.reflection.Person");
Field field = PersonClass.getField("name");
//使用Field的get方法可得到该字段的值
String name = (String)field.get(person);
System.out.println(name);
//使用Field的set方法可设置该字段的值
field.set(person, "xiaobai");
}
@Test
public void Test2() throws Exception {
Class PersonClass = Class.forName("com.reflection.Person");
Field field = PersonClass.getDeclaredField("age");
//对于private类型的字段同样要设置它的访问权限为true才可访问
field.setAccessible(true);
int age = (Integer)field.get(person);
System.out.println(age);
field.set(person, 20);
}
@Test
public void Test3() throws Exception {
Class PersonClass = Class.forName("com.reflection.Person");
Field field = PersonClass.getDeclaredField("sex");
field.setAccessible(true);
//字段和方法不同,对于静态的字段,同样要设置get和set的对象主体
String sex = (String)field.get(person);
System.out.println(sex);
field.set(person, "female");
}
}
以上就是java反射技术的全部内容了,最后跟大家说一下,java反射技术只有在学习框架的时候有用,我们编程很少会用到反射技术,因为我们一般都是用new来加载类的,但是反射技术却是各种各样的框架都能用到的核心技术,因为框架都是通过读配置文件来决定加载哪些类的,所有如果想学透框架的核心原理,java反射技术是必须掌握的技术。