1. 注解
1.1 注解的定義
注解就是源代碼的元數(shù)據(jù)抵恋,通熟的講就是代碼中的標簽纲菌。注解就有如下的特點:
- 注解是一個附屬品挠日,依賴于其他元素(包、類翰舌、方法嚣潜、屬性等等)存在。
- 注解本身沒有作用椅贱,在恰當?shù)臅r候由外部程序進行解析才會發(fā)生作用懂算。
1.2 注解的分類
- 按來源分
- JDK 自帶注解,例如:@Override, @Deprecated, @SuppressWornings 庇麦。
- 第三方注解计技。
- 自定義注解。
- 按生命周期劃分
- SOURCE:只存在于源代碼中山橄,編譯成 class 文件就不存在了垮媒。
- Class:存在于源代碼中和 class 文件中。
- RUNTIME:注解保留到運行時航棱。
1.3 元注解
元注解指的是用于修飾注解的注解睡雇,包括如下幾個:
- @Retention:指明 Annotation 的生命周期,傳入的值是一個枚舉類型饮醇,可選值為:
RetentionPolicy.SOURCE
RetentionPolicy.CLASS
RetentionPolicy.RUNTIME
- @Target:指明 Annotation 可以修飾程序哪些元素它抱,傳入的值為ElemetType[] 類型,值可為:
-
ElementType.CONSTRUCTOR
:構造器 -
ElementType.FIELD
:屬性 -
ElementType.LOCAL_VARIABLE
:局部變量 -
ElementType.METHOD
:方法 -
ElementType.PACKAGE
:包 -
ElementType.PARAMETER
:參數(shù) -
ElementType.TYPE
:類朴艰、接口(包括注解類型和 enum 聲明)
-
- @Documented:使用此修飾的注解將會被 javadoc 工具提取成文檔观蓄,使用此注解,其 @Retention 必須被設置為
RetentionPolicy.RUNTIME
呵晚。 - @Inherited:具有繼承性蜘腌。
1.4 自定義注解
自定義注解需要注意的問題:
使用 @interface 關鍵字定義。
自動繼承
java.lang.annotation.Annotation
接口饵隙。配置參數(shù)的類型只能是八大基本類型撮珠、String、Class、enum芯急、Annotation 和對應的數(shù)組類型勺届。
-
配置參數(shù)聲明的語法格式如下([] 表示可省略):
類型 變量名() [default 默認值];
如果只有一個配置參數(shù),其參數(shù)名必須為 value娶耍。
如果定義的注解含有配置參數(shù)免姿,那在使用注解時,必須指定參數(shù)值榕酒,指定形式為:“參數(shù)名=參數(shù)值”胚膊。如果只有一個參數(shù),直接寫參數(shù)值即可想鹰,定義中指定了默認值的參數(shù)可以不指定值紊婉,但沒有的一定要指定值。
沒有成員的注解為標記辑舷,包含成員的稱為元數(shù)據(jù)喻犁。
1.5 注解的解析
Info.java:
package com.hkl;
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.TYPE, ElementType.METHOD})
public @interface Info {
String info();
String birthday();
int age() default 0;
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@interface Desc{
String value();
}
App.java:
package com.hkl;
import java.lang.reflect.Method;
/**
* Hello world!
*
*/
@Info(info = "hkl", birthday = "2019/7/20")
@Desc("這是一個類")
public class App
{
@Info(info = "hkl", birthday = "2019/7/20", age = 22)
@Desc("這是一個方法")
public static void main( String[] args )
{
// 解析注解
try {
Class clazz = Class.forName("com.hkl.App");
// 獲取類修飾的注解
System.out.println("---------類中的注解---------");
if(clazz.isAnnotationPresent(Info.class)){
Info classInfo = (Info) clazz.getAnnotation(Info.class);
System.out.println(classInfo.info());
System.out.println(classInfo.birthday());
System.out.println(classInfo.age());
}
if(clazz.isAnnotationPresent(Desc.class)){
Desc classDesc = (Desc)clazz.getAnnotation(Desc.class);
System.out.println(classDesc.value());
}
// 獲取方法修飾的注解
Method[] methods = clazz.getMethods();
System.out.println("---------方法中的注解解析---------");
for(Method method : methods){
if(method.isAnnotationPresent(Desc.class)){
Desc methodDesc = (Desc)method.getAnnotation(Desc.class);
System.out.println(methodDesc.value());
}
if(method.isAnnotationPresent(Info.class)){
Info methodInfo = (Info)method.getAnnotation(Info.class);
System.out.println(methodInfo.info());
System.out.println(methodInfo.birthday());
System.out.println(methodInfo.age());
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
2. 反射
2.1 什么反射
反射指的是程序在運行期間借助反射 API 取得任何類的內(nèi)部信息,并通過這些內(nèi)部信息去操作對應對象的內(nèi)部屬性和方法何缓。
任何一個類肢础,在第一次使用時,就會被 JVM 加載到堆內(nèi)存的方法區(qū)中碌廓。JVM 加載類成功后传轰,就會在方法區(qū)中產(chǎn)生一個對應的 Class 對象(一個類只要一個 Class 對象),這個 Class 對象包含被加載類的全部結(jié)構信息氓皱。
2.2 獲取 Class 對象的常用方式
(1)類的 class 屬性
每一個類路召,都有一個 class 靜態(tài)屬性,這個靜態(tài)屬性就是類對應的 Class 對象波材。
Class<Person> cl1 = Person.class;
(2)Object 對象 的 getClass() 方法
Person p1 = new Person();
Class<Person> cl2 = (Class<Person>) p1.getClass();
(3)通過 Class 類的 forName() 方法(最常用)
try {
Class cl3 = Class.forName("com.llm.hkl.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
(4)通過 ClassLoader 類(不常用)
ClassLoader cl = Person.class.getClassLoader();
try {
Class cl4 = cl.loadClass("com.llm.hkl.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
2.3 反射的基本使用
反射的基本使用包括創(chuàng)建對象股淡,設置屬性和調(diào)用方法。Class 對象中大多數(shù) get 方法有 Declared 和無 Declared廷区,他們的區(qū)別是:
- 無 Declared:只能獲取到 public 修飾的唯灵,包括當前類和所有父類。
- 有 Declared:獲取到當前類所有的(含有 private)隙轻,但不包括其父類埠帕。
Person 類:
public class Person {
private String name;
private int age;
public String habbit;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private Person(String name, int age, String habbit) {
this.name = name;
this.age = age;
this.habbit = habbit;
}
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;
}
public String getHabbit() {
return habbit;
}
public void setHabbit(String habbit) {
this.habbit = habbit;
}
private String say(String str){
String str1 = name+"說:"+str;
System.out.println(str1);
return str1;
}
public void eat(String food){
System.out.println(name+"吃"+food);
}
@Override
public String toString() {
return "["+name+","+age+","+habbit+"]";
}
}
測試方法:
public class PersonTest {
@Test
public void ReflexTest() throws Exception{
System.out.println("---------- new 方式創(chuàng)建對象 ----------");
// 普通方式創(chuàng)建 Person 對象
Person p1 = new Person("hkl", 22);
// 直接設置屬性
p1.habbit = "編程";
// 調(diào)用方法
System.out.println(p1);
//無法直接設置私有屬性和調(diào)用私有方法
//p1.name = "hkl";
//p1.say(""Hello);
//反射方式創(chuàng)建對象
System.out.println("---------- 反射方式創(chuàng)建對象 ----------");
Class<Person> clazz = Person.class;
// 調(diào)用無參構造器參加對象
Constructor<Person> constructor1 = clazz.getConstructor();
Person p2 = constructor1.newInstance();
System.out.println(p2);
// 通過有參構造器
Constructor<Person> constructor2 = clazz.getConstructor(String.class, int.class);
Person p3 = constructor2.newInstance("hkl", 22);
System.out.println(p3);
// 通過私有的構造器
Constructor<Person> constructor3 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
constructor3.setAccessible(true);
Person p4 = constructor3.newInstance("hkl", 22, "編程");
System.out.println(p4);
//通過反射設置公有屬性
Field personFeildHabbit = clazz.getDeclaredField("habbit");
personFeildHabbit.set(p2, "編程");
//通過反射設置私有屬性
Field personFeildAge = clazz.getDeclaredField("age");
personFeildAge.setAccessible(true);
personFeildAge.set(p2, 18);
//通過反射調(diào)用方法
Method personMethodToString = clazz.getDeclaredMethod("toString");
// 方法的返回值為調(diào)用方法的返回值
String str = (String)personMethodToString.invoke(p2);
System.out.println(str);
// 通過反射調(diào)用私有方法
Method personMethodSay = clazz.getDeclaredMethod("say", String.class);
personMethodSay.setAccessible(true);
String str2 = (String)personMethodSay.invoke(p2, "Hello");
System.out.println(str2);
}
}
2.5 設計模式:代理模式
使用一個代理對象將原始對象包裝起來, 然后用該代理對象取代原始對象。任何對原始對象的調(diào)用都要通過代理對象玖绿,代理對象決定是否以及何時將方法調(diào)用轉(zhuǎn)到原始對象上敛瓷。
2.5.1 靜態(tài)代理
代理類和原始對象在編譯期間就確定下來了,不利于程序的擴展斑匪,且每一個代理只能為一個接口服務呐籽,這就會在開發(fā)的過程中產(chǎn)生多大的代理類。
靜態(tài)代理的實現(xiàn):
- 代理類和原始對象實現(xiàn)相同的接口。
- 代理類保持原始對象的引用狡蝶。
- 代理類里調(diào)用原始對象的方法庶橱。
ClothFactory 接口:
package com.hkl.proxy;
public interface ClothFactory {
String producer();
}
ClothFactoryProxy 類:
package com.hkl.proxy;
public class ClothFactoryProxy implements ClothFactory{
private ClothFactory clothFactory;
public ClothFactoryProxy(ClothFactory clothFactory) {
this.clothFactory = clothFactory;
}
@Override
public String producer() {
System.out.println("代理對象做一些準備");
clothFactory.producer();
System.out.println("代理對象做一些收尾工作");
return null;
}
}
NikeFactory 類:
package com.hkl.proxy;
public class NikeFactory implements ClothFactory{
public NikeFactory() {
}
@Override
public String producer() {
System.out.println("Nike 正在生產(chǎn)衣服");
return null;
}
}
測試方法:
@Test
public void staticProxyTest(){
// 創(chuàng)建被代理對象
NikeFactory nikeFactory = new NikeFactory();
// 創(chuàng)建代理對象
ClothFactoryProxy clothFactoryProxy = new ClothFactoryProxy(nikeFactory);
// 通過代理對象調(diào)用被代理對象的方法
clothFactoryProxy.producer();
}
2.5.2 動態(tài)代理模式
動態(tài)代理是通過 Java 的反射機制實現(xiàn)的。通過動態(tài)代理贪惹,只需一個代理對象就可以代理所有的對象苏章。
Humen :接口
package com.llm.proxy;
public interface Human {
String belief();
void eat(String food);
}
SuperMan:類
package com.hkl.proxy;
public class SuperMan implements Human {
@Override
public String belief() {
return "我相信,我能行!";
}
@Override
public void eat(String food) {
System.out.println("正在吃"+food);
}
}
動態(tài)代理類:
package com.hkl.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxy {
// 獲取代理對象
public static Object getInstance(Object obj){
MyInvocation h = new MyInvocation();
h.bind(obj);
// 動態(tài)的創(chuàng)建對象
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), h);
}
}
class MyInvocation implements InvocationHandler{
private Object obj;
/**
* 綁定被代理對象
* @param obj
*/
public void bind(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 代理對象調(diào)用被代理對象的方法
Object ret = method.invoke(obj, args);
return ret;
}
}
測試方法:
@Test
public void dynamicProxyTest(){
SuperMan sm = new SuperMan();
Human h = (Human)DynamicProxy.getInstance(sm);
System.out.println(h.belief());;
h.eat("麻辣燙");
NikeFactory nikeFactory = new NikeFactory();
ClothFactory clothFactory = (ClothFactory) DynamicProxy.getInstance(nikeFactory);
clothFactory.producer();
}