java之反射机制深度解析

反射是十分重要,要深入的学习和理解。反射可以称为是框架设计的灵魂,几乎所有的java框架中都可以看到反射的身影。那么

什么是反射 ?

反射是用来干嘛的 ?

反射该怎么实现呢 ?

目录

一、什么是反射?

1、Class类

1.1 获取Class类对象

2、通过反射获取类的属性方法等

3、通过反射创建对象、并获取方法等进行操作

二、反射是用来干嘛的 ?

三、反射该怎么实现呢 ?


一、什么是反射?

要理解什么是反射呢?首先要知道什么是动态语言,什么是静态语言?

动态语言:是在程序运行时可以改变其结构的语言。意思是说在运行时代码可以根据某些条件改变自身的结果,例如:C#,JavaScript,Python等;可以用外挂去模拟的理解,比如在玩绝地求生时,在游戏状态中,也就是程序运行状态下,依然可以使用外挂脚本更改游戏状态。

静态语言:是在运行时结构不可变的语言。例如:java,C,C++。

java不是动态语言,但是因为有了反射机制的存在,使得java有了类似动态语言的特性。

反射这一概念最早在1982年提出,主要指应用程序访问、检测、修改自身状态与行为的能力。在计算机科学领域,反射是指一类能够自我描述和自控制的应用。在Java编程语言中,反射是一种强有力的工具,是面向抽象编程的一种实现方式,它能使代码语句更加灵活,极大提高代码的运行时装配能力。

定义Java的反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

在这个定义中,可以提取到一个关键点:“在程序的运行状态中”,这也就代表,要想实现反射机制,就必须已经存在了一个类,而且必须要获取到该类的字节码文件。为什么这么说呢?这时我们要知道java代码在计算机中经历的阶段

java之反射机制深度解析_第1张图片

 通过上图可以看到,Class类对象至关重要,这个Class类包含了一个类的完整的结构信息。反射机制就是会在运行状态中,可以知道一个类的所有属性和方法,因此,反射机制和Class类对象必然密不可分。而且一个类有且仅有一个Class类对象。那么接下来就了解一下Class类。

1、Class类

首先要知道此Class,非彼class。小写的class是指在"public class A(){ .... }"的时候的关键字。而此处的Class,代表的是类的实例,表示正在运行的 Java 应用程序中的类和接口。也就是说任何一个类被加载时,即将类的.class文件(字节码文件)读入内存的同时,都自动为之创建一个java.lang.Class对象,Class类没有公共构造方法。Class类对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。这个类中提供了很多方法,以下是简单的分类介绍:

(1)获得类的相关方法
方法 描述
asSubclass(Class clazz) 把传递的类的对象转换成代表其子类的对象
getName() 获得类的完整路径名字
Cast 把对象转换成代表类或是接口的对象
getClassLoader() 获得类的加载器
getClasses() 返回一个数组,数组中包含该类中所有公共类和接口类的对象
getDeclaredClasses() 返回一个数组,数组中包含该类中所有类和接口类的对象
forName(String className) 根据类名返回类的对象
newInstance() 创建类的实例,(java 9 之后已被弃用)。
getPackage() 获得类的包
getSimpleName() 获得类的名字
getSuperclass() 获得当前类继承的父类的名字
getInterfaces() 获得当前类实现的类或是接口
(2)获得类中属性的相关方法
方法 描述
getField(String name) 获得某个公有的属性对象
getFields() 获得所有公有的属性对象
getDeclaredField(String name) 获得某个属性对象
getDeclaredFields() 获得所有属性对象
(3)获得类中方法的相关方法
方法 描述
getMethod(String name, Class... parameterTypes) 获得该类某个公有的方法
getMethods() 获得该类所有公有的方法
getDeclaredMethod(String name, Class... parameterTypes) 获得该类某个方法
getDeclaredMethods() 获得该类所有方法
(4)获得类中构造器的相关方法
方法 描述
getConstructor(Class... parameterTypes) 获得该类中与参数类型匹配的公有构造方法
getConstructors() 获得该类的所有公有构造方法
getDeclaredConstructor(Class... parameterTypes) 获得该类中与参数类型匹配的构造方法
getDeclaredConstructors() 获得该类所有构造方法
(5)获得类中注解的相关方法
方法 描述
getAnnotation(Class annotationClass) 返回该类中与参数类型匹配的公有注解对象
getAnnotations() 返回该类所有的公有注解对象
getDeclaredAnnotation(Class annotationClass) 返回该类中与参数类型匹配的所有注解对象
getDeclaredAnnotations() 返回该类所有的注解对象
(6)其他重要方法
方法 描述
isAnnotation() 如果是注解类型则返回true
isAnnotationPresent(Class annotationClass) 如果是指定类型注解类型则返回true
isAnonymousClass() 如果是匿名类则返回true
isArray() 如果是一个数组类则返回true
isEnum() 如果是枚举类则返回true
isInstance(Object obj) 如果obj是该类的实例则返回true
isInterface() 如果是接口类则返回true
isLocalClass() 如果是局部类则返回true
isMemberClass() 如果是内部类则返回true

