超详细Java反射总结(通俗易懂!!!)


前言

认识反射机制的作用,用途以及常用的类和方法。

一、什么是反射?

想要知道反射机制,得先了解java的两个重要概念:

  • 编译期:是指把源码交给编译器编译成计算机可以执行的文件的过程。在Java中也就是把Java代码编成class文件的过程.编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。
  • 运行期:是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来,在Java中把磁盘中的代码放到内存中就是类加载过程,类加载是运行期的开始部分。
    而java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。简单的说就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。

二、反射的作用

通过反射可以使程序代码访问装载到JVM中的类的内部信息。

  1. 获取已装载类的成员变量信息
    
  2. 获取已装载类的方法
    
  3. 获取已装载类的构造方法信息
    

三、常用类及其常用方法

1.Class类

要想知道一个类的属性和方法,必须先获得到该类的字节码文件对象。那么就得使用Class类中的方法。
如何获取一个Class实例?有三种方法:

方法一:直接通过一个类的静态变量class获取
Class cls = String.Class;

方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取
String s = “Hello”
Class cls = s.getClass();

方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取
Class cls = Class.forName(“java.lang.String”);

注:因为Class实例在JVM中是唯一的,所以上述方法获取的Class实例是同一个实例。

1.1.Class类的常用方法

类型访问方法返回值类型说明
包路径getPackage()Packge对象获取该类的存放路径
类名称getName()String 对象获取该类的名称
继承类getSuperclass()Class 对象获取该类继承的类
实现接口getlnterfaces()Class 型数组获取该类实现的所有接口
构造方法getConstructors()Constructor 型数组获取所有权限为 public 的构造方法
构造方法getDeclaredContruectors()Constructor 对象获取当前对象的所有构造方法
方法getMethods()Methods 型数组获取所有权限为 public 的方法
方法getDeclaredMethods()Methods 对象 获取当前对象的所有方法
成员变量getFields()Field 型数组获取所有权限为 public 的成员变量
成员变量getDeclareFileds()Field 对象获取当前对象的所有成员变量

1.2.Class类的常用方法演示

为了方便演示我创建了两个类:

package com.xync.hja;

public class Father {
	public String fatherName;
	private int fatherAge;
	public void hello() {
		System.out.println("I am father!");
	}
	public Father() {
		super();
	}

	public Father(String fatherName, int fatherAge) {
		super();
		this.fatherName = fatherName;
		this.fatherAge = fatherAge;
	}

	@Override
	public String toString() {
		return "Father [fatherName=" + fatherName + ", fatherAge=" + fatherAge + "]";
	}
	
}
package com.xync.hja;

public class Son extends Father implements Serializable{
	public String sonName;
	public String sonAge;
	@Override
	public void hello() {
		System.out.println("I am son!");
	}
	public Son() {
		super();
	}

	public Son(String sonName, String sonAge) {
		super();
		this.sonName = sonName;
		this.sonAge = sonAge;
	}

	private Son(String sonName) {
		super();
		this.sonName = sonName;
	}
	public void sonHello(String s) {
	 	System.out.println(s);
	}

	private void privacyMethod() {
		System.out.println("PrivateMethod!");
	}
	@Override
	public String toString() {
		return "Son [sonName=" + sonName + ", sonAge=" + sonAge + "]";
	}

}
1.2.1.getPackage()
public class Test {
	public static void main(String[] args) {
		Class father = Father.class;
		Package fatherPackage = father.getPackage();
		System.out.println(fatherPackage);
	}
}

结果如图:
在这里插入图片描述

1.2.2.getName()
public class Test {
	public static void main(String[] args) {
		Class father = Father.class;
		String fatherName = father.getName();
		System.out.println("类的完全限定名是:" + fatherName);
	}
}

结果如图:
在这里插入图片描述

