註解(也被稱爲 元數據)爲我們在代碼中添加信息提供了一種形式化的方法,使我們可以在稍後某個時刻非常方便地使用這些數據。
2、標準註解與元註解
Java內置了三種標準註解和四種元註解
3種標準註解(定義在java.lang中):
@Override 表示當前的方法定義將覆蓋超類中的方法。如果你不小心拼寫錯誤,或者方法簽名對不上覆蓋的方法,編譯器就會發出錯誤提示。
@Deprecated 如果程序員使用了註解爲它的元素,那麼編譯器會發出警告信息。
@SuppressWarning 關閉當前的編譯器警告信息。
4種元註解(元註解專職負責註解其他的註解)
@Target 表示該註解可以用於什麼地方。可能的ElementType參數包括:
CONSTRUCTOR:構造器的聲明
FIELD:域聲明(包括enum實例)
LOCAL_VARIABLE:局部變量聲明
METHOD:方法聲明
PACKAGE:包聲明
PARAMETER:參數聲明
TYPE:類、接口(包括註解類型)或enum聲明
@Retention 表示需要在什麼級別保存該註解信息。可選的RetentionPolicy參數包括:
SOURCE:註解將被編譯器丟棄
CLASS:註解在class文件中可用,但會被VM丟棄
RUNTIME:VM將在運行期也保留註解,因此可以通過反射機制讀取註解的信息
@Document 將此註解包含在Javadoc中
@Inherited 允許子類繼承父類中的註解
3、自定義一個註解
自定義一個簡單的註解(沒有元素的註解稱爲標記註解)
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {}
自定義一個包含元素的註解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
public int id();
public String description() default "no description";
}
4、使用自定義註解
import java.util.List;
import com.zhangmingji.annotation.UseCase;
public class PasswordUtils {
@UseCase(id=47, description="password must contain at least one numeric")
public boolean validatePassword(String password){
return (password.matches("\\w*\\d\\w*"));
}
@UseCase(id=48)
public String encryptPassword(String password){
return new StringBuilder(password).reverse().toString();
}
@UseCase(id=49, description="New passwords can't equal previously used ones")
public boolean checkForNewPassword(List<String> prePasswords, String password){
return !prePasswords.contains(password);
}
}
5、編寫註解處理器
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.zhangmingji.annotation.UseCase;
import com.zhangmingji.use.PasswordUtils;
public class UseCaseTracker {
public static void trackUseCases(List<Integer> useCases, Class<?> cl){
for(Method m : cl.getDeclaredMethods()){
UseCase uc = m.getAnnotation(UseCase.class);
if(uc != null){
System.out.println("Found Use Case:" + uc.id() + " " + uc.description());
useCases.remove(new Integer(uc.id()));
}
}
for(int i : useCases){
System.out.println("Warning: Missing use case-" + i);
}
}
public static void main(String[] args) {
List<Integer> useCases = new ArrayList<Integer>();
Collections.addAll(useCases, 47, 48, 49, 50);
trackUseCases(useCases, PasswordUtils.class);
}
}
運行結果:
Found Use Case:47 password must contain at least one numeric
Found Use Case:48 no description
Found Use Case:49 New passwords can't equal previously used ones
Warning: Missing use case-50
6、註解元素
在前面我們已經定義了一個包含元素id、description的註解,
註解元素可用的類型如下:
所有基本類型(int,float,boolean等)
String
Class
enum
Annotation
以上類型的數組
如果你使用了其他類型,那麼編譯器就會報錯;也不允許使用任何包裝類型;註解也可以作爲基本元素類型,也就是說註解可以嵌套
7、默認值限制
(1)、元素不能有不確定的值。也就是說,元素必須要麼具有默認值,要麼使用註解時提供元素的值。
(2)、對於非基本類型的元素,無論在源代碼中聲明時,或是在註解接口中定義默認值時,都不能以null作爲其值。
(3)、註解不支持繼承。
8、通過註解生成建表語句
package com.zhangmingji.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)//類、接口、或者ENUM聲明
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
public String name() default "";
}
package com.zhangmingji.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
boolean primaryKey() default false;
boolean allowNull() default true;
boolean unique() default false;
}
package com.zhangmingji.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
int value() default 0;
String name() default "";
Constraints constraints() default @Constraints;
}
package com.zhangmingji.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
String name() default "";
Constraints constraints() default @Constraints;
}
使用以上自定義的註解
package com.zhangmingji.use;
import com.zhangmingji.annotation.Constraints;
import com.zhangmingji.annotation.DBTable;
import com.zhangmingji.annotation.SQLInteger;
import com.zhangmingji.annotation.SQLString;
@DBTable(name="MEMBER")
public class Member {
@SQLString(30) String firstName;
@SQLString(50) String lastName;
@SQLInteger Integer age;
@SQLString(value=30, constraints=@Constraints(primaryKey=true))
String handle;
static int memberCount;
public String getHandle(){
return this.handle;
}
public String getFirstName(){
return this.firstName;
}
public String getLastName(){
return this.lastName;
}
public String toString(){
return handle;
}
public Integer getAge(){
return age;
}
}
實現處理器
package com.zhangmingji.process;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import com.zhangmingji.annotation.Constraints;
import com.zhangmingji.annotation.DBTable;
import com.zhangmingji.annotation.SQLInteger;
import com.zhangmingji.annotation.SQLString;
public class TableCreator {
public static void main(String[] args) throws Exception {
if(args.length < 1){
System.out.println("arguments: anotationed classes");
System.exit(0);
}
for(String className : args){
Class<?> cl = Class.forName(className);
DBTable dbTable = cl.getAnnotation(DBTable.class);
if(null == dbTable){
System.out.println("no DBTable anotations in class " + className);
continue;
}
String tableName = dbTable.name();
//如果名字爲空,則使用類名
if(tableName.length() < 1){
tableName = cl.getName().toUpperCase();
}
List<String> columDefs = new ArrayList<String>();
for(Field field : cl.getDeclaredFields()){
String columName = null;
Annotation[] anns = field.getDeclaredAnnotations();
if(anns.length < 1){
continue; //
}
if(anns[0] instanceof SQLInteger){
SQLInteger sInt = (SQLInteger) anns[0];
//如果沒有指定名稱,則使用屬性名稱
if(sInt.name().length() < 1){
columName = field.getName().toUpperCase();
}else{
columName = sInt.name();
}
columDefs.add(columName + " INT" + getConstraints(sInt.constraints()));
}
if(anns[0] instanceof SQLString){
SQLString sString = (SQLString) anns[0];
if(sString.name().length() < 1){
columName = field.getName().toUpperCase();
}else{
columName = sString.name();
}
columDefs.add(columName + " VARCHAR(" + sString.value() + ")"
+ getConstraints(sString.constraints()));
}
}
StringBuilder createCommand = new StringBuilder("CREATE TABLE " + tableName + "(");
for(String columDef : columDefs){
createCommand.append("\n " + columDef + "," );
}
String tableCreate = createCommand.substring(0, (createCommand.length() - 1)) + ");";
System.out.println("Table create SQL for " + className + " is :\n" + tableCreate);
}
}
/**
* 構造約束
* @param con
* @return
*/
public static String getConstraints(Constraints con){
String contraints = "";
if(!con.allowNull()){
contraints += " NOT NULL";
}
if(con.primaryKey()){
contraints += " PRIMARY KEY";
}
if(con.unique()){
contraints += " UNIQUE";
}
return contraints;
}
}
運行結果:
Table create SQL for com.zhangmingji.use.Member is :
CREATE TABLE MEMBER(
FIRSTNAME VARCHAR(30),
LASTNAME VARCHAR(50),
AGE INT,
HANDLE VARCHAR(30) PRIMARY KEY);
以上內容來自《Java編程思想》第四版,僅作爲學習和總結。