天天看点

Java注解and反射注解反射

注解

注解和注释类似,注释是写给程序员看的,为了方便程序员看懂程序的代码。

而注解不单单是给程序员看,更重要的是给程序看。不同的注解有不同的功能,除了系统定义的注解以外,我们也可以自定义注解。

注解是以@开始的。

系统内置的常见注解

@Override

这个用于判断方法是否是重写放到的注解,它会检查你的方法名是不是与父类中的方法同名,如果不是会报错,如果是则不报错。@Override注解只能用于方法。

@Deprecated

单词的意思是过时的。可以用于修饰方法,表示这个方法已经过时,应该选择别的方法使用,即使用了不会报错,只是不建议用。

@SuppressWarnings

压制警告,主要的作用是消除警告。究竟要压制什么警告,写在括号中。

@SuppressWarnings("unused")
int a = 10;
		
@SuppressWarnings({"rawtypes","unused"})
ArrayList list = new ArrayList();
           

@FunctionalInterface

函数式接口,用来修饰接口,接口中只能有一个方法。

自定义注解

想要创建一个注解,语法格式如下:

public @interface 注解名 {
	注解的属性
}
           

定义一个注解,需要指定以下信息:

  1. 注解能用在什么地方。(属性,方法,构造器,类…)

    @Target({ElementType.FIELD, ElementType.METHOD,…})

    元注解:@Target是元注解的一种。

    所谓的元注解就是注解注解的注解。

    @Target的取值范围:

    TYPE:用于注解类,接口,枚举。

    FIELD:用于注解属性。

    METHOD:用于注解方法。

    PARAMETER:用于注解方法的参数

    CONSTRUCTOR:用于注解构造器

    LOCAL_VARIABLE:用于注解局部变量

    ANNOTATION_TYPE:用于注解一个注解

    PACKAGE:用于注解包

    TYPE_PARAMETER:用于注解类型参数

    TYPE_USE:用于注解用了一个类型

  2. 注解的声明周期多久。(原码阶段,字节码阶段,运行期)

    @Retention(RetentionPolicy.SOURCE)

    @Retention也是一个元注解。

    取值范围

    RetentionPolicy.SOURCE:仅源代码有效,不会被编译成class文件。一般定义为SOURCE的注解,仅仅做检查用,检查是不是方法重写等。

    RetentionPolicy.CLASS:会编译成class文件,但是运行期无效。

    RetentionPolicy.RUNTIME:运行期有效,也就意味着,在运行期可以获取注解对象,读取注解中写入的信息。-----很有用,尤其是自己写类库的时候。

    其他元注解:

    @Documented用于表示注解会一起写入API文档(javadoc.exe)

    @Inherited一旦一个注解定义为可继承的,注解如果加给了父类,那么之类会把注解继承过来。

  3. 注解有哪些属性。

    注解可以包含属性,属性由3部分构成。 类型 属性名() 默认值;

    属性名的写法与 无参方法相同。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})
public @interface MyAnnotation {
	int a();
	String[] b();
	double c() default 3.14;
}
           

注解的一些注意事项:

  1. 注解中的属性名后面必须要有().
  2. 如果注解中的属性是一个数组,使用时,如果数组只有一个值,大括号可以省略
  3. 如果注解只有一个属性,且属性名为value,在使用时,value可以省略不写。
public class Person {
	@MyAnnotation(a = 10, b = {"hello","hehe"})
	private String name;
	private int age;
	
	@MyAnnotation(a = 20, b = "world")
	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;
	}
	
	@MyAnnotation(a = 100, b = "lanou" ,c = 25.8)
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
		
	}
	
	@Deprecated
	public void test() {
		
	}
	@MyAnnotation2(12)
	public void test2() {
		@SuppressWarnings(value = "unused")
		int a = 10;
		
		@SuppressWarnings({"rawtypes","unused"})
		ArrayList list = new ArrayList();
		
	}
	
}
           

学习注解的第一个目标:能看懂注解,看懂系统或者第三方类库中注解的含义,能会使用注解。

