JavaSE学习笔记——反射

Day14

  • 反射
    • Java Reflection
    • Java反射机制提供的功能
    • Java反射机制研究及应用
    • 一、Class类
      • 概念
      • Class类的常用方法
      • 实例化Class类对象的四种方法
    • 二、通过反射调用类的完整结构
      • 1. 继承的父类
      • 2. 实现的全部接口
      • 3. 全部的构造器
      • 代码
      • 4. 全部的属性
      • 5. 全部的方法
      • 6. 类所在的包
      • 代码
    • 三、通过反射调用类中的指定方法、指定属性
      • 调用指定方法
      • 调用指定属性
    • Java动态代理

反射

Java Reflection

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

Java反射机制提供的功能

  • 在运行时判断任意一个对象所属的类。
  • 在运行时构造任意一个类的对象。
  • 在运行时判断任意一个类所具有的成员变量和方法。
  • 在运行时调用任意一个对象的成员变量和方法。
  • 生成动态代理。

Java反射机制研究及应用

反射相关的主要API:

  • java.lang.Class:代表一个类
  • java.lang.reflect.Constructor:代表类的构造方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Method:代表类的方法

一、Class类

概念

  • 在Object类中定义了以下的方法,此方法将被所有子类继承:
    public final Class getClass()
    该方法返回值的类型是一个Class类,此类是Java反射的源头, 实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象获得类的名称。
package reflection;

public class TestClass {
	public static void main(String[] args) {
		Person p = new Person();
		Class cla = p.getClass();
//		Class cla = p.getClass();
	}
}

反射可以得到的信息包括:某个类的构造器、属性、方法、实现的接口。 对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。

  • Class本身也是一个类。
  • Class对象只能由系统建立。
  • 一个类在 JVM 中只会有一个Class实例。
  • 一个Class对象对应的是一个加载到JVM中的一个 .class文件
  • 每个类的实例都会记得自己是由哪个 Class 实例所生成的。
  • 通过Class可以完整地得到一个类的完整结构。

Class类的常用方法

方法 意义
static Class forName(String name) 根据类的全名(包名+类名)获取Class对象
Object newInstance() 创建目标类的对象
getName() 获取类的全名(包名+类名)
Class getSuperclass() 获取所有父类的Class对象
Class[ ] getInterfaces() 获取所有实现的接口
ClassLoader getClassLoader() 获取类的类加载器
Constructor[ ] getConstructors() 获取类的所有构造器
Field[ ] getDeclaredFields() 获取类的所有属性
Method getMethod(String name, Class… paramTypes) 获取类的指定方法
  • 注意:newInstance()方法,jdk9之后建议通过构造器调用,格式如:(clazz是一个Class对象)
    clazz.getConstructor().newInstance();

实例化Class类对象的四种方法

  • 若已知具体的类,通过类的class属性获取。
    例:Class clazz = String.class;
  • 已知某个类的实例,调用该实例的getClass()方法获取Class对象。
    例:Class clazz = “123”.getClass();
  • 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName() 获取,可能抛出ClassNotFoundException
    例:Class clazz = Class.forName(“java.lang.String”);
  • 其他方式(不做要求)。
    ClassLoader cl = this.getClass().getClassLoader();
    Class clazz4 = cl.loadClass(“类的全类名”);

一般常用forName()获取Class对象。

package reflection;

