Java反射(Reflection)机制!

 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());
	
	}
}

常用方法: 

  1. Class.forName();动态加载类。
  2. newInstance() :根据对象的class新建一个对象
  3. getSuperclass() 获取继承的父类(不仅可以获取当前类的父类,也可获取当前类父类的父类)
  4. getInterfaces() 获取继承的接口
  5. getDeclaredFields() 获取字段名字
  6. getDeclaredMethods();获取当前类的所有方法。
  7. getConstructors() :获得所有的构造函数。
  8. getModifiers() : 反射中获得修饰符
  9. getPackage() :反射中获得package
  10. getField(String name):反射中获得域成员。
  11. getFields() :获得域数组成员。
  12. isAnnotation() :判断是否为注解类型。
  13. isPrimitive() :判断是否为基本类型。
  14. isArray() :判断是否为数组类型。
  15. isEnum() :判断是否为枚举类型。
  16. 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));
		
	}
}

反射优点和缺点

优点:

  1. 反射可以在运行时动态地获取和操作类的信息,包括注解、字段、方法、构造函数等。

  2. 反射可以实现灵活的代码逻辑,比如可以通过反射获取类的实例并调用方法,而不需要事先在代码中声明这些方法。

  3. 反射可以支持框架和组件的集成,可以在不了解类的内部实现细节的情况下使用它们。

缺点:

  1. 反射的运行效率相比直接调用代码要慢,因为它需要动态获取和操作类的信息,而这些操作都需要额外的时间和资源。

  2. 反射对程序的安全性和稳定性有一定的影响,因为它可以绕过访问控制的机制,导致程序出现意料之外的异常情况。

  3. 反射的使用需要对类的结构和实现有一定的了解,否则容易出现错误或者不必要的开销,影响程序的可维护性和可读性。

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值