註解

註解

1. 註解概述

1.1 註解解釋
註釋:
	解釋代碼,給程序員看

註解:
	Java語言中的類、方法、變量、參數和包等都可以被標註。和Javadoc不同,Java標註可以通過反射獲取標註內容。在編譯器生成類文件時,標註可以被嵌入到字節碼中。Java虛擬機可以保留標註內容,在運行時可以獲取到標註內容。 當然它也支持自定義Java標註
	JDK1.5之後的特徵
	用於說明程序
	一般在框架中使用
	格式:
		@AnnotationName

文檔註釋:
	@param @return @Exeception 從根本上是一個註釋,不存在代碼編譯,不會生成對應的.class字節碼問題,只是提供給JavaDoc API文件生成工具。作爲標記生成對應的文檔。
	
註解是有一部分參與編譯
	@Override並不是沒編譯就有效果了,是因爲不管是Eclipse還是IDEA都可以預編譯Java代碼生成對應的.class文件的
1.2 註解作用
生成文檔:
	代碼中生成對應的JavaDoc API文檔
	@param @return 
	
	【IDEA JavaDoc工具使用參數】
		Other  Command Line Arguments : -encoding utf-8 -charset utf-8
		解決中文亂碼,因爲IDEA默認編碼集爲UTF-8 Windows GKB

代碼檢查:
	繼承重寫,或者說接口遵從之後的實現中,存在@Override
	
代碼數據獲取: [小框架]
	通過反射獲取指定註解中的一些內容,例如 配置,數據,操作,驗證。。。
1.3 Java中預定義的一些註解
@Override:
	重寫/實現方法的情況下,檢查方法聲明是否和父類或者接口中的方法聲明一致。強制格式檢查。
	
@Deprecated
	標註當前方法已過時,例如 Data日期類內的一些方法
	
@SuppressWarnings("all")
	壓制警告,可以用於一些代碼中存在明確無異常的情況下,壓制一些警告

2. Java中自定義註解

2.1 Java中自定義註解的方式
格式:
	public @interface AnnotationName {
		屬性列表;
	}
	
Annotation註解是可以編譯得到對應的.class字節碼文件,驗證了註解是可以參與編譯過程的

通過反編譯工具可以得到一下內容
【Annotation本質】
public interface MyAnnotation1 extends java.lang.annotation.Annotation {
}

MyAnnotation1
	本質是一個interface,同時java.lang.annotation.Annotation 子接口
package com.qfedu.a_annotation.MyAnnotation;

/**
 * 自定義註解!!!
 * public interface MyAnnotation1 extends java.lang.annotation.Annotation {
 * }
 *
 * @author Anonymous
 * @date 2020/3/10 11:01
 */
public @interface MyAnnotation1 {
    // 屬性 ==> 方法形式
}

2.2 Annotation註解屬性【難點】
屬性:
	開發書寫代碼使用註解的方式中,數據使用方式更加偏向於屬性概念。
		使用 
			1. 在書寫代碼中使用
				@MyAnnotation(id=1, name="騷磊", age=16)
			2. 利用反射時,會涉及到getXXX方法
				通過屬性名獲取對應值的概念來完成的
				
	【但是實際上是利用abstract方法來完成屬性概念的】

屬性使用的格式[實際按照方法格式操作]
	1. 屬性的值數據類型和對應具體數據 ==> 返回值類型和返回的數據
		屬性類型支持:
			a. 基本數據類型
			b. String類型
			c. 其他的註解類型
			d. 枚舉類型 
				枚舉就是一個帶有名字的常量,爲了更好的域閱讀性和操作
			e. 以上類型對相應的數組
		
		屬性值要求
			a. 定義屬性時可以使用default關鍵字,加上默認值,該屬性在使用的過程中是
			沒有強制要求屬性值,如果沒有賦予屬性值,採用對應的默認值操作,如果賦
			值,使用對應值
			
			b. 如果註解中有且只有一個value屬性,或者說註解中除value屬性之外,都有
			默認值,不管是類,方法,成員變量,包使用當前註解是可以直接在括號內加入
			對應數據類型數值、
			
			c. 如果屬性是數組類型, {}大括號保存,並且不同的內容,使用,隔開
	2. 屬性的鍵名字 ==> 方法的名字
2.3 元註解
給予註解的解釋,用於約束註解的一些操作問題
@Retention - 
	標識這個註解怎麼保存,是隻在代碼中,還是編入class文件中,或者是在運行時可以通過反射訪問。
	RetentionPolicy.RUNTIME:當前註解會編譯生成對應的.class字節碼文件,並且可以加
	載到JVM中,參與代碼執行
	RetentionPolicy.CLASS: 
	
別糾結,記下就好:
	RetentionPolicy.SOURCE:註解將被編譯器丟棄(該類型的註解信息只會保留在源碼裏,源碼經過編譯後,註解信息會被丟棄,不會保留在編譯好的class文件裏)
	@Override
	對應屬性RetentionPolicy.SOURCE
	在代碼編譯過程中,檢查方法格式是否正確,不參與代碼運行和解析。
	
@Documented 
	標記這些註解是否包含在用戶文檔中。
	是否可以通過JavaDoc工具,生成對應的API文檔
	
@Target 
	標記這個註解應該是哪種 Java 成員。
	屬性:
		ElementType
			TYPE: 當前註解可以用於類聲明
			METHOD: 當前註解可以用於方法聲明位置
			FIELD:當前註解可以用於成員變量聲明位置
@Inherited 
	標記這個註解是繼承於哪個註解類(默認 註解並沒有繼承於任何子類)
	
【重點】
	@Target目標
		可以作用範圍 類,方法,成員變量...
	@Retention
		RetentionPolicy.RUNTIME 常用
