Java反射机制笔记

一.反射(reflect)

1.反射的基本概念
2.java中的类反射
3.安全性和反射
4.反射的两个缺点
反射机制让java非常灵活

1.反射的基本概念

(1)反射需要掌握的类
	java.lang.Class<T>(类/类型)
	java.lang.reflecf.Constructor(构造方法)
	java.lang.reflecf.Field(属性)
	java.lang.reflecf.Method(方法)
	java.lang.reflecf.Modifier(修饰符)
	他们都可以创建对象

(2)反射机制的作用
	反编译:可以将文件从.class-->.java
	通过反射机制可以访问java类的属性,方法,构造方法等

2.java中的类反射

(1)获得某个类Class
Ⅰ.获取Class类型的对象的三种方式
	①方式一:利用Class类中的forName方法
		Class c1 = Class.forName("类名");
		这里的类名必须写全,即要带上包名写完整
		c1这个引用保存的是内存地址,指向了堆中的对象,该对象代表的是整个类
	
	②方式二:java中每个类型都有Class属性,直接通过这个也可以
		Class c2 = 类名.class;

	③java中Object定义了 getClass() 方法
		Class c3 = 对象.getClass();
		返回的是对象的运行时类
	因为类在JVM中只有一个,因此这三种方式返回的都是同一块地址
	注意:Class代表的是类型,不是类,每种类型都有Class属性

Ⅱ.区别
	①方式一是将类文件装载到JVM中的过程,因此类中的static语句块中的语句会被执行
	   但是方式二不会执行

Ⅲ.获取Class类型的对象之后,可以创建该“类/类型”的对象
	方法:Class中的newInstance();
	能够创建此Class对象所表示类型的新实例
		Class c = Class.forName("类型");
		Object o = c.newInstance();
		这个方式其实是调用了类中的无参数构造方法创建了一个对象
		所以要想使用反射机制创建对象,类中必须要有无参构造方法,没有会报错
(2)可变长参数
Ⅰ.语法
	类型... 参数;
	例如:public static void m1(int... a){}

Ⅱ.注意
	①表示类型为int的参数可能有0~n个
	②但如果有参数个数精确匹配方法,会优先调用参数匹配的方法
	③可变长参数可以等同看作是数组,可以通过for循环遍历
	④可变长参数只能在参数列表的最后的位置上且只能出现一次,跟C++中的默认参数一样,写在前面会有二义性

Ⅲ.Class可以用作变长参数
	public static void m3(Class... args){
		for(int i = 0;i<args.length;i++){
			Class c = args[i];
			System.out.println(c.newInstance);
		}
	}
	这样可以创建传进方法中对象的类型的对象
(3)IO+Properties+reflect
Ⅰ.IO+Properties的联合应用
	①Properties的介绍
		这个和Map一样的
		只不过它的key和value只能存储字符串类型
		且它的key不能重复,如果key重复会覆盖value值
	
	②动态代码的编写
		步骤:
		//创建属性对象
			Properties p = new Properties();
		
		//创建输入流
			FileInputStream fis = new FileInputStream("文件路径");
		
		//将输入流中的所有数据加载到属性对象中
			p.load(fis);//这样属性对象中就有(key和value的值了)
			
		//关闭流
			fis.close();

		//通过key获取value
			String value = p.getPriperty("key");
			
Ⅱ.说明
	①这样就完成了一个动态的配置文件,改变文件中的值,java代码获取到的value值也不一样
	②这种代码基本用于编写软件的属性文件,获取用户的数据库用户名和密码啥的,java中建议属性文件以.properties结尾
	③属性文件中数据要求
		key和value之间可以用空格,冒号,等号作为分割符
		如果出现多个,以第一个出现的作为分隔符

Ⅲ.IO+Properties+reflect的联合应用
	这三个合起来可以实现动态创建java对象的功能
	把类名写在属性文件中,只需要改变文件中的类型,就可以创建不同类型的对象,java程序不需要改动
(4)反编译某个类的所有属性Field
Ⅰ.获得所有属性的步骤
	①获取整个类
		Class c = Class.forName("类名");

	②获取属性Field
		Filed[] fs = c.getField();//这种方法只能获取所有public修饰的属性

	③获取所有属性
		Field[] fs = c.getDeclaredField();

	④获得属性的类型方式一,利用Field中的getName()方法
		fs[0].getName();

	⑤获得属性的类型方式二,利用Field中的getType()方法
		Class type = fs[0].getType();
		type.getName();
		或者 type.getSimpleName();//这个方法属性和名称都可以获取到

	⑥获得属性的权限修饰符
		int i = type.getModifiers();
		返回值是int类型
			0表示默认
			1表示public
			2表示private
			4表示protected
		
	⑦把刚刚获取到的权限修饰符转换成String,这样好看一点
		String strModifier = Modifier.toString(i);//静态方法直接类名加.调用就行

