zoukankan      html  css  js  c++  java
  • Java入门系列-27-反射

    咱们可能都用过 Spring AOP ,底层的实现原理是怎样的呢?

    反射常用于编写工具,企业级开发要用到的 Mybatis、Spring 等框架,底层的实现都用到了反射。能用好反射,就能提高我们编码的核心能力。

    反射机制

    JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。

    作用:

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

    常用的类:

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

    Class 类

    Class 类的实例表示正在运行的 Java 应用程序中的类和接口,Class 没有公共构造方法,Class 对象是在加载类时由 Java 虚拟机及通过调用类加载器中的 defineClass 方法自动构造的。

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

    获取 Class 对象

    获取 Class 对象有4种方式,前三种比较常用。

    首先创建一个类用于测试

    package com.jikedaquan.reflection;
    
    public class User {
    	
    	private int id;
    	private String username;
    	private String password;
    
    	public User() {
    	}
    	
    	public User(int id, String username, String password) {
    		super();
    		this.id = id;
    		this.username = username;
    		this.password = password;
    	}
    
    	public int getId() {
    		return id;
    	}
    
    	public void setId(int id) {
    		this.id = id;
    	}
    
    	public String getUsername() {
    		return username;
    	}
    
    	public void setUsername(String username) {
    		this.username = username;
    	}
    
    	public String getPassword() {
    		return password;
    	}
    
    	public void setPassword(String password) {
    		this.password = password;
    	}
    	
    	public void show() {
    		System.out.println("Hello");
    	}
    	
    	@Override
    	public String toString() {
    		return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
    	}
    }
    

    编写测试

    package com.jikedaquan.reflection;
    
    public class GetClass {
    
    	public static void main(String[] args) {
    		//方法1
    		try {
    			Class clz1=Class.forName("com.jikedaquan.reflection.User");
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    			System.out.println("找不到指定类");
    		}
    		//方法2
    		Class clz2=User.class;
    		//方法3
    		User user=new User();
    		Class clz3=user.getClass();
    		
    		//方法4 类的加载器
    		try {
    			Class clz4=GetClass.class.getClassLoader().loadClass("com.jikedaquan.reflection.User");
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    			System.out.println("找不到指定类");
    		}
    	}
    }
    

    方法1语法:Class Class对象 = Class.forName(包名+类名);

    方法2语法:Class Class对象 = 类名.class;

    方法3语法:Class Class对象 = 对象.getClass();
    getClass() 方法是从 Object 类中继承过来的

    获取类的结构

    Class 类常用方法

    方法名称 说明
    Annotation[] getAnnotations() 返回此元素上存在的所有注解
    Constructor getConstructor(Class<?>... parameterTypes) 获取指定参数的构造函数
    Constructor<?>[] getConstructors() 返回包含的公有构造方法
    Constructor<?>[] getDeclaredConstructors() 返回所有构造方法
    Field getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段
    Method getDeclaredMethod(String name, Class<?>... parameterTypes) 根据方法名和参数获取方法对象

    API 中可以看到有两种获取结构的方式:getDeclaredXxx()和getXxx();getDeclaredXxx()可以获取所有包括私有的

    获取类的结构

    package com.jikedaquan.reflection;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    public class GetClassStruct {
    
    	public static void main(String[] args) {
    		try {
    			Class clz=Class.forName("com.jikedaquan.reflection.User");
    			System.out.println("===========构造===========");
    			//获取构造方法
    			Constructor[] cons=clz.getDeclaredConstructors();
    			for (Constructor constructor : cons) {
    				System.out.println(constructor);
    			}
    			//获取字段
    			System.out.println("===========字段===========");
    			Field[] fields=clz.getDeclaredFields();
    			for (Field field : fields) {
    				System.out.println(field);
    			}
    			//获取方法
    			System.out.println("===========方法===========");
    			Method[] methods=clz.getDeclaredMethods();
    			for (Method method : methods) {
    				System.out.println(method);
    			}
    			//获取父类
    			System.out.println("===========父类===========");
    			Class supperClass=clz.getSuperclass();
    			System.out.println(supperClass.getName());
    			//获取实现的接口
    			System.out.println("===========接口===========");
    			Class[] interfaces=clz.getInterfaces();
    			for (Class interf : interfaces) {
    				System.out.println(interf);
    			}
    			//获取注解
    			System.out.println("===========注解===========");
    			Annotation[] annotations=clz.getAnnotations();
    			for (Annotation annotation : annotations) {
    				System.out.println(annotation);
    			}
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    调用类的指定方法、属性

    获取构造方法并实例化对象

    注意:jdk1.9弃用此方式实例化对象
    Object obj=clz.newInstance();

    通过反射获取有参或无参构造后方可实例化化对象

    package com.jikedaquan.reflection;
    
    import java.lang.reflect.Constructor;
    
    public class CallConstructor {
    
    	public static void main(String[] args) {
    		//获取User 的 Class
    		Class<User> clz=User.class;
    		
    		//获取无参构造方法并实例化
    		try {
    			//getConstructor()方法不传参即无参
    			Constructor<User> constructor=clz.getConstructor();
    			User user=constructor.newInstance();
    			System.out.println(user);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		//获取有参构造方法并实例化
    		try {
    			Constructor<User> constructor=clz.getConstructor(int.class,String.class,String.class);
    			User user=constructor.newInstance(18,"张三","abc123");
    			System.out.println(user);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    获取指定构造方法时,第二个参数为动态参数,不填写即获取无参构造方法,填写指定个数和指定类型.class可获取对应方式的构造方法。

    调用类中的方法

    package com.jikedaquan.reflection;
    
    import java.lang.reflect.Method;
    
    public class CallMethod {
    
    	public static void main(String[] args) {
    		//获取User 的 Class
    		Class<User> clz=User.class;
    		//获取无参方法  show
    		try {
    			Method method=clz.getMethod("show");
    			//执行clz中的方法
    			method.invoke(clz.getConstructor().newInstance());
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		//获取一个参数为String的方法
    		try {
    			Method method=clz.getMethod("setUsername", String.class);
    			//反射实例化对象
    			User user=clz.getConstructor().newInstance();
    			//执行这个对象的方法
    			method.invoke(user, "反射");
    			//测试结果
    			System.out.println(user);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    如果有多个参数,获取方法:getMethod("方法名称",参数1.class,参数2.class,参数3.class)

    多个参数执行时:method.invoke(对象,参数1,参数2,参数3);

    动态代理

    动态代理是指客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要创建目标类的代理对象。

    原理:

    使用一个代理将对象包装起来,然后用该代理对象取代原对象,任何对原始对象的调用都要通过dialing,代理对象决定是否以及何时将方法调用转到原始对象上。

    生活中海外代购其实就用到了代理,你可能不方便出国,但是代购可以,最终帮你完成购买行为。

    以代购为例子完成静态代理

    package com.jikedaquan.reflection;
    
    //购买接口(约定)
    interface Buy{
    	void buyProduct();
    }
    //被代理的
    class Customer implements Buy{
    
    	@Override
    	public void buyProduct() {
    		System.out.println("购买商品");
    	}
    }
    //代理
    class ProxyBuy implements Buy{
    	private Customer customer;
    	
    	public ProxyBuy(Customer customer) {
    		this.customer=customer;
    	}
    	
    	@Override
    	public void buyProduct() {
    		System.out.println("代理:出国");
    		//被代理的对象的行为
    		customer.buyProduct();
    		System.out.println("代理:回国");
    	}
    }
    
    public class TestStaticProxy {
    
    	public static void main(String[] args) {
    		Customer customer=new Customer();
    		ProxyBuy proxyBuy=new ProxyBuy(customer);
    		proxyBuy.buyProduct();
    	}
    }
    

    那么动态代理意味着不能只代理 Customer 类的行为,还可以代理其他类的行为

    package com.jikedaquan.reflection;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    //工厂接口
    interface Factory{
    	void product();
    }
    //电脑工厂
    class ComputerFactory implements Factory{
    
    	@Override
    	public void product() {
    		System.out.println("生产电脑");
    	}
    }
    //动态代理处理器
    class MyInvocationHandler implements InvocationHandler{
    	//要被代理的对象
    	private Object proxyObj;
    	//产生代理对象
    	public Object bind(Object proxyObj) {
    		this.proxyObj=proxyObj;
    		return Proxy.newProxyInstance(
    				proxyObj.getClass().getClassLoader(),
    				proxyObj.getClass().getInterfaces(), 
    				this
    				);
    	}
    	//代理对象实际执行的方法
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		System.out.println("代理:收收费");
    		Object result=method.invoke(proxyObj, args);
    		System.out.println("代理:代理完成");
    		return result;
    	}
    
    }
    
    public class TestDynamicProxy {
    
    	public static void main(String[] args) {
    		//创建代理对象生产器
    		MyInvocationHandler invocationHandler=new MyInvocationHandler();
    
    		//创建要被代理的对象
    		ComputerFactory computerFactory=new ComputerFactory();
    		//生产代理对象
    		Object factoryProxy=invocationHandler.bind(computerFactory);
    		Factory factory=(Factory) factoryProxy;
    		factory.product();
    
    		//创建另一个要被代理的对象(上个示例静态代理的对象和接口)
    		Customer customer=new Customer();
    		//生产代理对象
    		Object buyProxy=invocationHandler.bind(customer);
    		Buy buy=(Buy) buyProxy;
    		buy.buyProduct();
    	}
    }
    

    在 main 方法中,创建了一个 MyInvocationHandler 对象,通过 bind 方法可以传入任意要被代理的对象,实现了动态。

    重点来了,拿好小本子笔记!!!!!

    实现动态代理的步骤

    1.创建要被代理的类的接口

    2.创建要被代理的类实现类

    3.创建代理对象处理器(MyInvocationHandler),实现 InvocationHandler 接口

    4.编写生产代理对象的方法,方法内调用 Proxy.newInstance() 方法,返回代理对象

    5.重写 InvocationHandler 的 invoke 方法

    6.测试:创建代理对象生产器,生产代理对象

  • 相关阅读:
    创建用户中遇到的问题
    创建用户和用户组
    ActiveMq在linxu系统上的启动
    CentOs 6.5 安装jdk
    卸载CentOs6.5自带的OpenJDK
    centos 6.5 添加ip地址
    CentOs 6.5的安装
    jna的简单测试
    电脑硬盘空间怎么越来越小
    build path contains duplicate entry:'src' for project 'XXX'
  • 原文地址:https://www.cnblogs.com/AIThink/p/9971770.html
Copyright © 2011-2022 走看看