2.4 使用反射獲取註解中的內容【用途】
driverClass=com.mysql.jdbc.Driver
user=root
password=123456
url=jdbc:mysql://localhost:3306/db_name
# 配置文件保存到properties文件中,使用文件中內容讀取配置獲取屬性
# properties Properties System.getProperties();
# Properties ==> driverClass ==> com.mysql.jdbc.Driver ==> 完整的包名.類名
# Class.forName 
package com.qfedu.a_annotation.reflect;

import com.qfedu.a_annotation.Person;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;

/**
 * 解析properties文件讀取內容,使用反射創建對象,賦值成員變量
 *
 * @author Anonymous
 */
public class ReflectFile {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        Properties properties = new Properties();
        properties.load(new FileInputStream("./src/1.properties"));

        String className = properties.getProperty("className");
        String id = properties.getProperty("id");
        String name = properties.getProperty("name");
        System.out.println(className);
        Class<?> aClass = Class.forName(className);

        Person person = (Person) aClass.getConstructor().newInstance();

        System.out.println(person);

        Field declaredField = aClass.getDeclaredField("id");
        declaredField.setAccessible(true);
        declaredField.set(person, Integer.parseInt(id));

        Field declaredField2 = aClass.getDeclaredField("name");
        declaredField2.setAccessible(true);
        declaredField2.set(person, name);

        System.out.println(person);
    }
}
package com.qfedu.a_annotation.reflect;

import com.qfedu.a_annotation.MyAnnotation.MyAnnotation5;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 通過註解方式,獲取註解的屬性,創建對象,讀取數據
 * 通常用於代碼配置
 *      Servlet Listener Filter
 * @author Anonymous
 */
@MyAnnotation5(className = "com.qfedu.a_annotation.Person",
        id = 2,
        name = "騷磊")
public class ReflectAnnotatiaon {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        // 加載ReflectAnnotatiaon
        Class<ReflectAnnotatiaon> cls = ReflectAnnotatiaon.class;

        // 因爲註解是在類名之上,通過Class獲取對應的Annotation
        MyAnnotation5 annotation = cls.getAnnotation(MyAnnotation5.class);
        /*
        類似於註解的實現類
        class XXX implements MyAnnotation5 {
            public String className() {
                return "com.qfedu.a_annotation.Person";
            }
            public int id() {
                return 2;
            }
            public String name() {
                return "騷磊";
            }
        }
         */
        // 感覺是在執行對應的方法,獲取註解的屬性
        String s = annotation.className();
        int id = annotation.id();
        String name = annotation.name();

        System.out.println(s);
        System.out.println(id);
        System.out.println(name);

        Class<?> aClass = Class.forName(s);

        Constructor<?> constructor = aClass.getConstructor(Integer.class, String.class);

        Object o = constructor.newInstance(id, name);
        System.out.println(o);
    }
}
2.5 使用註解測試代碼運行【用途】
package com.qfedu.a_annotation.checkMethod;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 沒有任何的屬性,只是作爲是否需要測試的標記
 *
 * @author Anonymous
 */
// 註解有且只能用於方法上
@Target(ElementType.METHOD)
// 參與編譯和運行
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}
package com.qfedu.a_annotation.checkMethod;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 測試Tools類內的方法,如果方法帶有Check註解,執行測試,記錄異常
 *
 * @author Anonymous
 */
@ClassAnnotation(className = "com.qfedu.a_annotation.checkMethod.Utils")
public class TestProject {
    public static void main(String[] args)
            throws IOException, InvocationTargetException,
            IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InstantiationException {

        // 從註解中獲取對應的屬性
        Class<TestProject> cls  = TestProject.class;
        ClassAnnotation annotation = cls.getAnnotation(ClassAnnotation.class);
        String s = annotation.className();
        Class<?> aClass = Class.forName(s);

        Object tools = aClass.getConstructor().newInstance();
        // 獲取所有Tools類內的方法,不包括父類方法
        Method[] declaredMethods = aClass.getDeclaredMethods();

        // 出現錯誤的次數
        int count = 0;
        long l = System.currentTimeMillis();
        BufferedWriter br = new BufferedWriter(new FileWriter("./src/log.txt"));

        // 遍歷方法數組
        for (Method declaredMethod : declaredMethods) {
            declaredMethod.setAccessible(true);

            // 判斷當前方法是否帶有註解@Check,標記
            if (declaredMethod.isAnnotationPresent(Check.class)) {
                try {
                    // 執行方法
                    declaredMethod.invoke(tools);
                } catch (Exception e) {
                    count += 1;

                    // 1. 哪一個方法出現異常
                    br.write("方法:" + declaredMethod.getName());
                    br.newLine();
                    // 2. 發生什麼異常 獲取原因,獲取對應的類型
                    br.write("異常類型:" + e.getCause().getClass().getSimpleName());
                    br.newLine();
                    // 3. 異常信息
                    br.write("異常信息:" + e.getCause().getMessage());
                    br.newLine();
                    br.write("-----------------------------------------------------------------");
                    br.newLine();
                }
            }
        }

        long l1 = System.currentTimeMillis();
        br.write("出現錯誤的次數:" + count);
        br.newLine();
        br.write("總耗時:" + (l1 - l));
        br.newLine();

        br.close();
    }
}

3. 註解使用總結

1. 註解以後大多數情況下,都是使用過程,而不是自定義,會使用到框架中預處理好的註解。
2. 註解是給誰用的?
	a. 編譯器
	b. 解析代碼使用
	c. JVM運行代碼使用
3. 註解是一個標籤,有時候是做標記的,有時候標籤是有屬性的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章