注解和反射

注解

什么是注解?

通俗易懂的來(lái)說(shuō):注釋是給人看的,注解可以給機(jī)器看。

注解是從JDK5.0引入的新技術(shù)。

內(nèi)置注解

@Override:定義在java.lang.Override中,此方法只適用于修辭方法。表示一個(gè)方法聲明打算重寫父類中另一個(gè)方法。

@Deprecated:定義在java.lang.Deprecated中,表示當(dāng)前方法已經(jīng)過(guò)時(shí),不建議使用或者已經(jīng)存在了更好的方法來(lái)選擇。

@SuppressWarnings:定義在java.lang.SuppressWarnings中,用來(lái)抑制編譯時(shí)的警告信息。

元注解

@Target:表明在哪里使用這個(gè)注解,類或者方法

@Retention:表明在運(yùn)行時(shí)使用還是在源碼范圍使用runtime>class>sources

@Documented//表明是否生成Javadoc

@Inherited//表明子類可以繼承父類的注解

package com;

import java.lang.annotation.*;

@myAnnotation
public class Test1 {
}
//元注解
@Target(value = {ElementType.TYPE})//表明在方法還是類上使用。。
@Retention(value = RetentionPolicy.RUNTIME)//表明在運(yùn)行時(shí)使用還是源碼。。
@Documented//表明是否生成Javadoc
@Inherited//表明子類可以繼承父類的注解
@interface myAnnotation{
}

自定義注解

package com;

import java.lang.annotation.*;

@myAnnotation3("自定義")
@myAnnotation2(age = 18)
public class Test2 {
}
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface myAnnotation2{
    //注解的參數(shù):參數(shù)類型+參數(shù)名();這里的括號(hào)不代表方法?。?    String name() default "";
    int age();
    int id() default -1;
    String[] schools() default {"安陽(yáng)師范學(xué)院","軟件學(xué)院"};
}

@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface myAnnotation3{
    String value();//不成為的規(guī)定,如果注解只有一個(gè)參數(shù),最好用value進(jìn)行定義參數(shù)名,這樣標(biāo)注時(shí)可以省略value
}

反射機(jī)制

先了解一下靜態(tài)動(dòng)態(tài)語(yǔ)言:

動(dòng)態(tài)語(yǔ)言:是一類運(yùn)行時(shí)可以改變結(jié)構(gòu)的語(yǔ)言:例如新的函數(shù)、對(duì)象、甚至代碼可以被引進(jìn)。主要的動(dòng)態(tài)語(yǔ)言:JavaScript、PHP、Python。

靜態(tài)語(yǔ)言:與動(dòng)態(tài)語(yǔ)言不同的就是,運(yùn)行時(shí)無(wú)法改變自己的結(jié)構(gòu)。例如Java、C、C++。

Java不是動(dòng)態(tài)語(yǔ)言,但是我們可以稱為準(zhǔn)動(dòng)態(tài)語(yǔ)言,就是因?yàn)榉瓷錂C(jī)制的存在,因?yàn)榉瓷錂C(jī)制,可以讓java編程更具有靈活性,但是也會(huì)存在一些性能上的弊端。

1585377329185.png

獲得反射對(duì)象

package com;

import lombok.Data;

//反射
public class reflection {
    public static void main(String[] args) throws ClassNotFoundException {
        Class a = Class.forName("com.User");
        Class b = Class.forName("com.User");
        Class c = Class.forName("com.User");
        Class d = Class.forName("com.User");
        System.out.println(a);
        //這里的hashcode都是一個(gè)值,代表相同的對(duì)象
        System.out.println(b.hashCode());
        System.out.println(c.hashCode());
        System.out.println(d.hashCode());
    }
}

@Data
class User{
    private String name;
    private int age;
    private String password;
}

Class類的創(chuàng)建方式

class類的創(chuàng)建方式

第一個(gè)階段:源代碼階段,就是我們自己寫的java文件和編譯生成的.class字節(jié)碼文件。