1.2.3.getSuperclass()
public class Test {
	public static void main(String[] args) {
		Class son = Son.class;
		Class superclass = son.getSuperclass();
		System.out.println("son的父类对象是"+superclass);
	}
}

结果如图:
在这里插入图片描述

1.2.4.getInterfaces()
public class Test {
	public static void main(String[] args) {
		Class son = Son.class;
		Class[] interfaces = son.getInterfaces();
		for (Class cls : interfaces) {
			System.out.println("son实现的接口是:" + cls);
		}
	}
}

结果如图:
在这里插入图片描述

1.2.5.getConstructors()和getDeclaredConstructors()
public class Test {
	public static void main(String[] args) {
		Class son = Son.class;
		Constructor[] constructors = son.getConstructors();
		for (Constructor constructor : constructors) {
			System.out.println(constructor);
		}
System.out.println("-----------------------------------------");
		Constructor[] declaredConstructors = son.getDeclaredConstructors();
		for (Constructor declaredConstructor : declaredConstructors) {
			System.out.println(declaredConstructor);
		}
	}
}

结果如图:
在这里插入图片描述
这里值得注意的是Son只带有一个参数的构造方法的访问修饰符为private,用getConstructors()是访问不到private修饰的构造方法的,而getDeclaredConstructors()可以

1.2.6.getMethods()和getDeclaredMethods()
public class Test {
	public static void main(String[] args) {
		Class son = Son.class;
		Method[] sonMethods = son.getMethods();
		Method[] sonDeclaredMethods = son.getDeclaredMethods();
		for (Method sonMethod : sonMethods) {
			System.out.println("sonMethod为:" + sonMethod.getName());
		}
		System.out.println("-----------------------------------------------");
		for (Method sonDeclaredMethod : sonDeclaredMethods) {
			System.out.println("sonMethod为:" + sonDeclaredMethod.getName());
		}
	}
}

结果如图:
在这里插入图片描述
这里值得注意的是,调用getMethods()方法时,不仅把自己类中public方法得到了,还将父类所有的public修饰的方法得到了,而使用getDeclaredMethods()只能得到自己类的所有方法,包括用private修饰符修饰的方法

1.2.7.getFields()和getDeclareFileds()
public class Test {
	public static void main(String[] args) {
		Class son = Son.class;
		Field[] sonFields = son.getFields();
		Field[] sonDclaredFields = son.getDeclaredFields();
		for (Field sonField : sonFields) {
			System.out.println("sonFields为:" + sonField);
		}
		System.out.println("------------------------------");
		for (Field sonDclaredField : sonDclaredFields) {
			System.out.println("sonFields为:" + sonDclaredField);
		}
	}
}

结果如图:
在这里插入图片描述
不难发现,本次getFields()方法中也将获得了父类的用public修饰的成员变量,而getDeclareFileds()方法只得到了自己的所有成员变量

2.Counstructor类

我们通常会使用new操作符创建新的实例:
Son son = new Son;
如果想通过反射来创建新的实例,可以调用Class提供的newInstance()方法
Son son = Son.Class.newInstance();
但使用Class.newInstance()是有局限的,它只能调用该类的public无参构造方法。

2.1.Counstructor类的常用方法

类型访问方法返回值类型说明
构造方法名称getName()String 对象获取该构造方法的名称
修饰符getModifiers()int获取该构造方法的访问修饰符(int)
参数getParamenterTypes()Class 型数组获取构造方法的参数类型
对象newInstance(Object…args)实例对象使用当前的构造方法来创建一个对象

2.2.Counstructor类的常用方法演示

2.2.1.newInstance(Object…args)()

刚才我们说到使用Class.newInstance()是无法调用非public修饰的构造方法有参的构造方法的,我们可以用Counstructor类的**newInstance(Object…args)**来调用

public class Test {
	public static void main(String[] args) throws Exception {

			Constructor<Father> constructor = Father.class.getConstructor(String.class);
			Father father = constructor.newInstance("father");
			System.out.println(father);
	
	}
}

