反射

反射

​ 反射的概念是由 Smith 在 1982 年首次提出的,主要是指程序可以访问、检测和修改它本身状 态或行为的一种能力, 并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和 相关的语义。Java 中,反射是一种强大的工具。它使您能够创建灵活的代码,这些代码可以在 运行时装配,无需在组件之间进行源代码链接。反射允许我们在写与执行时,使我们的程序 代码能够接入装载到 JVM 中的类的内部信息,而不是源代码中选定的类协作的代码。这使反 射成为构建灵活的应用的主要工具。

但需注意的是:如果使用不当,反射的成本很高。

Java 中的类反射

​ Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查, 或者说“自审”或“自省”,并能直接操作程序的内部属性。

reflection的工作机制

​ 程序运行时,java 系统会一直对所有对象进行所谓的运行时类型识别,这项信息记录了每个对 象所属的类。通过专门的类可以访问这些信息。用来保存这些信息的类是class 类,class 类为 编写可动态操纵的 java 代码程序提供了强大功能

一个类在加载到内存中,就会存在一个表示当前类的Class类的对象(唯一的,不会变)

Class对象中存在当前这个类的所有内容(属性,构造器,功能…)创建对象时,其实是拿到Class对象的镜像

如果能够获取到表示一个类的Class对象,就可以进行各种操作…

Class对象是反射的源头如果能够获取Class对象,就可以使用Class类的中的各种功能来操作

构造 Class 对象有 3 种方式:

1、Class.forName();

try { 

 	// 构造 Class 对象的第一种方法 

 	Class clazz = Class.forName("java.lang.String"); 

	 Object obj = clazz.newInstance(); 

	 System.out.println(obj); 

 } catch ( ClassNotFoundException e ) { 

 		e.printStackTrace(); 

 } catch (IllegalAccessException e) { 

		 e.printStackTrace(); 

 } catch ( InstantiationException e) { 

		 e.printStackTrace(); 

 } 

2、类.class

try { 

 // 构造 Class 对象的第二种方法 

 	Class stringClass = String.class; 

 	System.out.println(stringClass); 

 } catch ( ClassNotFoundException e ) { 

 		e.printStackTrace(); 

 } catch (IllegalAccessException e) { 

		e.printStackTrace(); 

 } catch ( InstantiationException e) { 

 		e.printStackTrace(); 

 } 

3、Object.getClass()

try { 

 	// 构造 Class 对象的第三种方法

	String s = "s"; 

 	stringClass = s.getClass(); 

 	System.out.println(stringClass); 

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

 } catch (IllegalAccessException e) { 

	 e.printStackTrace(); 

 } catch ( InstantiationException e) { 

	 e.printStackTrace();
}

类对象的比较:

相同的类:

Class clazz = Class.forName("java.lang.String"); 

Class stringClass = String.class; 

System.out.println("字符串类对象的比较=" +(clazz == stringClass)); //true

不同的类:

Class stringClass = String.class; 

Class intClass = int.class; 

System.out.println("字 符 串类 对 象和 Int 类 对 象的 比较 =" +(stringClass ==  intClass)); //false

Java反射机制可以实现的功能

在运行时判断任意一个对象所属的类;

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

Class类等的使用:
基本的常用的方法
通过获取构造器->反射创建对象
获取方法->调用方法
获取属性–>操作属性

public class ReflectDemo03 {
	public static void main(String[] args) {
		//getModifiers() 返回Java语言修饰符的类或接口,编码在一个整数。 
		Class<String> cls = String.class;
		System.out.println(cls.getModifiers());
						                    System.out.println(Modifier.toString(cls.getModifiers()));
			//String getName()  
		System.out.println(cls.getName());  					//java.lang.String
System.out.println(cls.getSimpleName());  //String//getPackage() 这个类的包。 
​	System.out.println(cls.getPackage());
​	
​	//String getTypeName()  
​	System.out.println(cls.getTypeName());
	}

}	
public class RelectDemo04 {
	public static void main(String[] args) throws Exception {
		testConstructor(User.class);
	}
/*
- 反射获取构造器-->创建对象
- 
- Constructor<T> getConstructor(Class<?>... parameterTypes) 
  获取一个指定的公共额构造器对象

 		Constructor<?>[] getConstructors()  获取所有的公共的构造器对象
 		Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 
 			所有权限的方法中的指定一个
 		Constructor<?>[] getDeclaredConstructors()  所有的构造器

​	创建对象
​		1) Class类的newInstance() -->不推荐使用,默认调用空构造,没有就无法使用
​		2) Constructor类提供的newInstance(Object... initargs)方法创建对象,调用当前的构造器初始化信息
 */
public static void testConstructor(Class<User> cls) throws Exception {//获取某个指定的公共的构造器
​	Constructor<User> con = cls.getConstructor(int.class,String.class,String.class,String.class,Integer.TYPE);
​	System.out.println(con);//所有的构造器
​	Constructor[] cons = cls.getDeclaredConstructors();for(Constructor c:cons) {
​		System.out.println(c);}
​	
​	//1.创建对象Class类的newInstance()
​	User obj = cls.newInstance();
​	System.out.println(obj);
​	
​	//2.Constructor类提供的newInstance(Object... initargs)
​	User obj2 = con.newInstance(1001,"lisi","lisi123","女",18);
​	System.out.println(obj2);
​	
​	//私有的,可以放开权限
​	cons[1].setAccessible(true);
​	User obj3 = (User) cons[1].newInstance("liaoliao","1234");
​	cons[1].setAccessible(false); //关闭权限
​	System.out.println(obj3);}
}

Java 反射中的主要类和方法

软件包 java.lang.reflect

提供类和接口,以获取关于类和对象的反射信息。

1、Constructor 构造函数对象

class A { 

