反射
反射的概念是由 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());