Java 注解与反射(超详细!!!)
Java 注解与反射(超详细!!!)
文章目录
- Java 注解与反射(超详细!!!)
- 1.注解
- 1.1内置注解
- 1.1.1 @SuppressWarnings注解用法
- 1.2 元注解
- 1.3自定义注解
- 2.反射
- 2.1 反射的用途
- 2.2 反射的使用方法
- 2.3 class类型的对象使用场景1:
- 2.4 class类型的对象使用场景2:
- 2.5 通过反射创造对象
- 2.6 使用反射机制获取和调用类的构造方法,访问私有构造方法并创建对象
- 2.7 通过反射,访问并使用成员方法
1.注解
1.1内置注解
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
例如:
@override:定义在java.lang.override 中,此注释只适用于修辞方法,表示一个方法声明打算
重写超类中的另一个方法声明
@Deprecated:定义在java.lang.Deprecated中,此注释可以用于修辞方法,属性,类,表示不
鼓励程序员使用这样的元素,通常是因为它很危险或者存在更好的选择
@SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息.与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的,我们选择性的使用就好了
例如
@SuppressWarnings("all")@SuppressWarnings("unchecked")@SuppressWarnings(value={unchecked", "deprecation")
、、、
接下来,我们通过一个案例,讲解一下注解是如何使用的,该如何使用。
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.public class Main implements Sum {public static void main(String[] args) {Main m = new Main();m.sum();m.sum1();}@Deprecated // 不建议使用的注解public void sum1() {System.out.println("nihao11111");}@Overridepublic void sum() {System.out.println("nihao");}}
在此案例中,我们通过了@Deprecated
表明这个一个不建议使用的方法。但是,这仅仅是建议,我们仍然可以使用。
1.1.1 @SuppressWarnings注解用法
@SuppressWarnings
批注允许您选择性地取消特定代码段(即,类或方法)中的警告。其中的想法是当您看到警告时,您将调查它,如果您确定它不是问题,
就可以添加一个 @SuppressWarnings
批注,以使您不会再看到警告。虽然它听起来似乎会屏蔽潜在的错误,但实际上它将提高代码安全性,因为它将防止您对警告无动于衷 — 您看到的每一个警告都将值得注意。
例如下面这个例子,idea会爆一个警告,上述代码编译通过且可以运行,但每行前面的“感叹号”就严重阻碍了我们判断该行是否设置的断点了。这时我们可以在方法前添加@SuppressWarnings(“unused”)
去除这些“感叹号”。
Contents of collection 'items' are updated, but never queried
package 蓝桥杯国赛刷题;import java.util.ArrayList;
import java.util.List;public class commentTest01 {public static void main(String[] args) {commentTest01 CommentTest01 = new commentTest01();CommentTest01.addItems("1234");}public void addItems(String item) {List items = new ArrayList();items.add(item);}
}
1.2 元注解
元注解的作用就是负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明.
这些类型和它们所支持的类在java.lang.annotation包中可以找到分别是@Target
, @Retention
,@Documented
, @lnherited
@Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
@Retention :表示需要在什么级别保存该注释信息,用于描述注解的生命周期
@Document:说明该注解将被包含在javadoc中
@lnherited:说明子类可以继承父类中的该注解
1.3自定义注解
我们根据先前的注解写法,模仿自己写一个注解
通过观察创造注解包含以下元素@interface
,@Documented
,@Retention
,Target
使用 @interface
自定义注解时,自动继承了java.lang.annotation.Annotation
接口
@ intertace用来声明一个注解,格式:public @ intertace 注解名{定义内容}
其中的每一个方法实际上是声明了一个参数,方法的名称就是参数的名称返回值类型就是参数的类型( 返回值只能是基本类型,Class, String, enum )。可以通过default来声明参数的默认值。如果只有一个参数成员,一般参数名为value。注解元素必须要有值,我们定义注解元素时,经常使用空字符串,O作为默认值。
自定义注解样例:
//自定义注解
public class test03 {//注解可以显示赋值, 如果没有默认值,我们就必须给注解赋值//注解参数的顺序随意@MyAnnotation2(age = 18, name = "jacky")public void test() {}@MyAnnotation3("jacky")public void test2() {}
}@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{//注解的参数: 参数类型 + 参数名 + ();String name() default "";int age() default 0;int id() default -1;String[] schools() default {"peking university"};
}@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{//如果注解参数为value赋值时可以直接写值String value();
}
在这个例子我们,我们定义了两个注解,分别是@MyAnnotation2
,@MyAnnotation3
,其参数不相同。
2.反射
反射(Reflection)是一种 Java 程序运行期间的动态技术,可以在运行时(runtime)检査、修改其自身结构或行为。通过反射,程序可以访问、检测和修改它自己的类、对象、方法、属性等成员
在连接数据库中,反射的作用在于使用其特殊机制,直接调用目标类的构造方法。类如这段代码中
Class.forName("com.mysql.jdbc.Driver");
通过反射的机制,将 com.mysql.jdbc.Driver
这个类加载到 JVM 中。触发静态初始化块:MySQL 的驱动类在静态初始化块中会执行 DriverManager.registerDriver(new Driver())
,自动向 DriverManager
注册自己,使得后续可以通过 DriverManager.getConnection()
建立数据库连接。
2.1 反射的用途
- 动态加载类:程序可以在运行时动态地加载类库中的类;
- 动态创建对象:反射可以基于类的信息,程序可以在运行时,动态创建对象实例;
- 调用方法:反射可以根据方法名称,程序可以在运行时,动态地调用对象的方法(即使方法在编写程序时还没有定义)
- 访问成员变量:反射可以根据成员变量名称,程序可以在运行时,访问和修改成员变量(反射可以访问私有成员变量)
- 运行时类型信息:反射允许程序在运行时,查询对象的类型信息,这对于编写通用的代码和库非常有用;
Spring 框架使用反射来自动装配组件,实现依赖注入;
MyBatis 框架使用反射来创建resultType 对象,封装数据查询结果;
2.2 反射的使用方法
反射的第一步是获取 Class
对象。Class
对象表示某个类的元数据,可以通过以下几种方式获取:
//获取Class类型信息
public class Text02 {public static void main(String[] args) throws ClassNotFoundException {//方式1:通过类名Class stringClass1 = String.class;//方式2:通过Class类的forName()方法Class stringClass2 = Class.forName("java.lang.String");//方式3:通过对象调用getClass()方法Class stringClass3 = "".getClass();System.out.println(stringClass1.hashCode());//1604839423System.out.println(stringClass2.hashCode());//1604839423System.out.println(stringClass3.hashCode());//1604839423}}
2.3 class类型的对象使用场景1:
//Class类型的对象使用场景1
public class Text03 {public static void main(String[] args) {String json= "{\"name\":\"长安荔枝\",\"favCount\":234}"; Document doc=JSON.parseObject(json,Document.class);System.out.println(doc.getName());System.out.println(doc.getFavCount());}}
使用 JSON.parseObject
方法将 JSON 字符串解析为 Document
类的对象。在解析过程中,JSON 字符串中的数据会自动映射到 Document
类的对应字段中。
2.4 class类型的对象使用场景2:
通过 Class
对象在运行时获取一个类的相关信息,包括类名、包名、成员变量(字段)、成员方法等。
//Class类型的对象使用场景2
//获取丰富的类型内容
public class Text04 {public static void main(String[] args) throws ClassNotFoundException {Class clz = Class.forName("java.util.HashMap");//获取类名System.out.println("完全限定名:"+clz.getName());System.out.println("简单的类名:"+clz.getSimpleName());//获取包名System.out.println("package"+clz.getPackage().getName());System.out.println();//获取成员变量Field[] fieldArray =clz.getDeclaredFields();System.out.println("成员变量(字段)");for(Field field:fieldArray) {System.out.println(field);}System.out.println();//获取成员方法Method[] methodArray = clz.getDeclaredMethods();System.out.println("成员方法");for(Method method:methodArray) {System.out.println(method);} }
}
clz.getName()
返回类的完全限定名,包括包名,例如"java.util.HashMap"
。clz.getSimpleName()
返回类的简单名称,不包括包名,例如"HashMap"
。clz.getPackage().getName()
返回类所属的包名,例如"java.util"
。clz.getDeclaredFields()
返回一个Field
数组,包含了类声明的所有字段(包括私有字段)。clz.getDeclaredMethods()
返回一个Method
数组,包含了类声明的所有方法(包括私有方法)。
2.5 通过反射创造对象
有两种方式通过反射创造对象分别是:
方式一:通过 Class
对象直接调用 newInstance()
方法
方式二:通过获取构造方法(Constructor
)来创建对象。
//通过反射的方式,创建对象
public class Text05 {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {Class clz = Class.forName("com.apesource.demo01.Document");//方式1:直接通过Class对象,调用newInstance()方法Object objx = clz.newInstance();//相当于在执行无参构造方法//方式2:通过构造器(构造方法)//无参构造方法Constructor constructor1 = clz.getDeclaredConstructor();//获取无参构造方法System.out.println(constructor1);Object obj1 = constructor1.newInstance();//执行构造器(构造方法),创建对象//有参构造方法Constructor constructor2 = clz.getDeclaredConstructor(String.class);//获取有参构造方法System.out.println(constructor2);Object obj2 = constructor2.newInstance("两京十五日");Constructor constructor3 = clz.getDeclaredConstructor(int.class);//获取有参构造方法System.out.println(constructor3);Object obj3 = constructor3.newInstance(34);Constructor constructor4 = clz.getDeclaredConstructor(String.class,int.class);//获取有参构造方法System.out.println(constructor4);Object obj4 = constructor4.newInstance("风起陇西",64);System.out.println(objx);System.out.println(obj1);System.out.println(obj2);System.out.println(obj3);System.out.println(obj4);}
newInstance()
方法是Class
对象提供的一个方法,它调用类的无参构造方法来创建类的实例。- 注意:这个方法在 Java 9 以后已经被弃用,推荐使用
Constructor
对象来创建实例。 - 通过
getDeclaredConstructor()
方法获取Document
类的无参构造方法,然后调用newInstance()
方法创建实例。 - 注意:如果类中没有无参构造方法,调用
getDeclaredConstructor()
会抛出NoSuchMethodException
。 - 通过
getDeclaredConstructor(String.class)
获取带有一个String
参数的构造方法,并传入"两京十五日"
作为参数来创建对象。 - 通过
getDeclaredConstructor(int.class)
获取带有一个int
参数的构造方法,并传入34
作为参数来创建对象。
2.6 使用反射机制获取和调用类的构造方法,访问私有构造方法并创建对象
public class Text06 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {Class clz = Class.forName("com.apesource.demo01.Document");//获取一组构造器Constructor[] constructorArray1 = clz.getConstructors();//publicConstructor[] constructorArray2 = clz.getDeclaredConstructors();//public、private//获取指定构造器Constructor constructor1 = clz.getConstructor();Constructor constructor2 = clz.getDeclaredConstructor(String.class);System.out.println(constructor1);System.out.println(constructor2);//调用私有构造器,必须设置它的访问全限constructor2.setAccessible(true);//调用构造器,创建对象Object obj = constructor2.newInstance("长安三万里");System.out.println(obj);}}
getConstructors()
方法返回一个包含所有公共(public
)构造方法的数组。如果类中没有public
构造方法,则返回空数组。getDeclaredConstructors()
方法返回一个包含所有声明的构造方法的数组(包括私有的、受保护的和默认访问级别的构造方法)。getConstructor()
方法用于获取类的无参构造方法(必须是public
的)。如果没有无参构造方法或者不是public
,则抛出NoSuchMethodException
。getDeclaredConstructor(Class<?>... parameterTypes)
方法用于获取指定参数类型的构造方法。这里通过传入String.class
参数获取一个带有String
参数的构造方法。这个构造方法可以是任何访问级别(public
、private
、protected
、默认)。setAccessible(true)
用于绕过 Java 访问控制机制,使私有构造方法也可以被调用。如果不设置Accessible
为true
,那么调用私有构造方法时会抛出IllegalAccessException
。newInstance(Object... initargs)
方法使用指定的构造方法创建对象。这里调用了带有String
参数的构造方法,并传入"长安三万里"
作为参数。
2.7 通过反射,访问并使用成员方法
public class Text08 {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {//硬编码的方式
// Document doc1 = new Document();
// doc1.setName("海底两万里");
// doc1.setFavCount(10025);//反射的方式Class clz = Class.forName("com.apesource.demo01.Document");//获取类型信息Object doc1 = clz.newInstance();//创建对象//获取指定名称和参数类型的方法Method setNameMethod = clz.getMethod("setName", String.class);Method setFavCountMethod = clz.getMethod("setFavCount", int.class);//执行方法//doc1.setName("海底两万里");setNameMethod.invoke(doc1, "海底两万里");//doc1.setFavCount(10025);setFavCountMethod.invoke(doc1, 10025);System.out.println(doc1);}}
getMethod(String name, Class<?>... parameterTypes)
方法用于获取类的某个public
方法。方法名称和参数类型必须匹配才能成功获取方法。invoke(Object obj, Object... args)
方法用于调用指定的实例方法。setNameMethod.invoke(doc1, "海底两万里");
等同于doc1.setName("海底两万里");
。setFavCountMethod.invoke(doc1, 10025);
等同于doc1.setFavCount(10025);
。
以上便是Java注解与反射的全部内容了,笔者编写不易,恳请点赞收藏,欢迎评论区交流!!!