什么是反射
反射是java中的动态机制,它允许我们在程序运行期间再确定对象的实例化,方法的调用, 属性的操作等。使得程序的灵活度大大提升,但是同时也带来了更多的资源开销和较低的 运行效率。 程序不能过度的依赖反射机制。
java反射机制提供了什么功能?
1. 在运行时能够判断任意一个对象所属的类
2. 在运行时构造任意一个类的对象
3. 在运行时判断任意一个类所具有的成员变量和方法
4. 在运行时调用任一对象的方法
5. 在运行时创建新类对象
运用反射的优缺点
优点:
反射提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类
缺点:
(1)性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。
(2)使用反射会模糊程序内内部逻辑:程序员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。
反射获取方式
Class 类对象 Class的每一个实例用于表示JVM中加载的一个类,并且每个被JVM加载的类都 有且只有一个Class的实例。 通过Class我们可以得知其表示的类的一切信息:类名,包名,有哪些构造器,方法 属性等。并在运行期间获取后进行相关操作。 因此反射操作的第一步就是获取要操作的类的类对象。 获取一个类的类对象方式有: 1: 类名.class 例如: Class cls = String.class; Class cls = int.class; 注:基本类型只能通过上述方式获取类对象2: Class.forName(String className) 使用Class的静态方法forName传入要加载的类的完全限定名(包名.类名) 例如: Class cls = Class.forName("java.lang.String")3:类加载器ClassLoader形式
3.常用方法
Class cls = Class.forName("reflect.Person");
//通过类对象获取其表示的String的相关信息
String name = cls.getName(); //获取包名和类名
name = cls.getSimpleName(); //获取类名
System.out.println(cls.getPackage().getName()); //获取包名
//类对象提供了方法newInstance()可以调用无参且公开的构造器实例化
Object o = cls.newInstance();
2获取对应的构造器 Person(String name,int age)
// cls.getConstructor();//不传参获取的为无参构造器
//3通过构造器实例化对象 new Person("王五",22);
Object o = cls.newInstance("王五",22);
//获取当前类对象所表示的类的所有公开方法(包含从超类继承的方法)
Method[] methods = cls.getMethods();
//获取私有方法
Method method = cls.getDeclaredMethod("secret");
//强行打开访问权限
method.setAccessible(true);
利用反射机制调用方法: invoke
1.调用无参方法
public class ReflectDemo4 {
public static void main(String[] args) throws Exception {
Person p = new Person();
p.sayHello();
Scanner scanner = new Scanner(System.in);
System.out.println("请输入类名:");
String className = scanner.nextLine();
System.out.println("请输入方法名:");
String methodName = scanner.nextLine();
//实例化
// Class cls = Class.forName("reflect.Person");
Class cls = Class.forName(className);
Object o = cls.newInstance();//Person o = new Person();
//获取要调用的方法
//仅传入方法名时,是获取该无参方法
// Method method = cls.getMethod("sayHello");//表示的Person的成员方法sayHello()
Method method = cls.getMethod(methodName);
method.invoke(o);//o.sayHello()
}
}
2. 调用有参方法
public class ReflectDemo5 {
public static void main(String[] args) throws Exception {
Class cls = Class.forName("reflect.Person");
Object o = cls.newInstance();
Method method = cls.getMethod("dosome",String.class);//dosome(String)
method.invoke(o,"玩游戏");//p.dosome("玩游戏");
Method method1 = cls.getMethod("dosome",String.class,int.class);
method1.invoke(o,"看电视",5);
}
}
4.反射中使用的注解
定义@AutoRunClass 注解
该注解是用来标注那些可以被反射机制自动调用的类
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AutoRunClass {
}
定义注解时,我们通常会使用java内置的两个注解来加以修饰 1.@Retention 用来指定当前注解的保留级别。有三个可选值, 对应: RetentionPolicy.SOURCE 表示当前注解仅保留在源码中 RetentionPolicy.CLASS(默认值) 表示注解会保留在字节码中,但是反射机制不可用 RetentionPolicy.RUNTIME 表示保留在字节码文件中,但是可以被反射机制使用 通常我们定义的注解都会指定为RUNTIME级别,辅助反射机制的操作。2.@Target用于表示当前注解可以在什么位置上使用。可选项都定义在ElementType上 常见的有: ElementType.TYPE 在类上使用 ElementType.FIELD 在属性使用 ElementType.METHOD 在方法上使用 ...
判断是否被注解标注 : isAnnotationPresent(注解名.class)
public class ReflectDemo7 {
public static void main(String[] args) throws Exception {
//判断一个类是否有被@AutoRunClass标注
// Class cls = Class.forName("reflect.Person");
// Class cls = Class.forName("reflect.Student");
Class cls = Class.forName("reflect.Test2");
/*
出了Class之外,像Method,Filed等其他反射对象也支持isAnnotationPresent
方法,用来表示是否被指定注解标注。
比如:
Method的这个方法就是判断其表示的方法是否有被指定注解标注。
Constructor的这个方法就是判断其表示的构造器是否被指定注解标注。
*/
if(cls.isAnnotationPresent(AutoRunClass.class)){
System.out.println(cls.getName()+":被@AutoRunClass标注了!");
}else{
System.out.println(cls.getName()+":没有被@AutoRunClass标注了!");
}
}
}