目录
#java反射机制(简版)
介绍
### 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的静态方法forName,传入对应类的完全限定名(包名.类名)的 形式获取类对象 Class cls = Class.forName("java.lang.String"); - 3:通过类加载器ClassLoader加载类对象
import java.lang.reflect.Method;
/**
* java反射机制
* 反射是java中的动态机制,它允许我们在程序运行期间再确定类的实例化,方法的调用,
* 属性的调用等,而不是传统意义上的在编码期间确定。
*
* 因此,反射可以大大的提高代码的灵活度,但是随之而来的是更多的系统开销和较慢的
* 运行速度,因此不能过度的依赖反射。
*/
public class ReflectDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
/*
第一种方式 :
1:类名.class
例如:
Class cls = String.class;
Class cls = int.class (基本类型只能通过这种方式获取类对象)
*/
-----------------------------------------------------------------------------------------
/*
第二种方式
*/
//获取String的类对象
// Class cls = String.class;
/*
Class.forName()
该方法要求必须处理异常:ClassNotFoundException
当指定的字符串(对应类的完全限定名)有误时会抛出该异常,或指定的
路径下无法找到该类时也会抛出该异常(多发生于通过反射加载第三方
jar文件里的类,有时我们忘记将该jar导入环境变量中,导致JVM无法
通过正确的包路径找到它)。
*/
// Class cls = Class.forName("java.lang.String");
-----------------------------------------------------------------------------------------
/*
第三种方式
*/
/*
类加载器ClassLoader
类加载器有很多中不同的实现,创建方式也各不相同。
最常用的是如下方式:
ClassLoader loader = 当前类.class.getClassLoader();
类加载器除了可以加载类对象,还可以做很多和环境变量相关的操作,功能多。
*/
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);
//通过类对象获取其表示的类的所有方法
//获取所有公开方法和从超类继承的方法
// 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() 该方法可以使用其表示的类的无参构造器进行对象实例化
/**
* 使用反射机制进行对象的实例化
*/
public class ReflectDemo2 {
// public static void main(String[] args) throws ClassNotFoundException, //IllegalAccessException, InstantiationException { //可以每个抛出异常
public static void main(String[] args) throws Exception { //可以抛出一个大异常
Person p = new Person();//硬编码,编码期间确定实例化那个类
System.out.println(p);
/*
使用反射机制实例化
1:获取要实例化类的类对象
2:通过类对象的newInstance方法实例化
*/
//1加载类对象
// Class cls = Class.forName("reflect.Person");
// Scanner scanner = new Scanner(System.in);
// System.out.println("请输入要实例化的类名 : ");
// String className = scanner.nextLine();
// Class cls = Class.forName(className);
ClassLoader classLoader = ReflectDemo02.class.getClassLoader(); //获取加载类
Class cls = classLoader.loadClass("reflect.Person"); //开始加载输入的类
//2.类对象直接提供了可以通过公开的无参构造器实例化的功能
Object o = cls.newInstance();//调用无参构造器
System.out.println(o);
}
}
##使用有参构造器实例化对象
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 使用有参构造器实例化对象
*/
public class ReflectDemo3 {
public static void main(String[] args) throws Exception {
Person p = new Person("苍老师",55);//传参.会覆盖原有参数,不写参数,则会输出原有参数
System.out.println(p);
//加载类对象
Class cls = Class.forName("reflect.Person");
//获取Person的构造器Person(String ,int)
// cls.getConstructor();//不传任何参数时获取的仍然是无参构造器
//获取无参构造器
// 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);
}
}
##使用反射机制调用方法
### 调用无参数方法(比较省事代码少,但是有点"死")
import java.lang.reflect.Method;
import java.util.Scanner;
/**
* 使用反射机制调用方法
*/
public class ReflectDemo04 {
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();
//1.实例化
Class cls = Class.forName("reflect.Person");
// Class cls = Class.forName(className);
Object obj = new Person();
// Object obj = cls.newInstance();
//2调用方法
//2.1通过类对象获取要调用的方法
Method method = cls.getMethod("sayHello");//获取的是无参的sayHello方法
// Method method = cls.getMethod(methodName);//获取的是无参的sayHello方法
//2.2通过获取的方法对象来调用该方法
// obj.sayHello(); //因为obj指向的是及一个Person对象,因此反射机制可以调用到它的sayHello()
method.invoke(obj);
}
}
方法二(比较麻烦,但是灵活)
import java.lang.reflect.Method;
import java.util.Scanner;
/**
* 使用反射机制调用方法
*/
public class ReflectDemo04 {
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 = ReflectDemo04.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对象
}
}
###调用有参方法
import java.lang.reflect.Method;
/**
* 调用有参方法
*/
public class ReflectDemo5 {
public static void main(String[] args) throws Exception {
Class cls = Class.forName("reflect.Person");
Object obj = cls.newInstance();
//doSomeThing(String)
Method method = cls.getMethod("doSomeThing", String.class); //第一个传入有参方法名,
//第二传入所属类型的 .class
method.invoke(obj,"玩游戏");
//p.doSomeThing("玩游戏");
//doSomeThing(String,int)
Method method1 = cls.getMethod("doSomeThing", String.class,int.class); //同上
method1.invoke(obj,"作业",5); //方法名,String类型参数,int型参数
//p.doSomeThing("作业",5);
}
}
###访问私有方法
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();
/*
getMethod(),getMethods()
它们都是获取Class所表示的类的所有公开方法,包含从超类继承的
getDeclaredMethod(),getDeclaredMethods()
这两个方法获取的都是Class所表示的类中当前类自身定义的方法。包含私有方法
*/
// Method[] methods = cls.getMethods(); //获取所有公开方法,不包含私有
// Method[] methods = cls.getDeclaredMethods(); //获取所有公开方法,包含私有
// for(Method method : methods){
// System.out.println(method.getName());
// }
//调用自己的方法,不含有继承的
Method method = cls.getDeclaredMethod("dosome");
method.setAccessible(true);//强行打开dosome方法的访问权限
method.invoke(obj);//p.dosome()
}
}
后续
/** * 自动调用与当前类ReflectDemo7所在同一个包中,自动调用本类自己定义的无参的公开方法 */
/** * main方法上的参数String[] args的作用是 * 在命令行上使用java命令指定当前类时,可以传递参数进来,此时会被main上的String[] args接收 * * 例如: * java ReflectDemo7 arg1 arg2 arg3 * main方法执行后,args数组就有三个元素,对应的就是"arg1","arg2","arg3" * */
import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URISyntaxException;
/**
* 自动调用与当前类ReflectDemo7所在同一个包中,自动调用本类自己定义的无参的公开方法
*/
public class ReflectDemo7 {
/**
* main方法上的参数String[] args的作用是
* 在命令行上使用java命令指定当前类时,可以传递参数进来,此时会被main上的String[] args接收
*
* 例如:
* java ReflectDemo7 arg1 arg2 arg3
* main方法执行后,args数组就有三个元素,对应的就是"arg1","arg2","arg3"
*
*/
public static void main(String[] args) throws Exception {
/*
两个开发中常用的相对路径
*/
//1这里的当前目录表示的是当前ReflectDemo7这个类所在最外层包的上一级目录
// File dir = new File(
// ReflectDemo7.class.getClassLoader().getResource(".").toURI()
// );
//2这里的当前目录就是当前类所在的目录
File dir = new File(
ReflectDemo7.class.getResource(".").toURI()
);
System.out.println(dir.getName());
//通过当前类ReflectDemo7的类对象获取所在的包名
String packageName = ReflectDemo7.class.getPackage().getName();
System.out.println("ReflectDemo7类的包名是:"+packageName);
//获取ReflectDemo7.class文件所在的目录中所有.class文件
File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
for(File sub : subs){
//获取字节码文件的文件名
String fileName = sub.getName();
System.out.println(fileName);
/*
由于java命名要求,文件名必须与类名一致,
所以我们可以通过文件名得知该字节码
文件中保存的类的类名
*/
String className = fileName.substring(0,fileName.indexOf("."));
// System.out.println("类名:"+className);
//加载该类的类对象
Class cls = Class.forName(packageName+"."+className);
Object obj = cls.newInstance();
// System.out.println("加载的类为:"+cls.getName());
//通过类对象获取本类定义的所有方法
Method[] methods = cls.getDeclaredMethods();
//遍历每个方法,检查哪个方法是无参的
for(Method method : methods){
if(
method.getParameterCount()==0
&&
method.getModifiers() == Modifier.PUBLIC
){
System.out.println("自动调用"+className+"的方法:"+method.getName());
method.invoke(obj);
}
}
}
}
}
补充注解
补充注解部分转自
版权声明:本文为CSDN博主「程序媛 泡泡」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43884234/article/details/115055781
注解的分类
注解一共分为3大类,我们先来认识一下:JDK自带注解
元注解
自定义注解
2.1 JDK注解
JDK注解的注解,就5个:@Override :用来标识重写方法
@Deprecated标记就表明这个方法已经过时了,但我就要用,别提示我过期
@SuppressWarnings(“deprecation”) 忽略警告
@SafeVarargs jdk1.7出现,堆污染,不常用
@FunctionallInterface jdk1.8出现,配合函数式编程拉姆达表达式,不常用
2.2 元注解
用来描述注解的注解,就5个:@Target 注解用在哪里:类上、方法上、属性上等等
@Retention 注解的生命周期:源文件中、字节码文件中、运行中
@Inherited 允许子注解继承
@Documented 生成javadoc时会包含注解,不常用
@Repeatable注解为可重复类型注解,可以在同一个地方多次使用,不常用2.2.1 @Target ElementType…
描述注解存在的位置:ElementType.TYPE 应用于类的元素
ElementType.METHOD 应用于方法级
ElementType.FIELD 应用于字段或属性(成员变量)
ElementType.ANNOTATION_TYPE 应用于注解类型
ElementType.CONSTRUCTOR 应用于构造函数
ElementType.LOCAL_VARIABLE 应用于局部变量
ElementType.PACKAGE 应用于包声明
ElementType.PARAMETER 应用于方法的参数
2.2.2 @Retention RetentionPolicy…
该注解定义了自定义注解被保留的时间长短,比如某些注解仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中; 编译在class文件中的注解可能会被虚拟机忽略,而另一些在class被装载时将被读取。
为何要分字节码文件中有还是没有呢?如果没有时,反射技术就拿不到,从而就无法去识别处理。它的值一共3种:SOURCE 在源文件中有效(即源文件保留)
CLASS 在class文件中有效(即class保留)
RUNTIME 在运行时有效(即运行时保留)
3 自定义注解
注意:注解的语法写法和常规java的语法写法不同
创建包: cn.tedu. annotation
创建类: TestAnnotation.javapackage cn.tedu.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*本类用于完成自定义注解*/
public class TestAnnotation {
}
//2.通过@Target注解标记自定义注解的使用位置
/*3.通过元注解@Target规定自定义注解可以使用的位置
* 我们使用"ElementType.静态常量"的方式来指定自定义注解具体可以加在什么位置
* 而且,值可以写多个,格式:@Target({ElementType.XXX,ElementType.XXX}*/
@Target({ElementType.METHOD,ElementType.TYPE})//可以加在方法&类上
//3.通过@Retention注解标记自定义注解的生命周期
/*4.通过元注解@Retention规则自定义注解的生命周期
* 我们使用"RetentionPolicy.静态常量"的方式来指定自定义注解的生命周期
* 注意:值只能写一个:SOURCE CLASS RUNTIME 3选1 */
@Retention(RetentionPolicy.RUNTIME)//到运行时都有效
//1.定义自定义注解
/*1.首先注意:注解定义的语法与Java不同
* 2.定义自定义注解的格式:@interface 注解名*/
@interface Rice{
//5.我们可以给注解进行功能增强--添加注解的属性
/*5.注意:int age();不是方法的定义,而是给自定义注解添加了一个age属性*/
//int age();//给自定义注解添加一个普通属性age,类型是int
int age() default 0;//给自定义注解的普通属性赋予默认值0
/*6.注解中还可以添加特殊属性value
* 特殊属性的定义方式与普通属性一样,主要是使用方式不同
* 注意:特殊属性的名字必须叫value,但是类型不做限制
* 特殊属性也可以赋予默认值,格式与普通属性一样,不能简写
* */
//String value();//定义一个特殊属性value,类型是String
String value() default "Lemon";//定义特殊属性并给特殊属性赋予默认值
}//4.定义一个类用来测试自定义注解
//@Rice
class TestAnno{
/*测试1:分别给TestAnno类 name属性 eat方法都添加Rice注解
* 结论:属性上的注解报错了,说明自定义注解可以加在什么位置,由@Target决定*/
//@Rice//报错了
String name;
/*测试2:当我们给Rice注解添加了一个age属性以后,@Rice注解使用时直接报错
* 结论:当注解没有定义属性时,可以直接使用
* 当注解定义了属性以后,必须给属性赋值,格式:@Rice(age = 10)*/
/*测试3:给age属性赋予默认值以后,可以直接使用@Rice注解
* 不需要给age属性赋值,因为age属性已经有默认值0了*/
/*测试4:给Rice注解添加了特殊属性value以后,必须给属性赋值
* 只不过特殊属性赋值时可以简写成 @Rice("Apple")
* 测试5:如果特殊属性也赋予了默认值,那么可以直接使用这个注解
* 如果要给注解的所有属性赋值,每条赋值都不能简写*/
@Rice(age=10,value="orange")
//@Rice("Apple")
//@Rice(age = 10)
//@Rice(10)//报错,不可以简写,普通属性没有这种格式
public void eat(){
System.out.println("干饭不积极,思想有问题");
}
}
反射机制中查看注解
/** * 反射机制中查看注解,判断类是否被注解了 */
public class ReflectDemo8 {
public static void main(String[] args) throws Exception {
// Class cls = Class.forName("reflect.Person");
Class cls = Class.forName("reflect.ReflectDemo7");
//判断当前类对象所表示的类是否被注解@AutoRunClass标注了?
boolean tf = cls.isAnnotationPresent(AutoRunClass.class);
if(tf){
System.out.println(cls.getName()+":被注解@AutoRunClass标注了!");
}else{
System.out.println(cls.getName()+":没有被注解@AutoRunClass标注了!");
}
}
}
方法上查看注解
/** * 方法上查看注解 */
public class ReflectDemo9 {
public static void main(String[] args) throws Exception {
Class cls = Class.forName("reflect.Person");
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
/*
除了类对象Class之外,像方法对象Method,属性对象Field等都有
该方法,用于判断其表示的内容是否被某个注解标注了
*/
if(method.isAnnotationPresent(AutoRunMethod.class)){
System.out.println(method.getName()+":被注解@AutoRunMethod标注了");
}else{
System.out.println(method.getName()+":没有被注解@AutoRunMethod标注了");
}
}
}
}
在反射机制中获取注解的参数
/** * 在反射机制中获取注解的参数 */
public class ReflectDemo10 {
public static void main(String[] args) throws Exception {
Class cls = Class.forName("reflect.Person");
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
//判断该方法是否被注解@AutoRunMethod标注了
if(method.isAnnotationPresent(AutoRunMethod.class)){
//通过方法对象获取该注解
AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class);
int value = arm.value();
System.out.println(
"方法"+method.getName()+
"上的注解AutoRunMethod指定的参数值为:"+value
);
}
}
}
}