反射
反射:反射(reflection)机制是指在程序的运行时
借助于 Reflection API ,可以构造任意一个类的对象,知道任意一个对象所属的类,任意一个类的成员变量和方法,调用任意一个对象的属性和方法
。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。
某个类加载完之后,在堆内存的方法区中就构造一个 Class 类的对象
(一个类只有一个Class对象),这个对象就包含了这个类完整的结构信息,可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,形象的称之为:反射。
类的各个组成部分对应的对象:
域(成员变量):Field;
构造方法:Constructor;
成员方法:Method。
好处:
- 可以在程序运行过程中,操作这些对象。
- 可以解耦,提高程序的可扩展性。
java 代码运行经历的阶段
第一阶段:编写好的Java代码(.java),并且编译成Java字节码(.class),此时文件保存在硬盘
中。
第二阶段:要使用类与创建类对象,需要使用类加载器(ClassLoader)将类加载到内存中
。而Java中万物皆对象,所以类字节码加载到内存后,就会用一个对象来描述该字节码文件,即Class类的对象。(所有程序执行都需要先读取到内存中才能被CPU拿到执行)
第三阶段:使用Class类的对象去创建加载进内存的类的对象,这是运行时阶段。
获取 Class 对象的方式
-
Class.forName("全类名")
:将字节码文件加载进内存,返回Class对象。全类名:包名+类名。-
这种方式用于第一阶段,此时Java代码没有进内存,通过这个方法可以将类加载内存。
-
多用于配置文件,将类名定义在配置文件中。读取文件,加载类。
Class c = Class.forName("包名+类名");
-
-
类名.class
:通过类属性class获取-
这种方式用于第二阶段,此时类的字节码文件已经加载到内存中,可以通过 类名.class 获取Class类的对象。
-
多用于参数的传递。
Class c = 类名.class;
-
-
对象.getClass()
:通过对象的 getClass() 方法获取,继承了 Object 类的成员方法。-
这种方式用于第三阶段(运行时),此时类的对象已创建,用该对象的getClass()方法获取Class类的对象。
-
多用于对象获取该对象类字节码的方式。
Class c = 对象.getClass();
-
- 结论:
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
Class对象功能:
- 获取功能:
获取成员变量对象
Field[] getFields() :获取所有public修饰的成员变量
Field getField(String name) 获取指定名称的 public修饰的成员变量
Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field getDeclaredField(String name)
获取构造方法对象
Constructor<?>[] getConstructors()
Constructor<T> getConstructor(类<?>… parameterTypes)
Constructor<T> getDeclaredConstructor(类<?>… parameterTypes)
Constructor<?>[] getDeclaredConstructors()
获取成员方法对象
Method[] getMethods()
Method getMethod(String name, 类<?>… parameterTypes)
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, 类<?>… parameterTypes)
获取全类名
- String getName(
-
可以使用 Constructor 对象的 newInstance 方法创建类的对象:
-
如果使用空参数构造方法创建对象,可以直接使用 Class 的对象的 newInstance 方法创建类的对象。
通过Class的对象的newInstance()方法创建反射类的对象,则该类中必须含有一个无参构造器。通过反射创建对象时,如果该类没有无参构造器或者用有参构造器创建对象则用 Constructor 对象创建。
public class reflectTest {
public static String name;
public reflectTest(){} // 无参构造器
public reflectTest(String name){ // 有参构造器
this.name = name;
}
public int methodTest(int age){
System.out.println("测试反射执行方法");
return age;
}
}
public class Test{
public static void main(String[] args) throws Exception {
// 获取Class对象
Class c = reflectTest.class;
// 通过无参构造器创建对象,直接使用Class对象的newInstance()方法。
Object o1 = c.newInstance();
// 反射调用方法,方法参数为int
// 获取对应方法对
Method methodTest = c.getMethod("methodTest", int.class);
// 调用方法,invoke()传入对象与方法参数。
Object invoke = methodTest.invoke(o1, 1);
System.out.println(invoke);
// 使用有参构造器创建对象,使用反射的Constructor对象创建
Constructor constructor = c.getConstructor(String.class);
Object o2 = constructor.newInstance("qgl");
// 反射访问域
Field name = c.getField("name");
Object field = name.get(o2);
System.out.println(field);
}
}
类加载器
类加载器的作用:类加载器作用是用来把类(.class)装载进内存的。JVM 规范定义了如下类型的类的加载器。
-
引导类加载器(Bootstap Classloader):用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来
装载核心类库
。该加载器无法直接获取。 -
扩展类加载器(Extension Classloader):负责jre/lib/ext目录下的jar包或-D java.ext.dirs指定目录下的 jar 包装入工作库。
-
系统类加载器(System Classloader):负责 java -classpath或 -D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载器。(即
加载自己创建的类库
)
public class Test{
public static void main(String[] args) throws Exception {
// 获取系统类的加载器 又叫App加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
// 获取系统类加载器的父类加载器——扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
// 获取扩展类加载器的父类加载器——根加载器(C/C++)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
// 获取当前类的类加载器(自定义类)
ClassLoader classLoader;
classLoader = Class.forName("com.qgl.test.Test").getClassLoader();
System.out.println(classLoader);
// 获取JDK内置类库的类加载器
ClassLoader classLoader2;
classLoader2 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader2);
}
}
/*
输出
sun.misc.Launcher$AppClassLoader@18b4aac2 // 系统类加载器
sun.misc.Launcher$ExtClassLoader@4554617c // 扩展类加载器
null // 根(引导)类加载器获取不到
sun.misc.Launcher$AppClassLoader@18b4aac2
null // 根(引导)类加载器获取不到
*/