Java学习之路-反射(reflection)

188 阅读4分钟

刚学反射的时候,因为没什么应用场景,领悟不深,现在过后来再看,后面不少东西的依托都是源于反射的实现,可以说是相当重要的一个概念,但是我猜,这个应该在初学的时候大概率都会如笔者一般,懵懵懂懂吧,继续学习咯,随着学习的推进,经验的增加,会对程序的设计解读有着不同阶段的理解吧,现在想来还是基础得好啊。

1、反射

在程序运行时,可以根据给定的类动态的获取类的信息;通过反射方式使用此类 SUN设计的反射相关组件放在:java.lang.reflect包

探测器对象(Class类型的对象):首先得要得到(常见以下几种方式)

方法一
User user = new User();
Class<?> c =  user.getClass();
方法二
Class<?> clz2 = User.class;
方法三
Class<?> clz3 =	Class.forName(className); className:类的全路径
对基本数据类型的包装类
Class<?> clz4 = Integer.TYPE;
2、类加载器(ClassLoader)
1、系统类加载器
ClassLoader loader1 = ClassLoader.getSystemClassLoader();
2、扩展类加载器
ClassLoader loader2 = loader1.getParent();  
3、引导类加载器
ClassLoader loader3 = loader1.getParent();  //null

类加载器应用:通过类加载器获取项目资源【重要的】

ClassLoader loader1 = ClassLoader.getSystemClassLoader();
InputStream in = loader1.getResourceAsStream("db.properties");
//对properties文件,SUN设计了一个类:Properties类,直接解析此文件;
Properties prop = new Properties();
prop.load(in);
		
//获取文件信息
String user = prop.getProperty("user");
String password = prop.getProperty("password");
System.out.println("user ->" + user);
System.out.println("password -> " + password);
URL url =  loader1.getResource("db.properties"); 得到文件全路径
InputStream in = loader1.getResourceAsStream("db.properties"); 直接返回一个流
3、获取类信息

Field类型对象 | Constructor类型对象 | Method类型对象

Field[] fields = clz.getDeclaredFields();
for(Field f : fields) {
	System.out.println(Modifier.toString(f.getModifiers()) +"  " + f.getType().getSimpleName() + "  " + f.getName());
		}
		
		
//		Constructor[] cons = clz.getDeclaredConstructors();
//		for(Constructor c : cons) {
//			System.out.println(c);
//		}

//		Method[] methods =  clz.getDeclaredMethods();
//		for(Method m : methods) {
//			System.out.println(m);
//		}
4、反射应用

通过反射 :创建对象 | 设置属性 | 调用方法

		//得到一个Class对象
		String className ="cn.lanqiao.reflect.User";
		Class<?> clz = Class.forName(className);
		
		//应用1:通过反射 创建对象;(无参构造方法)
		
		//User obj = (User) clz.newInstance(); //默认调用本类的无参构造方法创建对象 -- 类必须有无参构造方法;
		//java.lang.NoSuchMethodException: cn.lanqiao.reflect.User.<init>()
//		System.out.println(obj);
//		User obj2 = (User) clz.newInstance();
//		System.out.println(obj==obj2);

		//应用2:带参构造方法;
		Constructor<?> con = clz.getDeclaredConstructor(String.class,String.class,int.class);
		User user = (User) con.newInstance("张一","java程序员",20);
		System.out.println(user);
		//应用2:设置属性;
		Class<?> clz= Class.forName("cn.lanqiao.User");
		Object obj = clz.newInstance(); //得到一个User对象(向上转型)
		//获取某一属性;
		Field nameField =  clz.getDeclaredField("name");
		//给属性赋值;
		//设置属性可访问
		nameField.setAccessible(true);
		//第1个参数为对象,第2个参数为属性值;
		nameField.set(obj, "张一");
		System.out.println(obj);
		//应用3:通过反射调用方法;
		Class clz = User.class;
		//创建对象
		Constructor<?> con = clz.getDeclaredConstructor(String.class,String.class,int.class); 
		Object obj = con.newInstance("张一","java程序员",20);
		//获取方法;
		//Method method = clz.getDeclaredMethod("toString");
		//调用方法;
		//Object val = method.invoke(obj);
		//System.out.println(val);
		
		//Method method = clz.getDeclaredMethod("getName");
		//Object val = method.invoke(obj);
		//System.out.println(val);
		
		Method method = clz.getDeclaredMethod("show", String.class);
		Object val = method.invoke(obj, "反射"); //动态代理模式(静态代理模式)
		System.out.println(val);
5、IOC原理

IOC:Inverstion Of Control --- 控制反转 (将程序中对象创建的控制权由用户转交给Spring IOC容器) ---- 程序中需要用到哪此对象,直接从容器中取(Spring IOC容器)

容器中的对象:会在项目启动时,完成初使化(完成对象的创建)

**问题来了:**Spring IOC容器如何创建对象 --- 反射

任何一个框架,都有配制文件,框架在运行会读取配制(自动),根据配制文件完成项目必须设制及初使化;

所以IOC容器就是根据配制中要初使化的对象,在项目启动时,完成对象的创建 (程序运行中需要的对象依赖IOC容器注入进来)

6、代理模式

AOP (Aspect Oriented Programming) 的缩写:面向切面编程 ; 实现的是对功能的扩展不需要通过修改源码实现,而是在程序运行中动态注入进来;

AOP思想:将方法的内的核心业务与非核心业务分离(非核心业务切出去),非核心业务在程序运行时可以动态的植入进来 --- 如何将非核心业务(对核心业务的增强)植入进来? ---- 解决方法:代理模式(动态代理实现的)

代理模式:对方法功能的扩展不通过修改源码,而是通过代理对象实现增强(保留原核心业务,再增强新业务功能)

实现代理模式2种方式:静态代理模式 | 动态代理模式

代理模式 要求核心业务的实业类(GovTicketServiceImpl)与代理类 实现同一个接口(TicketService)

**静态代理模式:**需要手动创建一个代理类

动态代理模式 :相对静态代理模式,代理不需要手动创建,会自动生成一个代理类对象 ; 在 JDK中SUN设计了一个类,能够动态的生成代理对象

java.lang.reflect.Proxy类 --- 动态生成代理对象的类
java.lang.reflect.InvocationHandler :接口
 public class Proxy{
 	public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
  }
  三个参数:
  第1个参数为核心业务实现类的类加载器,
  第2个参数为核心业务实现类的接口
  第3个参数为实现InvocationHandler接口的一个对象(内部用反射实现对方法的调用)
  

public interface TicketService {
	public String saleTicket();
	
}

public class GovTicketServiceImpl implements TicketService {
	@Override
	public String saleTicket() {
		System.out.println("您成功在12306网站购买一张车票");
		return "一张车票";
	}
}

public static void main(String[] args){

  TicketService proxyService 
			= (TicketService)Proxy.newProxyInstance(
						GovTicketServiceImpl.class.getClassLoader(), 
						GovTicketServiceImpl.class.getInterfaces(), new InvocationHandler() { //匿名内部类
							@Override
							public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
								System.out.println("携程提供一张100元优惠卷..........");
								//通过反射调用方法
								Object val = method.invoke(ticketService, args); //售票的方法;
								
								System.out.println("携程提供售后服务.........");
								return val;
							}
						});
		
			String val = proxyService.saleTicket();

}