Java 反射

Java 反射

文章目录

  • Java 反射
    • @[toc]
  • 1、什么是反射
  • 2、反射的原理
  • 3、反射的优缺点
  • 4、反射的用途
  • 5、反射技术常用API
  • 6、反射技术使用步骤
  • 7、反射的入口类Class
  • 8、反射实现类的实例化
  • 9、反射访问实例的字段
  • 10、反射调用实例的方法
  • 11、反射总结

1、什么是反射

​ Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

​ Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。


2、反射的原理

下图是类的正常加载过程、反射原理与class对象:

Class对象的由来是将.class文件读入内存,并为之创建一个Class对象。

Java 反射_第1张图片


3、反射的优缺点

优点

  1. 在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

  2. 允许程序创建和控制任何类的对象,无需提前硬编码目标类

  3. 提高了Java程序的灵活性和扩展性,降低了耦合性,提高自适应能力

  4. 反射的应用领域 (开源框架,如MyBatis、Spring等)

缺点

  1. 性能问题

​ 反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射

​ 反射机制主要应用在对灵活性扩展性要求很高的系统框架上

  1. 代码维护问题

​ 反射会模糊程序内部逻辑,可读性较差.

​ 反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题


4、反射的用途

  1. 反编译:.class–>.java

  2. 通过反射机制访问java对象的属性,方法,构造方法等

  3. 当我们在使用IDEA,比如Ecplise时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。

  4. 反射最重要的用途就是开发各种通用框架。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。


5、反射技术常用API

反射常用的Java类型

java.lang.Class<T>              //可获取类和类的成员信息 
java.lang.reflect.Constructor<T>//可调用类的构造方法
java.lang.reflect.Field         //可访问类的属性  
java.lang.reflect.Method        //可调用类的方法 

6、反射技术使用步骤

  1. 导入 java.lang.reflect.*
  2. 获得需要操作的类的java.lang.Class对象
  3. 调用Class的方法获取Field、Method等对象
  4. 使用反射API操作实例成员

7、反射的入口类Class

Class类是Java反射机制的起源和入口

  • 每个类都有自己向关的Class对象
  • 提供了获取类信息的相关方法

Class类存放类的结构信息

  • 类名
  • 父类﹑接口
  • 构造方法﹑方法﹑属性
  • 注解
  • ……

获取Class实例常用方式

//方法1:对象.getClass()
Student stu = new Student();
Class clazz = stu.getClass();			
//方法2:类.class
Class clazz = Student.class;	
//方法3:Class.forName()
Class clazz = Class.forName("xxx.xxx.Student");

获取类型的基本信息的常用方法:

Java 反射_第2张图片


定一个BaseClass为父类

package reflect.entity;

public class BaseClass {

}

定一个Person类并继承BaseClass父类实现Serializable接口

package reflect.entity;

import java.io.Serializable;
import java.io.IOException;

public final class Person extends BaseClass implements Serializable {

	// 成员变量
	private String name;
	static final int age = 30;
	protected String address;
	public String message;

	// 成员方法
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	static final int getAge() {
		return age;
	}
	protected String getAddress() {
		return address;
	}
	private void silentMethod() throws IOException, NullPointerException {
		System.out.println("这是悄悄话");
	}

	/*[以下声明用于通过反射获取构造方法信息测试]*/
    //无参构造函数
	public Person() { }
    //有参构造函数
	private Person(String name) {
		this.name = name;
	}

    protected Person(String name, String address, String message) {
		this.name = name;
		this.address = address;
		this.message = message;
	}
    
	@Override
	public String toString() {
		return "{name:" + name + ", age:" + age + ", address:" + address
				+ ", message:" + message + "}";
	}
}

通过反射获取类的信息示例:

package reflect.classinfo;

import java.lang.reflect.Modifier;

import reflect.entity.Person;

/**
 * 获取类型的基本信息
*/
public class GetClassInfo {
	
