一、類加載器
1.什么是類加載器,作用是什么?
類加載器就加載字節碼文件(.class)
2.類加載器的種類
類加載器有三種,不同類加載器加載不同的
1)BootStrap:引導類加載器:加載都是最基礎的文件
2)ExtClassLoader:擴展類加載器:加載都是基礎的文件
3)AppClassLoader:應用類加載器:三方jar包和自己編寫java文件
怎么獲得類加載器?(重點)
ClassLoader 字節碼對象.getClassLoader();
package cn.qlq; public class Demo { public static void main(String[] args) { //獲得Demo字節碼文件的類加載器 Class clazz = Demo.class;//獲得Demo的字節碼對象 ClassLoader classLoader = clazz.getClassLoader();//獲得類加載器 //getResource的參數路徑相對classes(src) //獲得classes(src)下的任何的資源 String path = classLoader.getResource("cn/qlq/jdbc.properties").getPath(); //classLoader.getResourceAsStream(""); System.out.println(path); } }
二、注解 @xxx
注解在目前而言最主流的應用:代替配置文件
關於配置文件與注解開發的優缺點:
注解優點:開發效率高 成本低
注解缺點:耦合性大 並且不利於后期維護
1.jdk5提供的注解
@Override:告知編譯器此方法是覆蓋父類的
@Deprecated:標注過時
@SuppressWarnings:壓制警告
發現的問題:
不同的注解只能在不同的位置使用(方法上、字段上、類上)
2.自定義注解:
1.注解是給機器看的,注釋是給程序員看的,這是兩者的區別。現在各大框架都在使用注解,而我們程序員需要做的就是知道如何使用注解,而對其底層原理卻不清楚,今天看了一段視頻,現在淺談一下注解的使用。
2.注解的使用:
大體分為三部分: 定義注解、使用注解、解析注解。在框架中定義與解析框架都已經為我們做好了。
(1)定義注解:定義一個簡單的注解:
1 import java.lang.annotation.ElementType; 2 import java.lang.annotation.Retention; 3 import java.lang.annotation.RetentionPolicy; 4 import java.lang.annotation.Target; 5 6 7 @Target(ElementType.METHOD) 8 @Retention(RetentionPolicy.RUNTIME) 9 public @interface MyAnnotation { 10 //定義注解的屬性,這不是方法 11 String name();//必選注解 12 int value() default 20;//有屬性就是可選屬性 13 }
做一個解釋吧:定義前面的@Target,與@Retention又稱為元注解,限制定義的注解的特性:
@Target定義注解使用的位置,值有:看名字一般就很明顯了吧

@Retention:限定注解的可見范圍:值有

一個圖解釋上面的可見范圍:

(2)注解的使用:
1 package annotation; 2 3 public class UseMyAnnotion { 4 5 // 這里只用一個屬性,另一個value屬性有默認值不用設置 6 @MyAnnotation(name = "QizoZhi") 7 public void show(String str){ 8 System.out.println(str); 9 } 10 }
(3)解析注解:這里使用了底層的映射原理
1 package annotation; 2 3 import java.lang.reflect.InvocationTargetException; 4 import java.lang.reflect.Method; 5 6 public class MyAnnotationParser { 7 8 public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { 9 // 獲取字節碼對象 10 Class clazz = UseMyAnnotion.class; 11 Method method = clazz.getMethod("show", String.class); 12 // 獲取方法上面的注解 13 MyAnnotation annotation = method.getAnnotation(MyAnnotation.class); 14 // 獲取注解屬性值 15 System.out.println(annotation.name()+"\t"+annotation.value()); 16 17 // 取到值就可以根據業務處理數據 18 19 //激活方法,也就是讓方法執行 20 method.invoke(new UseMyAnnotion(), "HH"); 21 } 22 }
到這里注解基本上就完了,我們需要了解底層實現原理,真正開發的時候定義注解以及解析注解一般不用我們寫,我們只需要學會使用框架提供的注解,但是使用注解的開發也有缺點。對后期的維護增加了困難。
Java注解之Retention、Documented、Target介紹參考:https://www.cnblogs.com/qlqwjy/p/8516551.html
補充:關於注解繼承
有關Annotation的繼承說明:
1. 方法上的注解可以被繼承,如果方法被重寫將不會被繼承。(接口和類上的方法都是如此)
1、類和抽象類上的注解可以被繼承,而且注解加上@Inherited才可以被繼承
3、接口上的注解不能被繼承
測試如下:
注解:
package annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { // 定義注解的屬性,這不是方法 String name();// 必選注解 int value() default 20;// 有屬性就是可選屬性 }
(1)類和方法的注解測試
父類:
package annotation; @MyAnnotation(name = "類注解") public class ParentClass { @MyAnnotation(name = "show方法上面") public void show(String str) { } @MyAnnotation(name = "show2方法上面") public void show2(String str) { } }
子類:
package annotation; public class SubClass extends ParentClass { @Override public void show2(String str) { } }
測試:
package annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class MyAnnotationParser { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException { // 獲取類上的注解 MyAnnotation annotationsByType = SubClass.class.getAnnotation(MyAnnotation.class); printAnnotation(annotationsByType); // 獲取方法上面的注解 Method method = SubClass.class.getMethod("show", String.class); MyAnnotation annotation = method.getAnnotation(MyAnnotation.class); printAnnotation(annotation); Method method2 = SubClass.class.getMethod("show2", String.class); MyAnnotation annotation2 = method2.getAnnotation(MyAnnotation.class); printAnnotation(annotation2); } private static void printAnnotation(MyAnnotation annotation) { if (annotation != null) { System.out.println(annotation.name() + "\t" + annotation.value()); } else { System.out.println("null"); } } }
結果: 類上的注解沒繼承到,方法show1的注解繼承到了,方法show2被重寫了所以沒有獲取到繼承的注解。
null
show方法上面 20
null
修改注解加上@Inherited
package annotation; 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; @Target({ ElementType.METHOD, ElementType.TYPE, ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface MyAnnotation { // 定義注解的屬性,這不是方法 String name();// 必選注解 int value() default 20;// 有屬性就是可選屬性 }
再次測試結果: 類上的注解也可以獲取到
類注解 20 show方法上面 20 null
(2)測試接口的注解:接口上的注解不能被繼承,接口中抽象方法的注解可以繼承(與類中方法一樣)
package annotation; 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; @Target({ ElementType.METHOD, ElementType.TYPE, ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface MyAnnotation { // 定義注解的屬性,這不是方法 String name();// 必選注解 int value() default 20;// 有屬性就是可選屬性 }
package annotation; @MyAnnotation(name = "類注解") public interface ParentInterface { @MyAnnotation(name = "show方法上面") public void show(String str); @MyAnnotation(name = "show2方法上面") public void show2(String str); }
package annotation; public interface SubInterface extends ParentInterface { @Override public void show2(String str); }
package annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class MyAnnotationParser { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException { // 獲取類上的注解 MyAnnotation annotationsByType = SubInterface.class.getAnnotation(MyAnnotation.class); printAnnotation(annotationsByType); // 獲取方法上面的注解 Method method = SubInterface.class.getMethod("show", String.class); MyAnnotation annotation = method.getAnnotation(MyAnnotation.class); printAnnotation(annotation); Method method2 = SubInterface.class.getMethod("show2", String.class); MyAnnotation annotation2 = method2.getAnnotation(MyAnnotation.class); printAnnotation(annotation2); } private static void printAnnotation(MyAnnotation annotation) { if (annotation != null) { System.out.println(annotation.name() + "\t" + annotation.value()); } else { System.out.println("null"); } } }
結果:
null
show方法上面 20
null
補充:@Target({}) 用於組合復雜的注解,例如:
@Target({})修飾的Index注解
package javax.persistence; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; @Target({}) @Retention(RUNTIME) public @interface Index { String name() default ""; String columnList(); boolean unique() default false; }
Table注解中使用上述注解:
/** <a href="http://www.cpupk.com/decompiler">Eclipse Class Decompiler</a> plugin, Copyright (c) 2017 Chen Chao. */ package javax.persistence; import java.lang.annotation.Target; import java.lang.annotation.Retention; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Target(TYPE) @Retention(RUNTIME) public @interface Table { String name() default ""; String catalog() default ""; String schema() default ""; UniqueConstraint[] uniqueConstraints() default {}; Index[] indexes() default {}; }
使用方法如下:
@Entity @Getter @Setter @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @Table(indexes = { @Index(name = "type", columnList = "type"), @Index(name = "enable", columnList = "enable") }) public class Dictionary extends AbstractSequenceEntity { private static final long serialVersionUID = 581214652229924448L; private String value; private String type; private Boolean enable; }
補充:@Inherited注解的使用
如果注解聲明的時候加上此元注解,則該注解可以被子類繼承(只對類上的注解生效);字段以及方法聲明的注解無效。測試如下:
源碼如下:
/** <a href="http://www.cpupk.com/decompiler">Eclipse Class Decompiler</a> plugin, Copyright (c) 2017 Chen Chao. */ package java.lang.annotation; /** * Indicates that an annotation type is automatically inherited. If * an Inherited meta-annotation is present on an annotation type * declaration, and the user queries the annotation type on a class * declaration, and the class declaration has no annotation for this type, * then the class's superclass will automatically be queried for the * annotation type. This process will be repeated until an annotation for this * type is found, or the top of the class hierarchy (Object) * is reached. If no superclass has an annotation for this type, then * the query will indicate that the class in question has no such annotation. * * <p>Note that this meta-annotation type has no effect if the annotated * type is used to annotate anything other than a class. Note also * that this meta-annotation only causes annotations to be inherited * from superclasses; annotations on implemented interfaces have no * effect. * * @author Joshua Bloch * @since 1.5 * @jls 9.6.3.3 @Inherited */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Inherited { }
源碼都解釋了,只對類有效。對實現接口也無效。
注解如下:
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; @Target({ ElementType.METHOD, ElementType.TYPE, ElementType.FIELD }) //@Inherited @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { // 定義注解的屬性,這不是方法 String name();// 必選注解 int value() default 20;// 有屬性就是可選屬性 }
1. 測試接口繼承:
父接口
@MyAnnotation(name = "Parent") public interface ParentInterface { @MyAnnotation(name = "ParentInterface method1") void method1(); }
子接口
public interface SubInterface extends ParentInterface { }
測試代碼:
import java.lang.reflect.Field; import java.lang.reflect.Method; import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.commons.lang3.reflect.MethodUtils; public class PlainTest { public static void main(String[] args) throws ClassNotFoundException { // 獲取類上注解 Class<?> class1 = ClassUtils.getClass("SubInterface"); MyAnnotation annotation = class1.getAnnotation(MyAnnotation.class); System.out.println("===類==="); System.out.println(class1); System.out.println(annotation); // 獲取方法上注解 Method matchingAccessibleMethod = MethodUtils.getMatchingAccessibleMethod(class1, "method1"); MyAnnotation annotation2 = matchingAccessibleMethod.getAnnotation(MyAnnotation.class); System.out.println("===方法==="); System.out.println(matchingAccessibleMethod); System.out.println(annotation2); } }
(1)注解未聲明@Inherited,結果如下:
===類===
interface SubInterface
null
===方法===
public abstract void ParentInterface.method1()
@MyAnnotation(value=20, name=ParentInterface method1)
(2)注解聲明@Inherited,結果如下:
===類===
interface SubInterface
null
===方法===
public abstract void ParentInterface.method1()
@MyAnnotation(value=20, name=ParentInterface method1)
結果:
| 未聲明@Inherited | 聲明@Inherited | |
| 父接口類上注解能否被繼承 | 否 | 否 |
| 父接口方法上注解能否被繼承 | 能 | 能 |
2. 測試接口實現
(1)父接口:
@MyAnnotation(name = "Parent") public interface ParentInterface { @MyAnnotation(name = "ParentInterface method1") void method1(); }
(2)實現類
public class Children implements ParentInterface { @Override public void method1() { } }
測試代碼:
import java.lang.reflect.Field; import java.lang.reflect.Method; import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.commons.lang3.reflect.MethodUtils; public class PlainTest { public static void main(String[] args) throws ClassNotFoundException { // 獲取類上注解 Class<?> class1 = ClassUtils.getClass("Children"); MyAnnotation annotation = class1.getAnnotation(MyAnnotation.class); System.out.println("===類==="); System.out.println(class1); System.out.println(annotation); // 獲取方法上注解 Method matchingAccessibleMethod = MethodUtils.getMatchingAccessibleMethod(class1, "method1"); MyAnnotation annotation2 = matchingAccessibleMethod.getAnnotation(MyAnnotation.class); System.out.println("===方法==="); System.out.println(matchingAccessibleMethod); System.out.println(annotation2); } }
(1)注解未聲明@Inherited,結果如下:
===類===
class Children
null
===方法===
public void Children.method1()
null
(2)注解聲明@Inherited,結果如下:
===類===
class Children
null
===方法===
public void Children.method1()
null
結果:
| 注解未聲明@Inherited | 注解聲明@Inherited | |
| 實現類類能否繼承接口注解 | 否 | 否 |
| 實現類方法能否繼承接口注解 | 否 | 否 |
3.測試類繼承:
父類:
import lombok.Data; @MyAnnotation(name = "Parent") @Data public class Parent { @MyAnnotation(name = "Parent name") private String name; @MyAnnotation(name = "ParentInterface method1") public void method1() { } @MyAnnotation(name = "ParentInterface method2") public void method2() { } }
子類:
public class Children extends Parent { @Override public void method2() { } }
測試代碼:
import java.lang.reflect.Field; import java.lang.reflect.Method; import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.commons.lang3.reflect.MethodUtils; public class PlainTest { public static void main(String[] args) throws ClassNotFoundException { // 獲取類上注解 Class<?> class1 = ClassUtils.getClass("Children"); MyAnnotation annotation = class1.getAnnotation(MyAnnotation.class); System.out.println("===類==="); System.out.println(class1); System.out.println(annotation); // 獲取方法上注解 Method matchingAccessibleMethod = MethodUtils.getMatchingAccessibleMethod(class1, "method1"); MyAnnotation annotation2 = matchingAccessibleMethod.getAnnotation(MyAnnotation.class); System.out.println("===方法==="); System.out.println(matchingAccessibleMethod); System.out.println(annotation2); // 獲取重寫的方法上注解 Method method2 = MethodUtils.getMatchingAccessibleMethod(class1, "method2"); MyAnnotation method2Anno = method2.getAnnotation(MyAnnotation.class); System.out.println("===override方法==="); System.out.println(method2); System.out.println(method2Anno); // 獲取字段上注解 Field field = FieldUtils.getField(class1, "name", true); if (field != null) { MyAnnotation annotation3 = field.getAnnotation(MyAnnotation.class); System.out.println("===屬性==="); System.out.println(field); System.out.println(annotation3); } } }
測試結果如下:
(1)注解未聲明@Inherited,結果如下:
===類===
class Children
null
===方法===
public void Parent.method1()
@MyAnnotation(value=20, name=ParentInterface method1)
===override方法===
public void Children.method2()
null
===屬性===
private java.lang.String Parent.name
@MyAnnotation(value=20, name=Parent name)
(2)注解聲明@Inherited,結果如下:
===類===
class Children
@MyAnnotation(value=20, name=Parent)
===方法===
public void Parent.method1()
@MyAnnotation(value=20, name=ParentInterface method1)
===override方法===
public void Children.method2()
null
===屬性===
private java.lang.String Parent.name
@MyAnnotation(value=20, name=Parent name)
結果:
| 注解未聲明@Inherited | 注解聲明@Inherited | |
| 子類能否繼承父類類上注解 | 否 | 能 |
| 子類繼承的屬性能否繼承注解 | 能 | 能 |
| 子類繼承的方法(未覆蓋)能否繼承注解 | 能 | 能 |
| 子類繼承的方法(覆蓋)能否繼承注解 | 否 | 否 |
