Java反射机制是指Java程序在运行时,能够动态地获取类的信息、构造对象、调用方法、访问变量等功能的机制。它允许程序在运行时通过类名获取类的信息,包括类的方法、字段和构造函数等信息,并能够在运行时创建对象、调用方法或访问字段。
反射机制可以使Java的代码更加灵活,也可以提高代码的复用性和扩展性。
Java反射机制提供的功能
- 在运行时获取任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时获取任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法。
反射相关的主要类
在JDK中主要由以下类来实现Java反射机制
- java.lang.Class:代表一个类,用于描述一个类的属性和方法的类,它可以被视为一个类的元数据。表示正在运行的Java应用程序中的Class类型信息的实例。
- java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法,包括抽象方法。它是用来封装反射类方法的一个类。
- java.lang.reflect.Field:提供有关类的属性信息,以及它的动态访问权限。它是一个封装反射类属性的类。
- java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器。提供有关类的构造方法信息,以及对它的动态访问权限。它是一个封装反射类构造方法的类。
- java.lang.reflect.Object:是所有Java类的父类。所有对象都默认继承Object类。
获取Class对象的三种方式
方式一:通过对象静态属性.class来获取对应的Class对象
方式二:Object类中的getClass()方法,必须要明确具体的类,并且要创建对象。
方式三:通过反射。只要通过给定的类的字符串名称就可以获取该类forName
public class Demo01 {
public static void main(String[] args) throws ClassNotFoundException {
//方式1:通过类名访问class
Class stringCls1=String.class;
//方式2 :通过实例访问getClass()
String s="";
Class stringCls2=s.getClass();
//方式3通过Class类的静态方法forName(类名)
Class stringCls3=Class.forName("java.lang.String");
System.out.println(stringCls1.hashCode());
System.out.println(stringCls2.hashCode());
System.out.println(stringCls3.hashCode());
}
}
一、Class类的作用
-
获取类的信息:在反射中,可以通过Class类获取一个已知类的所有信息,如类名、父类、接口、方法、字段等。
-
动态创建对象:通过Class类的newInstance()方法可以动态创建一个类的对象。
-
动态调用方法:在反射中,可以通过Class类获取类的方法,并且可以使用反射的方式动态调用方法。
-
动态设置/获取字段值:通过Class类的getField()和setField()方法,可以获取和设置一个类的字段值。
-
动态代理:通过动态代理,可以在运行时动态地生成一个代理类,实现对目标类的拦截和增强,从而实现AOP编程。
public class Demo02 {
public static void main(String[] args) {
Class clssss=String.class;
printClassInfo(clssss);
}
//获取Class类型信息
public static void printClassInfo(Class cls) {
//类名
System.out.println("类()的名称:"+cls.getSimpleName());
System.out.println("完全限定名:"+cls.getName());
System.out.println("类/接口的类型的名称:"+cls.getTypeName());
//父类
Class superCls=cls.getSuperclass();
System.out.println("类的父类:"+superCls.toString());
//实现的接口
Class[] interfaceCls=cls.getInterfaces();
System.out.println("当前类实现的接口:");
for(Class icls:interfaceCls) {
System.out.println(icls);
}
//package包
Package pck=cls.getPackage();
if(pck!=null) {
System.out.println("类所在包的名称:"+pck.getName());
}
//判断类型
System.out.println("是否为接口:"+cls.isInterface());
System.out.println(cls.isArray());
System.out.println(cls.isEnum());
}
}
常用方法:
- Class.forName();动态加载类。
- newInstance() :根据对象的class新建一个对象
- getSuperclass() 获取继承的父类(不仅可以获取当前类的父类,也可获取当前类父类的父类)
- getInterfaces() 获取继承的接口
- getDeclaredFields() 获取字段名字
- getDeclaredMethods();获取当前类的所有方法。
- getConstructors() :获得所有的构造函数。
- getModifiers() : 反射中获得修饰符
- getPackage() :反射中获得package
- getField(String name):反射中获得域成员。
- getFields() :获得域数组成员。
- isAnnotation() :判断是否为注解类型。
- isPrimitive() :判断是否为基本类型。
- isArray() :判断是否为数组类型。
- isEnum() :判断是否为枚举类型。
- getClassLoader() :获得类的类加载器。
二、Method类的作用
-
获取方法信息:可以通过Method类获取方法的名称、参数列表、返回类型等信息。
-
调用方法:可以使用Method类的invoke()方法来调用方法,传入实例对象和方法参数,即可执行该方法。
invoke 主要是用来调用某个类中的方法的,但是他不是通过当前类直接去调用而是通过反射的机制去调用。最简单的用法是可以把方法参数化 invoke(class, method)
-
动态创建和修改方法:可以使用Method类和其他反射类结合,动态地创建和修改方法,实现动态代码生成的功能。
-
判断方法访问权限:可以通过Method类的getModifiers()方法获取方法的修饰符,进而判断方法的访问权限。
//反射操作获取方法
//每一个方法都会被封装成一个Method对象
public class Demo11 {
public static void main(String[] args) {
Class cls=Double.class;
//获取所有public方法包含父类
// Method[] methods=cls.getMethods();
//获取所有定义的方法仅包含当前类
Method[] methods=cls.getDeclaredMethods();
for(Method method:methods) {
System.out.println("方法的访问修饰符"+Modifier.toString(method.getModifiers()));
System.out.println("方法的返回值类型:"+method.getReturnType());
System.out.println("方法的名称:"+method.getName());
//获取所有的参数类型
Parameter[] params=method.getParameters();
for(Parameter p:params) {
System.out.println(p.getName());
System.out.println(p.getType());
System.out.println("-------");
}
}
}
}
//反射操作调用方法
public class Demo12 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
//获取Class对象
Class cls=Base.class;
Object obj=cls.newInstance(); //创建Base对象
//按照方法名称和参数类型获取Method方法对象
// create();
// Method method=cls.getMethod("create");
Method method=cls.getMethod("create", int.class);
//Method对象的invoke()作用
//以反射的方式执行create()方法
int r=(int) method.invoke(obj, 100);
System.out.println(r);
}
}
class Base{
public int create() {
return create(100);
}
public int create(int i) {
return (int) (Math.random()*i*10);
}
}
//以反射的方式调用静态方法
public class Demo13 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//计算以10为底的对数
//获取某个指定数字的位数
System.out.println(Math.log10(545)+1);
//反射作用
Class cls=Math.class;
Method methodLog10=cls.getMethod("log10", double.class);
int size=Double.valueOf((double) methodLog10.invoke(null, 545)).intValue()+1;
System.out.println(size);
}
}
三、Filed类的作用
-
获取类的所有成员变量:通过Class对象的getDeclaredFields()方法可以获取一个类的所有成员变量。这些成员变量包括公共、私有、受保护、静态和实例变量。
public class Demo08 {
public static void main(String[] args) {
Class cls=Integer.class;
//所有public访问修饰符定义的字段
//Field[] fields=cls.getFields();
//所有定义的字段
Field[] fields=cls.getDeclaredFields();
for(Field field:fields) {
System.out.println("成员变量访问修饰符(int)"+field.getModifiers());
System.out.println("成员变量访问修饰符:"+Modifier.toString(field.getModifiers()));
System.out.println("成员变量类型"+field.getType());
System.out.println("成员变量名称"+field.getName());
System.out.println();
}
}
}
-
获取单个成员变量:可以使用Class对象的getField()方法获取指定名字的成员变量。而getDeclaredField()方法可以获取指定名字和修饰符的成员变量。
public class Demo08 {
public static void main(String[] args) throws NoSuchFieldException, SecurityException {
Class cls=Integer.class;
//getField()方法获得指定名字的方法
Field field1=cls.getField("MAX_VALUE");
//getDeclaredFields()获取指定名字和访问修饰符的方法
Field field2=cls.getDeclaredField("MIN_VALUE");
field2.setAccessible(true); //true为公有
System.out.println(field1);
System.out.println(field2);
}
}
-
获取和修改成员变量的值:可以使用Field对象的get()方法获取一个对象中成员变量的值,而set()方法可以修改成员变量的值。需要注意的是,如果访问的是私有成员变量,需要先调用setAccessible(true)方法来打开访问权限。
四、Constructor类的作用
-
实例化对象:可以使用Constructor类的newInstance()方法动态地创建一个类的对象,实现与new关键字相同的效果。
创建对象
通过反射方式创建对象的过程:Class--->Constructor--->某个类的对象
1.正常情况
String str=new String(“hello”);
2.反射情况
Constructor con=String.class.getConstructor(String.class)
String str=(String)con.newlnstance(“hello”);只有当要用某个类的无参构造方法创建该类对象时,可以省略创建Constructor类对象的这个过程。 Date d=(Date)Class.forName(“java.util.Date”).newlnstance();
-
获取构造方法信息:可以使用Constructor类的getModifiers()、getParameterCount()、getParameterTypes()等方法获取构造方法的修饰符、参数个数、参数类型等信息。
-
调用构造方法:可以使用Constructor类的newInstance(Object... initargs)方法调用构造方法,并传递实际参数。
@SuppressWarnings("all")
public class Demo05 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class cls=Example1.class;
System.out.println("所有构造方法");
//获取所有公有的构造方法
Constructor[] constructArray=cls.getConstructors();
//获取所有定义的构造方法
Constructor[] constructArray=cls.getDeclaredConstructors();
for(Constructor construct:constructArray) {
System.out.println(construct);
}
//获取一个私有的构造方法
Constructor privateConstruct=cls.getDeclaredConstructor(String.class);
System.out.println("获取私有的构造方法:"+privateConstruct);
//调用私有的构造方法
privateConstruct.setAccessible(true);
Example1 ex=(Example1) privateConstruct.newInstance("work");
System.out.println(ex);
}
}
class Example1{
private Example1(String s) {
System.out.printf("Example1类的有参构造:s=%s\n",s);
}
private Example1(float f) {
System.out.printf("Example1类的有参构造:f=%f\n",f);
}
public Example1() {
System.out.println("Example1类的无参构造!");
}
public Example1(int a) {
System.out.printf("Example1类的有参构造:a=%d\n",a);
}
public Example1(int a,double b) {
System.out.printf("Example1类的有参构造:a=%d,b=%f\n",a,b);
}
}
public class Demo04 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Class cls=Example.class;
//调用无参构造方法创建Example类型对象
Example ex1=(Example) cls.newInstance();
System.out.println(ex1);
//获取所有的构造方法
System.out.println("所有的构造方法:");
Constructor[] constructArray=cls.getConstructors();
for(Constructor construct:constructArray) {
System.out.println(construct);
}
System.out.println("------------------");
//调用有参构造方法创建Example类型对象
//1.获取指定参数类型的构造方法
//无参构造
// Constructor construct=cls.getConstructor();
//有1个参数的构造方法
//Constructor construct=cls.getConstructor(int.class);
//有两个参数的构造方法
Constructor construct=cls.getConstructor(int.class,double.class);
System.out.println(construct);
//第二步执行构造方法创建Example类型对象
Example ex2=(Example) construct.newInstance(112,3.1415926);
System.out.println(ex2);
}
}
class Example{
public Example() {
System.out.println("Example类的无参构造!");
}
public Example(int a) {
System.out.printf("Example类的有参构造:a=%d\n",a);
}
public Example(int a,double b) {
System.out.printf("Example类的有参构造:a=%d,b=%f\n",a,b);
}
}
五、Object类的作用
-
获取类的信息:通过调用Object类的getClass()方法,可以得到一个对象的Class对象,从而获取到该对象所属类的相关信息。
-
反射调用方法:通过反射机制,我们可以动态地获取一个类的方法,然后通过invoke()方法调用该方法,从而实现动态调用方法的功能。而在反射调用方法的过程中,Object类则用作实参对象,即被调用方法的对象实例。
继承关系的判断
instanceof运算符 用于测试一个对象是否为某种特定类型的实例。它可以检查对象是否为某个构造函数的实例,或者是否为其父类的实例。该运算符的前后都跟的是应用类型。
public class Demo07 {
public static void main(String[] args) {
//instanceof 运算符 前后都跟引用类型
Object obj=Integer.valueOf(2468);
System.out.println("是否为Integer类型?"+(obj instanceof Integer));
System.out.println("是否为Double类型?"+(obj instanceof Double));
System.out.println("是否为Number类型?"+(obj instanceof Number));
System.out.println("是否为Comparable接口?"+(obj instanceof Comparable));
//isAssignableFrom判断类型和类型之间的关系
System.out.println("Integer<=Integer?"+Integer.class.isAssignableFrom(Integer.class));
System.out.println("Integer<=Number?"+Integer.class.isAssignableFrom(Number.class));
System.out.println("Integer=>Number?"+Number.class.isAssignableFrom(Integer.class));
System.out.println("Integer=>Double?"+Double.class.isAssignableFrom(Integer.class));
System.out.println("Integer=>Comparable?"+Comparable.class.isAssignableFrom(Integer.class));
}
}
反射优点和缺点
优点:
-
反射可以在运行时动态地获取和操作类的信息,包括注解、字段、方法、构造函数等。
-
反射可以实现灵活的代码逻辑,比如可以通过反射获取类的实例并调用方法,而不需要事先在代码中声明这些方法。
-
反射可以支持框架和组件的集成,可以在不了解类的内部实现细节的情况下使用它们。
缺点:
-
反射的运行效率相比直接调用代码要慢,因为它需要动态获取和操作类的信息,而这些操作都需要额外的时间和资源。
-
反射对程序的安全性和稳定性有一定的影响,因为它可以绕过访问控制的机制,导致程序出现意料之外的异常情况。
-
反射的使用需要对类的结构和实现有一定的了解,否则容易出现错误或者不必要的开销,影响程序的可维护性和可读性。