结果如图:
在这里插入图片描述
注意,如果Father.class.getConstructor(参数类型)中传入的参数类型与 constructor.newInstance(参数类型)传入的参数类型不一致的话,会抛 java.lang.IllegalArgumentException异常

2.2.2.getName()
public class Test {
	public static void main(String[] args) {
		Class son = Son.class;
		Constructor[] constructors = son.getConstructors();
		for (Constructor constructor : constructors) {
			System.out.println("构造方法名为:" + constructor.getName());
		}
	}
}

结果如图:
在这里插入图片描述

2.2.3.getModifiers()
public class Test {
	public static void main(String[] args) {
		Class son = Son.class;
		Constructor[] sonDeclaredconstructors = son.getDeclaredConstructors();
		for (Constructor sonDeclaredconstructor : sonDeclaredconstructors) {
			System.out.println("构造方法名为:" + sonDeclaredconstructor.getModifiers());
		}
	}
}

结果如图:
在这里插入图片描述我们可以发现getModifiers()返回值的类型是int,我们并不好理解1或者2的意义,所以我们可以用Modifier工具类toString()方法

public class Test {
	public static void main(String[] args) {
		Class son = Son.class;
		Constructor[] sonDeclaredconstructors = son.getDeclaredConstructors();
		for (Constructor sonDeclaredconstructor : sonDeclaredconstructors) {
			System.out.println("构造方法名为:" + Modifier.toString(sonDeclaredconstructor.getModifiers()));
		}
	}
}

结果如图:
在这里插入图片描述

2.2.4.getParamenterTypes()
public class Test {
	public static void main(String[] args) throws Exception {

		Constructor<Father> constructor = Father.class.getConstructor(String.class);
		Class<?>[] parameterTypes = constructor.getParameterTypes();
		for (Class parameterType : parameterTypes) {
			System.out.println(parameterType.getName());
		}

	}
}

结果如图:
在这里插入图片描述

3.Field类

对于任意的一个Object实例,只要我们获取了它的Class,就可以获取它的一切信息,比如通过一个Class实例获取字段信息(成员变量)。

3.1.Field类的常用方法

类型访问方法返回值类型说明
字段名称getName()String 对象返回该Field对象所表示的字段的名称
字段类型getType()Class表示获取当前Field对象所表示字段的类型
修饰符getModifiers()int类型表示获取当前Field对象所表示字段的修饰符
对象get(Object obj)Object类型表示获取指定对象obj中当前Field对象所表示的字段的值
对象set(Object obj, Object value)Object类型返回void类型,表示将指定对象obj中当前Field对象所表示的字段的值设置为value

3.2.Field类的常用方法演示

3.2.1.getName()
public class Test {
	public static void main(String[] args) {
		Class cls = Father.class;
		Field[] fields = cls.getDeclaredFields();
		for (Field field : fields) {
			System.out.println(field.getName());
		}

	}
}

结果如图:
在这里插入图片描述

3.2.2.getType()
public class Test {
	public static void main(String[] args) {
		Class cls = Father.class;
		Field[] fields = cls.getDeclaredFields();
		for (Field field : fields) {
			System.out.println(field.getType());
		}

	}
}

结果如图:
在这里插入图片描述

3.2.3.getType()
public class Test {
	public static void main(String[] args) {
		Class cls = Father.class;
		Field[] fields = cls.getDeclaredFields();
		for (Field field : fields) {
			System.out.println(Modifier.toString(field.getModifiers()));
		}

	}
}

结果如图:
在这里插入图片描述

3.2.4.get(Object obj)
public class Test {
	public static void main(String[] args) throws Exception {
		Class cls = Father.class;
		Constructor constructor = cls.getConstructor(String.class);
		Father father = (Father) constructor.newInstance("Bob");
		Field field = cls.getField("fatherName");
		System.out.println(field.get(father));
	}
}