第二階段:是字節(jié)碼文件被類加載器加載后的文件。是Class類對(duì)象階段。

第三階段:就是運(yùn)行時(shí)階段,這時(shí)候我們可以根據(jù)創(chuàng)建好的對(duì)象獲取Class對(duì)象。

三種方式獲取Class對(duì)象:(對(duì)應(yīng)上述三種階段)

  • class.forName("全類名")

    *多用于配置文件,因?yàn)槔ㄌ?hào)中寫字符串

  • 類名.class

    *多用于參數(shù)的傳遞

  • 對(duì)象.getClass()

    *多用于對(duì)象獲取字節(jié)碼方式

結(jié)論:同一個(gè)字節(jié)碼文件(*.class)在程序運(yùn)行的過(guò)程中,只會(huì)被加載一次,無(wú)論使用哪一種方式獲取的Class對(duì)象都是一樣的。

package com;

import lombok.Data;

public class Test3 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        //方式一:通過(guò)對(duì)象獲取,運(yùn)行時(shí)獲取
        Class c1 = person.getClass();
        System.out.println(c1);
        //方式二:forName獲得,源代碼階段獲取,靜態(tài)方法
        Class c2 = Class.forName("com.Student");
        System.out.println(c2);
        //方式三:通過(guò)類名.class獲取,類加載器獲取
        Class c3 = Student.class;
        System.out.println(c3);
        //方式四:基本內(nèi)置類型的包裝類都有一個(gè)TYPE屬性
        Class c4 = Integer.TYPE;
        System.out.println(c4);
        //獲得父類類型
        Class c5 = c1.getSuperclass();
        System.out.println(c5);
    }
}
@Data
class Person{
    public String name;
}
class Student extends Person{
    public Student() {
        this.name = "學(xué)生";
    }
}
class Teacher extends Person{
    public Teacher() {
        this.name = "老師";
    }
}

所有類型的Class對(duì)象

package com;

import java.lang.annotation.ElementType;