还有一些方法没有列出,可以查看相关API文档。

说了Class类这么多东西,但是应该怎么去获取Class类对象呢?不获取肯定是不能用的,所以要想办法先获取到Class类对象。

1.1 获取Class类对象

获取类对象的方法一共有三种:

//1:已知一个类的全类名,且该类在类路径下,可调用Class类的静态方法forName()方法获取Class对象。
  Class.forName(String className);

//2:若已知具体的类,通过类的class属性获取,此方法安全性最高,程序性能最高。
   类.class(); 

//3:已知某个类的实例,调用该类的实例的getClass()方法获取Class方法。
   new 类().getClass();

示例:

public class ObtainClass {
	public static void main(String[] args) throws ClassNotFoundException {
		Person person = new Person();

		//方式1:
		Class c1 = Class.forName("Person");
		System.out.println("c1: "+c1);
		
		//方式2:
		Class c2 = Person.class;
		System.out.println("c2: "+c2);
		
		//方式3:
		Class c3 = person.getClass();
		System.out.println("c3: "+c3);	
	}
}

class Person {
	private String name;
	public int age;
	
	public Person(){//无参构造方法
		System.out.println("人");
	}
}

//输出
/*
人
c1: class Person
c2: class Person
c3: class Person
*/

获取到Class类对象之后,我们就可以使用Class类中方法对一个类进行操作了。我们可以获取类中的方法、属性、构造方法、注解等。其实这个过程,就是反射机制的部分了。一般情况下,我们正常new对象的过程是:

而,反射则是:

 接下来就用一个示例,了解一下反射的应用。

2、通过反射获取类的属性方法等

首先写一个类,类中添加属性和方法,还有构造方法,重写toString方法。

//被反射的类

public class Cat {
	private String name;
	public int age;
	public boolean Sex;
	
	private void behavior(int s) {
		System.out.println("捉老鼠"+s+"只");
	}
	
	public void eat() {
		System.out.println("吃东西");
	}
	
	@Override
	public String toString() {
		return "Cat [name=" + name + ", age=" + age + "]";
	}

	public Cat(){
		System.out.println("无参构造函数");
	}
	
