JEE.反射

JEE.反射

  • 反射机制的概念
  • 反射的优缺点
  • 反射的本质
  • 反射的常用方法
  • 实例
    • 获取Class对象的方法
    • 反射实例化对象
    • 反射调用方法
    • 反射读写属性
    • BeanUtils

反射机制的概念

反射机制使在运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意个对象,都能够调用它的任意一个方法

在Java中,只要给定类的名字,就可以通过反射机制来获得类的所有信息

反射是Java语言中的一种机制,通过这种机制可以动态地实例化对象、读写属性、调用方法

反射也是很多框架开发的基础,比如hibernate,struts等框架

早在我们使用jdbc连接数据库的时候就已经使用到了Java的反射机制

Class.forName("com.mysql.jdbc.Driver.class");//加载MySql的驱动类

反射的优缺点

优点:

  • 能够在运行时动态获取类的实例,提高灵活性
  • 与动态编译结合

缺点:

  • 使用反射性能较低,需要解析字节码(.class文件),将内存中的对象进行解析
  • 相对不安全,因为它破坏了封装性(通过反射可以获得私有方法和属性)

解决方案:

  1. 通过setAccessible(true)关闭JDK的安全检查来提升反射速度
  2. 多次创建一个类的实例时,有缓存会快很多
  3. ReflectASM工具类,通过字节码生成的方式加快反射速度

反射的本质

拿Student举例(如下图),当Student被实例化时,实际是通过JVM(Java虚拟机)编译成.class文件,而反射机制就是在类被加载出来时,会生成一个Class对象,而这个被编译完成的.class文件生成的Student模型相当于是Class对象的子类(每一个class文件都有一个对应的Class模型,Class模型用于表示这个类的类型信息),并且Student类对于Class模型来说就是全透明的,包括private(私有化)属性和方法都是可以直接修改和调用的。

所以反射机制实际上是在被编译后的文件上动手脚

JEE.反射_第1张图片

反射的常用方法

组成部分 访问方法 返回值类型 说明
包路径 getPackage() Package对象 获得该类的存放路径
类名称 getName() String对象 获得该类的名称
继承类 getSuperclass() Class对象 获得该类继承的类
实现接口 getInterfaces() Class型数组 获得该类实现的所有接口
构造方法 getConstructors() Constructor型数组 获得所有权限为public的构造方法
getConstructor(Class…parameterTypes) Constructor对象 获得权限为public的指定构造方法
getDealaredConstructors() Constructor型数组 获得所有构造方法,按声明顺序返回
getDealaredConstructor(Class…parameterTypes) Constructor对象 获得指定的构造方法
方法 getMethods() Method型数组 获取所有权限为public的方法
getMethod(String name,Class…parametersTypes) Method对象 获得权限为public的指定方法
getDeclareMethods() Method型数组 获得所有方法,按声明顺序返回
getDeclareMethod(String name,Class…parameterTypes) Method对象 获得指定方法
成员变量 getFields() Field型数组 获得所有权限为public的成员变量
getField(Sgtring name) Field对象 获得权限为public的指定成员变量
getDeclaredFields() Field型数组 获得所有成员变量,按声明顺序返回
getDeclaredField(String name) Field对象 获得指定的成员变量
内部类 getClasses() Class型数组 获得所有权限为public的内部类
getDeclaredClasses() Class型数组 获得所有内部类
内部类的声明类 getDeclaringClass() Class对象 如果该类为内部类,则返回它的成员类,否则返回null

**注意:**通过getFileds()和getMethods()方法依次获得权限为public的成员变量和方法时,将包含从超类(父类)中继承到的成员变量和方法;而通过getDeclaredFields()和getDeclaredMethods()只能获得在本类中定义的所有成员变量和方法

实例

获取Class对象的方法

先创建一个测试用的Student类

package com.demo;

public class Student {

	private String sid;

	private String sname;

	public Integer age;

	static {
		System.out.println("加载进jvm中!");
	}

	public Student() {
		super();
		System.out.println("调用无参构造方法创建了一个学生对象");
	}

	public Student(String sid) {
		super();
		this.sid = sid;
		System.out.println("调用带一个参数的构造方法创建了一个学生对象");
	}

	public Student(String sid, String sname) {
		super();
		this.sid = sid;
		this.sname = sname;
		System.out.println("调用带二个参数的构造方法创建了一个学生对象");
	}

	@SuppressWarnings("unused")
	private Student(Integer age) {
		System.out.println("调用Student类私有的构造方法创建一个学生对象");
		this.age = age;
	}