public class AllClass {
    public static void main(String[] args) {
        Class c1 = Object.class;    //類
        Class c2 = Integer.class;   //整型
        Class c3 = Comparable.class;    //接口
        Class c4 = int[].class;  //一維數(shù)組
        Class c5 = int[][].class;//二維數(shù)組
        Class c6= Override.class;//注解
        Class c7 = void.class;//void
        Class c8 = Class.class;//Class
        Class c9 = ElementType.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對(duì)象.png

Java內(nèi)存分析

Java內(nèi)存分析.png

方法區(qū)相當(dāng)于一個(gè)特殊的堆。

類加載器
加載過(guò)程

聽課的個(gè)人理解(不知道對(duì)不對(duì)):

首先加載內(nèi)存,把各種類的數(shù)據(jù)轉(zhuǎn)換成Class對(duì)象,存入堆中。然后進(jìn)行鏈接,鏈接結(jié)束m=0,最后clinit方法從堆中取出來(lái)需要的類,合并代碼。

反射對(duì)象Class對(duì)象功能:

以下從jdk文檔中查到:

獲取成員變量
Field[] getFields() :獲取所有public修飾的成員變量
Field getField(String name)   獲取指定名稱的 public修飾的成員變量

Field[] getDeclaredFields()  獲取所有的成員變量,不考慮修飾符
Field getDeclaredField(String name) 
package com.ClassForMethod;
import com.pojo.User;
import java.lang.reflect.Field;

//獲取成員變量
public class test1 {
    public static void main(String[] args) throws NoSuchFieldException {
        User a = new User();
        Class c1 = a.getClass();
        //獲取public修飾的成員變量
        Field[] fields = c1.getFields();
        for (Field f:fields){
            System.out.println(f);
        }
        System.out.println("=======================");
        //獲取指定名稱的public修飾的成員變量
        Field f = c1.getField("age");
        System.out.println(f);
        System.out.println("=======================");
        //獲取所有的成員變量
        Field[] fields1 = c1.getDeclaredFields();
        for (Field fList:fields1){
            System.out.println(fList);
        }
        System.out.println("=======================");
        Field field = c1.getDeclaredField("sex");
        System.out.println(field);
    }
}
獲取構(gòu)造方法們
Constructor<?>[] getConstructors()  
Constructor<T> getConstructor(類<?>... parameterTypes)  

Constructor<?>[] getDeclaredConstructors()  
Constructor<T> getDeclaredConstructor(類<?>... parameterTypes)  
package com.ClassForMethod;

import com.pojo.User;

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

public class test2 {
    public static void main(String[] args) throws Exception{
        User u = new User();
        Class classUser = u.getClass();
        //查詢到public修飾的所有構(gòu)造方法
        System.out.println("========查詢到public修飾的所有構(gòu)造方法==========");
        Constructor[] c1s = classUser.getConstructors();
        for (Constructor constructor:c1s){
            System.out.println(constructor);
        }
        //括號(hào)內(nèi)如果不寫就是獲取無(wú)參,帶參的就是獲取帶參,并且參數(shù)順序要與原來(lái)位置一樣
        System.out.println("========查詢帶參的構(gòu)造方法==========");
        Constructor c2 = classUser.getConstructor();
        System.out.println(c2);
        System.out.println("========查詢不帶參的構(gòu)造方法==========");
        Constructor c3 = classUser.getConstructor(String.class,int.class);
        System.out.println(c3);
        //我們可以獲取帶參的構(gòu)造方法后進(jìn)行賦值
        User user1 = (User)c3.newInstance("小明",18);
        System.out.println(user1);
        System.out.println("========查詢不帶參的構(gòu)造方法==========");
        //對(duì)于一般的無(wú)參構(gòu)造函數(shù),我們都不會(huì)先獲取無(wú)參構(gòu)造器之后在進(jìn)行初始化。而是直接調(diào)用Class類內(nèi)的newInstance()方法
        Object user3 = classUser.newInstance();
        System.out.println(user3);
    }
}
獲取成員方法們
Method[] getMethods()  
Method getMethod(String name, 類<?>... parameterTypes)  

Method[] getDeclaredMethods()  
Method getDeclaredMethod(String name, 類<?>... parameterTypes)
package com.ClassForMethod;

import com.pojo.User;

import java.lang.reflect.Method;

public class test3 {
    public static void main(String[] args) throws Exception{
        Class userClass = User.class;
        Method[] methods = userClass.getMethods();
        System.out.println("============查詢public修飾的方法===============");
        for (Method m:methods){
            System.out.println(m);//查詢出來(lái)很多的原因是我們的user類歸屬object類,所以這里有很多其他的
        }
        System.out.println("============查詢public修飾帶參的方法===============");
        Method method = userClass.getMethod("sleep");
        System.out.println(method);
        User u = new User("小明",1);
        method.invoke(u);//執(zhí)行方法,放入對(duì)象。
        Method method2 = userClass.getMethod("sleep",String.class);
        System.out.println(method2);
        method2.invoke(u,"小紅");//注意這里多個(gè)參數(shù)時(shí),這里要傳值
        
        
         Field field = userClass.getDeclaredField("name");
        field.setAccessible(true);//不能直接操作私有屬性,我們先關(guān)閉安全監(jiān)測(cè)
        field.set(u,"明明");
        System.out.println(u.getName());

    }
}
獲取簡(jiǎn)單方法、簡(jiǎn)單類名
String getName() 
package com.ClassForMethod;

import com.pojo.User;

import java.lang.reflect.Method;

public class test04 {
    public static void main(String[] args) {
        Class user = User.class;
        Method[] methods = user.getMethods();
        for (Method m:methods){
            String easy = m.getName();
            System.out.println(easy);
        }
    }
}

注意:我們除了上面說(shuō)可以用Declared修飾的方法獲取所有的方法名、成員變量名。。。還可以使用暴力反射來(lái)獲取。

xxx.setAccessible(true);

類加載器:

類加載器的作用:是把類(class)裝載進(jìn)內(nèi)存,JVM規(guī)范定義了三種類型的類加載器。

引導(dǎo)類加載器:用C++編寫,是JVM自帶類的加載器,負(fù)責(zé)Java平臺(tái)核心庫(kù)。用來(lái)裝載類庫(kù),這個(gè)加載器無(wú)法直接獲取。

擴(kuò)展類加載器:負(fù)責(zé)jre/lib/text目錄下的jar包或者-D java.ext.dirs指定目錄下的jar包裝入工作庫(kù)。

系統(tǒng)類加載器:負(fù)責(zé)java -classpath或者-D java.class.path所指的目錄下的類與jar包裝入工作,是最常用的加載器。

類加載器作用
package com;

public class classLoader {
    public static void main(String[] args) throws ClassNotFoundException {
        //獲取系統(tǒng)類的加載器
        ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemLoader);

        //獲取系統(tǒng)類加載器的父類加載器->擴(kuò)展類加載器
        ClassLoader parent = systemLoader.getParent();
        System.out.println(parent);

        //獲取擴(kuò)展類加載器的父類加載器->根加載器(C++)
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);

        //測(cè)試當(dāng)前類是哪一個(gè)加載器加載的
        ClassLoader classLoader = Class.forName("com.classLoader").getClassLoader();
        System.out.println(classLoader);

        //測(cè)試jdk內(nèi)置是哪一個(gè)加載器加載的
        ClassLoader sysClassLoder = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(sysClassLoder);
    }
}

