反射
反射的概念是由 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,我们希望做什么并 且它满足我们的要求。用于字段和方法接入时反射要远慢于直接代码。性能问题的程 度取决于程序中是如何使用反射的。如果它作为程序运行中相对很少涉及的部分,缓慢的性能将不会是一个问题。
使用反射会模糊程序内部实际要发生的事情。程序人员希望在源代码中看到程序的逻 辑,反射等绕过了源代码的技术会带来维护问题。反射代码比相应的直接代码更复杂。解决这些问题的最佳方案是保守地使用反射——仅在它可以真正增加灵活性的地方
——记录其在目标类中的使用。