JAVA类加载机制与反射,动态代理Proxy串联分析与应用

JAVA类加载机制与反射,通过反射绕过泛型的编译验证

1.类加载机制

当程序使用一个还未被加载到内存的类时,JVM会通过加载、连接、初始化三个步骤将该类进行初始化。类的加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象。

(1).CLASS类

  • Class类也是类的一种,只是名字和class关键字高度相似。Java是大小写敏感的语言。
  • Class类的对象内容是你创建的类的类型信息,比如你创建一个shapes类,那么,Java会生成一个内容是shapes的Class类的对象
  • Class类的对象不能像普通类一样,以 new shapes() 的方式创建,它的对象只能由JVM创建,因为这个类没有public构造函数
  • Class类的作用是运行时提供或获得某个对象的类型信息,和C++中的typeid()函数类似。这些信息也可用于反射。

获得一个Class类对象

第一种办法,Class类的forName函数注意,这里forName(arg)里面传入的是类的全称

public class shapes{}  
Class obj= Class.forName("shapes");

第二种办法,使用对象的getClass()函数

public class shapes{}
shapes s1=new shapes();
Class obj=s1.getClass();
Class obj1=s1.getSuperclass();//这个函数作用是获取shapes类的父类的类型

第三种办法,使用类字面常量注意,使用这种办法生成Class类对象时,不会使JVM自动加载该类(如String类)。而其他办法会使得JVM初始化该类。

Class obj=String.class;
Class obj1=int.class;

(2)静态加载类

静态加载类导致的弊端是,假如一个类里面需要加载多个类,其中某一个类有问题,或者不存在,则在程序编译的过程中,会导致编译报错;尤其是在一个主干上加载一些功能型模块,我们一般希望某个功能出问题不要影响其他功能的实现,这就需要引入动态加载类的概念
public class StaticLoadingClass {
	public static void main(String[] args) {
		//new 创建对象 是静态加载类,在编译时刻就需要加载所有可能使用到的类
		String[] args1=new String[]{"QQ","WEIXIN"};
		if("QQ".equals(args1[0])){
			QQ qq =new QQ();            //<------假设只定义了QQ这个类,这句话会正确编译
			qq.begin();
		}
		if("WEIXIN".equals(args1[1])){
			WEIXIN weixin=new WEIXIN();  //<-----假设没有定义WEIXIN这个类,那么这句话编译时报错
			weixin.begin();
		}
	}
}

(3)动态加载类

