反射与注解:
什么是反射?
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。. 这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。. 反射被视为动态语言的关键
反射的使用
反射第一步:获取要操作的类的类对象
类对象:Class类
JVM(java虚拟机)每当加载一个类时(读取要操作的类的字节码文件),就会实例化一个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");
3:ClassLoader类加载形式获取类对象
*/
String name = cls.getName();//获取类名(完全限定名)
System.out.println(name);
name = cls.getSimpleName();//仅获取类名
System.out.println(name);
//列如:
Scanner scanner=new Scanner(System.in);
System.out.println("请输入一个类名:");
String className=scanner.nextLine();
Class cls=Class.forName(className);
String name = cls.getName();//获取类名(完全限定名)
System.out.println(name);
name = cls.getSimpleName();//仅获取类名
System.out.println(name);
/*
通过类对象获取其表示的类的所有公开方法
Method类:方法对象
该类的每一个实例用于表示某个类中的某个方法
*/
Method[] methods=cls.getMethods();
for (Method method:methods){
System.out.println(method.getName());
}
使用反射机制实例化对象
1.加载类对象 调用的包名.类名
Class cls=Class.forName("reflect.Person");
//2.Class提供了一个方法:newInstance()方法,可以调用其表示的类的公开的无参构造进行实例化
Object o=cls.newInstance();
//列如:
Scanner scanner=new Scanner(System.in);
while (true){
System.out.println("请输入一个类名:");
String className=scanner.nextLine();
Class cls=Class.forName(className);
Object o=cls.newInstance();
System.out.println(o);
}
获取类的公开方法:
/*
通过类对象获取其表示的类的所有公开方法
Method类:方法对象
该类的每一个实例用于表示某个类中的某个方法
*/
Method[] methods=cls.getMethods();
for (Method method:methods){
System.out.println(method.getName());
}
获取构造器:
Class cls=Class.forName("reflect.Person");
//Constructor类的每一个实例用于表示一个构造方法
// Constructor c=cls.getConstructor();//不传参获取的就是无参构造器
Constructor c=cls.getConstructor(String.class);//Person(String name)
Object o=c.newInstance("王五");//new Person("王五")
System.out.println(o);
Constructor cs=cls.getConstructor(String.class,int.class);
Object oc=cs.newInstance("132131w",126);
System.out.println(oc);
调用方法:
Scanner scanner=new Scanner(System.in);
System.out.println("请输入类名:");
String className=scanner.nextLine();
System.out.println("请输入方法名:");
String methodName=scanner.nextLine();
//1.加载类对象
Class cls=Class.forName(className);
//2.实例化
Object obj=cls.newInstance();//使用默认构造器实例化对象,Person p=new Person
//根据类对象获取要操作的方法对象
Method method=cls.getMethod(methodName);
//调用方法
method.invoke(obj);//p.sayGoodBye
//调用一个参数的方法
Class cls=Class.forName("reflect.Person");
Object obj=cls.newInstance();
Method method=cls.getMethod("say",String.class);
method.invoke(obj,"大家好~~~");
//调用两个的参数方法
Method method1=cls.getMethod("say", String.class, int.class);
method1.invoke(obj,"今天周末",2);
调用私有方法(一般不要使用):
Class cls=Class.forName("reflect.Person");
Object obj=cls.newInstance();
/*
Method getDeclaredMethod(String method,Class...args);
获取Class表示的类自身定义的一个方法(不含从超类继承的,含本类重写的方法)
Method[] getDeclaredMethods();
获取Class表示的类自身定义的所有方法(不含从超类继承的,含本类重写的方法)
*/
Method method=cls.getDeclaredMethod("dosome");
/*
Method提供的方法:
int getModifiers()
可以获取当前方法的访问修饰符
返回的int值不需要记住具体的数字,可以从Modifier来的畅想进行对应即可
*/
if (method.getModifiers()== Modifier.PRIVATE){//判断该方法是否为private方法
method.setAccessible(true);//强行打开访问
}
method.invoke(obj);//p.dosome()
在反射机制中判定是否有被某个注解标注的类
public static void main(String[] args) throws URISyntaxException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
File dir=new File(
ReflectDemo7.class.getResource(".").toURI()
);
File[] subs=dir.listFiles(f->f.getName().endsWith(".class"));
for (File file:subs) {
String fileName=file.getName();
String className=fileName.substring(0,fileName.indexOf("."));
Class cls=Class.forName("reflect."+className);
//判断当前类是否被注解@AutoRunClass标注过
// boolean tf=cls.isAnnotationPresent(AutoRunClass.class);
// System.out.println(className+"是否被标注"+tf);
if (cls.isAnnotationPresent(AutoRunClass.class)){
Object o=cls.newInstance();
Method[] methods=cls.getDeclaredMethods();
for (Method method:methods) {
if (method.isAnnotationPresent(AutoRun.class)){
AutoRun ar=method.getAnnotation(AutoRun.class);
int value=ar.value();
System.out.println("@AutoRun("+value+")");
System.out.println("自动调用"+cls.getName()+"的方法"+method.getName()+"()");
method.setAccessible(true);
method.invoke(o);
}
}
}
}
}
调用含有s的无参方法
/**
* 自动调用当前类Text所在包中所有的类中名字含有s的无参的public方法
*/
public class Text {
public static void main(String[] args) throws URISyntaxException {
File file=new File(Text.class.getResource(".").toURI());
System.out.println(file.getAbsolutePath());
File[] subs=file.listFiles(f->f.getName().endsWith(".class"));
for (File sub:subs) {
String fielName=sub.getName();
String className=fielName.substring(0,fielName.indexOf("."));
System.out.println("类名"+className);
try {
Class cls = Class.forName("reflect."+className);
Object obj=cls.newInstance();
Method[] methods=cls.getDeclaredMethods();
for (Method method:methods) {
if (method.getModifiers()== Modifier.PUBLIC
&& method.getParameterCount()==0){
System.out.println("自动执行"+cls.getName()+"的方法"+method.getName()+"()");
method.invoke(obj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
调用别的类的main方法:
/*
Test2.class.getResource(".").toURI()
//这里的"."当前目录指定是Test2所在的包
Test2.class.getClassLoader().getResource(".").toURI()
//这里的"."是当前类Test2所在的包的根包位置
*/
//定位包: map包
File dir=new File(
Text2.class.getClassLoader().getResource("./map").toURI()
);
System.out.println(dir.getAbsolutePath());
File[] subs=dir.listFiles(f->f.getName().endsWith(".class"));
for (File sub:subs) {
String fileName=sub.getName();
String className=fileName.substring(0,fileName.indexOf("."));
try {
Class cls = Class.forName("map."+className);
System.out.println("准备执行"+cls.getName()+"的main方法");
Method method=cls.getMethod("main",String[].class);
method.invoke(null,(Object)new String[]{});
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
System.out.println("没有main方法");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
调用在同一包含有s的无参方法:
/*
Test2.class.getResource(".").toURI()
//这里的"."当前目录指定是Test2所在的包
Test2.class.getClassLoader().getResource(".").toURI()
//这里的"."是当前类Test2所在的包的根包位置
*/
//定位包: map包
File dir=new File(
Text2.class.getClassLoader().getResource("./map").toURI()
);
System.out.println(dir.getAbsolutePath());
File[] subs=dir.listFiles(f->f.getName().endsWith(".class"));
for (File sub:subs) {
String fileName=sub.getName();
String className=fileName.substring(0,fileName.indexOf("."));
try {
Class cls = Class.forName("map."+className);
System.out.println("准备执行"+cls.getName()+"的main方法");
Method method=cls.getMethod("main",String[].class);
method.invoke(null,(Object)new String[]{});
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
System.out.println("没有main方法");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
注解:
/**
* 注解的声明使用关键字 @interface
*
* 声明一个注解后,通常会使用java内置的两个注解来说明当前注解的一些特征:
* @Retention注解用于声明我们定义的注解的保留级别
* 有三个可选值:
* RetentionPolicy.SOURCE 仅保留在源代码中,编译后的class文件中没有该注解
* RetentionPolicy.CLASS 注解保留在字节码文件中,但是反射机制不能使用它
* RetentionPolicy.RUNTIME 注解保留在字节码文件中,并且可以被反射机制调用
* @Retention不指定时,默认级别为CLASS
*
* @Target注解用于声明我们定义的注解可以在哪里被标注
* 对应的可选值有很多,都在ElementType上有所定义
* 例如:
* ElementType.TYPE 在类上可以使用当前注解
* ElementType.METHOD 在方法上可以使用当前注解
* ....
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AutoRunClass {
/*
可以为注解声明参数
语法:
类型 参数名() [default 默认值]
注:如果当前注解只有一个参数时,参数名通常使用value。这样的好处是,使用该直接时为
这个参数赋值可以写成:@AutoRun(3)
否则,如果参数名不叫value,例如叫count.那么使用时必须写为:@AutoRun(count=3)
*/
int value() default 1;
}