ReflectDemo1:
/**
* JAVA 反射机制
* 反射是java的动态机制,可以在程序[运行期间]在确定对象的实例化,方法的调用等操作.
* 动态机制可以提高代码的灵活度,但是运行效率较低并且开销较大.
* 因此不能过度的依赖反射机制.
*/
public class ReflectDemo1 {
public static void main(String[] args) {
/*
类对象 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");
Class cls = Class.forName("java.util.ArrayList");
3:类加载器模式ClassLoader
*/
//获去String的类对象
Class cls = String.class;
String name = cls.getName();//获取当前类对象所表示的类的完全限定名
System.out.println(name);
name = cls.getSimpleName();//仅获取类名
System.out.println(name);
/*
java.lang.reflect.Method
Method的每一个实例用于表示一个方法
*/
Method[] methods = cls.getMethods();//获取当前类对象所表示的类的所有公开方法
for(Method method : methods){
String methodName = method.getName();
System.out.println(methodName);
}
/*
Package的每一个实例用于表示一个包
*/
Package pack = cls.getPackage();
System.out.println(pack.getName());//包名
}
}
ReflectDemo2:
public class ReflectDemo2 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Person p = new Person();
System.out.println(p);
/*
反射机制实例化
1:获取对应的类对象
2:通过类对象的newInstance()方法实例化
*/
// Class cls = Person.class;
// Class cls = Class.forName("reflect.Person");
/*
java.util.ArrayList
java.util.HashMap
java.util.Date (日期类,实例化后输出的是当前系统时间)
reflect.Person
*/
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个类的完全限定名");
String className = scanner.nextLine();
Class cls = Class.forName(className);
//newInstance()方法会调用类的公开的无参构造器
Object obj = cls.newInstance();
System.out.println(obj);
}
}
ReflectDemo3:
/**
* 使用指定的构造器实例化对象
*/
public class ReflectDemo3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Person p1 = new Person();
System.out.println(p1);
Person p2 = new Person("李四",55);
System.out.println(p2);
//1获取类对象
Class cls = Class.forName("reflect.Person");
/*
2
java.lang.reflect.Constructor
Constructor的每一个实例用于表示一个构造器,使用它可以相当于调用该构造器实例化对象
*/
// Constructor c = cls.getConstructor();//获取默认无参的构造器
// c.newInstance();//不需要传入实参
//Person(String,int)
Constructor c = cls.getConstructor(String.class,int.class);
Object obj = c.newInstance("王五",33); //new Person("王五",33);
System.out.println(obj);
}
}
ReflectDemo4:
/**
* 使用反射机制调用方法
*/
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 obj = cls.newInstance();//Object obj = new Person();
//调用方法
//1获取待调用方法的方法对象
// Method method = cls.getMethod("sayHello");//至少要传入方法名
Method method = cls.getMethod(methodName);
//2通过方法对象来调用此方法
method.invoke(obj);//obj.sayHello()
}
}
ReflectDemo5:
/**
* 调用有参方法
*/
public class ReflectDemo5 {
public static void main(String[] args) throws Exception {
Class cls = Class.forName("reflect.Person");
Object obj = cls.newInstance();
Method method = cls.getMethod("say",String.class);//say(String info)
method.invoke(obj,"哈哈哈");//obj.say("哈哈哈")
Method method2 = cls.getMethod("say",String.class,int.class);
method2.invoke(obj,"嘿嘿",5);
}
}
ReflectDemo6:
/**
* 反射机制访问私有成员
*/
public class ReflectDemo6 {
public static void main(String[] args) throws Exception {
Person p = new Person();
// p.heiheihei();//编译不通过,因为类的外部不可以方法类的私有成员
Class cls = Class.forName("reflect.Person");
Object obj = cls.newInstance();
/*
Class类中的方法:
getMethod()和getMethods()
用来获取其表示的类的公开方法(包含从超类继承的)
getDeclaredMethod()和getDeclaredMethods()
用来获取类本身自己定义的方法(包含私有方法,但是不包含从超类继承的)
*/
// Method[] methods = cls.getMethods();
// Method[] methods = cls.getDeclaredMethods();
// for(Method method : methods){
// System.out.println(method.getName());
// }
Method method = cls.getDeclaredMethod("heiheihei");
method.setAccessible(true);//强行打开你的访问权限
method.invoke(obj);
method.setAccessible(false);//调用后尽量还原权限
}
}
ReflectDemo7:
/**
* 反射机制中访问注解
*/
public class ReflectDemo7 {
public static void main(String[] args) throws Exception {
/*
查看Person类是否有被注解@AutoRunClass标注?
反射对象:
Class 类对象:它的每一个实例用于表示一个类
Method 方法对象:它的每一个实例用于表示一个方法
Constructor 构造器对象
Field 属性对象
Annotation 注解对象
所有反射对象都支持一个方法:
boolean isAnnotationPresent(Class cls)
该方法用于判断当前反射对象表示的内容是否被参数类对象表示的注解标注了,若被标注则返回true
*/
//1加载类对象
Class cls = Class.forName("reflect.Person");
//2判断当前类对象cls表示的类Person是否被指定的注解@AutoRunClass标注了
boolean tf = cls.isAnnotationPresent(AutoRunClass.class);
//如果在反射机制访问不到注解,通常原因就是该注解的保留级别没有设置为RUNTIME导致的
System.out.println("是否被标注了:"+tf);
}
}
ReflectDemo8:
/**
* 反射机制访问注解参数
*/
public class ReflectDemo8 {
public static void main(String[] args) throws Exception {
/*
获取Person类中sayHello方法上的注解@AutoRunMethod中的参数值
*/
//加载Person的类对象
Class cls = Class.forName("reflect.Person");
//通过类对象获取sayHello方法
Method method = cls.getDeclaredMethod("sayHello");
//判断该方法是否被注解@AutoRunMethod标注
if(method.isAnnotationPresent(AutoRunMethod.class)){
//确定被标注,我们就可以通过反射对象获取对应的注解了
AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class);
int value = arm.value();//通过注解对象获取对应的参数:value
System.out.println("参数value的值为:"+value);
}
}
}
Person:
/**
* 使用当前类测试反射机制
*/
@AutoRunClass
public class Person {
private String name = "张三";
private int age = 22;
public Person(){}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@AutoRunMethod(25)
public void sayHello(){
System.out.println(name+":hello!");
}
public void sayHi(){
System.out.println(name+":hi!");
}
public void sayGoodbye(){
System.out.println(name+":bye!");
}
@AutoRunMethod(7)
public void watchTV(){
System.out.println(name+":看电视");
}
@AutoRunMethod
public void playGame(){
System.out.println(name+":玩游戏");
}
public void say(String info){
System.out.println(name+":"+info);
}
public void say(String info,int count){
for (int i = 0; i < count; i++) {
System.out.println(name+":"+info);
}
}
private void heiheihei(){
System.out.println("我是Person的私有方法!!!");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Test1:
/**
* 利用反射机制调用Person类中所有的无参且公开的方法
* 提示:
* Method类上有一个方法:
* int getParameterCount()
* 该方法用于获取当前方法对象所表示的方法有多少个参数
*/
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
/*
1:通过类对象获取本类定义的所有方法
2:遍历每一个方法后先判断是否为无参方法
3:对无参方法的method调用invoke执行这个方法
*/
Class cls = Class.forName("reflect.Person");
Object obj = cls.newInstance();
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
if(method.getParameterCount()==0 //判断是否无参
&&
method.getModifiers()==Modifier.PUBLIC//判断此方法是否为public
){
method.invoke(obj);
}
}
}
}
Test2:
/**
* 实例化与当前类Test2在同一个包中的所有类
* 用无参构造器实例化
*/
public class Test2 {
public static void main(String[] args) throws Exception {
/*
固定写法,使用哪个类就是定位该类的字节码文件所在的目录
思路:
因为java中文件名(不含后缀的部分)与其中的类名一致.
因此我们遍历目录中所有的文件就可以通过文件名得知每个类的名字
然后利用Class加载类对象从而实例化
*/
File dir = new File(
Test2.class.getResource(".").toURI()
);
String packname = Test2.class.getPackage().getName();
System.out.println(packname);
//通过Test2.class所在的目录获取其中所有子项
File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
for(File sub : subs){
String fileName = sub.getName();
//根据文件名截取出类名
String className = fileName.substring(0,fileName.indexOf("."));
System.out.println(className);
Class cls = Class.forName(packname+"."+className);//"reflect.Person"
Object obj = cls.newInstance();
System.out.println(obj);
}
}
}
Test3:
/**
* 调用与当前类Test3所在同一个包中哪些类中名字含有s的无参方法.
*/
public class Test3 {
public static void main(String[] args) throws Exception {
/*
1:先通过Test3类定位它所在的目录
2:获取该目录中所有的.class文件,并根据文件名解析出类名
3:对每个类逐一加载类对象
4:通过类对象实例化
5:通过类对象获取其定义的所有方法,并遍历每一个方法
6:每个方法要判断是否名字中含有s,并且是否为无参方法,如果同时满足上述两点则调用该方法
*/
//1
File dir = new File(Test3.class.getResource(".").toURI());
String packageName = Test3.class.getPackage().getName();//根据Test3这个类获取它的包名
//2
File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
for(File sub : subs){
String fileName = sub.getName();
String className = fileName.substring(0,fileName.indexOf("."));
//3
Class cls = Class.forName(packageName+"."+className);
//4
Object obj = cls.newInstance();//实例化对象
//5
Method[] methods = cls.getDeclaredMethods();
//6
for(Method method : methods){
/*
String中的方法:
boolean contains(String s)
判断当前字符串是否包含给定的字符串s。如果包含则返回true
*/
if(method.getName().contains("s") //判断方法名中是否包含字符s。
&&
method.getParameterCount()==0 //判断是否为无参
){
method.invoke(obj);
}
}
}
}
}
Test4:
/**
* 输出与当前类Test4在同一个包中所有类是否被@AutoRunClass标注
*/
public class Test4 {
public static void main(String[] args) throws Exception {
//1
File dir = new File(Test4.class.getResource(".").toURI());
String packageName = Test4.class.getPackage().getName();
//2
File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
for(File sub : subs) {
String fileName = sub.getName();
String className = fileName.substring(0, fileName.indexOf("."));
//3
Class cls = Class.forName(packageName + "." + className);
//根据类对象判断该类是否被注解@AutoRunClass标注了
boolean tf = cls.isAnnotationPresent(AutoRunClass.class);
if(tf) {
System.out.println(className + ":被标注了");
}else{
System.out.println(className + ":没有被标注");
}
// System.out.println(className + (tf?":被标注了":":没有被标注"));
}
}
}
Test5:
/**
* 调用与当前类Test5所在同一个包中被注解@AutoRunClass标注的类中那些被注解@AutoRunMethod标注的方法
*/
public class Test5 {
public static void main(String[] args) throws Exception {
/*
1:先通过Test5类定位它所在的目录
2:获取该目录中所有的.class文件,并根据文件名解析出类名
3:对每个类逐一加载类对象
4:首先根据类对象判断该类是否被注解@AutoRunClass标注了,如果没有标注则直接进入下轮循环,查看下一个类
5:如果该类被@AutoRunClass标注了则对该类进行实例化生成一个对象
6:通过类对象获取其定义的所有方法,并遍历每一个方法
7:首先判断该方法是否被注解@AutoRunMethod标注了,如果被标注了则调用该方法
*/
//1
File dir = new File(Test5.class.getResource(".").toURI());
String packageName = Test5.class.getPackage().getName();
//2
File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
for(File sub : subs) {
String fileName = sub.getName();
String className = fileName.substring(0, fileName.indexOf("."));
//3
Class cls = Class.forName(packageName + "." + className);
//4根据类对象判断该类是否被注解@AutoRunClass标注了
if(cls.isAnnotationPresent(AutoRunClass.class)) {
System.out.println(className + ":被标注了");
//5
Object obj = cls.newInstance();//实例化该对象
//6
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
//7
if(method.isAnnotationPresent(AutoRunMethod.class)){
System.out.println(method.getName()+":被标注了");
method.invoke(obj);
}
}
}
}
}
}
test6:
/**
* 调用与当前类Test6在同一个包中被注解@AutoRunClass标注的类中那些被注解@AutoRunMethod标注的方法若干次(次数由该方法注解的参数确定)
*/
public class Test6 {
public static void main(String[] args) throws Exception {
//1
File dir = new File(Test6.class.getResource(".").toURI());
String packageName = Test6.class.getPackage().getName();
//2
File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
for(File sub : subs) {
String fileName = sub.getName();
String className = fileName.substring(0, fileName.indexOf("."));
//3
Class cls = Class.forName(packageName + "." + className);
//4根据类对象判断该类是否被注解@AutoRunClass标注了
if(cls.isAnnotationPresent(AutoRunClass.class)) {
System.out.println(className + ":被标注了");
//5
Object obj = cls.newInstance();//实例化该对象
//6
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
//7
if(method.isAnnotationPresent(AutoRunMethod.class)){
System.out.println(method.getName()+":被标注了");
//通过方法对象获取在该方法上的注解@AutoRunMethod
AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class);
int value = arm.value();//获取该注解的参数
System.out.println("开始调用"+method.getName()+"()方法"+value+"次");
for(int i=0;i<value;i++) {
method.invoke(obj);
}
}
}
}
}
}
}
/**
* Java提供了元注解,是专门为我们定义的注解附加某些特性的
* 比如:
* @Target:用于指定我们定义的注解可以被应用在什么位置
* 具体的位置要使用ElementType来指定
*
* @Retention:用于指定我们定义的注解的保留级别
* 有三个可选值:
* RetentionPolicy.SOURCE 注解仅保留在.java文件的源代码中,编译后的字节码文件中看不到被标注的注解
* RetentionPolicy.CLASS 保留在字节码文件中,但是反射机制不能访问(默认的保留级别)
* RetentionPolicy.RUNTIME 保留在字节码文件中,并且可以被反射机制访问
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunClass {
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunMethod {
/*
为注解添加参数
格式:
类型 参数名() [default 默认值]
例如:
int value();
当这样定义后,当前注解就支持传入一个int行的参数。
外面在使用注解时可以传参:
@AutoRunMethod(11) 此时value参数的值为11
在定义参数的同时可以为该参数指定默认值:
int value() default 1;
如果参数指定了默认值,那么在使用该注解时就不强制要求必须传参了,此时如果不传参则当前参数使用默认值:
@AutoRunMethod(11) 此时value参数的值为11
@AutoRunMethod 此时value参数的值使用默认值1
注:
如果注解仅定义了一个参数时,参数名尽量选取为"value"。如果指定为别的名字,那么在使用注解传参时会麻烦。
结合多个参数说明传参语法:
@注解名(参数1=对应值,参数2=对应值) 这里参数的顺序可以与注解中定义参数的顺序不一致
例如:
在AutoRunMethod注解中定义了两个参数:
int age();
String name();
在使用注解时,传参如下:
@AutoRunMethod(age=11,name="张三")
@AutoRunMethod(name="李四",age=22)
对于只有一个参数时:
如果注解仅定义了一个参数时,参数名如果不叫"value",那么传参时:参数名=参数值
例如,注解定义参数:
int num() default 1;
使用时传参:
@AutoRunMethod(num=22)
如果只有一个参数,且参数名为"value",那么传参时:
@AutoRunMethod(22) 可以省去参数名
注意:上述省略参数名的情况仅限于注解定义了一个参数时,如果包含多个参数时,在传参时每个参数名都不可以忽略
例如:
int value() default 1;
String name();
在使用注解时:
@AutoRunMethod(value=22,name="XXXX") 可以的
@AutoRunMethod(name="XXXX",value=22) 可以的
@AutoRunMethod(22,name="XXXX") 不可以,此时value的参数名不可以忽略!!!
*/
int value() default 1;
// int age() default 22;
// String name();
}