思考:下面的class.forMame可以动态的给,假设场景是,用户在前台点击多个功能按钮中的一个,会传不同的参数到这里,假如是QQ,这个QQ功能模块是能正常使用的,假如某个用户点击WEIXIN这个模块,这时程序才会报错,这样就隔离了QQ和微信的功能模块之间的关系
DynamicLoadingClass.java
public class DynamicLoadingClass {
	public static void main(String[] args) {
		try {
			//动态加载类,在运行时刻加载
			Class c=Class.forName("javareflect.loadingClass.QQ");
			//通过类类型,创建该类对象
			Tencent tx=(Tencent)c.newInstance();
			tx.begin();
			Class b=Class.forName("javareflect.loadingClass.WEIXIN");
			Tencent tx1=(Tencent)b.newInstance();
			tx1.begin();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
Tencent.java
interface Tencent {
	public void begin();	
}
QQ.java
public class QQ implements Tencent{
	public void begin(){
		System.out.println("QQ......begin");
	}
}

2.通过反射查看类的信息

下面例子是通过反射获取方法,成员变量,构造函数的信息
package javareflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ClassUtil {
	/*
	 * 打印类的信息,包活类的成员函数
	 * 
	 * @param obj 该对象所属类的信息
	 */
	public static void printClassMessage(Object obj) {
		// 要获取类的信息 首先获取类的类类型
		Class<? extends Object> c = obj.getClass();// 传递的是哪个子类的对象,c就是该类的类类型
		// 获取类的名称
		System.out.println("类的名称是:" + c.getName());
		/*
		 * Method类,方法对象 一个成员方法就是一个Method对象 public Method[]
		 * getMethods()返回某个类的所有公用(public)方法包括其继承类的公用方法, 当然也包括它所实现接口的方法。 public
		 * Method[] getDeclaredMethods()对象表示的类或接口声明的所有方法,
		 * 包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法。
		 */
		Method[] ms = c.getMethods();
		for (int i = 0; i < ms.length; i++) {
			// 得到方法的返回值类型的类类型
			Class<?> returnType = ms[i].getReturnType();
			System.out.print(returnType.getSimpleName() + " ");
			// 得到方法名称
			System.out.println(ms[i].getName() + "(");
			// 获取参数类型--->得到参数列表的类型的类类型
			Class[] paramType = ms[i].getParameterTypes();
			for (Class<?> paramTypeList : paramType) {
				System.out.print(paramTypeList.getName() + ",");
			}
			System.out.println(")");
		}
	}

	private static void getFieldMessage(Object obj) {
		Class c = obj.getClass();
		/*
		 * 成员变量也是对象 java.lang.reflect.Field Field类封装了关于成员变量的操作
		 * getFields()方法获取的是所有的public的成员变量的信息
		 * getDeclaredFields()方法获取的是该类自己声明的成员变量的信息
		 */
		// Field[] fs = c.getFields();
		Field[] fs = c.getDeclaredFields();
		for (Field field : fs) {
			// 得到成员变量的类型的类类型
			Class fieldType = field.getType();
			String typeName = fieldType.getName();
			// 得到成员变量的名称
			String fieldName = field.getName();
			System.out.println(typeName + " " + fieldName);
		}
	}
	
	private static void getConstructMessage(Object obj){
		Class c=obj.getClass();
		/*
		 * 构造函数也是对象
		 * java.lang.Constructor封装了构造函数信息
		 * getConstructors()获取所有的public的构造函数
		 * getDeclaredConstructors()得到所有构造函数
		 */
		//Constructor[] cs = c.getConstructors();
		Constructor[] cs = c.getDeclaredConstructors();
		for(Constructor construor:cs){
			System.out.println(construor.getName()+"(");
			//获取构造函数的参数列表,得到的是参数列表的类类型
			Class[] paramTypes=construor.getParameterTypes();
			for(Class class1:paramTypes){
				System.out.print(class1.getName()+",");
			}
			System.out.println(")");
		}
	}
	public static void main(String[] args) {
		String str = "1";
		printClassMessage(str);
		System.out.println("+++++++++++++++++++++++++++++++++");
		getFieldMessage(str);
		System.out.println("+++++++++++++++++++++++++++++++++");
		getConstructMessage(str);
	}
}

3.方法的反射

(1)如何获取某个方法:
方法的名称和方法的参数列表才能唯一决定某个方法
(2)方法反射的操作
method.invoke(对象,参数列表)
这里有一个需要注意的是:c.getDeclaredMethod(name, parameterTypes) 表示的类或接口声明的所有方法,假如要获取一个私有的private方法,需要在反射出这个方法后,给这个方法开启私有访问权限
Method m2 = c.getDeclaredMethod("printNullParameterTypes"); 
m2.setAccessible(true);// 开启私有访问权限
m2.invoke(a);
package javareflect;

import java.lang.reflect.Method;

/*
 * 1.如何获取某个方法:方法名称和方法的参数列表才能唯一决定某个方法
 * 2.方法的反射操作:  method.invoke(对象,参数列表)
 */
public class MethodInvoke {
	public static void main(String[] args) {
		// 要获取print(int,int)方法 1.要获取一个方法就是获取类的信息,获取类的信息首先要获取类的类类型
		A a = new A();
		Class c = a.getClass();
		/*
		 * 2.获取方法 名称和参数列表决定 getMethod(name, parameterTypes)获取的是public方法
		 * c.getDeclaredMethod(name, parameterTypes) 表示的类或接口声明的所有方法
		 */
		try {
			// Method m=c.getMethod("print", new Class[]{int.class,int.class});
			Method m = c.getMethod("print", int.class, int.class);

			// 方法反射操作
			// a.print(10,20);
			// m.invoke(a, new Object[]{10,20});
			Object obj = m.invoke(a, 10, 20);

			/*
			 * Method m1=c.getMethod("printNullParameterTypes"); Object
			 * obj1=m1.invoke(a);
			 */

			Method m2 = c.getDeclaredMethod("printNullParameterTypes");
			m2.setAccessible(true);// 开启私有访问权限
			m2.invoke(a);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

class A {
	private void printNullParameterTypes() {
		System.out.println("null参数");
	}

	public void print(int a, int b) {
		System.out.println(a + b);
	}
}

4.通过Class,Method来认识泛型的本质

java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了,我们可以通过方法的反射绕过编译
package javareflect;

import java.lang.reflect.Method;
import java.util.ArrayList;

/*
 * 通过Class,Method认识泛型本质
 */
public class GenericityReflect {
	public static void main(String[] args) {
		ArrayList list=new ArrayList();
		
		ArrayList<String> list1=new ArrayList<String>();
		list1.add("hello");
		//list1.add(10); 编译错误
		Class c1 =list.getClass();
		Class c2=list1.getClass();
		System.out.println(c1==c2);
		//反射操作都是编译之后的操作
		/*
		 * c1==c2结果返回true 表示编译之后集合的泛型是去泛型化的
		 * java中集合的泛型是为了防止错误输入,只在编译阶段有效,绕过编译阶段就失效了
		 * 我们可以通过方法的反射操作,绕过编译,验证上一句话
		 */
		try {
			Method m=c2.getMethod("add", Object.class);
			Object obj=m.invoke(list1,10);
			System.out.println(list1.size());
			System.out.println(list1);
//			for(String str:list1){
//				System.out.println(str);
//			}  不能这么编译,因为有int型也传入进去了
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

}










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值