	public static void main(String[] args) {
		Class clz = Person.class;
		String fullName = clz.getName();
		String simpleName = clz.getSimpleName();
		System.out.println("以下是 " + fullName + " 类的基本信息");

		// 获取Person类所在的包
		Package pkg = clz.getPackage();

		// 获得此对象所表示的实体(类、接口、基本类型或 void)的超类的 Class
		// 如果此对象表示 Object 类、一个接口、一个基本类型或 void,则返回 null
		// 如果此对象表示一个数组类,则返回表示该 Object 类的 Class 对象
		Class superClass = clz.getSuperclass();
		System.out.println(simpleName + " 类的超类是:" + superClass.getName());

		// 获得此对象所表示的类或接口实现的接口
		// 如果此对象表示一个不实现任何接口的类或接口,则此方法返回一个长度为 0 的数组。
		// 如果此对象表示一个基本类型或 void,则此方法返回一个长度为 0 的数组。
		Class[] interfaces = clz.getInterfaces();

		//getModifiers()方法返回int类型值表示该字段的修饰符。其中,该修饰符是java.lang.reflect.Modifier的静态属性
		int modifier = clz.getModifiers();
	}
}

获取构造方法信息的常用方法:

Java 反射_第3张图片

获取构造方法信息示例代码:

package reflect.classinfo;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;

import reflect.entity.Person;

/**
 * 获取构造方法信息
*/
public class GetClassConstructorsInfo {

	public static void main(String[] args) {

		// 获取Person类声明的所有构造方法
		// 它们是公共、保护、默认(包)访问和私有构造方法
		// 如果此 Class 实例表示一个接口、一个基本类型、一个数组类或 void,则此方法返回一个长度为 0 的数组
		Constructor[] cons = Person.class.getDeclaredConstructors();

		// 构造方法的一些信息
		System.out.println("=========构造方法展示=========");
		for (Constructor con : cons) {
			System.out.print("访问修饰符:");
			int modifier = con.getModifiers();
			// 判断该构造方法的访问修饰符
			if ((modifier & Modifier.PUBLIC) == Modifier.PUBLIC)
				System.out.println("public");
			else if ((modifier & Modifier.PROTECTED) == Modifier.PROTECTED)
				System.out.println("protected");
			else if ((modifier & Modifier.PRIVATE) == Modifier.PRIVATE)
				System.out.println("private");
			else System.out.println("default(package)");
            
			// 获取构造方法的参数列表
			Class[] params = con.getParameterTypes();
			if (params.length == 0) {
				System.out.println("该构造方法没有参数");
			} else {
				System.out.print("该构造方法的参数列表为:[");
				for (int i = 0; i < params.length; i++) {
					if (i != 0)System.out.print(", ");
					System.out.print(params[i].getName());
				}
				System.out.println("]");
			}
		}
	}
}

获取属性信息的常用方法:

Java 反射_第4张图片

获取属性信息示例:

package reflect.classinfo;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import reflect.entity.Person;

/**
 * 获取属性信息
 */
public class GetClassFieldsInfo {

	public static void main(String[] args) {

		// 获取Person中的所有属性,
		// 包括公共、保护、默认(包)访问和私有属性,但不包括继承的属性,
		// 如果该类或接口不声明任何属性,或者此 Class 实例表示一个基本类型、一个数组或 void,则此方法返回一个长度为 0 的数组。
		Field[] fields = Person.class.getDeclaredFields();

		// 展示属性的一些信息
		System.out.println("===========属性展示==========");
		for (Field field : fields) {
			System.out.println("属性名:" + field.getName());
			System.out.println("类型:" + field.getType().getName());

			System.out.print("访问修饰符:");
			int modifier = field.getModifiers();
			// 判断该属性的访问修饰符
			if ((modifier & Modifier.PUBLIC) == Modifier.PUBLIC)
				System.out.println("public");
			else if ((modifier & Modifier.PROTECTED) == Modifier.PROTECTED)
				System.out.println("protected");
			else if ((modifier & Modifier.PRIVATE) == Modifier.PRIVATE)
				System.out.println("private");
			else
				System.out.println("default(package)");

			// 判断该属性是否有static修饰符
			if ((modifier & Modifier.STATIC) == Modifier.STATIC)
				System.out.println("这是一个静态属性");
			// 判断该属性是否有final修饰符
			if ((modifier & Modifier.FINAL) == Modifier.FINAL)
				System.out.println("这是一个final属性");
		}
	}
}

