Java反射
一、什么是反射
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
一个类有多个组成部分,例如:成员变量、方法、构造方法等,反射就是加载类,并解剖出类的各个组成部分。
二、class类
1、除了int等基本类型外,Java的其他类型全部都是class(包括interface)。例如:
String
Object
Runnable
Exception
2、class是由JVM在执行过程中动态加载的。JVM在第一次读取到一种class类型时,将其加载进内存。
每加载一种class,JVM就为其创建一个Class类型的实例,并关联起来。(这里的Class类型是一个名叫Class的class类)
3、以String类为例,当JVM加载String类时,它首先读取String.class文件到内存,然后,为String类创建一个Class实例并关联起来:Class cls = new Class(String);
这个Class实例是JVM内部创建的。
由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。
这种通过Class实例获取class信息的方法称为反射(Reflection)。
例如有两个程序员,一个程序员在写程序的时需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码是不能通过编译的。此时,利用Java反射的机制,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。
Java的反射机制它知道类的基本结构,这种对Java类结构探知的能力,我们称为Java类的“自审”。如eclipse中,一按点,编译工具就会自动的把该对象能够使用的所有的方法和属性全部都列出来,供用户进行选择。这就是利用了Java反射的原理,是对我们创建对象的探知、自审。
反射机制的类
java.lang.Class;
//用于获取相关的类对象,常用获取类对象方法有forName(String className),类名.class,对象.getClass()
java.lang.reflect.Constructor;
//java.lang.reflect.Executable类的直接子类,
//用于表示类的构造方法。通过Class对象的getConstructors()方法可以获得当前运行时类的构造方法。
java.lang.reflect.Field;
//类用于封装成员变量信息,调用Class对象的getField()方法或getFields()可以获得当前运行时类的指定成员变量或所有成员变量。
java.lang.reflect.Method;
//java.lang.reflect.Executable类的直接子类,用于封装成员方法的信息
//调用Class对象的getMethod()方法或getMethods()方法可以获得当前运行时类的指定方法或所有方法。
java.lang.reflect.Modifier;
//通过调用Modifier.toString(int mod)方法返回修饰符常量所应的字符串。
三、反射机制提供的相关API
参考链接https://www.w3cschool.cn/java/java-reflex.html
1、通过一个对象获得完整的包名和类名 :
package net.xsoftlab.baike;
public class TestReflect {
public static void main(String[] args) throws Exception {
TestReflect testReflect = new TestReflect();
System.out.println(testReflect.getClass().getName());
// 结果 net.xsoftlab.baike.TestReflect
}
}
2、实例化Class类对象
package net.xsoftlab.baike;
public class TestReflect {
public static void main(String[] args) throws Exception {
Class<?> class1 = null;
Class<?> class2 = null;
Class<?> class3 = null;
// 一般采用这三种形式
class1 = Class.forName("net.xsoftlab.baike.TestReflect");
class2 = new TestReflect().getClass();
class3 = TestReflect.class;
}
}
3、获取一个对象的父类与实现的接口
package net.xsoftlab.baike;
import java.io.Serializable;
public class TestReflect implements Serializable {
private static final long serialVersionUID = -2862585049955236662L;
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
// 取得父类
Class<?> parentClass = clazz.getSuperclass();
System.out.println("clazz的父类为:" + parentClass.getName());
// clazz的父类为: java.lang.Object
// 获取所有的接口
Class<?> intes[] = clazz.getInterfaces();
System.out.println("clazz实现的接口有:");
for (int i = 0; i < intes.length; i++) {
System.out.println((i + 1) + ":" + intes[i].getName());
}
// clazz实现的接口有:
// 1:java.io.Serializable
}
}
4、通过反射机制实例化一个类的对象
package net.xsoftlab.baike;
import java.lang.reflect.Constructor;
public class TestReflect {
public static void main(String[] args) throws Exception {
Class<?> class1 = null;
class1 = Class.forName("net.xsoftlab.baike.User");
// 第一种方法,实例化默认构造方法,调用set赋值
User user = (User) class1.newInstance();
user.setAge(20);
user.setName("Rollen");
System.out.println(user);
// 结果 User [age=20, name=Rollen]
// 第二种方法 取得全部的构造函数 使用构造函数赋值
Constructor<?> cons[] = class1.getConstructors();
// 查看每个构造方法需要的参数
for (int i = 0; i < cons.length; i++) {
Class<?> clazzs[] = cons[i].getParameterTypes();
System.out.print("cons[" + i + "] (");
for (int j = 0; j < clazzs.length; j++) {
if (j == clazzs.length - 1)
System.out.print(clazzs[j].getName());
else
System.out.print(clazzs[j].getName() + ",");
}
System.out.println(")");
}
// 结果
// cons[0] (java.lang.String)
// cons[1] (int,java.lang.String)
// cons[2] ()
user = (User) cons[0].newInstance("Rollen");
System.out.println(user);
// 结果 User [age=0, name=Rollen]
user = (User) cons[1].newInstance(20, "Rollen");
System.out.println(user);
// 结果 User [age=20, name=Rollen]
}
}
class User {
private int age;
private String name;
public User() {
super();
}
public User(String name) {
super();
this.name = name;
}
public User(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User [age=" + age + ", name=" + name + "]";
}
}
5、获取某个类的全部属性
package net.xsoftlab.baike;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class TestReflect implements Serializable {
private static final long serialVersionUID = -2862585049955236662L;
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
System.out.println("===============本类属性===============");
// 取得本类的全部属性
Field[] field = clazz.getDeclaredFields();
for (int i = 0; i < field.length; i++) {
// 权限修饰符
int mo = field[i].getModifiers();
String priv = Modifier.toString(mo);
// 属性类型
Class<?> type = field[i].getType();
System.out.println(priv + " " + type.getName() + " " + field[i].getName() + ";");
}
System.out.println("==========实现的接口或者父类的属性==========");
// 取得实现的接口或者父类的属性
Field[] filed1 = clazz.getFields();
for (int j = 0; j < filed1.length; j++) {
// 权限修饰符
int mo = filed1[j].getModifiers();
String priv = Modifier.toString(mo);
// 属性类型
Class<?> type = filed1[j].getType();
System.out.println(priv + " " + type.getName() + " " + filed1[j].getName() + ";");
}
}
}
6、获取某个类的全部方法
package net.xsoftlab.baike;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class TestReflect implements Serializable {
private static final long serialVersionUID = -2862585049955236662L;
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
Method method[] = clazz.getMethods();
for (int i = 0; i < method.length; ++i) {
Class<?> returnType = method[i].getReturnType();
Class<?> para[] = method[i].getParameterTypes();
int temp = method[i].getModifiers();
System.out.print(Modifier.toString(temp) + " ");
System.out.print(returnType.getName() + " ");
System.out.print(method[i].getName() + " ");
System.out.print("(");
for (int j = 0; j < para.length; ++j) {
System.out.print(para[j].getName() + " " + "arg" + j);
if (j < para.length - 1) {
System.out.print(",");
}
}
Class<?> exce[] = method[i].getExceptionTypes();
if (exce.length > 0) {
System.out.print(") throws ");
for (int k = 0; k < exce.length; ++k) {
System.out.print(exce[k].getName() + " ");
if (k < exce.length - 1) {
System.out.print(",");
}
}
} else {
System.out.print(")");
}
System.out.println();
}
}
}
7、通过反射机制调用某个类的方法
package net.xsoftlab.baike;
import java.lang.reflect.Method;
public class TestReflect {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
// 调用TestReflect类中的reflect1方法
Method method = clazz.getMethod("reflect1");
method.invoke(clazz.newInstance());
// Java 反射机制 - 调用某个类的方法1.
// 调用TestReflect的reflect2方法
method = clazz.getMethod("reflect2", int.class, String.class);
method.invoke(clazz.newInstance(), 20, "张三");
// Java 反射机制 - 调用某个类的方法2.
// age -> 20. name -> 张三
}
public void reflect1() {
System.out.println("Java 反射机制 - 调用某个类的方法1.");
}
public void reflect2(int age, String name) {
System.out.println("Java 反射机制 - 调用某个类的方法2.");
System.out.println("age -> " + age + ". name -> " + name);
}
}
四、总结
优点:
(1)能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。
(2)与Java动态编译相结合,可以实现无比强大的功能
缺点:
(1)使用反射的性能较低
(2)使用反射相对来说不安全
(3)破坏了类的封装性,可以通过反射获取这个类的私有方法和属性