	public Cat(String name , int age){
		System.out.println("有参构造函数");
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public boolean isSex() {
		return Sex;
	}

	public void setSex(boolean sex) {
		Sex = sex;
	}
}

然后通过反射获取Cat类中的属性、方法等。 

//反射类的操作
import java.lang.reflect.*;

public class ReflectTest {
	public static void main(String[] args) throws ClassNotFoundException{
		Cat c = new Cat();

		Class c1 = Class.forName("Cat");
		try {
			
			// 获取类中的属性
			System.out.println("#######获取类中的属性######");
			System.out.println("获取公有age属性对象");
			System.out.println(c1.getField("age"));// 获取公有age属性对象

			System.out.println("获取所有的属性对象");
			Field[] str = c1.getDeclaredFields();// 获取所有的属性对象
			for (Field n : str) {
				System.out.println(n);
			}

			// 获取类中的方法
			System.out.println("#######获取类中的方法######");
			System.out.println("获取eat公有方法");
			System.out.println(c1.getMethod("eat"));
			System.out.println("获取Cat类中的所有方法");
			Method[] me = c1.getDeclaredMethods();
			for (Method m : me) {
				System.out.println(m);
			}

			// 获取构造方法
			System.out.println("#######获取类中的构造方法######");
			System.out.println("获取类中公共类型的构造方法");
			System.out.println(c1.getConstructor());
			System.out.println("获取类中所有构造方法");
			Constructor[] co = c1.getConstructors();
			for (Constructor s : co) {
				System.out.println(s);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
/*输出结果:
无参构造函数
#######获取类中的属性######
获取公有age属性对象
public int Cat.age
获取所有的属性对象
private java.lang.String Cat.name
public int Cat.age
public boolean Cat.Sex
#######获取类中的方法######
获取eat公有方法
public void Cat.eat()
获取Cat类中的所有方法
public java.lang.String Cat.getName()
public java.lang.String Cat.toString()
public void Cat.setName(java.lang.String)
public void Cat.eat()
public boolean Cat.isSex()
public void Cat.setSex(boolean)
private void Cat.behavior(int)
public void Cat.setAge(int)
public int Cat.getAge()
#######获取类中的构造方法######
获取类中公共类型的构造方法
public Cat()
获取类中所有构造方法
public Cat(java.lang.String,int)
public Cat()
*/

3、通过反射创建对象、并获取方法等进行操作

上面对Cat类的反射操作只是获取,反射当然不止这些内容,前面所用的基本上都是Class类中的方法,java还提供了一个java.lang.reflect包,这个包中提供了许多强大功能。通过这个包,我们可以通过反射创建对象,并对获取的方法、属性进行操作。这个包中的方法请查阅相关API。

示例:

import java.lang.reflect.Method;

public class ReflectionTest02 {
	public static void main(String[] args) {
		Cat t = new Cat();
		Class c = t.getClass();
		try {
			//创建对象
			Object o = c.getConstructor().newInstance();//通过反射创建一个对象,本质是调用了Cat类的无参构造器
			//本质上创建的这个对象就是一个Cat对象
			System.out.println(o);
			
			Cat o1 = c.getConstructor(String.class,int.class).newInstance("小猫",3);//创建有参对象
			System.out.println(o1);
			
			//获取方法并操作
			Method m = c.getMethod("eat");
			m.invoke(o);//invoke方法是激活的意思,里面的参数是要操作的对象名和要输入的参数(无参的方法不用输入参数)
			Method m1 = c.getDeclaredMethod("behavior", int.class);//获取方法
			m1.setAccessible(true);//setAccessible(true)取消了Java的权限控制检查(注意不是改变方法或字段的访问权限),
			m1.invoke(o, 3);
			
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}

/*输出结果:
无参构造函数
无参构造函数
Cat [name=null, age=0]
有参构造函数
Cat [name=小猫, age=3]
吃东西
捉老鼠3只
*/

二、反射是用来干嘛的 ?

如果看完了第一部分,并有了一些理解,在第一部分就能看出反射的一些作用。不过呢,反射的绝大部分用途可不仅仅 是第一部分所列出的内容。真正体现反射的作用的在框架里。大概学习到Spring、JDBC等框架时,才能真正看到反射的强大。

三、反射该怎么实现呢 ?

看第一部分(手动狗头)

 

你可能感兴趣的:(JAVA,java)