Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息,这个过程叫做反射。
正常获得实例化对象的方式:
通过反射方式解析实例化对象的方式:
Java反射机制提供的功能:
在Object类中定义了以下的方法,此方法将被所有子类继承:
● public final Class getClass()
以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。
Class可以获取获取当前类的属性、方法和构造器以及实现的接口&继承的父类。
方法名 | 功能说明 |
---|---|
static Class forName(String name) | 返回指定类名 name 的 Class 对象 |
Object newInstance() | 调用缺省构造函数,返回该Class对象的一个实例 |
getName() | 返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称 |
Class getSuperClass() | 返回当前Class对象的父类的Class对象 |
Class [] getInterfaces() | 获取当前Class对象的接口 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
Class getSuperclass() | 返回表示此Class所表示的实体的超类的Class |
Constructor[] getConstructors() | 返回一个包含某些Constructor对象的数组 |
Field[] getDeclaredFields() | 返回Field对象的一个数组 |
Method getMethod(String name,Class … paramTypes) | 返回一个Method对象,此对象的形参类型为paramType |
通过实例看一下是怎么创建的:
import java.lang.annotation.ElementType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionTest {
/*
* 如何获取Class的实例
*/
public static void main(String[] args){
//1、通过类.class的方式获取
Class clazz = Person.class;
System.out.println(clazz);
//2、调用Class的静态方法forName(String str)
Class clazz2 = Class.forName("com.atguigureflection.Person");
System.out.println(clazz2);
System.out.println(clazz == clazz2); //true
//3、调用运行时类对象的getClass()
Person p = new Person();
Class clazz3 = p.getClass();
System.out.println(clazz3);
//4、调用ClassLoader
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass("com.atguigureflection.Person");
System.out.println(clazz4);
}
}
那么另一个问题来了,哪些随想可以有Class对象呢?
看看下面的你应该就懂了哪些能.class了。
import java.lang.annotation.ElementType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionTest {
/*
* ___.class
*注意:只要元素类型与维度一样,就是同一个Class
*/
public static void main(String[] args){
Class c1 = Object.class;
Class c2 = Comparable.class;
Class c3 = String[].class;
Class c4 = int[][].class;
Class c5 = ElementType.class;
Class c6 = Override.class;
Class c7 = int.class;
Class c8 = void.class;
Class c9 = Class.class;
}
}
刚才在上面提到了一个ClassLoader,也就是类加载器。加载器肯定是跟加载有关,那就从加载说起吧。
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要类加载器参与。
链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
初始化:
执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。
通过一小段代码来理解一下
public class ClassLoadingTest {
public static void main(String[] args) {
System.out.println(A.m);//100
}
}
class A {
static {
m = 300;
}
static int m = 100;
}
/*
第二步:链接结束后m=0
第三步:初始化后,m的值由()方法执行决定
这个A的类构造器()方法由类变量的赋值和静态代码块中的语句按照顺序合并产生,类似于
(){
m = 300;
m = 100;
}
*/
那么什么时候会发生类初始化呢?
主要分为两类:主动引用和被动引用
类的主动引用(一定会发生类的初始化)
类的被动引用(不会发生类的初始化)
类加载器作用是用来把类(class)装载进内存的。共有以下几种类加载器。
1、引导类加载器(bootstrap class loader):
2、扩展类加载器(extensions class loader)
3、应用程序类加载器(application class loader)
import java.io.InputStream;
public class ClassLoaderTest1 {
public static void main(String[] args) {
//1.获取一个系统类加载器
ClassLoader classloader = ClassLoader.getSystemClassLoader();
System.out.println(classloader);
//2.获取系统类加载器的父类加载器,即扩展类加载器
classloader = classloader.getParent();
System.out.println(classloader);
//3.获取扩展类加载器的父类加载器,即引导类加载器
classloader = classloader.getParent();
System.out.println(classloader);
//4.测试当前类由哪个类加载器进行加载
try {
classloader = Class.forName("exer2.ClassloaderDemo").getClassLoader();
System.out.println(classloader);
//5.测试JDK提供的Object类由哪个类加载器加载
classloader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classloader);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//6.关于类加载器的一个主要方法:getResourceAsStream(String str):获取类路径下的指定文件的输入流
InputStream in = ClassLoaderTest1.class.getClassLoader().getResourceAsStream("exer2\\jdbc.properties");
System.out.println(in);
}
}
例如:
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) {
//1.根据全类名获取对应的Class对象
String name = "atguigu.java.Person";
Class clazz = null;
try {
clazz = Class.forName(name);
//2.调用指定参数结构的构造器,生成Constructor的实例
Constructor con = clazz.getConstructor(String.class,Integer.class);
//3.通过Constructor的实例创建对应类的对象,并初始化类属性
Person p2 = (Person) con.newInstance("Peter",20);
System.out.println(p2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
具体说一下方法吧:
1.实现的全部接口
public Class>[] getInterfaces()
确定此对象所表示的类或接口实现的接口。
2.所继承的父类
public Class Super T> getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。
3.全部的构造器
public Constructor[] getConstructors() 返回此 Class 对象所表示的类的所有public构造方法。
public Constructor[] getDeclaredConstructors()
返回此 Class 对象表示的类声明的所有构造方法。
Constructor类中:
4.全部的方法
public Method[] getDeclaredMethods()
返回此Class对象所表示的类或接口的全部方法
public Method[] getMethods()
返回此Class对象所表示的类或接口的public的方法
Method类中:
5.全部的Field
public Field[] getFields()
返回此Class对象所表示的类或接口的public的Field。
public Field[] getDeclaredFields()
返回此Class对象所表示的类或接口的全部Field。
Field方法中:
6. Annotation相关
7.泛型相关
8.类所在的包 Package getPackage()
通过反射,调用类中的方法,通过Method类完成。步骤:
在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。
在Field中:
用反射获取某个类的信息,并用反射使用某个类
1、声明一个类:com.homework.demo.Demo,
(1)包含静态变量:学校school(显式初始化为"一中")
(2)包含属性:班级名称className
(3)并提供构造器,get/set等
(4)实现Serializable和Comparable接口(按照班级名称排序)
2、把com.homework.demo.class导出为一个school.jar并放到D:\ProgramFiles\Java\jdk1.8.0_151\jre\lib\ext目录(注意,以你自己的JDK安装目录为准)
3、在测试类Test的test01()测试方法中,用反射获取Demo类的Class对象,并获取它的所有信息,包括类加载器、包名、类名、父类、父接口、属性、构造器、方法们等。
4、在测试类Test的test02()测试方法中,用反射获取school的值,并修改school的值,然后再获取school的值。
5、在测试类Test的test03()测试方法中,用反射创建Demo类的对象,并设置班级名称className属性的值,并获取它的值。
6、在测试类Test的test04()测试方法中,用反射获取有参构造创建2个Demo类的对象,并获取compareTo方法,调用compareTo方法,比较大小。
代码实现:
Demo类:
package com.homework.demo;
import java.io.Serializable;
public class Demo implements Serializable,Comparable<Demo>{
private static final long serialVersionUID = 1L;
private static String school = "一中";
private String className;
public Demo(String className) {
super();
this.className = className;
}
public Demo() {
super();
}
public static String getScool() {
return school;
}
public static void setScool(String scool) {
Demo.school = scool;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
@Override
public String toString() {
return "Demo [className=" + className + "]";
}
@Override
public int compareTo(Demo o) {
return this.className.compareTo(o.getClassName());
}
}
ReflectTest类:
package com.homework.demo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import org.junit.Test;
/*
用反射获取Demo类的Class对象,并获取它的所有信息,包括类加载器、包名、类名、父类、父接口、属性、构造器、方法们等。
*/
public class Test {
@Test
public void test01() throws ClassNotFoundException{
Class clazz = Class.forName("com.homework.demo.Demo");
ClassLoader classLoader = clazz.getClassLoader();
System.out.println("类加载器:" + classLoader);
Package pkg = clazz.getPackage();
System.out.println("包名:"+pkg.getName());
int cMod = clazz.getModifiers();
System.out.println("类的修饰符:"+Modifier.toString(cMod));
System.out.println("类名:"+clazz.getName());
System.out.println("父类:"+clazz.getSuperclass().getName());
Class[] interfaces = clazz.getInterfaces();
System.out.println("父接口:"+Arrays.toString(interfaces));
Field[] dfs = clazz.getDeclaredFields();
for(int i = 0 ; i < dfs.length; i++){
System.out.println("第" + (i+1) + "个字段");
int fMod = dfs[i].getModifiers();
System.out.println("修饰符:"+Modifier.toString(fMod));
System.out.println("数据类型:"+dfs[i].getType().getName());
System.out.println("属性名:"+dfs[i].getName());
}
Constructor[] dcs = clazz.getDeclaredConstructors();
for(int i = 0; i < dcs.length; i++){
System.out.println("第" + (i+1) + "个构造器:");
int csMod = dcs[i].getModifiers();
System.out.println("修饰符:"+Modifier.toString(csMod));
System.out.println("构造器名:"+dcs[i].getName());
System.out.println("形参列表:"+Arrays.toString(dcs[i].getParameterTypes()));
}
Method[] dms = clazz.getDeclaredMethods();
for(int i = 0; i < dms.length; i++){
System.out.println("第" + (i+1) + "个成员方法:");
int csMod = dms[i].getModifiers();
System.out.println("修饰符:"+Modifier.toString(csMod));
System.out.println("返回值类型:"+dms[i].getReturnType().getName());
System.out.println("方法名:"+ dms[i].getName());
System.out.println("形参列表"+Arrays.toString(dms[i].getParameterTypes()));
}
}
//用反射获取school的值,并修改school的值,然后再获取school的值
@Test
public void test02() throws Exception{
Class clazz = Class.forName("com.homework.demo.Demo");
Field field = clazz.getDeclaredField("school");
field.setAccessible(true);
Object value = field.get(null);
System.out.println("school = " +value);
field.set(null, "宁夏大学");
value = field.get(null);
System.out.println("school = "+ value);
}
/*
* 用反射创建Demo类的对象,并设置班级名称className属性的值,并获取它的值
*/
@Test
public void test03() throws Exception{
Class clazz = Class.forName("com.homework.demo.Demo");
Object object = clazz.newInstance();
Field field = clazz.getDeclaredField("className");
field.setAccessible(true);
Object value = field.get(object);
System.out.println("className = " + value);
field.set(object, "20080408班");
value = field.get(object);
System.out.println("className = " + value);
}
/*
* 用反射获取有参构造创建2个Demo类的对象,并获取compareTo方法,调用compareTo方法,比较大小。
*/
@Test
public void test04() throws Exception{
Class<?> clazz = Class.forName("com.homework.demo.Demo");
Constructor c = clazz.getDeclaredConstructor(String.class);
Object obj1 = c.newInstance("1997正式版");
Object obj2 = c.newInstance("1999克隆版");
Method m = clazz.getDeclaredMethod("compareTo", Object.class);
System.out.println("obj1与obj2比较结果:"+m.invoke(obj1, obj2));
}
}
关于反射的知识基本上都说完了,当然还有一些反射的应用,比如动态代理,这块的知识会在设计模式统一汇总。看完觉得不错的关于以下呗,以后会持续更新的。