获取方法信息的常用方法:

Java 反射_第5张图片

获取方法信息示例:

package reflect.classinfo;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import reflect.entity.Person;

/**
 * 获取方法信息
 */
public class GetClassMethodsInfo {
	
	public static void main(String[] args) {

		// 获取Person中的所有方法,
		// 包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法,
		// 如果该类或接口不声明任何方法,或者此 Class 实例表示一个基本类型、一个数组或 void,则此方法返回一个长度为 0 的数组。
		Method[] methods = Person.class.getDeclaredMethods();

		// 展示方法的一些信息
		System.out.println("===========方法展示==========");
		for (Method method : methods) {
			System.out.println("方法名:" + method.getName());
			System.out.println("返回值类型:" + method.getReturnType().getName());

			// 获取方法的参数列表
			Class[] params = method.getParameterTypes();
			if (params.length == 0) {
				System.out.println("该方法没有参数");
			} else {
				System.out.print("该方法的参数列表为:[");
				for (int i = 0; i < params.length; i++) {
					if (i != 0)
						System.out.print(", ");
					System.out.print(params[i].getName());
				}
				System.out.println("]");
			}

			System.out.print("访问修饰符:");
			int modifier = method.getModifiers();
			// 判断该方法的访问修饰符
			if ((modifier & Modifier.PUBLIC) == Modifier.PUBLIC)
				System.out.println("public");
			else if ((modifier & Modifier.PROTECTED) == Modifier.PROTECTED)
				System.out.println("protected");
			else if ((modifier & Modifier.PRIVATE) == Modifier.PRIVATE)
				System.out.println("private");
			else
				System.out.println("default(package)");

			// 判断该方法是否有static修饰符
			if ((modifier & Modifier.STATIC) == Modifier.STATIC)
				System.out.println("这是一个静态方法");
			// 判断该方法是否有final修饰符
			if ((modifier & Modifier.FINAL) == Modifier.FINAL)
				System.out.println("这是一个final方法");
			// 判断该方法是否有abstract修饰符
			if ((modifier & Modifier.ABSTRACT) == Modifier.ABSTRACT)
				System.out.println("这是一个抽象方法");
			// 判断该方法是否有synchronized修饰符
			if ((modifier & Modifier.SYNCHRONIZED) == Modifier.SYNCHRONIZED)
				System.out.println("这是一个同步方法");

			// 获取方法所属的类或接口的Class实例
			Class declaringClass = method.getDeclaringClass();
			System.out.println("方法声明在:" + declaringClass.getName() + " 中");
			
			// 获取方法抛出的异常类型,即throws子句中声明的异常
			Class[] exceptions = method.getExceptionTypes();
			if (exceptions.length > 0) {
				System.out.print("该方法抛出的异常有:[");
				for (int i = 0; i < exceptions.length; i++) {
					if (i != 0)
						System.out.print(", ");
					System.out.print(exceptions[i].getName());
				}
				System.out.println("]");
			}
			System.out.println("----------------------------");
		}
	}
}

8、反射实现类的实例化

java.lang.Class
public T newInstance()

//关键代码:
Class clazz = Class.forName("reflect.entity.Person");
Object obj = clazz.newInstance();				

java.lang.reflect.Constructor
public T newInstance(Object… initargs)