结果如图:
在这里插入图片描述
注意:get方法中的参数是要传入你想访问的字段所属类的对象

3.2.5set.(Object obj,Object value)
public class Test {
	public static void main(String[] args) Exception {
		Class cls = Father.class;
		Constructor constructor = cls.getConstructor();
		Father father = (Father) constructor.newInstance();
		Field field = cls.getField("fatherName");
		field.set(father, "Bob(我是通过set方法传入的值~)");
		System.out.println("fatherName为:" + field.get(father));
	}
}

结果如图:
在这里插入图片描述

4.Method类

Method类是Java语言中的一个重要类,它用于表示类或接口中的方法。

4.1.Method类的常用方法

类型访问方法返回值类型说明
方法名称getName()String 对象获取该方法对象的名称
返回值类型getReturnType()Class<?>[]表示获取方法返回类型
参数类型getParameterTypes()Class<?>[]表示获取方法参数类型数组
对象invoke(Object obj, Object… args)Object类型在指定对象上调用此方法

4.2.Method类的常用方法演示

4.2.1.getName()
public class Test {
	public static void main(String[] args) throws Exception {
		Class cls = Son.class;
		Method[] methods = cls.getDeclaredMethods();
		for (Method method : methods) {
			System.out.println(method.getName());
		}

	}
}

结果如图:
在这里插入图片描述

4.2.2.getReturnName()
public class Test {
	public static void main(String[] args) throws Exception {
		Class cls = Son.class;
		Method[] methods = cls.getDeclaredMethods();
		for (Method method : methods) {
			System.out.println(method.getName()+ "返回值类型是:" + method.getReturnType());
		}

	}
}

结果如图:
在这里插入图片描述

4.2.3.getParameterTypes()
public class Test {
	public static void main(String[] args) Exception {
		Class cls = Son.class;
		Method method = cls.getMethod("sonHello", String.class);
		Class[] parameterTypes = method.getParameterTypes();
		for (Class parameterType : parameterTypes) {
			System.out.println(parameterType.getName());
		}

	}
}

结果如图:
在这里插入图片描述

4.2.4.invoke(Object obj,Object… args)
4.2.4.1.调用方法
public class Test {
	public static void main(String[] args) throws Exception{
		Class cls = Son.class;
		Son father = (Son) cls.newInstance();
		Method method = cls.getMethod("sonHello", String.class);
		method.invoke(father, "通过invoke调用Son的方法");
	}
}

结果如图:
在这里插入图片描述

4.2.4.2.调用静态方法
public class Test {
	public static void main(String[] args) throws Exception {
		Method m = Integer.class.getMethod("parseInt", String.class);
		Integer n = (Integer) m.invoke(null, "12345");
		System.out.println(n);
	}
}

结果如图:
在这里插入图片描述
我们可以发现调用静态方法时,invoke(Object obj,Object… args)方法的第一个参数填null就可以调用静态方法

4.2.4.3.调用非public方法

如果想要使用非public方法,我们需要通过在Method.setAccessible(Boolean boolean)方法传入参数true才可以使用,否则会抛异常

public class Test {
	public static void main(String[] args) throws Exception {
		Son son = new Son();
		Method method = son.getClass().getDeclaredMethod("privacyMethod");
		method.setAccessible(true);
		method.invoke(son);
	}
}

结果如图:
在这里插入图片描述

4.2.4.4.多态

还有一个问题,在Father类和Son类中都一个hello方法,如果我通过一个父类的引用指向子类创建对象,那么invoke调用的方法执行的是父类的还是子类的呢?

public class Test {
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
			IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
		Father cls = new Son();
		Method method = cls.getClass().getMethod("hello");
		method.invoke(cls);
	}
}

结果如图:
在这里插入图片描述
我们可以发现最终执行的是子类的方法,因此,在使用反射调方法时,依然遵循这多态原则

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值