> 文章列表 > 注解和反射

注解和反射

注解和反射

一、注解(Annotation)是什么?

注解(Annotation)是从JDK1.5开始引入的新技术。

注解的作用

  • 不是程序本身,可以对程序做出解释,这一点和注释(comment没什么区别)
  • 可以被其他程序读取(比如编译器)

注解的格式

注解是以“@注释名”在代码中存在的,还可以添加一些参数值,如@SuppressWarnings( value = "unchecked")

二、内置注解

@Override

定义在java.lang.Override中,此注解只适用于修饰方法,表示一个方法声明打算重写超类中的另一个方法声明

@Deprecated

定义在java.lang.Deprecated中,此注解可以用于修饰方法、属性、类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或者存在更好的选择

@SuppressWarnings

定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息

        与前两个不同的是,只需要添加一个参数才能正确使用,这些参数都是定义好的,只需要选择性的使用即可。

  • SuppressWarnings("all")
  • SuppressWarnings("unchecked")
  • SuppressWarnings(value={"unchecked","deprecation"})等等

三、元注解

  • 元注解的作用就是负责注解其他注解,Java定义了4个标准的元注解,它们被用来提供对其他注解类型做说明
  • 这些类型和它们所支持的类在java.lang.annotation包下可以找到

4个元注解解释

  • @Target:用于描述注解的使用范围(可以理解为注解可以用在什么地方)
  • @Retention:表示需要在什么级别保存该注解信息,用于描述注解的生命周期(RunTime>Class>Source)
  • @Document:说明该注解将包含在javadoc中
  • @Inherited:说明子类可以继承父类中的该注解

四、自定义注解

  • 使用 @interface 自定义注解时,自动继承java.lang.annotation.Annotation接口
  • 格式:public @interface 注解名{ 定义内容}
  • 其中的每一个方法上实际上是声明了一个配置参数
  • 方法的名称就是参数的名称
  • 返回值类型就是参数的类型(Class、String、enum)
  • 可通过default来声明参数的默认值
  • 如果只有一个参数,参数名为value
  • 注解元素必须要有值,在定义注解元素时,经常使用空字符串,0作为默认值
//自定义注解
public class Test03 {@MyAnnotation2( name="来一沓Java" )public void test(){}
}
@Target( {ElementType.TYPE,ElementType.METHOD} )//分别作用于类上和方法上
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{//注解的参数:参数类型  +  参数名 ();String name() default "";int age() default 0;int id() default -1;//如果默认值为-1,代表不存在
}

五、反射

5.1、反射机制概述

  • Refleection(反射)是Java被视为准动态语言的关键,反射机制允许程序在执行期间借助Refleection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

                Class c = Class.forName("java.lang.String")

  • 加载完类之后,在堆内存的方法区中就产生了一个Class对象(一个类只有一个Class对象,因为类只加载一次),这个对象包含了完整的类结构信息。我们可以通过这个对象知道类的结构。

5.2、反射机制提供的功能

1、在运行时判断任意一个对象所属的类;

2、在运行时构造任意一个类的对象;

3、在运行时判断任意一个类所具有的成员变量和方法

4、在运行时调用任意一个对象的方法;

5、生成动态代理

5.3、反射的优缺点

优点

反射提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类

缺点

1.使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此Java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。

2.程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。

3.使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如Applet,那么这就是个问题了。

4.由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),因此使用反射可能会导致意料之外的副作用,如代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化

Class类

在Object类中定义了getClass()方法,该方法被所有子类继承

public final Class getClass()

该方法的返回值的类型是一个Class类,该类是反射的源头,该类可以通过对象反射来获取类的名称。

Class类常用方法

方法名 作用
static ClassforName(String name) 返回指定类名name的Calss对象
Object newInstance() 调用缺省构造函数,返回Class对象的一个实例
getName() 返回该Class对象所表示的实体的名称(如类、接口、数组类、void)
Class getSuperClass() 返回当前Class对象的父类的Class对象
Class[] getinterfaces() 获取当前Class对象接口
ClassLoader getClassLoader() 返回该类的类加载器
Constructor[] getConstructors() 返回包含某些Constructor对象的数组
Method getMethod(String name,Class.. T) 返回一个Method对象,该对象的形参类型是paramType
Field[] getDeclaredFields() 返回Field对象的一个数组

有Class对象的类型有哪些

  • class:外部类、成员内部类、静态内部类、局部内部类、匿名内部类
  • 接口
  • 数组:只要元素类型与维度一样,都是同一个class对象
  • 枚举
  • 注解@interface
  • 基本数据类型
  • void