Constructor cons = clazz.getDeclaredConstructor(String.class);
cons.setAccessible(true);
Object obj = cons.newInstance("New Person");
package reflect.access;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class AccessPersonConstructors {
  // 测试反射调用构造方法  
  public static void main(String[] args) {
     
     try {
         Class clz = Class.forName("reflect.entity.Person");
         Object obj = clz.newInstance();
         System.out.println(obj);

         // 获取Person的无参构造
         Constructor c1 = clz.getDeclaredConstructor();
         // Person的无参构造为public,这里可以直接访问
         obj = c1.newInstance();
         System.out.println(obj);

         // 获取Person的单参构造
         Constructor c2 = clz.getDeclaredConstructor(String.class);
         // Person的单参构造为private,这里已超出其访问范围,不能直接访问
         // 通过setAccessable方法,设定为可以访问
         c2.setAccessible(true);
         obj = c2.newInstance("New Person");
         System.out.println(obj);

         // 获取Person的三参构造
         Constructor c3 = clz.getDeclaredConstructor(String.class,
                 String.class, String.class);
         // Person的三参构造为protected,这里已超出其访问范围,不能直接访问
         // 通过setAccessable方法,设定为可以访问
         c3.setAccessible(true);
         obj = c3.newInstance("New Person", "beijing", "Hello!");
         System.out.println(obj);
     } catch (ClassNotFoundException e) {
         e.printStackTrace();
     } catch (SecurityException e) {
         e.printStackTrace();
     } catch (NoSuchMethodException e) {
         e.printStackTrace();
     } catch (IllegalArgumentException e) {
         e.printStackTrace();
     } catch (InstantiationException e) {
         e.printStackTrace();
     } catch (IllegalAccessException e) {
         e.printStackTrace();
     } catch (InvocationTargetException e) {
         e.printStackTrace();
     }
  }
}

9、反射访问实例的字段

java.lang.reflect.Field

Java 反射_第6张图片

package reflect.access;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import reflect.entity.Person;

/**
 * 通过反射方式访问属性
*/
public class AccessPersonFields {
	// 通过反射操作Person的属性
	public static void main(String[] args) {
		try {
			// 通过反射加载一个Person实例
			Class cls = Class.forName("reflect.entity.Person");
			Object person = cls.newInstance();
			// 获取name属性
			Field name = cls.getDeclaredField("name");
			// name属性为private,这里已超出其访问范围,不能直接访问
			// 通过setAccessable方法,设定为可以访问
			name.setAccessible(true);
			// 先取值看一下
			System.out.println("赋值前的name:" + name.get(person));
			// 为name属性赋值
			name.set(person, "New Person");
			// 展示一下赋值效果
			System.out.println("赋值后的name:" + name.get(person));
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		}
	}
}

10、反射调用实例的方法

java.lang.reflect.Method

public  Object  invoke( Object obj, Object...args)
package reflect.access;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import reflect.entity.Person;
/**
 * 反射调用实例的方法
 */
public class AccessPersonMethods {

	public static void main(String[] args) {
		//反射操作Person的方法
		try {
			//通过反射加载Person类
			Class clz = Class.forName("reflect.entity.Person");

			//根据方法名和参数列表获取static final int getAge()方法,无参可以不写或用null表示
			Method getAge = clz.getDeclaredMethod("getAge", null);
			//getAge方法为default(package),这里已超出其访问范围,不能直接访问
			//通过setAccessable方法,设定为可以访问
			getAge.setAccessible(true);
			//调用getAge方法并传参,没有参数可以不写或用null表示
			//getAge方法为静态方法,调用时可以不指定具体Person实例
			Object returnAge = getAge.invoke(null, null);
			System.out.println("年龄是:" + returnAge);

			Object person = clz.newInstance(); //创建Person实例

			//根据方法名和参数列表获取private void silentMethod()方法
			Method silentMethod = clz.getDeclaredMethod("silentMethod", null);
			//silentMethod方法为private,这里已超出其访问范围,不能直接访问
			//通过setAccessable方法,设定为可以访问
			silentMethod.setAccessible(true);
			//调用silentMethod方法并传参,没有参数可以不写或用null表示
			silentMethod.invoke(person, null);

			//根据方法名和参数列表获取public void setName(String)方法
			Method setName = clz.getDeclaredMethod("setName", String.class);
			//setName方法为public,这里可以直接访问
			//调用setName方法并传参
			setName.invoke(person, "New Person");
			//验证一下结果,调用public String getName()方法得到name的值
			Object returnName = clz.getDeclaredMethod("getName").invoke(person);
			System.out.println("刚才设定的name是:" + returnName);

		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
}

11、反射总结

Java 反射_第7张图片

你可能感兴趣的:(Java高阶,java,java-ee)