Ⅱ.获取某个特定的属性Field
	①用处
		通过Field中的方法我们可以实现,动态的给不同的属性调用set和get方法
	②步骤
		//获取类
			Class c = Class.forName("类名");
	
		//获取属性
			Field f = getDecclareField("属性名");
	
		//通过获取到的属性调用其的set和get方法,但是私有属性这个方法不管用
			Object o = c.newInstance();//要调用set和get方法我们需要先创建一个对象
			f.set(对象名,值);
			System.out.printfln(f.get(o));
	
		//如果想要调用私有属性的set和get方法,我们需要在对象创建完后打破封装
			Object o = c.newInstance();
			//打破封装
			f.setAccessible(true);
			//这样就可以了,但打破封装会有安全性的问题
			f.set(对象名,值);
			System.out.println(f.get(o));
(5)反编译某个类的所有方法Method
Ⅰ.获取所有方法的步骤
	①获取类
		Class c = Class.forName("类名");

	②获取所有方法
		Method[] ms = c.getDeclaredMethods();

		for(Method m:ms){
			//获取方法修饰符
			System.out.println(Modifier.toString(m.getModifiers()));
			//获取方法的返回值类型
			Class returnType = m.getReturnType();
			System.out.println(returnType.getSimpleName());
			//获取方法名
			System.out.println(m.getName());
			//获取形参列表
			Class[] parameterTypes = m.getParameterTypes();
			for(Class parameterType:parameterTypes){
				System.out.println(parameterType.getSimpleName());
			}
		}

Ⅱ.反编译的过程
		Class c = Class.forName("类名");
		Method[] ms = c.getDeclaredMethods();

		StringBuffer sb = new StringBuffer();
		sb.append(Modifier.toString(c.getModifiers())+" class ");
		sb.append(c.getSimpleName()+"{\n");
			
		for(Method m:ms){
			sb.append("\t");
			sb.append(Modifier.toString(m.getModifiers())+" ");
			sb.append(m.getReturnType().getSimpleName()+" ");
			sb.append(m.getName+"(");

			//形参
			Class[] parameterTypes = m.getParameterTypes();
			for(int i = 0;i<parameterTypes.length;i++){
				Class parameterType = parameterTypes[i];
				if(i==parameterTypes.length){
					sb.append(parameterType.getSimpleName());
				}else{
					sb.append(parameterType.getSimpleName()+",");
				}
			}
			sb.append("){}\n");
		}
		sb.append(")");

Ⅲ.获取某个特定的方法Method来执行
	①获取类
		Class c = Class.forName("类名");

	②获取某个特定的方法并执行
		//通过:方法+形参列表能唯一确定一个方法
		//形参用类型.class表示,因为该方法需要传的是class参数
		Method m = c.getDeclaredMethod("方法名",参数列表);

		//执行方法
		//先创建对象
		Object o = c.newInstance();
		//调用o对象的m方法
		Object retValue = m.invoke(o,实参列表);
(5)反编译某个类的所有构造方法
Ⅰ.获取所有构造方法步骤
	//获取类
		Class c = ClassforName("类名");
	
	//获取所有构造方法
		Constructor[] cs = c.getDeclaredConstructors();

		for(Constructor con:cs){
			//获取修饰符
			System.out.println(Modifier.toString(con.getModifiers()));
			//获取构造方法名
			System.out.println(c.getName());
			//构造方法的形式参数列表
			Class[] parameterTypes = con.getParameterTypes();
			for(Class parameterType:parameterTypes){
				System.out.println(parameterType.getSimpleName());
			}
		}

Ⅱ.反编译构造方法
过程和普通方法差不多就不写了

Ⅲ.获取某个特定的构造方法,用来创建对象
	//获取类
	Class c = Class.forName("类名");

	//获取特定的构造方法
	//构造方法只需要形参列表就可以跟其他构造方法区分开了
	//同样这里的形参列表也是类型.class
	Constructor con = c.getDeclaredConstructor(形参列表);

	//用获取的构造方法创建对象
	Object o = con.newInstance(实参列表);
(6)通过反射获取某个类的父类或父接口
Ⅰ.步骤
	//获取类
	Class c = Class.forName("类名");
	
	//获取父类
	Class superClass = c.getSuperclass();
	System.out.println(superClass.getName());

	//获取父接口
	//父接口可能有多个,因此返回的是一个Class数组
	Class[] ins = c.getInterfaces();
	for(Class in:ins){
		System.out.println(in.getName());
	}

3.反射的两个缺点

(1)反射打破了封装性,会导致安全性出现问题
(2)反射的效率比一般的直接调用要慢得多,因为反射不是一个直接执行方法的操作
   它是一种解释执行,它告诉JVM怎么做
   因此尽量不要大量使用
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值