	public String getSid() {
		return sid;
	}

	public void setSid(String sid) {
		this.sid = sid;
	}

	public String getSname() {
		return sname;
	}

	public void setSname(String sname) {
		this.sname = sname;
	}

	public void hello() {
		System.out.println("你好!我是" + this.sname);
	}

	public void hello(String name) {
		System.out.println(name + "你好!我是" + this.sname);
	}

	@SuppressWarnings("unused")
	private Integer add(Integer a, Integer b) {
		return new Integer(a.intValue() + b.intValue());
	}

	@Override
	public String toString() {
		return "Student [sid=" + sid + ", sname=" + sname + ", age=" + age + "]";
	}
	
}

获取Class对象的三种常用方法

//1.Class.forName("类的权限命名") 获取Class,需要抛出异常或者套用try catch
Class<Student> clazz =(Class<Student>)Class.forName("com.demo.Student");
	
//2. 类.class
Class clazz02 = Student.class;
		
//3. 对象.getClass()
Student stu = new Student();
Class clazz03 = stu.getClass();

反射实例化对象

package com.demo;

import java.lang.reflect.Constructor;

public class Demo {

	public static void main(String[] args) throws Exception{
		Class<Student> clazz =(Class<Student>)Class.forName("com.demo.Student");
		//获取无参构造,Constructor:构造函数
		Constructor<Student> c1 = clazz.getConstructor();
		//实例学生对象
		Student stu01 = c1.newInstance();
		stu01.setSname("小明");
		System.out.println(stu01);
					
		//获取有参构造
		Constructor<Student> c2 = clazz.getConstructor(String.class);
		Student stu02 = c2.newInstance("1");
		stu02.setSname("小黑");
		System.out.println(stu02);
					
		//获取有参构造
		Constructor<Student> c3 = clazz.getConstructor(String.class,String.class);
		Student stu03 = c3.newInstance("2", "小哈");
		System.out.println(stu03);
					
		//获取私有化的有参构造:Declared
		Constructor<Student> c4 = clazz.getDeclaredConstructor(Integer.class);
		 //允许访问,Accessible访问控制符
		c4.setAccessible(true);
		Student stu04 = c4.newInstance(22);
		System.out.println(stu04);
	}
	
}

输出结果:

JEE.反射_第2张图片

在反射获取构造函数中判断参数个数的方法:使用可变参数(如果前面还有其他参数,就必须放在最后),查看getConstructor源码即可发现:

JEE.反射_第3张图片

反射调用方法

//导包
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

//获取私有化的有参构造:Declared
	Constructor<Student> c4 = clazz.getDeclaredConstructor(Integer.class);
	//允许访问,Accessible访问控制符
	c4.setAccessible(true);
	Student stu04 = c4.newInstance(22);
	System.out.println(stu04);


Method method = clazz.getMethod("hello");
	stu04.setSname("hello");
	//调用方法
	method.invoke(stu04);
					
	Method method02 = clazz.getMethod("hello",String.class);
	method02.invoke(stu04, "world");
					
//获取私有方法
Method method03 = clazz.getDeclaredMethod("add",Integer.class, Integer.class);
	method03.setAccessible(true);
	int rv = (int)method03.invoke(stu04, 1,1);
	System.out.println(rv);

运行结果:

JEE.反射_第4张图片

反射读写属性

//导包
import java.lang.reflect.Field;

//获取私有化的有参构造:Declared
Constructor<Student> c4 = clazz.getDeclaredConstructor(Integer.class);
	//允许访问,Accessible访问控制符
	c4.setAccessible(true);
	Student stu04 = c4.newInstance(22);
	System.out.println(stu04);
		
	//设置私有属性
	Field f = clazz.getField("age");
	f.set(stu04, 78);
	System.out.println(stu04);
	System.out.println(f.get(stu04));
					
	//获取私有属性
	Field f01 = clazz.getDeclaredField("sname");
	f01.setAccessible(true);
	//调用属性
	f01.set(stu04, "hello world");
	System.out.println(stu04);

输出结果:

JEE.反射_第5张图片

BeanUtils

除了上述获取属性的方法之外,还可以通过BeanUtil.getproperty(对象名,“属性”)的方法获取属性,其原理是先调用类中的公开get属性方法获取再通过反射机制进行获取

上面和JavaBean的封装和设计有关:一个对象类中私有属性,公开get(得到值)、set(注入值)方法的意义是提供统一控制获取和处理类中私有属性的方法入口

你可能感兴趣的:(笔记,java,jvm,开发语言)