1.Java反射机制:反射机制允许程序在运行时通过反射的API获取类中的描述,方法,并且允许我们在运行时改变fields内容或者去调用methods
2.反射机制提供的功能:
1.运行时判断任意一个对象所属的类
2.运行时构造任意一个类的对象
3.运行时判断任意一个类所具有的成员变量和方法
4.运行时调用任意一个对象的方法
1.运行时检测对象的类型
2.动态构造某个类的对象
3.检测类的属性和方法
4.任意调用对象的方法
5.修改构造函数、方法、属性的可见性
6.通过Class对象,获取该类实现的接口,父类,声明的属性
7.获取该类对象所拥有的方法和属性,还可以获得该类的构造方法及通过构造方法获得实例。也可以动态的调用这个实例的成员方法。
eg:
1.获取对象类型名:
obj.getClass.getName();
2.调用未知对象的方法: --- 如果对象类型是未知的,通过反射,判断它是否包含 print 方法,并调用它
public class ReflectionHelloWorld {
public static void main(String[] args){
Foo f = new Foo();
Method method;
try {
method = f.getClass().getMethod("print", new Class<?>[0]);
method.invoke(f);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Foo {
public void print() {
System.out.println("abc");
}
}
3.创建对象:
1.通过Class对象的 newInstance()方法
Class<?> c =Class.forName("demo.Foo");
Foo f = (Foo)c.newInstance();
2.获取构造函数,通过构造函数,创建对象 -- Constructor<?>对象 的newInstance()方法
import java.lang.reflect.Constructor;
public class ReflectionHelloWorld {
public static void main(String[] args){
// 创建Class实例
Class<?> c = null;
try{
c=Class.forName("myreflection.Foo");
}catch(Exception e){
e.printStackTrace();
}
// 创建Foo实例
Foo f1 = null;
Foo f2 = null;
// 获取所有的构造函数
Constructor<?> cons[] = c.getConstructors();
try {
f1 = (Foo) cons[0].newInstance();
f2 = (Foo) cons[1].newInstance("abc");
} catch (Exception e) {
e.printStackTrace();
}
f1.print();
f2.print();
}
}
class Foo {
String s;
public Foo(){}
public Foo(String s){
this.s=s;
}
public void print() {
System.out.println(s);
}
}
4.实例化Class类对象的三种方式:
Class.forName("Reflect.Demo");
new Demo().getClass();
Demo.class;
5.通过Class实例化其他类对象:
Class<?> demo=Class.forName("Reflect.Person");
Constructor<?> cons[]=demo.getConstructors();
Object object = cons[0].newInstance(args);
6.通过Class实例化接口
Class<?> intes[]=demo.getInterfaces();
7.取得其他类中的父类
Class<?> superClass=demo.getSuperclass();
8.调用类中的方法:1.获取到要执行的方法 Method method = obj.getMethod("方法名");
2.调用方法 method.invoke(对象实例)
Demo demo = Class.forName("demo.Demo");
Method method = demo.getMethod("toString");
method.invoke(demo.newInstance());
//获得所有的方法
Method[] methods = demo.getDeclaredMethods();
9.通过反射操作属性:
Field field = demo.getDeclaredField("sex");
field.setAccessiable(true);
field.set(obj,"男");
Field[] fields = Demo.class.getDeclaredFields(); //类中任何可见性的属性不包括基类
fields = Demo.class.getFields(); //只能获得public属性包括基类的
10.使用数组:
Class string = Class.forName("java.lang.String");
Object object= Array.newInstance(string, 10);
Array.set(object, 5, "this is a test");
String s = (String) Array.get(arr, 5);
</pre><pre code_snippet_id="660991" snippet_file_name="blog_20150507_1_5011483" name="code" class="html"> 11.调用对象的方法
</pre><pre code_snippet_id="660991" snippet_file_name="blog_20150507_1_5011483" name="code" class="html">public class InvokeTester {
public int add(int param1, int param2) {
return param1 + param2;
}
public String echo(String msg) {
return "echo: " + msg;
}
public static void main(String[] args) throws Exception {
Class<?> classType = InvokeTester.class;
Object invokeTester = classType.newInstance();
// Object invokeTester = classType.getConstructor(new
// Class[]{}).newInstance(new Object[]{});
// 获取InvokeTester类的add()方法
Method addMethod = classType.getMethod("add", new Class[] { int.class,
int.class });
// 调用invokeTester对象上的add()方法
Object result = addMethod.invoke(invokeTester, new Object[] {
new Integer(100), new Integer(200) });
System.out.println((Integer) result);
// 获取InvokeTester类的echo()方法
Method echoMethod = classType.getMethod("echo",
new Class[] { String.class });
// 调用invokeTester对象的echo()方法
result = echoMethod.invoke(invokeTester, new Object[] { "Hello" });
System.out.println((String) result);
}
}
输出结果:
300
echo: Hello
</pre><pre code_snippet_id="660991" snippet_file_name="blog_20150507_1_5011483" name="code" class="html">
InvokeTester类的main()方法中,运用反射机制调用一个InvokeTester对象的add()和echo()方法
add()方法的两个参数为int 类型,获得表示add()方法的Method对象的代码如下:
Method addMethod=classType.getMethod("add",new Class[]{int.class,int.class}); Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,如果参数为基本类型数据,必须转换为相应的包装类型的对象。invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回。
在本例中,尽管InvokeTester 类的add()方法的两个参数以及返回值都是int类型,调用add Method 对象的invoke()方法时,只能传递Integer 类型的参数,并且invoke()方法的返回类型也是Integer 类型,Integer 类是int 基本类型的包装类:
Object result=addMethod.invoke(invokeTester, new Object[]{new Integer(100),new Integer(200)});
System.out.println((Integer)result); //result 为Integer类型
12.<span style="font-family: Arial, Helvetica, sans-serif;">动态创建和访问数组</span><pre name="code" class="java">
java.lang.Array 类提供了动态创建和访问数组元素的各种静态方法。 例程ArrayTester1 类的main()方法创建了一个长度为10 的字符串数组,接着把索引位置为5 的元素设为“hello”,然后再读取索引位置为5 的元素的值:
package reflect3;
import java.lang.reflect.Array;
public class ArrayTester1 {
public static void main(String[] args) throws Exception{
Class<?> classType = Class.forName("java.lang.String");
// 创建一个长度为10的字符串数组
Object array = Array.newInstance(classType, 10);
// 把索引位置为5的元素设为"hello"
Array.set(array, 5, "hello");
// 获得索引位置为5的元素的值
String s = (String) Array.get(array, 5);
System.out.println(s);
}
}
输出结果:
hello
13.
运行时变更field内容
与先前两个动作相比,“变更field内容”轻松多了,因为它不需要参数和自变量。首先调用Class的getField()并指定field名称。获得特定的Field object之后便可直接调用Field的get()和set()。
package reflect3;
import java.lang.reflect.Field;
public class RefField {
public double x;
public Double y;
public static void main(String args[]) throws NoSuchFieldException,
IllegalAccessException {
Class c = RefField.class;
Field xf = c.getField("x");
Field yf = c.getField("y");
RefField obj = new RefField();
System.out.println("变更前x=" + xf.get(obj));
//变更成员x值
xf.set(obj, 1.1);
System.out.println("变更后x=" + xf.get(obj));
System.out.println("变更前y=" + yf.get(obj));
//变更成员y值
yf.set(obj, 2.1);
System.out.println("变更后y=" + yf.get(obj));
}
}
运行结果:
变更前x=0.0
变更后x=1.1
变更前y=null
变更后y=2.1
=============================反射应用========================== JDK动态代理: 与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。 public class MyHandler implements InvocationHandler { private Object target; public Object bind(Object target) { this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("事物开始"); Object result = method.invoke(target, args); System.out.println("事物结束"); return result; } }Cglib动态代理 JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。 public class MyCglib implements MethodInterceptor { private Object target; public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 回调方法 enhancer.setCallback(this); // 创建代理对象 return enhancer.create(); } @Override // 回调方法 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("事物开始"); proxy.invokeSuper(obj, args); System.out.println("事物结束"); return null; } }
3.Java Reflection APIs:
1.Class类: 代表一个类
2.Field类: 代表类的成员变量(类的属性)
3.Method类: 代表类的方法
4.Constructor类: 代表类的构造方法
5.Array类: 提供了动态创建数组,以及访问数组的元素的静态方法
4.获取要处理的类或对象的Class对象: 三种方式: ---- 获取Class对象,通过完整类名或对象的方式
1.使用Class的 forName()
eg:Class.forName("java.lang.Class");
2.使用XXX.class语法
eg:String.class
3.使用具体某个对象.getClass方法
eg:
String str = "abc";
Class<?> clz = str.getClass;
先看一个例子:这个例子对于指定的类名,使用反射来获取该类中的所有声明的方法,(使用第一种获取Class对象的方法)(主要代码如下:):
|
/**
* 使用反射来获取Class中的生命的方法,包括私有的方法
*/
import
java.lang.reflect.Method;
public
class
Reflection1 {
public
static
void
main(String[] args)
throws
Exception {
//使用Class去调用静态方法forName()获得java.lang.Class的Class对象
Class<?> tClass = Class.forName(
"java.lang.Class"
);
//获取该class中声明的所有方法
Method[] methods = tClass.getDeclaredMethods();
for
(Method method : methods) {
System.out.println(method);
}
}
}
|
5.Class类的API常用方法:
①: getName():获得类的完整名字。
②: getFields():获得类的public类型的属性。
③: getDeclaredFields():获得类的所有属性。
④: getMethods():获得类的public类型的方法。
⑤: getDeclaredMethods():获得类的所有方法。
⑥:getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字parameterTypes参数指定方法的参数类型。
⑦:getConstructors():获得类的public类型的构造方法。
⑧:getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型。
⑨:newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
先看上面的⑧和⑨其中都能生成对象,但是因为构造函数有无参和有参构造函数两种,所以我们分两种情况考虑
情况一:如果是无参的构造函数来生成对象:
<a>首先我们去获取Class对象,然后直接通过Class对象去调用newInstance()方法就可以
1
2
|
Class<?> tclass = Reflection2.
class
;
Object reflection2 = classType.newInstance();
|
1
2
3
4
|
Class<?> classType = Reflection2.
class
;
// Object reflection2 = classType.newInstance();
Constructor<?> constructor = classType.getConstructor(
new
Class[] {});
Object reflection2 = constructor.newInstance(
new
Object[] {});
|
情况二:现在是有参构造函数,那我们只有一种方法来通过反射生成对象:
1
2
3
|
Class<?> tClass = Person.
class
;
Constructor cons = classType.getConstructor(
new
Class[]{String.
class
,
int
.
class
});
Object obj = cons.newInstance(
new
Object[]{“zhangsan”,
19
});
|
接下来根据以上的一些常用的方法,使用反射举几个例子(使用反射来访问类中的方法):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package
com.jiangqq.reflection;
import
java.lang.reflect.Constructor;
import
java.lang.reflect.Method;
/**
* 反射练习二,使用反射访问类中的方法
*
* @author jiangqq
*
*/
public
class
Reflection2 {
public
int
sum(
int
a,
int
b) {
return
a + b;
}
public
String addStr(String str) {
return
"This is the:"
+ str;
}
public
static
void
main(String[] args)
throws
Exception {
Class<?> classType = Reflection2.
class
;
// Object reflection2 = classType.newInstance();
Constructor<?> constructor = classType.getConstructor(
new
Class[] {});
Object reflection2 = constructor.newInstance(
new
Object[] {});
// 通过反射进行反射出类中的方法
Method sumMethod = classType.getMethod(
"sum"
,
new
Class[] {
int
.
class
,
int
.
class
});
//invoke方法的值永远只是对象
Object result1 = sumMethod.invoke(reflection2,
new
Object[] {
6
,
10
});
System.out.println((Integer) result1);
Method addStrMethod = classType.getMethod(
"addStr"
,
new
Class[] { String.
class
});
Object result2 = addStrMethod.invoke(reflection2,
new
Object[] {
"tom"
});
System.out.println((String) result2);
}
}
|
④:通过反射机制调用对象的私有方法,访问对象的私有变量....
我们大家都知道,在Java语言中,如果我们对某些变量,或者方法进行private的声明,然后我们在其他类中进行不能去调用这些方法和变量,但是通过反射机制,这些私有声明将不复存在【提醒一点:在写程序的时候,我们最好不要故意经常去使用反射机制来打破这种私有保护...】
要实现这种功能,我们需要用到AccessibleObject类中的public void setAccessible(boolean flag)方法:
使用这个方法,把参数flag设置成true,然后我们的field或者method就可以绕过Java语言的语法访问的检查
具体使用如下:
<a>使用反射去访问私有方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package
com.jiangqq.reflection;
public
class
Test01 {
private
String getName(String name) {
return
"This i:"
+ name;
}
}
package
com.jiangqq.reflection;
import
java.lang.reflect.Method;
public
class
TestPrivate01 {
public
static
void
main(String[] args)
throws
Exception {
Test01 p =
new
Test01();
Class<?> classType = p.getClass();
Method method = classType.getDeclaredMethod(
"getName"
,
new
Class[] { String.
class
});
method.setAccessible(
true
);
Object object = method.invoke(p,
new
Object[] {
"tom"
});
System.out.println((String)object);
}
}
|
<b>使用反射机制去访问私有变量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package
com.jiangqq.reflection;
public
class
Test02 {
private
String name=
"张三"
;
private
String getName()
{
return
name;
}
}
package
com.jiangqq.reflection;
import
java.lang.reflect.Field;
import
java.lang.reflect.Method;
public
class
TestPrivate02 {
public
static
void
main(String[] args)
throws
Exception {
Test02 p =
new
Test02();
Class<?> classType = p.getClass();
Field field = classType.getDeclaredField(
"name"
);
//设置true,使用可以绕过Java语言规范的检查
field.setAccessible(
true
);
//对变量进行设置值
field.set(p,
"李四"
);
Method method = classType.getDeclaredMethod(
"getName"
,
new
Class[] {});
method.setAccessible(
true
);
Object object = method.invoke(p,
new
Object[] {});
System.out.println((String) object);
}
}
|
反射技术的应用
反射技术大量用于Java设计模式和框架技术,最常见的设计模式就是工厂模式(Factory)和单例模式(Singleton)。
- 单例模式(Singleton)
保证在Java应用程序中,一个类Class只有一个实例存在。
如:建立目录,数据库连接都需要这样的单线程操作。
目的: 节省内存空间,保证我们所访问到的都是同一个对象。
单例模式要求保证唯一 可以通过静态变量保证单列模式的唯一性。
单例模式有以下两种形式:
- 第一种形式:
package reflect;
public class Singleton {
/*
* 注意这是private私有的构造方法, 只供内部调用
* 外部不能通过new的方式来生成该类的实例
*/
private Singleton() {
}
/*
* 在自己内部定义自己一个实例,是不是很奇怪?
* 定义一个静态的实例,保证其唯一性
*/
private static Singleton instance = new Singleton();
// 这里提供了一个供外部访问本class的静态方法,可以直接访问
public static Singleton getInstance() {
return instance;
}
}
/**
*测试单例模式
*/
class SingRun{
public static void main(String[] args){
//这样的调用不被允许,因为构造方法是私有的。
//Singleton x=new Singleton();
//得到一个Singleton类实例
Singleton x=Singleton.getInstance();
//得到另一个Singleton类实例
Singleton y=Singleton.getInstance();
//比较x和y的地址,结果为true。说明两次获得的是同一个对象
System.out.println(x==y);
}
}
- 第二种形式:
public class Singleton {
//先申明该类静态对象
private static Singleton instance = null;
//创建一个静态访问器,获得该类实例。加上同步机制,防止两个线程同时进行对对象的创建
public static synchronized Singleton getInstance() {
//如果为空,则生成一个该类实例
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
两种形式大体上是差不多的
- 工厂模式(Factory)
工厂模式:著名的Jive论坛 ,就大量使用了工厂模式。
为什么工厂模式是如此常用?因为工厂模式利用Java反射机制和Java多态的特性可以让我们的程序更加具有灵活性。用工厂模式进行大型项目的开发,可以很好的进行项目并行开发。就是一个程序员和另一个程序员可以同时去书写代码,而不是一个程序员等到另一个程序员写完以后再去书写代码。其中的粘合剂就是接口和配置文件。
利用接口可以将调用和实现相分离。
那么这是怎么样去实现的呢?工厂模式可以为我们解答。
我们先来了解一下软件的生命周期: 分析、设计、编码、调试、测试。
分析: 就是指需求分析,就是知道这个软件要做成什么样子,要实现什么样的功能。
设计: 设计的时候要考虑到怎么样高效的实现这个项目,如果让一个项目团队并行开发。这时候,通常先设计接口,把接口给实现接口的程序员和调用接口的程序员,在编码的时候,两个程序员可以互不影响的实现相应的功能,最后通过配置文件进行整合。
代码示例:
/**
*
*定义接口
*/
package com.reflection.Factory;
/**
* @interface: InterfaceTest
* @author: chenliang
* @description: 声明一个测试接口
*/
public interface InterfaceTest {
public void getName();//定义获得名字的方法
}
实现这个接口,重写其中定义的方法
接口实现方:
/**
* @class: Test1
* @author: chenliang
* @description: 测试类
*/
package com.reflection.Factory;
public class Test1 implements InterfaceTest {
@Override
public void getName() { //测试方法
System.out.println("test1");
}
}
/**
* @class: Test2
* @author: chenliang
* @description: 测试类
*/
package com.reflection.Factory;
public class Test2 implements InterfaceTest {
@Override
public void getName() { //测试方法
System.out.println("test2");
}
}
可以发现,当接口定义好了以后,不但可以规范代码,而且可以让程序员有条不紊的进行功能的实现。实现接口的程序员根本不用去管,这个类要被谁去调用。
那么怎么能获得这些程序员定义的对象呢?在工厂模式里,单独定义一个工厂类来实现对象的生产,注意这里返回的接口对象。
工厂类,生产接口对象:
/**
* 本类为工厂类,用于生成接口对象
*/
/**
* @class: Factory
* @author: chenliang
* @description: 模仿创建一个工厂类
*/
package com.reflection.Factory;
import java.io.InputStream;
import java.util.Properties;
public class Factory{
private static Properties prop=new Properties(); //创建私有的静态的Properties对象
static{ //静态代码块,在创建这个类的实例之前执行,且只执行一次,用来加载配置文件
try {
InputStream ips = Factory.class.getClassLoader().getResourceAsStream("file.properties"); //加载配置文件
prop.load(ips);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 单例模式,保证该类只有一个Factory对象
*/
private static Factory factory=new Factory();
private Factory(){} //构建一个私有构造方法
public static Factory getFactory(){ //返回工厂对象的方法
return factory;
}
/**
* 本方法为公有方法,用于生产接口对象
* @return: InterfaceTest接口对象
*/
public InterfaceTest getInterface(){
InterfaceTest interfaceTest = null; //定义接口对象
try {
String classInfo = prop.getProperty("test"); //根据键,获得值,这里的值是类的全路径
Class<?> c = Class.forName(classInfo); //利用反射,生成Class对象
Object obj = c.newInstance(); //获得该Class对象的实例
interfaceTest = (InterfaceTest)obj; //将Object对象强转为接口对象
} catch (Exception e) {
e.printStackTrace();
}
return interfaceTest; //返回接口对象
}
}
配置文件内容:
test=com.reflection.Factory.Test1
通过这个类,可以发现,在调用的时候,得到的是个接口对象。而一个接口变量可以指向实现了这个接口的类对象。在利用反射的时候,我们并没有直接把类的全路径写出来,而是通过键获得值。这样的话,就有很大的灵活性,只要改变配置文件里的内容,就可以改变我们调用的接口实现类,而代码不需做任何改变。在调用的时候,我们也是通过接口调用,甚至我们可以连这个接口实现类的名字都不知道。
调用方:
/**
* @author: chenliang
* @class: FactoryTest
* @description: 测试工厂类
*/
package com.reflection.Factory;
public class FactoryTest {
public static void main(String[] args) {
Factory factory = Factory.getFactory(); //获得工厂类的实例
InterfaceTest interObj = factory.getInterface(); //调用获得接口对象的方法,获得接口对象
interObj.getName(); //调用接口定义的方法
}
}
上面的代码就是调用方法。大家可以发现,在调用的时候,我们根本没有管这个接口定义的方法要怎么样去实现它,我们只知道这个接口定义这个方法起什么作用就行了。上面代码运行结果要根据配置文件来定。如果配置文件里的内容是test= com.reflection.Factory.Test2。那么表示调用com.reflection.Factory.Tes2这个类里实现接口的方法,这时候打印“test2”。如果配置文件里的内容是test= com.reflection.Factory.Test1。那么表示调用com.reflection.Factory.Test1这个类里实现接口的方法,这时候打印“test1”。
反射机制是框架技术的原理和核心部分。通过反射机制我们可以动态的通过改变配置文件(以后是XML文件)的方式来加载类、调用类方法,以及使用类属性。这样的话,对于编码和维护带来相当大的便利。在程序进行改动的时候,也只会改动相应的功能就行了,调用的方法是不用改的。更不会一改就改全身。
利用反射解析自定义注解
Java注解能够提供代码的相关信息,同时对于所注解的代码结构又没有直接影响。在这篇教程中,我们将学习Java注解,如何编写自定义注解,注解的使用,以及如何使用反射解析注解。
注解是Java 1.5引入的,目前已被广泛应用于各种Java框架,如Hibernate,Jersey,Spring。注解相当于是一种嵌入在程序中的元数据,可以使用注解解析工具或编译器对其进行解析,也可以指定注解在编译期或运行期有效。
在注解诞生之前,程序的元数据存在的形式仅限于java注释或javadoc,但注解可以提供更多功能,它不仅包含元数据,还能作用于运行期,注解解析器能够使用注解决定处理流程。举个例子,在Jersey webservice中,我们在一个方法上添加了PATH注解和URI字符串,在运行期,jersey会对其进行解析,并决定作用于指定URI模式的方法。
在Java中创建自定义注解
创建自定义注解与编写接口很相似,除了它的接口关键字前有个@符号。我们可以在注解中定义方法,先来看个例子,之后我们会继续讨论它的特性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package
com.journaldev.annotations;
import
java.lang.annotation.Documented;
import
java.lang.annotation.ElementType;
import
java.lang.annotation.Inherited;
import
java.lang.annotation.Retention;
import
java.lang.annotation.RetentionPolicy;
import
java.lang.annotation.Target;
@Documented
@Target
(ElementType.METHOD)
@Inherited
@Retention
(RetentionPolicy.RUNTIME)
public
@interface
MethodInfo{
String author()
default
"Pankaj"
;
String date();
int
revision()
default
1
;
String comments();
}
|
- 注解方法不能有参数。
- 注解方法的返回类型局限于原始类型,字符串,枚举,注解,或以上类型构成的数组。
- 注解方法可以包含默认值。
- 注解可以包含与其绑定的元注解,元注解为注解提供信息,有四种元注解类型:
1. @Documented – 表示使用该注解的元素应被javadoc或类似工具文档化,它应用于类型声明,类型声明的注解会影响客户端对注解元素的使用。如果一个类型声明添加了Documented注解,那么它的注解会成为被注解元素的公共API的一部分。
2. @Target – 表示支持注解的程序元素的种类,一些可能的值有TYPE, METHOD, CONSTRUCTOR, FIELD等等。如果Target元注解不存在,那么该注解就可以使用在任何程序元素之上。
3. @Inherited – 表示一个注解类型会被自动继承,如果用户在类声明的时候查询注解类型,同时类声明中也没有这个类型的注解,那么注解类型会自动查询该类的父类,这个过程将会不停地重复,直到该类型的注解被找到为止,或是到达类结构的顶层(Object)。
4. @Retention – 表示注解类型保留时间的长短,它接收RetentionPolicy参数,可能的值有SOURCE, CLASS, 以及RUNTIME。
Java内置注解
Java提供3种内置注解。
1. @Override – 当我们想要覆盖父类的一个方法时,需要使用该注解告知编译器我们正在覆盖一个方法。这样的话,当父类的方法被删除或修改了,编译器会提示错误信息。大家可以学习一下为什么我们总是应该在覆盖方法时使用Java覆盖注解。
2. @Deprecated – 当我们想要让编译器知道一个方法已经被弃用(deprecate)时,应该使用这个注解。Java推荐在javadoc中提供信息,告知用户为什么这个方法被弃用了,以及替代方法是什么。
3. @SuppressWarnings – 这个注解仅仅是告知编译器,忽略它们产生了特殊警告,比如:在java泛型中使用原始类型。它的保持性策略(retention policy)是SOURCE,在编译器中将被丢弃。
我们来看一个例子,展示了如何使用内置注解,以及上述示例中提及的自定义注解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
package
com.journaldev.annotations;
import
java.io.FileNotFoundException;
import
java.util.ArrayList;
import
java.util.List;
public
class
AnnotationExample {
public
static
void
main(String[] args) {
}
@Override
@MethodInfo
(author =
"Pankaj"
, comments =
"Main method"
, date =
"Nov 17 2012"
, revision =
1
)
public
String toString() {
return
"Overriden toString method"
;
}
@Deprecated
@MethodInfo
(comments =
"deprecated method"
, date =
"Nov 17 2012"
)
public
static
void
oldMethod() {
System.out.println(
"old method, don't use it."
);
}
@SuppressWarnings
({
"unchecked"
,
"deprecation"
})
@MethodInfo
(author =
"Pankaj"
, comments =
"Main method"
, date =
"Nov 17 2012"
, revision =
10
)
public
static
void
genericsTest()
throws
FileNotFoundException {
List l =
new
ArrayList();
l.add(
"abc"
);
oldMethod();
}
}
|
我相信这个例子是很明了的,展示了不同场景下注解的使用方式。
Java注解解析
我们将使用Java反射机制从一个类中解析注解,请记住,注解保持性策略应该是RUNTIME,否则它的信息在运行期无效,我们也不能从中获取任何数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
package
com.journaldev.annotations;
import
java.lang.annotation.Annotation;
import
java.lang.reflect.Method;
public
class
AnnotationParsing {
public
static
void
main(String[] args) {
try
{
for
(Method method : AnnotationParsing.
class
.getClassLoader()
.loadClass((
"com.journaldev.annotations.AnnotationExample"
))
.getMethods()) {
// checks if MethodInfo annotation is present for the method
if
(method
.isAnnotationPresent(com.journaldev.annotations.MethodInfo.
class
)) {
try
{
// iterates all the annotations available in the method
for
(Annotation anno : method.getDeclaredAnnotations()) {
System.out.println(
"Annotation in Method '"
+ method +
"' : "
+ anno);
}
MethodInfo methodAnno = method
.getAnnotation(MethodInfo.
class
);
if
(methodAnno.revision() ==
1
) {
System.out.println(
"Method with revision no 1 = "
+ method);
}
}
catch
(Throwable ex) {
ex.printStackTrace();
}
}
}
}
catch
(SecurityException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
|
以上程序的输出是:
1
2
3
4
5
6
|
Annotation
in
Method
'public java.lang.String com.journaldev.annotations.AnnotationExample.toString()'
: @com.journaldev.annotations.MethodInfo(author=Pankaj, revision=1, comments=Main method,
date
=Nov 17 2012)
Method with revision no 1 = public java.lang.String com.journaldev.annotations.AnnotationExample.toString()
Annotation
in
Method
'public static void com.journaldev.annotations.AnnotationExample.oldMethod()'
: @java.lang.Deprecated()
Annotation
in
Method
'public static void com.journaldev.annotations.AnnotationExample.oldMethod()'
: @com.journaldev.annotations.MethodInfo(author=Pankaj, revision=1, comments=deprecated method,
date
=Nov 17 2012)
Method with revision no 1 = public static void com.journaldev.annotations.AnnotationExample.oldMethod()
Annotation
in
Method
'public static void com.journaldev.annotations.AnnotationExample.genericsTest() throws java.io.FileNotFoundException'
: @com.journaldev.annotations.MethodInfo(author=Pankaj, revision=10, comments=Main method,
date
=Nov 17 2012)
|
注解API非常强大,被广泛应用于各种Java框架,如Spring,Hibernate,JUnit。可以查看《Java中的反射》获得更多信息。