Java注解与反射
(一)注解
注解Annotation:Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。
注解Annotation的作用:
- 不是程序本身,可以对程序作出解释;
- 可以被其他程序读取;
- 注解一般是以“@注释名”在代码中存在;
- 可以通过反射机制编程实现对这些元数据的访问;
内置注解
@Override:检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated:标记过时方法,不安全的,废弃的。如果使用该方法,会报编译警告。
@SuppressWarnings :指示编译器去忽略注解中声明的警告。该注解可以添加参数:
@SuppressWarnings(“all”)
@SuppressWarnings(“unchecked”)
@SuppressWarnings(value={“unchecked”,”deprecation”})
public class Test {
public static void main(String[] args) {
Test00 test00 = new Test00();
test00.test00(); //@Deprecated,使用了不建议的方法
}
@SuppressWarnings("all") //指示编译器去忽略注解中声明的警告
private static class Test00 implements Runnable{
@Override //检查该方法是否是重写方法
public void run() {
}
@Deprecated
public void test00(){
System.out.println("!");
}
}
}
元注解
元注解的作用是负责注解其他注解,如:@Retention,@Documented,@Target ,@Inherited
- @Target:用于描述注解的使用范围(即在什么情况下可以使用);
- @Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期;
- @Documented:说明该注解被包含在javadoc中;
- @Inherited :说明子类可以继承父类中的该注解;
@Test01 //利用注解Target可以将接口作用在方法上
public void test02(){
}
//表示是否将注解生成在JAVAdoc中
@Documented
//子类可以继承父类的注解
@Inherited
//描述注解的使用范围,在方法中,类中使用
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//需要在运行保存该注释信息 (RUNTIME>CLASS>SOURCE)
@Retention(value = RetentionPolicy.RUNTIME)
@interface Test01{} //自定义注解,而非接口
注:如果注解只有一个值,建议使用value,value在赋值时可以省略。
自定义注解
自定义注解一般使用@interface来定义。
@interface用来声明一个注解,格式为:public @interface 注解名{定义内容}- 注解中的每个方法实际上是声明了一个参数
- 方法名称是参数名称,返回值类型是基本类型的参数
- 可以使用
default来设置参数的默认值,以致于在引用注解时,不用再去赋值(通常用0,“”) - 如果注解中的参数只有一个的话,一般使用
value,引用时可以省略value
public class Test {
//注解可以显示赋值, 如果没有设置默认值,就必须给注解赋值
// @MyAnnotation(name = "vxzx", id = 1, age = 12,school = {"xxxx"})
@MyAnnotation() //有默认值,可以省略
@MyAnnotaion1("VXZX")
public static void main(String[] args) {
}
}
//自定义注解@interface
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{ //里面的数据如果有默认值,就可以省略
//注解的参数:参数类型 + 参数名()
String name() default "";
int id() default -1; //-1不存在
int age() default 0;
String[] school() default {"xxxx"};
}
@interface MyAnnotaion1{
//如果注解只有一个值,建议使用value,value在赋值时可以省略,其他的则不行
String value();
}
(二)反射
反射(Reflection)时Java被视为动态语言的关键,反射机制运行程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法、注解等。

Java反射机制提供的功能:
-
运行时判断任意一个对象所属的类;
-
运行时构造任意一个类的对象;
-
运行时判断任意一个类所具有的成员变量和方法;
-
运行时获取泛型信息;
-
运行时调用任意一个对象的成员变量和方法;
-
运行时处理注解;
-
生成动态代理;
Class类
一个Class对象包含了特定的某个结构的有关信息,一个加载的类在JVM中只会有一个Class实例。
Class类的常用方法:

public class ClassP02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("annotation.demo2.User");
//获取类的名字:
System.out.println("包名 + 类名:" + c1.getName()); //包名 + 类名
System.out.println("类名:" + c1.getSimpleName()); //类名
System.out.println("================");
//获取类的属性:
//获取该类中的public属性
// Field[] fields = c1.getFields();
// for (Field field : fields) {
// System.out.println("获取该类中的public属性:" + field);
// }
//获取该类中的所有属性:
Field[] fields1 = c1.getDeclaredFields();
for (Field field : fields1){
System.out.println("获取该类中的所有属性:" + field);
}
//获得指定属性的值:
Field address = c1.getDeclaredField("address");
System.out.println("获得指定属性的值:" + address);
System.out.println("+++++++++++++++");
//获得类的方法:
//获得本类和父类的public方法:
Method[] methods = c1.getMethods();
for (Method method : methods){
System.out.println("本类和父类的public方法:" + method);
}
//获得本类的所有方法:
Method[] declaredMethods = c1.getDeclaredMethods();
for (Method method1 : declaredMethods){
System.out.println("本类的所有方法:" + method1);
}
//获得指定的方法:
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println("指定的方法:" + getName);
System.out.println("指定的方法:" + setName);
System.out.println("**********************");
//获得构造器:
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
constructors = c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println("获得所有构造器:" + constructor);
}
//获得指定构造器:
Constructor constructor = c1.getConstructor(String.class, Integer.class, String.class);
System.out.println("获得指定构造器:" + constructor);
}
}
获取Class类的实例:
1)若已知具体的类,通过类的class属性获取,该方法最安全,程序性能最高。
Class c1 = Person.class;
2)已知某个类的实例,调用该实例的getClass()方法获取对象:
Person person = new Person();
Class c2 = person.getClass();
3)已知一个类的全名,且该类在类的路径下,可通过Class类的静态方法forName()获取,可能抛出异常:
Class c3 = Class.forName(包名.类名.类);
4)内置基本数据类型可以直接使用类名.Type:
Class c4 = Integer.TYPE;
System.out.println(c4);
//一个类中只能有一个类对象可以获取:
Class class2 = Class.forName("annotation.demo2.User");
Class class3 = Class.forName("annotation.demo2.User");
Class class4 = Class.forName("annotation.demo2.User");
System.out.println(class2.hashCode());
System.out.println(class3.hashCode());
System.out.println(class4.hashCode());
//所打印出来的哈希值都是一样的。
Class可以对以下属性进行使用:
public static void main(String[] args) {
Class c1 = Object.class; //类
Class c2 = Comparable.class; //接口
Class c3 = String[].class; //数组
Class c4 = int[][].class; //二维数组
Class c5 = Override.class; //注解
Class c6 = ElementType.class; //枚举类型
Class c7 = Integer.class; //基本数据类型
Class c8 = void.class; //void
Class c9 = Class.class; //Class
}
类的加载与ClassLoader的理解
加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时的数据结构,生成一个class对象。
链接:将Java类的二进制代码合并到JVM的运行状态中的过程。
- 验证:检查是否符合JVM规范和安全;
- 准备:为类变量(static)分配内存,设置默认值;
- 解析:常量池内的符号引用(常量名)替换为直接引用(地址);
初始化:
- 执行
()方法,将类变量和static代码块整合到一起; - 初始化类时,如果父类没初始化,则会先初始化父类;
- 保证类的
()方法在多线程环境中被正确加锁和同步;

public class Reflect04 {
public static void main(String[] args) {
Testa a = new Testa();
System.out.println(a.m);
}
}
class Testa{
static {
int m = 300;
System.out.println("静态代码块");
System.out.println(m);
}
static int m = 100;
public Testa(){
System.out.println("~~~~~~~");
}
}
/*输出结果:
静态代码块
300
~~~~~~~
100
*/
JVM在执行程序时,始终第一个先调用static方法块,再依次执行其他。
类的初始化:
类的主动引用(一定会发生类的初始):
- JVM启动时,先初始化main()方法所在的类;
- new一个类的对象;
- 调用类的静态成员和静态方法;
- 使用反射对类进行操作;
- 初始化一个类,父类没被初始,则会先初始化该父类;
类的被动引用(不会发生类的初始化):
- 访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化;
- 通过数组定义类引用,不会触发该类的初始化;
- 引用常量不会触发该类的初始化;(常量已经在常量池中了)
/测试类的初始化:
public class Reflect04_1 {
static { //始终第一个被初始化
System.out.println("main()方法所在的类");
}
public static void main(String[] args) throws ClassNotFoundException {
//主动引用:
//先加载父类初始化,在进行子类初始化:
// Son son = new Son();
//反射的主动引用:
// Class c1 = Class.forName("annotation.demo2.Son");
//子类访问父类的静态变量,不会主动引用:子类不会被加载
// System.out.println(Son.f);
//通过定义数组,不会引起主动引用:
// Son[] sons = new Son[5];
//通过引用常量,不会引起主动引用:
System.out.println(Son.z);
}
}
class Father{
static int f = 100;
static {
System.out.println("父类的引用");
}
}
class Son extends Father{
static {
System.out.println("子类的引用");
int m = 300;
}
static int m = 100;
static final int z = 1;
}
类加载器:
类加载器的作用:将class文件字节码内容加载到内存中,作为方法区中类数据的访问入口。
类缓存:某个类被加载到加载器中,将会维持一段时间。最后可以用JVM垃圾回收这些对象。
类加载器的分类:
- 引导类加载器(Bootstap Classloader);
- 扩展类加载器(Extension Classloader);
- 系统类加载器(System Classloader);
检查类是否已加载:自底向上
尝试加载类:自顶向下