調(diào)用方法的三種性能檢測(cè):

package com;

import java.lang.reflect.Method;

public class performance {
    public static void main(String[] args) throws Exception {
        //性能測(cè)試,關(guān)閉了安全監(jiān)測(cè)
        test01();
        test02();
        test03();
    }

    public static void  test01(){
        //普通方法調(diào)用
        User user = new User();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            user.getName();
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start+"ms");
    }
    public static void test02() throws Exception {
        //反射調(diào)用
        User user = new User();
        Class userClass = User.class;
        Method getName = userClass.getMethod("getName",null);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
         getName.invoke(user,null);
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start+"ms");
    }
    public static void test03() throws Exception {
        //反射調(diào)用,關(guān)閉安全監(jiān)測(cè)
        User user = new User();
        Class userClass = User.class;
        Method getName = userClass.getMethod("getName",null);
        long start = System.currentTimeMillis();
        getName.setAccessible(true);
        for (int i = 0; i < 100000000; i++) {
            getName.invoke(user,null);
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start+"ms");
    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. 注解 1.1 注解的定義 注解就是源代碼的元數(shù)據(jù),通熟的講就是代碼中的標(biāo)簽。注解就有如下的特點(diǎn): 注解是一個(gè)...
    猶夢(mèng)漁樵閱讀 313評(píng)論 1 0
  • 一.Java反射機(jī)制。 1.反射機(jī)制的定義。 Java反射機(jī)制是指在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能知道這個(gè)類的所...
    大鵬的鵬閱讀 539評(píng)論 0 0
  • 注解 ?一般情況下,注解是用來(lái)給程序員、編譯器提示的,就比如 @NoNull 就是用來(lái)提示不為空,在null時(shí),編...
    ZuYuan閱讀 1,686評(píng)論 0 5
  • 最近在研究阿里開源的ARouter框架,碰到了較多注解的使用。結(jié)合ButterKnife開源框架的使用,越來(lái)越體會(huì)...
    zizi192閱讀 3,367評(píng)論 0 11
  • 基礎(chǔ) 自定義注解 反射 動(dòng)態(tài)語(yǔ)言是一類在運(yùn)行時(shí)可以改變其結(jié)構(gòu)的語(yǔ)言,Java不是動(dòng)態(tài)語(yǔ)言,但Java可以稱之為“準(zhǔn)...
    小王_Ryan閱讀 236評(píng)論 0 1

友情鏈接更多精彩內(nèi)容