public class TestClass {
	public static void main(String[] args) {
		Person p = new Person();
		//第一种:已知具体的类
		Class c1 = Person.class;
		//第二种:已知类的实例
		Class c2 = p.getClass();
		//第三种:已知类的全名(包名+类名)
		try {
			Class c3 = Class.forName("reflection.Person");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

二、通过反射调用类的完整结构

1. 继承的父类

  • public Class getSuperclass()
    返回此 Class 对象所表示的类(接口、基本类型)的父类的 Class对象。

2. 实现的全部接口

  • public Class[ ] getInterfaces()
    返回此 Class 对象所表示的类或接口实现的全部接口。

3. 全部的构造器

  • public Constructor[ ] getConstructors()
    返回此 Class 对象所表示的类的所有public构造方法。
  • public Constructor[ ] getDeclaredConstructors()
    返回此 Class 对象表示的类声明的所有(包括私有)构造方法。
  • public Constructor get(Declared)Constructor(Class… paramType)
    返回指定参数的构造器,若参数列表为空,则返回无参构造器。

注意:
1.调用私有构造器、属性、方法后,要用 setAccessible(true); 解除private限制。
2.缺省、私有、受保护修饰的都要使用Declared。

Constructor类中:

  • 获取构造器修饰符:public int getModifiers()
  • 获取构造器方法名称:public String getName()
  • 获取构造器参数类型:public Class[ ] getParameterTypes()

代码

1.上述功能代码实现
JavaSE学习笔记——反射_第1张图片

package reflection;

public class Person {
	String name;
	int age;
	
	public Person() {}
	
	public Person(String name) {
		this.name = name;
	}
	
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
}
package reflection;

public interface Move {
	void move();
}

package reflection;

public interface Work {
	void work();
}
package reflection;

public class Student extends Person implements Move, Work{
	String school;
	
	public Student() {}
	
	@SuppressWarnings("unused")
	private Student(String name, String school) {
		super(name);
		this.school = school;
	}
	
	public Student(String name, int age, String school) {
		super(name, age);
		this.school = school;
	}
	
	@Override
	public void work() {
		System.out.println("Study");	
	}

	@Override
	public void move() {
		System.out.println("By bus");
	}
	
	@SuppressWarnings("unused")
	private void printInfo(int i, int s) {
		System.out.println(i + s);
	}
	
	public void showInfo(String str, int i) {
		System.out.println(str + i);
	}

	public String returnInfo(String str, int i, Student stu) {
		return str + i + stu.name;
	}

}
package reflection;

import java.lang.reflect.Constructor;

public class TestClass {
	public static void main(String[] args) {
		try {
			Class cla = Class.forName("reflection.Student");//获取Class对象
			Class superCla = cla.getSuperclass();//获取全部的父类Class对象
			System.out.println("父类:" + superCla.getName());//父类:reflection.Person
			
			Class[] impleInter = cla.getInterfaces();
			for(Class c : impleInter) {
				System.out.println("实现的接口:" + c.getName());//实现的接口:reflection.Move
			}                                                   //实现的接口:reflection.Work
			
			Constructor[] con1 = cla.getConstructors();//获取public构造器
			for(Constructor c : con1) {
				System.out.println("构造器名称:" + c.getName() + "\t修饰符:" + c.getModifiers());
			}//构造器名称:reflection.Student	修饰符:1
			
			Constructor[] con2 = cla.getDeclaredConstructors();//获取所有构造器
			for(Constructor c : con2) {
				System.out.println("构造器名称:" + c.getName() + "\t修饰符:" + c.getModifiers());
				//构造器名称:reflection.Student	修饰符:1	//构造器名称:reflection.Student	修饰符:2
				Class[] clazz = c.getParameterTypes();//获取构造器参数类型
				for(Class c1 : clazz) {
					System.out.println("参数类型:" + c1.getName());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

2.使用反射机制创建对象

package reflection;

import java.lang.reflect.Constructor;
/**
 * 使用反射机制创建对象
 * 无参构造器
 * @author MCC
 *
 */
public class TestClass {
	public static void main(String[] args) {
		try {
			Class<?> clazz = Class.forName("reflection.Student");
			Constructor<?> con = clazz.getConstructor();//调用无参构造器
			Object obj = con.newInstance();//无参构造器新建对象 ,不能传入参数
			Student stu = (Student)obj;
			stu.name = "XiaoMing";
			stu.age = 21;
			stu.school = "阳光中学";
			System.out.println(stu.name + " " + stu.age + " " + stu.school);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
package reflection;

import java.lang.reflect.Constructor;
/**
 * 使用反射机制创建对象
 * 有参构造器
 * @author MCC
 *
 */
public class TestClass {
	public static void main(String[] args) {
		try {
			Class<?> clazz = Class.forName("reflection.Student");
			Constructor<?> con = clazz.getConstructor(String.class, int.class, String.class);//调用三个参数构造器
			Object obj = con.newInstance("XiaoMing", 21, "阳光中学");//使用上述构造器新建对象并赋值,若不赋值则初始化为默认值
			Student stu = (Student)obj;
			System.out.println(stu.name + " " + stu.age + " " + stu.school);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
package reflection;

import java.lang.reflect.Constructor;
/**
 * 使用反射机制创建对象
 * 私有构造器
 * @author MCC
 *
 */
public class TestClass {
	public static void main(String[] args) {
		try {
			Class<?> clazz = Class.forName("reflection.Student");
			Constructor<?> con = clazz.getDeclaredConstructor(String.class, String.class);//调用两个参数的私有构造器
			con.setAccessible(true);//解除private限制
			Object obj = con.newInstance("XiaoMing", "阳光中学");
			Student stu = (Student)obj;
			System.out.println(stu.name + " " + stu.age + " " + stu.school);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

4. 全部的属性

  • public Field[ ] getFields()
    返回此Class对象所表示的类或接口的public的Field。
  • public Field[ ] getDeclaredFields()
    返回此Class对象所表示的类或接口的全部Field。
  • public Field get(Declared)Field(String name)
    返回类或接口的指定Field。

Field类中:

  • 获取Field修饰符:public int getModifiers()
  • 获取Field名称:public String getName()
  • 获取Filed类型:public Class getType()

5. 全部的方法

  • public Method[ ] getMethods()
    返回此Class对象所表示的类或接口的public的方法。
  • public Method[ ] getDeclaredMethods()
    返回此Class对象所表示的类或接口的全部方法。
  • public Method get(Declared)Method(String name, Class… paramType)
    返回类或接口的指定方法。

Method类中:

  • 获取方法修饰符:public int getModifiers(),返回值为1,代表public;返回值为2,代表private
  • 获取方法名称:public String getName()
  • 获取方法参数类型:public Class[ ] getParameterTypes()
  • 获取方法返回值类型:public Class getReturnType()

6. 类所在的包

  • Package getPackage()
    返回此Class对象所表示的类或接口所在的包。

代码

package reflection;

import java.lang.reflect.*;

public class TestClass {
	public static void main(String[] args) {		
	try {
		Class<?> clazz = Class.forName("reflection.Student");
		//获取全部属性
//		Field[] fd = clazz.getFields();
		Field[] fd = clazz.getDeclaredFields();
		for(Field f : fd) {
			System.out.println("名称:" + f.getName() + "修饰符:" + f.getModifiers() + "类型:" + f.getType());
		}
		//获取全部方法
		Method[] me = clazz.getMethods();
		for(Method m : me) {
			System.out.println("名称:" + m.getName() + "修饰符:" + m.getModifiers() + "返回值类型:" + m.getReturnType());
			Class<?>[] ms = m.getParameterTypes();
//			if(ms != null && ms.length > 0) {//不输出空参数
				for(Class<?> mc : ms) {
					System.out.println("参数类型:" + mc.getName());
				}
//			}
		}
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
		}
	}
}
package reflection;

import java.lang.reflect.*;
		try {
			Class<?> clazz = Class.forName("reflection.Student");
//			System.out.println(clazz.getPackageName());
			Package pac = clazz.getPackage();
			System.out.println(pac.getName());
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

三、通过反射调用类中的指定方法、指定属性

调用指定方法

通过反射,调用类中的方法,通过Method类完成。步骤:

  • 1.通过Class类的 getMethod(String name, Class… parameterTypes) 方法获取一个Method对象,参数1是方法名,参数2是该方法参数类型的Class对象(区分方法重载)。
  • 2.使用 Object invoke(Object obj, Object[ ] args) 进行调用,参数1是调用该方法的对象,参数2是传入该方法的实参。

注意:
Object invoke(Object obj, Object … args)

  • Object 对应原方法的返回值,若原方法无返回值,此时返回null。
  • 若原方法若为静态方法,此时形参Object obj可为null。
  • 若原方法形参列表为空,则Object[ ] args为null。
  • 若原方法声明为private,则需要在调用 invoke() 方法前,调用方法对象的setAccessible(true)方法,才可访问private方法。

代码

package reflection;

import java.lang.reflect.*;

public class AppointMethod {
	public static void main(String[] args) {
		try {
			//调用指定的普通方法
			Class<?> clazz = Class.forName("reflection.Student");
			Constructor<?> con = clazz.getConstructor(String.class, int.class, String.class);
			Object obj = con.newInstance("小明", 21, "阳光中学");//有参构造器实例化对象
			//获取指定方法
			Method m = clazz.getMethod("showInfo", String.class, int.class);
			m.invoke(obj, "lalala", 123);//lalala123
			
			//调用指定的私有方法
			Constructor<?> con1 = clazz.getConstructor();
			Object obj1 = con1.newInstance();//无参构造器实例化对象
			//获取指定方法
			Method m1 = clazz.getDeclaredMethod("printInfo", int.class, int.class);
			//私有方法使用前一定要设置可访问
			m1.setAccessible(true);
			m1.invoke(obj1, 2, 1);//3
			
			//调用无参方法
			Method m2 = clazz.getMethod("move");//无参时,第2个参数不写
			m2.invoke(obj);//无参,因此不用初始化,参数2不写 //By bus
			
			//调用重载方法
			//重载方法参数名相同,但参数列表不同,只需将参数列表改动为想要调用的方法即可
			
			//调用有返回值方法
			Method m3 = clazz.getMethod("returnInfo", String.class, int.class, Student.class);
//			String str = (String) m3.invoke(obj, "xyz", 2, (Student)obj);
			String str = (String) m3.invoke(obj, "xyz", 2, obj);
			System.out.println(str);//xyz2小明
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
}

完整代码

package reflection;

import java.lang.reflect.*;
/**
 * 反射机制调用指定方法、属性
 * @author MCC
 *先获取所有方法,再调用其中的一个指定方法,若已知指定方法,可以直接调用
 */
public class AppointMethod {
	public static void main(String[] args) {
		try {
			System.out.println("=============方法=============");
			Class<?> clazz = Class.forName("reflection.Student");
			//获取所有方法
			Method[] me = clazz.getDeclaredMethods();
			for(Method m : me) {
				System.out.println("方法名:" + m.getName() + "\t修饰符:" + m.getModifiers());
				Class<?>[] cla = m.getParameterTypes();
				for(Class<?> c : cla) {
					System.out.println("参数类型" + c.getName());
				}
			}
			System.out.println("=============构造器=============");
			/**
			 * 运行结果:
			 * 方法名:move	修饰符:1
			 * 方法名:work	修饰符:1
			 * 方法名:printInfo	修饰符:2
			 * 参数类型int
			 * 参数类型reflection.Student
			 * 方法名:showInfo	修饰符:1
			 * 参数类型java.lang.String
			 * 参数类型int
			 */
			//获取所有构造器,目的:挑选合适的构造器实例化对象
			Constructor<?>[] co = clazz.getDeclaredConstructors();
			for(Constructor<?> c : co) {
				System.out.println("构造器名:" + c.getName() + "\t修饰符:" + c.getModifiers());
				Class<?>[] cla = c.getParameterTypes();
				for(Class<?> ca : cla) {
					System.out.println("参数类型:" + ca.getName());
				}
			}
			/**
			 * 运行结果:
			 * 构造器名:reflection.Student	修饰符:1
			 * 参数类型:java.lang.String
			 * 参数类型:int
			 * 参数类型:java.lang.String
			 * 构造器名:reflection.Student	修饰符:2
			 * 参数类型:java.lang.String
			 * 参数类型:java.lang.String
			 * 构造器名:reflection.Student	修饰符:1
			 */
			//调用指定构造器实例化对象
//			Object obj = clazz.getConstructor(String.class, int.class, String.class).newInstance("小明", 21, "阳光中学");
//			Student stu = (Student)obj;
			//调用指定方法
			//接下来可以使用stu进行操作,或者进行如下操作
			Constructor<?> con = clazz.getConstructor(String.class, int.class, String.class);
			Object obj = con.newInstance("小明", 21, "阳光中学");//有参构造器实例化对象
			//获取指定方法
			Method m = clazz.getMethod("showInfo", String.class, int.class);
			m.invoke(obj, "lalala", 123);
			//调用指定的私有方法
			Constructor<?> con1 = clazz.getConstructor();
			Object obj1 = con1.newInstance();//无参构造器实例化对象
			//获取指定方法
			Method m1 = clazz.getDeclaredMethod("printInfo", int.class, int.class);
			//私有方法使用前一定要设置可访问
			m1.setAccessible(true);
			m1.invoke(obj1, 2, 1);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
}

调用指定属性

在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的 set() 和 get() 方法就可以完成设置和取得属性内容的操作。

  • public Field getField(String name):返回此Class对象表示的类或接口的指定的public的Field。
  • public Field getDeclaredField(String name):返回此Class对象表示的类或接口的指定的Field。

在Field中:

  • public Object get(Object obj):获取指定对象obj上此Field的属性内容。
  • public void set(Object obj, Object value):设置指定对象obj上此Field的属性内容。

注:在类中属性都设置为private的前提下,在使用 set() 和 get() 方法时,首先要使用Field类中的 setAccessible(true) 方法将需要操作的属性设置为可以被外部访问。public void setAccessible(true) 访问私有属性时,让这个属性可见。

package reflection;

import java.lang.reflect.*;

public class AppointField {
	public static void main(String[] args) {
		try {
			//反射机制创建一个对象
			Class<?> clazz = Class.forName("reflection.Student");
			Object obj = clazz.getConstructor().newInstance();
//			Student stu = (Student)obj;
			//访问指定属性
			Field f = clazz.getDeclaredField("school");
			//如果是私有属性,要设置权限
//			f.setAccessible(true);
			f.set(obj, "阳光中学");
			String str = (String)f.get(obj);
			System.out.println(str);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Java动态代理

需求:一个Java项目包括100个类,每个类有10个方法,一共有1000个方法,现在要在每个方法执行的开始和结束增加一个输出语句,即,在方法执行前输出:开始执行,在方法执行结束后输出:执行完毕。我们要手动更改这1000个方法吗?因为每个方法要增加的内容相同,有没有一个统一的处理办法呢?

  • Proxy :专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。
  • 创建一个动态代理类所对应的Class对象:
    static Object newProxyInstance(ClassLoader loader, Class[ ] interfaces, InvocationHandler h)
    参数意义:
    ClassLoader loader:代理类的类加载器。
    Class[ ] interfaces:实现类实现的全部接口。
    InvocationHandler h:代理类的对象。
    返回值:
    接口的代理对象,要用接口类型接收。

动态代理步骤:

  • 1.创建一个实现 InvocationHandler接口的类,并实现 invoke()方法,以完成代理的具体操作。
  • 2.创建被代理的类以及实现其他接口
  • 3.通过 Proxy 的静态方法:
    newProxyInstance(ClassLoader loader, Class[ ] interfaces, InvocationHandler h) 创建一个接口代理对象,注意,该对象若要进行强制类型转换,只能用接口类型转换和接收。
  • 4.通过步骤3创建的接口代理对象调用实现类的方法。

注意:如果一个类想要通过 Proxy.newProxyInstance()的方法被代理,那么该类一定要实现了某些接口,没有实现接口的类,不能被动态代理。
JavaSE学习笔记——反射_第2张图片

package reflection.proxy;
/**
 * 接口
 * @author MCC
 *
 */
public interface Inter {
	void showInfo();
	double count();
}
package reflection.proxy;
/**
 * 实现类
 * @author MCC
 *
 */
public class ImpInter implements Inter{
	double fnum;
	double lnum;
	
	public ImpInter() {}
	
	public ImpInter(double fnum, double lnum) {
		this.fnum = fnum;
		this.lnum = lnum;
	}

	@Override
	public void showInfo() {
		System.out.println("fnum:" + this.fnum + "\tlnum:" + this.lnum);
	}

	@Override
	public double count() {
		double res = this.fnum + this.lnum;
		return res;
	}

}
package reflection.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 动态代理类:代理的实际是接口
 * @author MCC
 *
 */
public class DynamicProxy implements InvocationHandler{
	
	Object obj;//被代理对象
	
	public DynamicProxy(Object obj) {
		this.obj = obj;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println(method.getName() + " 开始执行!");
		Object res = method.invoke(this.obj, args);
		System.out.println(method.getName() + " 执行完毕!");
		return res;
	}
	
}
package reflection.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Test {
	public static void main(String[] args) {
//		Inter test = new ImpInter();
//		InvocationHandler handler = new DynamicProxy(new ImpInter());
		InvocationHandler handler = new DynamicProxy(new ImpInter(1.21, 3.62));
//		Inter it = (Inter)Proxy.newProxyInstance(handler.getClass().getClassLoader(), new ImpInter().getClass().getInterfaces(), handler);
		Inter it = (Inter)Proxy.newProxyInstance(DynamicProxy.class.getClassLoader(), new ImpInter().getClass().getInterfaces(), handler);
		it.showInfo();
		it.count();
	}
}

你可能感兴趣的:(JavaSE学习笔记——反射)