文章目录
java反射机制
反射是java中的动态机制,它允许我们在程序【运行期间】再确定类的实例化,方法的调用,属性的调用等,而不是传统意义上的在编码期间确定。
因此,反射可以大大的提高代码的灵活度,但是随之而来的是更多的系统开销和较慢的运行速度,因此不能过度的依赖反射。
Class类
- Class的每一个实例用于表示JVM加载的一个类,所以我们也称Class的实例
为类的类对象。 - 当JVM加载一个类时会同时实例化一个Class的实例与之对应,这个Class实例
中会保存该类的一切信息(类名,有哪些方法,构造器,属性,注解等等) - 我们在程序运行期间通过某个类的类对象来操作这个类。因此使用反射操作某个类的第一件事就是获取该类的类对象
获取一个类的类对象有三种方式:
- 1:类名.class(基本类型只能通过这种方式获取类对象)
例如: Class cls = String.class; Class cls = int.class - 2:Class.forName(String className)
通过指定一个类的完全限定名来获取该类的类对象。
完全限定名格式:包名.类名
Class cls = Class.forName("java.lang.String");//获取String的类对象
Class.forName()
该方法要求必须处理异常:ClassNotFoundException
当指定的字符串(对应类的完全限定名)有误时会抛出该异常,或指定的
路径下无法找到该类时也会抛出该异常(多发生于通过反射加载第三方
jar文件里的类,有时我们忘记将该jar导入环境变量中,导致JVM无法
通过正确的包路径找到它)。
- 3:通过类加载器ClassLoader加载类对象
类加载器有很多中不同的实现,创建方式也各不相同。
最常用的是如下方式:
ClassLoader loader = 当前类.class.getClassLoader();
类加载器除了可以加载类对象,还可以做很多和环境变量相关的操作,功能多。
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");
// 类加载器ClassLoader
ClassLoader 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());
}
}
}
使用反射机制进行对象的实例化
Class提供的方法:
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");//获取无参方法sayHello
Method 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);//用完后关闭访问权限(好习惯)
}
}
Method对象提供了获取表示的方法相关信息的一组操作
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+"个参数");
}
}
}
注解
注解可以协助反射机制做更多的操作(我们常用的场景)。
- 注解可以被作用在如:类上,方法上,属性上,构造器上,参数上等。
- 可以使用JAVA内置的注解来加以限制可被作用的位置:
@Target注解用于说明当前注解仅被作用域某些地方,这些位置可使用ElementType指定 - 例如:当前注解仅在类上或构造器上使用
@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 extends Annotation> T getAnnotation(Class<T> 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);
}
}
}