 public A() { 

  

 } 

 public A( String s ) { 

  

} 

}

… 

A a = new A(); 

Class aClass = a.getClass(); 

//得到类对象的所有公共的构造函数对象 

Constructor[] constructors = 	aClass.getConstructors(); 

// 得到类对象特定的公共构造函数对象 

Constructor c = aClass.getConstructor(String.class); 

// 获取全部声明的构造方法 

Constructor[] c1 = aClass.getDeclaredConstructors(); 

 	for ( Constructor c1 : constructors ) { 

 System.out.println( "构造方法的名称=" + c1.getName() ); 

	 System.out.println( " 构造方法的修饰符 =" +  

	Modifier.toString(c1.getModifiers()) ); 

 	Class[] clazz1 = c1.getParameterTypes(); 

	 for ( Class cs : clazz1 ) { 

 		System.out.println("参数类型:"+cs.getName()); 

 } 

 }

2、 Method获取所有方法

Method[] ms = aClass.**getDeclaredMethods**(); 

 for ( Method ms1 : ms ) { 

		 System.out.println(); 

 System.out.println( "方法的名称=" + ms1.getName() ); 

 		System.out.println( "方法的修饰符=" + ms1.getModifiers() + ":"  

\+ Modifier.toString(ms1.getModifiers()) ); 

		 System.out.println( " 方 法 的 修 饰 符 是 否 为 public=" +  

	Modifier.isPublic(ms1.getModifiers()) ); 

 	Class[] clazz1 = ms1.getParameterTypes(); 

 	for ( Class cs : clazz1 ) { 

 System.out.println("参数类型:"+cs.getName()); 

 } 

 		System.out.println( "方法是否带有可变数量的参数=" +  ms1.isVarArgs() ); 

 		System.out.println( " 方 法 的 返 回 类 型 : 

"+ms1.getReturnType().getName()); 

 } 

3、Field获取所有属性

Field[] f = aClass.getDeclaredFields(); 

 		B b = new B(); 

 for ( Field f1 : f ) { 

 		System.out.println(); 

 		System.out.println( "变量的名称=" + f1.getName() ); 

 		System.out.println( " 变量的修饰符 =" +  

Modifier.toString(f1.getModifiers()) ); 

 		System.out.println( "变量的类型=" + f1.getType().getName() ); 

		 System.out.println( "变量的值=" + f1.get(b) ); 

 }

4、Class

Class[] classes = aClass.getInterfaces(); 

 	for ( Class c3 : classes ) { 

 		System.out.println("接口名称:" + c3.getName()); 

 } 

	 // 获取类对象的父类 

 	Class c4 = aClass.getSuperclass(); 

 		System.out.println("父类名称:" + c4); 

	 // 获取类对象的包对象 

 String pName = String.class.getPackage().getName(); 

 		System.out.println("String 所在的包名称:" + pName); 

		 System.out.println("aClass 是否为接口:" + aClass.isInterface()); 

 		System.out.println("C 是否为接口:" + C.class.isInterface()); 

 		System.out.println("类名:" + String.class.getName()); 

		 System.out.println("类名的简称:" + String.class.getSimpleName());

实例化对象

创建对象的方式,有new 、克隆、反序列化,再加一种,根据Class对象,使用newInstance() 或者构造器实例化对 象。

//获取源头 

Class<>clz=Class.forName("com.shsxt.ref.simple.User"); 

//第一种:通过newInstance()创建对象 

	User user=(User)clz.newInstance(); 

	user.setUname("sxt"); 

	user.setUpwd("good"); 

//第二种:通过getDeclaredConstructors()创建对象,取得全部构造函数(注意顺序) 

Constructor<?>[] cons=clz.getDeclaredConstructors(); 

	for(Constructor<?>c:cons){ 

		System.out.println(c); 

	}

//注意观察上面的输出结果,再实例化,否则参数容易出错 

User u1=(User)cons[0].newInstance("shsxt","good"); 

User u2=(User)cons[1].newInstance("sxt"); 

User u3=(User)cons[2].newInstance(); 

System.out.println(u1.getUname()+u1.getUpwd()); 

注意**:newInstance()**是调用空构造,如果空构造不存在,会出现异常。由此可知,使用其他构造器创建对象比较

麻烦,使用空构造非常简单。确保空构造存在

父类与接口

Class<>clz=Class.forName("com.shsxt.ref.simple.User"); 

//获取所有接口 

Class<?>[] inters=clz.getInterfaces(); 

	for(Class<?> in:inters){ 

	System.out.println(in.getName()); 

	}

	//获取父类 

	Class<?> cls=clz.getSuperclass(); 
	
System.out.println("继承的父类为:"+cls.getName());

安全性和反射

​ 在处理反射时安全性是一个较复杂的问题。反射经常由框架型代码使用,由于这一点,我们可 能希望框架能够全面介入代码,无需考虑常规的介入限制。但是,在其它情况下,不受控制的 介入会带来严重的安全性风险,例如当代码在不值得信任的代码共享的环境中运行时。

反射的两个缺点

反射是一种强大的工具,但也存在一些不足。

 性能问题。使用反射基本上是一种解释操作,我们可以告诉 JVM,我们希望做什么并 且它满足我们的要求。用于字段和方法接入时反射要远慢于直接代码。性能问题的程 度取决于程序中是如何使用反射的。如果它作为程序运行中相对很少涉及的部分,缓慢的性能将不会是一个问题。

 使用反射会模糊程序内部实际要发生的事情。程序人员希望在源代码中看到程序的逻 辑,反射等绕过了源代码的技术会带来维护问题。反射代码比相应的直接代码更复杂。解决这些问题的最佳方案是保守地使用反射——仅在它可以真正增加灵活性的地方

——记录其在目标类中的使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值