Java之註解與反射
註解(Annotation)簡介
註解(Annotation)是從JDK5.0引入的新技術
Annotation作用:註解(Annotation)可以被其他程式如編譯器等讀取
Annotation格式:@"註釋名",當然可以新增一些引數值(形如:
@Retention(RetentionPolicy.RUNTIME)
)它是JDK1.5及以後版本引入的一個特性,與類、介面、列舉是在同一個層次。它可以宣告在包、類、欄位、方法、區域性變數、方法引數等的前面,用來對這些元素進行說明,註釋。
Annotation
註解可以通過反射去讀取
註解的定義
內建註解
@Override
定義在java.lang.Override
中,此註釋只適用於修辭方法,表示宣告一個方法打算重寫超類中的另一個方法。
@Deprecated
定義在java.lang.Deprecated
中,此註釋只適用於修辭方法,屬性,類,表示不支援使用這樣的元素
@SupperWarnings
定義在java.lang.SupperWarnings
中,用來抑制編譯告警時的警告資訊,需要新增一個引數才能正確使用
元註解
元註解的作用就是負責註解其他註解,java定義了4個標準的meta-annotation型別:@Target/@Retention/@Document/@Inherited
元註解中重點為@Target
和@Retention
@Target:用於描述註解的作用範圍(比如作用在某個類或某個方法)
@Retention:描述註解的生命週期,對於反射就是可以在什麼時候獲取該註解(SOURCE<CLASS<RUNTIME)
自定義註解
使用@interface可以自定義註解。
@interface 註解名{
//自定義的註解內容
//如果引數只有一個值,建議寫value
String value();
}
例子
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class TestAnnotationDemo3 {
//測試自定義註解
@MyAnnotation2("qifei")
@MyAnnotation(name = "wuhu", id = 10)
public void test(){
System.out.println("test");
}
}
@Target(value = {ElementType.TYPE,ElementType.METHOD}) //作用於 方法和類
@Retention(RetentionPolicy.RUNTIME) //作用於 執行時
@interface MyAnnotation{
//定義引數
String[] name() default ""; //引數預設為空,引數名為name
int id();
}
@Target(value = {ElementType.TYPE,ElementType.METHOD}) //作用於 方法和類
@Retention(RetentionPolicy.RUNTIME) //作用於 執行時
@interface MyAnnotation2{
//如果引數只有一個值,建議寫value
String value();
}
反射(Reflection)機制
Java反射(Reflection)是Java非常重要的動態特性,通過使用反射我們不僅可以獲取到任何類的成員方法、成員變數、構造方法等資訊,還可以動態建立Java類例項、呼叫任意的類方法、修改任意的類成員變數值等。註解和反射是各種java web框架的底層實現機制與靈魂。
Class類
反射首先要用到的就是Class類,Class類是一個描述類的類,但是Class本身也是一個類,一個在記憶體中載入的類在JVM中只會有一個對應的Class的例項話物件,通過Class可以完整的得到一個類中所有被載入的結構。Class類就是反射的根源
獲取Class類例項
1、已知具體的類,通過類的class屬性獲取
Class c = Object.class;
2、已知某個類的例項,通過呼叫getClass()方法獲取class物件
Person person = new Person();
Class c = person.getClass();
3、已知一個類的全類名,且該類在類路徑下,可通過Class類的靜態方法forName()獲取class物件
Class c = Class.forName("com.reflection.demmo");
4、基本內建型別的包裝類的Type屬性獲取class物件
Class c = Integer.TYPE;
例子:
//測試class類的建立方式
public class ReflactionDemo02 {
public static void main(String[] args) throws ClassNotFoundException {
Person student = new Student();
System.out.println("這個人是:" + student.name);
//獲取Student類的class例項物件
//1、通過getClass() 獲得
Class c1 = student.getClass();
System.out.println(c1.hashCode());
//2、通過Class類的forName() 獲得
Class c2 = Class.forName("com.reflaction.Student");
System.out.println(c2.hashCode());
//3、通過student類的class屬性獲得
Class c3 = Student.class;
System.out.println(c3.hashCode());
//4、基本內建型別的包裝類的Type屬性
Class c4 = Integer.TYPE;
System.out.println(c4.hashCode());
//獲得父類型別
Class c5 = c1.getSuperclass();
System.out.println(c5);
}
}
class Person{
String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public Student(){
this.name = "學生";
}
}
class Teacher extends Person{
public Teacher(){
this.name = "老師";
}
}
這個人是:學生
1627674070
1627674070
1627674070
1360875712
class com.reflaction.Person
具有Class例項物件的資料型別
//所有型別的class物件
public class ReflactionDemo03 {
public static void main(String[] args) {
Class c1 = Object.class; //類
Class c2 = Runnable.class; //介面
Class c3 = String[].class; //一維陣列
Class c4 = String[][].class; //二維陣列
Class c5 = Override.class; //註解
Class c6 = ElementType.class; //列舉
Class c7 = Integer.class; //基本資料型別包裝類
Class c8 = void.class; //void
Class c9 = Class.class; //Class
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
}
}
class java.lang.Object
interface java.lang.Runnable
class [Ljava.lang.String;
class [[Ljava.lang.String;
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class
反射獲取類執行時的完整結構
Field()
Field[] fields = c1.getFields(); //getFields 只能找到pubilc屬性
fields = c1.getDeclaredFields(); //getDeclaredFields 可以找到所有屬性
for (Field field : fields) {
System.out.println(field);
}
Method()
getMethods()
獲得本類和繼承類的所有public方法
getDeclaredMethods()
獲取本類的所有方法
getMethod("方法名", 引數型別.class)
獲得指定的方法
//獲得本類和繼承類的所有public方法
Method[] methods = c1.getMethods();
for (Method method : methods
) {
System.out.println("getMethods(): " + method);
}
//獲取本類的所有方法
methods = c1.getDeclaredMethods();
for (Method method : methods
) {
System.out.println("getDeclaredMethods(): " + method);
}
//獲得指定的方法
Method setName = c1.getMethod("setName", String.class);
Method getName = c1.getMethod("getName");
System.out.println(setName);
System.out.println(getName);
Constructor()
//獲取所有的構造器
Constructor[] constructors = c1.getConstructors(); //獲取本類和父類的所有構造方法
for (Constructor constructor : constructors
) {
System.out.println(constructor);
}
System.out.println("=====================================");
constructors = c1.getDeclaredConstructors(); //獲取本類所有構造方法
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("=====================================");
//獲取指定的構造器
Constructor constructor = c1.getConstructor(String.class, int.class, int.class);
System.out.println(constructor);
反射類構造器獲得例項化物件
- 反射獲得User類的class物件
Class.forName()
- 呼叫有參或無參構造方法例項化物件
class.newInstance()
public static void main(String[] args) throws Exception{
//反射獲得User類的class物件
Class c1 = Class.forName("com.reflaction.User");
User user = (User) c1.newInstance(); //呼叫無參構造例項化物件
System.out.println(user);
//獲得有參構造方法
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
//通過構造器獲得例項話物件
User ago = (User) constructor.newInstance("ago", 18, 1); //有參構造獲得例項化物件
System.out.println(ago);
}
User{name='null', age=0, id=0}
User{name='ago', age=18, id=1}
反射獲取呼叫普通方法
方式1: 反射獲取目標類的class物件(Class.forName()) ==> 獲取有參構造器(class.getDeclaredConstructor()) ==> 獲取目標類例項化物件(constructor1.newInstance(param1, param2, param3 ,...)) ==> 呼叫方法(例項化物件.Method())
Class c2 = Class.forName("com.reflaction.User");
Constructor constructor1 = c2.getDeclaredConstructor(String.class, int.class, int.class);
User zh1z3ven = (User) constructor1.newInstance("zh1z3ven", 20, 2);
System.out.println(zh1z3ven.getAge());
System.out.println(zh1z3ven.getName());
zh1z3ven.setId(20);
System.out.println(zh1z3ven.getId());
方式2: 反射獲取目標類class物件(Class.forName()) ==> 獲取指定方法(class.getMethod("方法名", 引數)) ==> 啟用執行方法(.invoke(目標物件, 引數))
Class c2 = Class.forName("com.reflaction.User");
User user2 = (User) c2.newInstance();
Method setName = c2.getMethod("setName", String.class);
setName.invoke(user2, "kfei");
System.out.println(user2.getName());
反射操作屬性
- 獲取class物件
class.getDeclaredField("屬性名")
獲取屬性.set()
方法設定屬性- 若為private修飾,出現無許可權操作異常,可用
.setAccessible(true)
關閉安全檢測,獲取操作許可權
Class c3 = Class.forName("com.reflaction.User");
User user4 = (User) c3.newInstance();
Field name = c3.getDeclaredField("name");
name.setAccessible(true);
name.set(user4 ,"zgo");
System.out.println(user4.getName());
Invoke(Object obj, Obj... args)
Object對應原方法返回值,若無返回值,返回null
若原方法為靜態方法,形參Object obj可為null
若原方法形參列表為空,Obj... args可為null
若原方法宣告為private,在呼叫invoke()前,顯示呼叫方法物件的
.setAccessible(true)
方法,關閉安全檢測,獲取訪問許可權
反射呼叫Runtime()
import org.apache.commons.io.IOUtils;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class RuntimeExecDemo {
public static void main(String[] args) throws Exception{
String command = "ifconfig";
Class c = Class.forName("java.lang.Runtime"); //獲取Runtime類的class物件
Constructor constructor = c.getDeclaredConstructor(); //獲取構造器
constructor.setAccessible(true); //關閉安全檢測,暴力反射
Object runtime = constructor.newInstance(); //構造runtime物件
Method exec = c.getMethod("exec", String.class); //獲取exec方法
Process process = (Process) exec.invoke(runtime, command); //啟用執行exec
//獲取命令回顯結果
InputStream inputStream = process.getInputStream(); //獲取process輸入流中輸出的資料
String ifconfig = IOUtils.toString(inputStream, "GBK"); //位元組流轉字元流
System.out.println(ifconfig); //列印結果
}
}
反射操作泛型
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
public class TestFanXingDemo {
public void test01(Map<String, User> map, List<User> list){
System.out.println("test01");
}
public Map<String, User> test02(){
System.out.println("test02");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Method method = TestFanXingDemo.class.getMethod("test01", Map.class, List.class);
method.setAccessible(true); //關閉安全檢測,暴力反射
Type[] genericParameterTypes = method.getGenericParameterTypes(); //獲得泛型的引數型別
for (Type genericParameterType : genericParameterTypes) {
System.out.println("#genericParameterType: " + genericParameterType); //遍歷泛型型別
if (genericParameterType instanceof ParameterizedType){ //判斷泛型引數型別是否是引數化型別
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); //獲得真實引數資訊
//迴圈遍歷引數
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
}
反射操作註解
ORM = Object Relationship Mapping ==> 物件關係對映
類和表對應,類的屬性和表的欄位對應,物件和記錄對應
利用註解和反射完成類和表的結構的對映關係
import java.lang.annotation.*;
import java.lang.reflect.Field;
public class TestDemo12 {
//反射操作註解
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.reflaction.Student2");
//通過反射獲得全部註解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//獲得註解的value值
TableStudent tableStudent = (TableStudent) c1.getAnnotation(TableStudent.class);
String value = tableStudent.value();//獲取註解的value
System.out.println(value);
//獲取指定類的註解value
Field f = c1.getDeclaredField("name");
FieldStudent annotation = f.getAnnotation(FieldStudent.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@TableStudent("db_student")
class Student2{
@FieldStudent(columnName = "db_id", type = "int", length = 10)
private int id;
@FieldStudent(columnName = "db_age", type = "int", length = 10)
private int age;
@FieldStudent(columnName = "db_name", type = "varchar", length = 3)
private String name;
public Student2() {
}
public Student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public void setId(int id) {
this.id = id;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
//類名的註解
@Target(ElementType.TYPE) //作用範圍為類
@Retention(RetentionPolicy.RUNTIME) //作用為執行時可獲取
@interface TableStudent{
String value();
}
//屬性的註解
@Target(ElementType.FIELD) //作用範圍為類
@Retention(RetentionPolicy.RUNTIME) //作用為執行時可獲取
@interface FieldStudent{
String columnName();
String type();
int length();
}
結尾
參考文章:
https://www.cnblogs.com/nice0e3/p/13498308.html
https://www.cnblogs.com/nice0e3/p/13494147.html
關於Process類、反射操作泛型和註解因為暫時剛需不大且還沒研究透,記錄的較少,後門會另出文章記錄一下。
到此JavaSE部分基本就學完了(除了GUI)後面要開JavaWeb和框架了,沖沖衝!