类的加载过程

  1. 类的加载:将类的class文件读入内存,并为之创建一个java.lang.Class对象,该过程是由类加载器完成
  2. 类的链接:将类的二进制数据合并到JRE中
    1. 验证:确保加载的类的信息符合JVM规范,无安全问题
    2. 准备:正式为类变量(static)分配内存并设置类变量默认初始化阶段,在方法区中进行内存分配
    3. 解析:虚拟机常量池内的常量名替换为地址的过程
  3. 类的初始化:JVM负责对类进行初始化
    1. 执行类构造器<clinit>()方法的过程:类构造器<clinit>()方法是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的;类构造器是构造类信息
    2. 当初始化一个类的时候,如发现它的父类还没有进行初始化,则需要先触发它父类的初始化
    3. 虚拟机会保证一个类的<clinit>()方法在多线程中被正确加锁和同步

类的初始化

  •  类的主动引用,一定发生类的初始化
    • 当虚拟机启动。先初始化main所在的类
    • new一个类的对象
    • 调用类的静态成员(除final常量)和静态方法
    • 使用java.lang.reflect包下的方法对类进行反射调用
    • 一个类的父类没有初始化,会先初始化它的父类
  • 类的被动引用,不会发生类的初始化
    • 访问一个静态域时,只有真正声明这个域的类才会被初始化(当子类引用父类的静态变量,子类不会被初始化)
    • 数组定义类引用,不会触发该类的初始化
    • 引用常量不会触发该类的初始化

六、获得类的信息

实体类

public class User {private String name;private int id;private int age;public User() {}public User(String name, int id, int age) {this.name = name;this.id = id;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\\'' +", id=" + id +", age=" + age +'}';}
}
//获得类的信息
public class Test04 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {Class name = Class.forName( "com.RXJ.annotation.User" );//获得类的名字System.out.println( name.getName() );//全限定名System.out.println( name.getSimpleName() );//类名System.out.println("==============");//获得类的属性Field[] fields = name.getFields();//只能找到public属性fields = name.getDeclaredFields();//找到全部属性for (Field field : fields) {System.out.println(field);}System.out.println("==============");//通过指定属性的值Field n1 = name.getDeclaredField( "name" );System.out.println( n1 );System.out.println("==============");//获得类的方法//因为重载,所以要有参数,不然它不知道去找那个Method[] methods = name.getMethods();//获取本类及其父类的所有public的方法for (Method method : methods) {System.out.println( method );}System.out.println("==============");Method[] declaredMethods = name.getDeclaredMethods();//获取本类的所有方法for (Method declaredMethod : declaredMethods) {System.out.println( declaredMethod );}System.out.println("==============");//获得指定的方法Method getName = name.getMethod( "getName", null );Method setName = name.getMethod( "setName", String.class );System.out.println( getName );System.out.println( setName );System.out.println("==============");//获得构造器Constructor[] constructors = name.getConstructors();//public的for (Constructor constructor : constructors) {System.out.println( constructor );}Constructor[] declaredConstructors = name.getDeclaredConstructors();//所有构造器for (Constructor declaredConstructor : declaredConstructors) {System.out.println( declaredConstructor );}//获得指定的构造器,只需要加参数即可}
}

七、通过反射动态创建对象

//通过反射动态创建对象
public class Test05 {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {//获得class对象Class c = Class.forName( "com.RXJ.annotation.User" );//构造一个对象User user = (User) c.newInstance();//本质上是调用了无参构造器,因此要求类必须有无参构造器System.out.println(user);//通过构造器创建对象Constructor constructor = c.getDeclaredConstructor( String.class, int.class, int.class );Object newUser = constructor.newInstance( "来一沓Java", 1, 18 );System.out.println( newUser );//通过反射调用普通方法User user2 = (User) c.newInstance();//通过反射获得一个方法Method setName = c.getDeclaredMethod( "setName", String.class );//invoke激活的意思//(对象,"方法的值")setName.invoke( user2,"来一沓Java" );System.out.println( user2 );//通过反射操作属性User user3 = (User) c.newInstance();Field name = c.getDeclaredField( "name" );//关闭权限检测,因为不能直接操作私有属性//Accessible是启动和禁用安全访问检查的空间name.setAccessible( true );name.set( user3,"来一沓Java" );System.out.println( user3.getName() );}
}

八、通过反射操作泛型

//通过反射操作泛型
public class Test06 {public void test01(Map<String,User> map, List<User> list){System.out.println("test01");}public Map<String,User> test02(){System.out.println("test02");return null;}public static void main(String[] args) throws NoSuchMethodException {Method test1 = Test06.class.getMethod( "test01", Map.class, List.class );Type[] types = test1.getGenericParameterTypes();//获得参数化类型for (Type type : types) {System.out.println(type);//ParameterizedType     参数化类型if (type instanceof ParameterizedType){Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();for (Type typeArgument : typeArguments) {System.out.println(typeArgument);}}}}
}