反射是java中的动态机制,它允许我们在程序【运行期间】再确定类的实例化,方法的调用,属性的调用等,而不是传统意义上的在编码期间确定。
因此,反射可以大大的提高代码的灵活度,但是随之而来的是更多的系统开销和较慢的运行速度,因此不能过度的依赖反射。
Class cls = Class.forName("java.lang.String");//获取String的类对象Class.forName()该方法要求必须处理异常:ClassNotFoundException
当指定的字符串(对应类的完全限定名)有误时会抛出该异常,或指定的
路径下无法找到该类时也会抛出该异常(多发生于通过反射加载第三方
jar文件里的类,有时我们忘记将该jar导入环境变量中,导致JVM无法
通过正确的包路径找到它)。
package reflect;import java.lang.reflect.Method;
//反射
public class ReflectDemo1 {public static void main(String[] args) throws ClassNotFoundException {//获取String的类对象
// Class cls = String.class;
// Class cls = Class.forName("java.lang.String");// 类加载器ClassLoaderClassLoader classLoader = ReflectDemo1.class.getClassLoader();Class cls = classLoader.loadClass("java.lang.String");//查看类名//获取类的完全限定名(包名.类名)String className = cls.getName();System.out.println("类名:"+className);//仅获取类名className = cls.getSimpleName();System.out.println("类名:"+className);//通过类对象获取其表示的类的所有方法//获取所有公开方法和从超类继承的方法java.lang.reflect.Method方法对象,与类对象都是反射对象,只不过它的每一个实例仅表示一个方法。通过方法对象可获获取其表示的方法的一切信息(方法名,返回值类型,参数列表,访问修饰符)*///Class提供的getMethods()方法可以获取其表示的类的所有公开方法(包含从超类继承的方法)
// Method[] methods = cls.getMethods();
// for(Method method : methods){
// System.out.println(method.getName());
// }//获取本类定义的方法(包含私有方法,但是不含有从超类继承的方法)Method[] methods = cls.getDeclaredMethods();for(Method method : methods){System.out.println(method.getName());}}
}
Object newInstance()
Class的newInstance()方法使用的是其表示的类的【无参的,公开的构造器实例化】
package reflect;/*** 使用反射机制进行对象的实例化*/
public class ReflectDemo2 {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {Person p = new Person();//硬编码,编码期间确定实例化那个类System.out.println(p);/*使用反射机制实例化1:获取要实例化类的类对象2:通过类对象的newInstance方法实例化*///1加载类对象
// Class cls = Class.forName("reflect.Person");ClassLoader classLoader = ReflectDemo2.class.getClassLoader();Class cls = classLoader.loadClass("reflect.Person");//2通过类对象实例化//Object o = cls.newInstance();//调用无参构造器System.out.println(o);}
}
java.lang.reflect.Constructor
反射对象中的构造器对象
它的每一个实例仅表示一个类中的某个构造器
package reflect;import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;/*** 使用有参构造器实例化对象*/
public class ReflectDemo3 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {Person p = new Person("刘苍松",55);System.out.println(p);//加载类对象Class cls = Class.forName("reflect.Person");//获取无参构造器
// Constructor c = cls.getConstructor();
// Object o = c.newInstance();//先获取指定的构造器:Person(String name,int age)Constructor c = cls.getConstructor(String.class,int.class);Object o = c.newInstance("刘苍松",55);System.out.println(o);}
}
package reflect;import java.lang.reflect.Method;
import java.util.Scanner;/*** 使用反射机制调用方法*/
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();//实例化ClassLoader classLoader = ReflectDemo4.class.getClassLoader();
// Class cls = classLoader.loadClass("reflect.Person");Class cls = classLoader.loadClass(className);Object o = cls.newInstance();//new Person()//调用方法//1通过类对象获取要调用的方法
// Method method = cls.getMethod("sayHello");//获取无参方法sayHelloMethod method = cls.getMethod(methodName);//2通过方法对象执行该方法method.invoke(o);//o.sayHello() o实际上是一个Person对象}
}
package reflect;import java.lang.reflect.Method;/*** 调用有参方法*/
public class ReflectDemo5 {public static void main(String[] args) throws Exception {Class cls = Class.forName("reflect.Person");Object o = cls.newInstance();//say(String info)Method m = cls.getMethod("say",String.class);m.invoke(o,"hello~~");//p.say("hello~~")//say(String info,int sum)Method m2 = cls.getMethod("say",String.class,int.class);m2.invoke(o,"嘿嘿",5);}
}
package reflect;import java.lang.reflect.Method;/*** 使用反射机制调用私有方法*/
public class ReflectDemo6 {public static void main(String[] args) throws Exception {Person p = new Person();
// p.hehe();//编译不通过!Class cls = Class.forName("reflect.Person");Object o = cls.newInstance();/*报错:java.lang.NoSuchMethodException没有这个方法异常上述报错可能原因:1:确实没有这个方法(方法名写错了,参数列表不对)2:方法为私有方法,而使用了getMethod()获取。Class中定义的:Method getMethod(String name,Class... pt)Method[] getMethods()上述两个操作仅能获取到类中的所有公开方法(含从超类继承的)下面两个方法是用于获取类中自己定义的所有方法(不含从超类继承的,但是包含私有的)Method getDeclaredMethod(String name,Class... pt)Method[] getDeclaredMethods()*///获取私有方法:private void hehe()
// Method m = cls.getMethod("hehe");//获取私有方法不能用getMethod,(这用来获取公开方法)Method m = cls.getDeclaredMethod("hehe");m.setAccessible(true);//强行打开访问权限m.invoke(o);//o.hehe()m.setAccessible(false);//用完后关闭访问权限(好习惯)}
}
package reflect;import java.lang.reflect.Method;
import java.lang.reflect.Modifier;/*** Method对象提供了获取表示的方法相关信息的一组操作*/
public class ReflectDemo7 {public static void main(String[] args) throws Exception {Class cls = Class.forName("reflect.Person");Method[] methods = cls.getDeclaredMethods();//获取Person类定义的所有方法for (Method m : methods) {String name = m.getName();//获取方法名System.out.println(name);int modifier = m.getModifiers();//获取访问修饰符if(modifier== Modifier.PUBLIC){System.out.println("公开方法");}else if(modifier== Modifier.PRIVATE){System.out.println("私有方法");}int count = m.getParameterCount();//获取当前方法的参数个数System.out.println("有"+count+"个参数");}}
}
注解可以协助反射机制做更多的操作(我们常用的场景)。
@Target({ElementType.TYPE,ElementType.CONSTRUCTOR})
public @interface 注解名 {
}
@Retention注解用于说明当前注解的保留级别,级别用RetentionPolicy指定
RetentionPolicy.SOURCE 注解仅保留在源码中
RetentionPolicy.CLASS(默认) 注解保留在字节码文件中,但不可被反射机制访问
RetentionPolicy.RUNTIME 注解保留在字节码文件中且可被反射机制访问
注:在反射机制中如果想访问注解,注解的保留级别要用RUNTIME
例
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunClass {
}
类型 参数名() [default 默认值]
如果一个注解中只有一个参数时,参数名建议使用value.
原因:
实际使用注解时,传参格式为:参数名=参数值
例如:
如果当前注解参数定义如下:
int sum() default 1;
那么在使用该注解时为sum传参要写作:
@AutoRunMethod(sum=2)
当一个注解定义了多个参数时,上述传参机制不考虑顺序
例如,当前注解有两个参数时:
int sum() default 1;
String name();
在使用当前注解传参时,可写作:
@AutoRunMethod(sum=1,name=“xxx”)
或
@AutoRunMethod(name=“xxx”,sum=1)
但是对于仅有一个参数时,每次传参都指定名字过于累赘
@AutoRunMethod(sum=2)
因此,如果仅有一个参数时,若参数名为value时,则可以忽略参数名。
int value();
使用时:
@AutoRunMethod(2)
若有2个及以上参数时,就算其中一个参数名字为value也不能传参时省略
例如,当前注解有两个参数时:
int value() default 1;
String name();
在使用当前注解传参时,可写作:
@AutoRunMethod(value=1,name=“xxx”)
或
@AutoRunMethod(name=“xxx”,value=1)
@AutoRunMethod(name=“xxx”,1) 编译不通过!!
例
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunMethod {int value() default 1;
}
所有反射对象:
Class,Method,Constructor,Field等都提供了下面的方法:
boolean isAnnotationPresent(Class cls)
用于判断当前反射对象表示的内容是否被参数类对象表示的注解标注了
例
public class ReflectDemo8 {public static void main(String[] args) throws ClassNotFoundException {Class cls = Class.forName("reflect.Person");//判断当前cls表示的类是否被注解@AutoRunClass标注了boolean is = cls.isAnnotationPresent(AutoRunClass.class);if(is){System.out.println("被标注了!");}else{System.out.println("没有被标注");}}
}
若希望获取一个注解的参数需要两步:
获取指定的注解
所有的反射对象都提供了方法:
public T getAnnotation(Class annotationClass)
通过注解对象获取对应的参数
public class ReflectDemo9 {public static void main(String[] args) throws Exception {Class cls = Class.forName("reflect.Person");Method method = cls.getDeclaredMethod("sayHello");//确定该方法被注解@AutoRunMethod标注了if(method.isAnnotationPresent(AutoRunMethod.class)){//通过方法对象获取其表示的方法上的指定注解AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class);//通过注解对象调用对应的参数方法获取该参数的值(这里是获取value参数的值)int value = arm.value();System.out.println("参数值:"+value);}}
}