public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器:getSystemClassLoader()
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//获取系统类加载器的父类加载器-->扩展类加载器:
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
//获取扩展类加载器的父类加载器-->根加载器:
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
//测试当前类时哪个类加载器加载的:
ClassLoader c1 = Class.forName("annotation.demo2.ClassP01").getClassLoader();
System.out.println(c1);
//测试JDK内的类是哪个类加载的:
c1 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(c1);
//如何获得系统类加载器可以加载的路径:
String property = System.getProperty("java.class.path");
System.out.println(property);
}
利用反射创建一个class对象
创建类的对象:
- 通过Class类的getDeclaredConstructor(Class....parameterTypes)取得本类的指定形参类型的结构器;再用newInstance(指定形参的值)再插入对象的值;
- 向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数;
- 通过Constructor实例化对象;
public class Reflect05 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class c1 = Class.forName("annotation.demo2.User");
//构造一个对象:
User user = (User) c1.newInstance();
System.out.println(user);
//通过构造器创建对象:
User user1 = (User) c1.getDeclaredConstructor(String.class, Integer.class, String.class).newInstance("VXZX", 001, "广州");
System.out.println(user1);
}
}
利用反射调用指定的方法
通过反射,调用类中的方法,通过Method类完成。
- 通过Class类的getMethod(String name,Class....parameterTypes)方法取得一个Method对象,并设置该方法操作时所需要的参数类型。
- 然后,使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象参数信息。
注意:若原方法声明为private,则需要在调用invoke()之前,显式调用setAccessible(true)方法,才可以访问private方法的数据。
setAccessible:作用是启动和关闭访问安全检查的开关。
- Method、Field和Constructor对象都有setAccessable()方法
- true:表示反射对象在使用访问时,会取消Java语言访问检查
- false:表示反射对象在使用访问时,会开启Java语言访问检查
//通过反射调用普通方法:
User user2 = (User) c1.newInstance();
//通过反射获取一个方法:
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke():激活对象
//(对象,"方法值")
setName.invoke(user2, "VXLZX");
System.out.println(user2.getName());
//通过反射操作属性:
User user3 = (User) c1.newInstance();
//获取的属性不能为private
Field name = c1.getDeclaredField("name");
//如果想执行操作,需要关闭权限检测:
name.setAccessible(true);
name.set(user3,"User3的name");
System.out.println(user3.getName());
其中,在性能方面----- 普通方法性能 > 关闭安全检查的反射方法性能 > 反射方法性能
反射操作注解
反射的强大之处就在于,它可以跟注解结合起来一起使用,最大化简化我们的操作。
public class Reflect08 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
//通过反射获取注解:
Class c1 = Class.forName("annotation.demo2.Student2");
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获取注解value的值:
Table table = (Table) c1.getAnnotation(Table.class);
String value = table.value();
System.out.println(value);
//获取类指定的注解:(name)
Field f = c1.getDeclaredField("name");
Field annotation = f.getAnnotation(Field.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@Table("db_student")
class Student2{
private int id;
@Field(columnName = "db_name", type = "varchar", length = 3)
private String name;
private int age;
public Student2() {
}
public Student2(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student2{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
//类名的注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) //
@interface Table{
String value();
}
//属性的注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD) //
@interface Field{
String columnName();
String type();
int length();
}
注解,反射基本就这些了。

浙公网安备 33010602011771号