学习注解的终极目标:某天自己写类库,写注解,让别人用。注解往往是结合反射使用的。

反射

生活中的反射

太阳光照射到镜子上,会发生反射。反射时光的方向会发生改变(略微的反向)。

代码中的反射,改变了我们使用对象的方式。之前:对象调方法,对象设置属性。反射刚好反了一下。方法调用对象,属性调用对象。

在Java中反射是比较底层的内容,想要实现反射需要使用Class类。

什么是Class类

类是相同事物的抽象。类在描述一类事物。例如:人都有姓名,性别,年龄,还有吃饭、睡觉、打豆豆。

通过创建对象,可以形成各种不同的人。

类是由谁来描述的呢?Class就是描述类的类。类也有共性,属性,方法,构造器,父类,接口。每一个类都有对应一个Class实例。你可以认为一个Class对象对应的是一个xx.class文件。

Class没有创建方法,每个类的Class实例是在这个类的class文件加载到内存中的时候,由ClassLoader创建。我们只能获取Class类的对象。

Class类有以下几种获取方式:

  1. 类名.class
    基本数据类型也可以获取对应的Class实例 用法是int.class double.class 普通类获取Class对象的方法:String.class Person.class
  2. 对象.getClass()
  3. Class.forName(“类的完整字符串描述”)
//获取Class对象的方式一: 类名.class
		Class class1 = int.class;
		System.out.println(class1);
		Class class2 = String.class;
		System.out.println(class2);
		Class class3 = Person.class;
		System.out.println(class3);
		
		//获取Class对象的方式二: 对象.getClass();
		String str = "hello";
		Class class4 = str.getClass();
		System.out.println(class4);
		System.out.println(class4 == class2);
		Person p = new Person();
		Class class5 = p.getClass();
		System.out.println(class5);
		System.out.println(class3 == class5);
		
		
		//获取Class对象的方式三: Class.forName("完整类名")
		Class class6 = Class.forName("java.util.ArrayList");
		System.out.println(class6);
		
		Class class7 = Class.forName("com.lanou.lessonclass.Person");
		System.out.println(class7);
           

反射

在Java中提供了一套机制,这种机制允许你通过Class对象来获取类的全部信息(属性,方法,构造器,接口,父类,注解等等),获取之后,你可以对他们做你想做的事情。哪怕是私有的也可以操作。

通过Class对象获取类的属性

在Java中Filed类是用于描述属性的类。

通过Class对象获取类的属性的方式有4个:

getFields() //获取类中所有的public属性(含继承链上的public属性)

getField(String name) //获取类中指定名称的public属性。(含继承链上的 public属性)

getDeclaredFields() //获取类中所有的属性(不含继承链上的属性)

getDeclaredFiled(String name) //获取类中指定名称的属性

通过Class对象获取类的构造器

在Java中Constructor类用于描述类的构造器。

通过Class对象获取类的构造器的方式有4个:

getConstructor(Class …parameterTypes) // 获取本类中指定参数的public构造器

getConstructors() //获取本类中全部的public构造器

getDeclaredConstructor(Class…parameterTypes) //获取本类中指定参数的构造器

getDeclaredConstructors() //获取本类中全部的构造器。

通过Class对象获取类的方法

在Java中Method是用于描述类的方法。

通过Class对象获取类的方法的方式有4种:

getMethods() // 获取全部的public方法(含继承链上的方法)

getMethod(String methodName, Class…parameterTypes) //获取指定名称的public方法

getDeclaredMethods() //获取本类中所有的方法

getDeclaredMethod(String methodName, Class…paremeterTypes) //获取本类中指定名称的方法。

反射的作用

反射给的第一感觉:脱了裤子放屁!

实际上反射的价值就在于能动态获取信息!

虽然写了很多很多代码,但是代码一旦写成,复用性很高。而且不需要导包!因为Class.forName(“类名”)可以以字符串的方式获取类名,再通过类名得到类的Class对象。

反射多用于写框架,几乎所有的框架底层都是靠反射实现的。