反射机制
反射机制:程序运行期间,获取类内部的结构(属性,方法等信息)。反射机制和new的方式都可以创建类的对象,一般推荐使用new的方式。而反射创建对象通常用于框架层面,比如Spring,Mybatis等框架内部就用到了反射
反射获取类内部结构
首先定义一个Java类:
public class Student {
private int id;
public String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
private Student() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public String printName(String name){
System.out.println("调用printName()");
return name;
}
private void printPrivate(){
System.out.println("调用printPrivate()");
}
}
假如不用反射,我们可以直接使用下面代码创建类的实例对象,并调用此类的属性和方法:
/**
* 未使用反射
*/
@Test
public void test(){
Student xf = new Student(1, "xf");
xf.name="aaa";
String name = xf.getName();
System.out.println(name);
System.out.println(xf);
}
基于反射机制,同样也能获取类的内部结构:
/**
* 使用反射
*/
@Test
public void test2()throws Exception{
Class cls= Student.class;
//通过反射创建Student类的对象
Constructor constructor = cls.getConstructor(int.class, String.class);
Object obj = constructor.newInstance(2, "ww");
Student student=(Student)obj;
System.out.println(student);
//通过反射,调用对象的属性、方法
Field name = cls.getDeclaredField("name");
name.set(student,"HN");
System.out.println(student);
Method method = cls.getDeclaredMethod("printName", String.class);
method.invoke(student,"CC");
//通过反射调用类的私有结构
//私有属性
Field id = cls.getDeclaredField("id");
id.setAccessible(true);
id.set(student,3);
System.out.println(student);
//调用私有方法
Method method1 = cls.getDeclaredMethod("printPrivate");
method1.setAccessible(true);
method1.invoke(student);
}
输出如下:
java.lang.Class类理解
java文件经过编译后生成一个或多个字节码文件(.class文件),当我们对.class文件解释运行时。相当于将某个字节码文件加载到内存,此过程就称为类的加载。加载到内存中的类,就称为运行时类,此运行时类也就是Class的一个实例
- 获取Class实例的三种方式
/**
* 获取Class实例
*/
@Test
public void test3() throws ClassNotFoundException{
//1.通过类.class
Class cls1=Student.class;
//2.通过类的对象
Student student=new Student(1,"xf");
Class cls2=student.getClass();
//3.通过全类名获取
Class cls3=Class.forName("cn.nihao.xf.entity.Student");
System.out.println(cls1==cls2); //true
System.out.println(cls1==cls3); //true
//补充:只要类或对象元素类型和纬度一样,就是同一个Class
}
反射的动态性
- 静态代理
主要包括抽象服务类(接口)、代理类、真实角色类(被代理类)。在编译期间,代理类和被代理类就确定下来了。优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展。 缺点:每一个代理类都必须实现一遍委托类(真实角色)的接口,如果接口增加方法, 则代理类也必须跟着修改。如果委托对象非常多,则静态代理类就非 常臃肿,难以胜任。
/**
* 抽象接口
*/
interface ClothFactory{
void produce();
}
/**
* 代理类
*/
class ProxyClothFactory implements ClothFactory{
public ClothFactory clothFactory; //引用真实角色
public ProxyClothFactory(ClothFactory clothFactory){
this.clothFactory=clothFactory;
}
@Override
public void produce() {
System.out.println("代理开始工作");
clothFactory.produce();
System.out.println("代理收尾工作");
}
}
/**
* 真实角色类
*/
class Nike implements ClothFactory{
@Override
public void produce() {
System.out.println("Nike生产服装");
}
}
public class AppTest
{
public static void main(String[] args) {
ClothFactory nike=new Nike();
ClothFactory proxy=new ProxyClothFactory(nike);
proxy.produce(); //解耦,通过代理类去执行逻辑
}
}
- 动态代理
动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象,通常用于调试和远程方法调用。
/**
* 抽象接口
*/
interface Human{
String getBelife();
void eat(String food);
}
/**
* 代理类(动态代理)
* 1.如何根据加载到内存中的真实角色类,动态创建代理类对象
* 2.当通过代理类的对象调用方法时,去动态调用被代理类的同名方法
*
*/
class ProxyFactory {
//返回代理类的对象
public static Object getProcyInstance(Object obj){
MyInvocationHandler handler=new MyInvocationHandler();
handler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
}
}
/**
*
*/
class MyInvocationHandler implements InvocationHandler{
private Object obj; //被代理类对象
public void bind(Object obj){
this.obj=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object value = method.invoke(obj, args);
return value;
}
}
/**
* 真实角色类(被代理类)
*/
class SuperMan implements Human{
public SuperMan() {
super();
}
@Override
public String getBelife() {
return "I believe I can fly!";
}
@Override
public void eat(String food) {
System.out.println("I like eat"+food);
}
}
public class AppTest
{
public static void main(String[] args) {
//代理类
SuperMan superMan = new SuperMan();
//procyInstance:代理类的对象
Human procyInstance =(Human) ProxyFactory.getProcyInstance(superMan);
String belife = procyInstance.getBelife();
System.out.println(belife);
procyInstance.eat("fish");
}
}
总结
了解反射机制原理,反射主要表现出它强大的动态性。后面在框架中会频繁用到反射,反射同时也会帮助我们更进一步了解框架的运用,因为底层多数都用到了反射机制。