Java反射詳解


反射

反射概念:

對Class類的理解:

面向對象的思維方式:萬事萬物皆對象

Class類也就是字節碼文件向上抽取形成一個類。

使用反射

首先需要提供一些類:為后面使用反射做好測試環境

一個自定義注解

package com.example.demo1.test02;

import java.lang.annotation.*;

import static java.lang.annotation.ElementType.*;
/*
Target定義當前注解能夠修飾程序中的那些元素
Retention:定義注解的生命周期
 */

@Target({TYPE, FIELD,METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}

一個自定義的接口

package com.example.demo1.test02;/*
 *@author wupeng
 *@time 2021/6/19-10:47

 */
public interface MyInterface { //自定義接口
    void myMethod();
}

一個Person類

package com.example.demo1.test02;/*
 *@author wupeng
 *@time 2021/6/18-17:35

 */

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.List;


public class Person implements Serializable {

    public String name;
    private Integer age;
     String sex;
    protected List<String> likes;

    private void eat() {
        System.out.println("Person-eat");
    }
    public void sleep() {
        System.out.println("Person--sleep");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public List<String> getLikes() {
        return likes;
    }

    public void setLikes(List<String> likes) {
        this.likes = likes;
    }

    public Person(String name, Integer age, String sex, List<String> likes) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.likes = likes;
    }

    public Person() {
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", likes=" + likes +
                '}';
    }
}

一個繼承Person的student類

package com.example.demo1.test02;/*
 *@author wupeng
 *@time 2021/6/19-10:21
 */
import java.util.List;

@MyAnnotation(value = "hello")
public class Student extends Person implements MyInterface{

    private int sno;
    double height;
    protected double weight;
    public double score;

    @MyAnnotation(value = "hi method")
    public String showInfo() {
        return "我是一名學生";
    }
    public String showInfo(int a,int b) {
        return "重載方法-》我是一名學生";
    }
    private void work(int a) {
        System.out.println("找工作稱為一只碼畜");
    }

    void happy() {
        System.out.println("做人最重要的就是開心");
    }

    public Student() {
        System.out.println("空參構造器");
    }
    public Student(double height,int sno) {
        this.sno = sno;
        this.height = height;
    }
    private Student(int sno) {
        this.sno = sno;
    }
    Student(int sno,double weight) {
        this.sno = sno;
        this.weight = weight;
    }
    protected Student(int sno,double height,double weight) {
        this.sno = sno;
    }

    @MyAnnotation(value = "hello myMethod")
    @Override
    public void myMethod() throws RuntimeException {
        System.out.println("重寫了myMethod方法");
    }

    @Override
    public String toString() {
        return "Student{" +
                "sno=" + sno +
                ", height=" + height +
                ", weight=" + weight +
                ", score=" + score +
                '}';
    }
}

獲取運行時類的完整結構

package com.example.demo1.test02;/*
 *@author wupeng
 *@time 2021/6/19-10:26

 */

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test  {
    /*獲取字節碼信息的四中方式
    獲取的字節碼信息是同一個,因為只有一次類加載
    方式1和方式2不常用:原因得到字節碼信息的目的就是獲取類里面的東西

     */
    public static void main(String[] args) throws ClassNotFoundException {
        //以Person的字節碼信息為案例
//        方式1 :通過getClass方法獲取
        Person p = new Person();
        Class c1 = p.getClass();
        System.out.println(c1);
//        方式2:通過內置Class屬性
        Class c2 = Person.class;
        System.out.println(c2);
        System.out.println(c1==c2);
//   方式3:通過全限定類名,調用Class提供的靜態方法
        Class c3 = Class.forName("com.example.demo1.test02.Person");

        //方式4:(了解)利用類的加載器
        ClassLoader loader = Test.class.getClassLoader(); //系統類加載器
        System.out.println(loader);
        Class c4 = loader.loadClass("com.example.demo1.test02.Person"); //具體對象的字節碼信息
    }
}

獲取字節碼信息的方式

package com.example.demo1.test02;/*
 *@author wupeng
 *@time 2021/6/19-10:26

 */

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test  {
    /*獲取字節碼信息的四中方式
    獲取的字節碼信息是同一個,因為只有一次類加載
    方式1和方式2不常用:原因得到字節碼信息的目的就是獲取類里面的東西

     */
    public static void main(String[] args) throws ClassNotFoundException {
        //以Person的字節碼信息為案例
//        方式1 :通過getClass方法獲取
        Person p = new Person();
        Class c1 = p.getClass();
        System.out.println(c1);
//        方式2:通過內置Class屬性
        Class c2 = Person.class;
        System.out.println(c2);
        System.out.println(c1==c2);
//   方式3:通過全限定類名,調用Class提供的靜態方法
        Class c3 = Class.forName("com.example.demo1.test02.Person");

        //方式4:(了解)利用類的加載器
        ClassLoader loader = Test.class.getClassLoader(); //系統類加載器
        System.out.println(loader);
        Class c4 = loader.loadClass("com.example.demo1.test02.Person"); //具體對象的字節碼信息
    }
}

可以作為Class類的實例的種類:

package com.example.demo1.test02;/*
 *@author wupeng
 *@time 2021/6/19-10:39
 */
import com.sun.javaws.IconUtil;
import org.springframework.http.converter.json.GsonBuilderUtils;

public class Demo {
    /*
    1.類:外部類,內部類
    2.接口
    3.注解
    4.數組
    5.基本數據類型
    6.void
     */
    public static void main(String[] args) {

        Class c1 = Person.class;
        Class c2 = Comparable.class;
        System.out.println(c2);
        Class c3 = Override.class;
        System.out.println(c3);

        int[] arr1 = {1,2,3};
        Class c4 = arr1.getClass();
        int[] arr2 = {5,6,7};
        Class c5 = arr2.getClass();
        System.out.println(c5==c4);  //同一個維度,同一個類型的數組得到的字節碼就是同一個 結果為true

        Class c6 = int.class;
        System.out.println(c6);
        Class c7 = void.class;
        System.out.println(c7);
    }
}

獲取運行時類的反射結構:

獲取構造器:

package com.example.demo1.test02;/*
 *@author wupeng
 *@time 2021/6/19-11:21
 */
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test01 {
//    獲取字節碼信息
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class cls = Student.class;
        //通過字節碼信息可以獲取構造器
        /*
        為什么會是一個數組呢,這個數組是什么?
        將每個構造器向上抽取形成一個大類,構造器類
         */
        Constructor[] constructors = cls.getConstructors();  //只能獲取當前運行時類被public修飾的構造器

        for(Constructor c : constructors) {
            System.out.println(c);
        }
        System.out.println("-----------------------------");
        Constructor[] c2 = cls.getDeclaredConstructors();  //獲取的是所有的構造器
        for(Constructor c:c2) {
            System.out.println(c);
        }
        System.out.println("------------------------");
        //獲取指定的構造器
        Constructor con1 = cls.getConstructor();  //獲取的是空構造器
        System.out.println(con1);
        System.out.println("------------------------------");
//        得到兩個參數的有參構造器
        Constructor con2 = cls.getConstructor(double.class,int.class);
        System.out.println(con2);
        System.out.println("-------------------------");
        //得到一個參數的有參構造器,並且是private修飾的
        Constructor con3 = cls.getDeclaredConstructor(int.class);
        System.out.println(con3);

//        有了構造器之后就可以創建對象了
        Object o1 = con1.newInstance();
        System.out.println(o1);

        Object o2 = con2.newInstance(15.5, 18);
        System.out.println(o2);
    }
}

獲取屬性和對屬性進行賦值:

package com.example.demo1.test02;/*
 *@author wupeng
 *@time 2021/6/19-11:53

 */

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class Test02 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Class cls = Student.class;
        //獲取public屬性
        Field[] fields = cls.getFields();

        for(Field f : fields) {
            System.out.println(f);
        }
        System.out.println("---------------------");
        //獲取運行時類的所有屬性
        Field[] declaredFields = cls.getDeclaredFields();
        for(Field f : declaredFields) {
            System.out.println(f);
        }
        //獲取指定的屬性
        Field score = cls.getField("score");
        System.out.println(score);
        //獲取指定的屬性
        Field sno = cls.getDeclaredField("sno");
        System.out.println(sno);

        //獲取修飾符
        int modifiers = sno.getModifiers();  //底層每一個書對應一個修飾符 Modifier
        System.out.println( Modifier.toString(modifiers));


        //獲取屬性的數據類型
        Class type = sno.getType();
        System.out.println(type.getName());

        //獲取屬性的名字
        String name = sno.getName();
        System.out.println(name);
        System.out.println("--------------------------");
//        給屬性賦值
        /*
        給屬性設置值必須要有對象
         */
        Field scp =  cls.getField("score");
        Object obj = cls.newInstance();
        score.set(obj,18);
        System.out.println(obj);

    }
}

獲取方法和調用方法:

package com.example.demo1.test02;/*
 *@author wupeng
 *@time 2021/6/19-17:08

 */

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Test03 {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        //獲取字節碼信息
        Class cls = Student.class;
        //獲取方法,會獲取運行時類的方法以及父類中的所有方法
        Method[] methods = cls.getMethods();
        for(Method m:methods) {
            System.out.println(m);
        }
        System.out.println("-----------------------------");
        //獲取到的是Sutdent中的所有方法,獲取不到父類
        Method[] declaredConstructor= cls.getDeclaredMethods();
        for(Method m:declaredConstructor) {
            System.out.println(m);
        }
        System.out.println("-----------------------------");
        //獲取指定的方法
        Method showInfo = cls.getMethod("showInfo");
        System.out.println(showInfo);
        Method showInfo1 = cls.getMethod("showInfo", int.class, int.class);
        System.out.println(showInfo1);
        Method work = cls.getDeclaredMethod("work",int.class);
        System.out.println(work);
        System.out.println("-----------------------------");
        //獲取方法的具體結構

        System.out.println(work.getName());
        System.out.println(Modifier.toString(work.getModifiers()));
        //返回值
        System.out.println(work.getReturnType());
//        參數列表 返回的是一個數組
        Class[] parameterTypes = work.getParameterTypes();
       for(Class c : parameterTypes) {
           System.out.println(c);
       }

//       獲取注解和異常
        Method myMethod = cls.getMethod("myMethod");
        Annotation[] annotations = myMethod.getDeclaredAnnotations();
        for(Annotation a:annotations) {    //獲取的注解是運行時期的注解
            System.out.println(a);
        }
        System.out.println("------------------異常");
        //獲取異常
        Class[] types = myMethod.getExceptionTypes();
        for(Class c:types) {
            System.out.println(c);
        }

//        調用方法
        Object o = cls.newInstance();
        myMethod.invoke(o);  //調用o對象的myMethod方法
        System.out.println(showInfo.invoke(o));

    }
}

獲取類的接口,所在包,注解

package com.example.demo1.test02;/*
 *@author wupeng
 *@time 2021/6/19-17:40

 */

import java.lang.annotation.Annotation;

public class Tets04 {
    public static void main(String[] args) {
        Class cls = Student.class;
        //獲取接口
        Class[] inter = cls.getInterfaces();

        for(Class i:inter) {
            System.out.println(i);
        }


        //得到父類的接口
        Class superclass = cls.getSuperclass();
        Class[] interfaces = superclass.getInterfaces();
        for (Class i:interfaces) {
            System.out.println(i);
        }

        //獲取運行時類所在的包
        Package aPackage = cls.getPackage();
        System.out.println(aPackage);
        System.out.println(aPackage.getName());


        //獲取運行類的注解
        Annotation[] annotations = cls.getAnnotations();
        for (Annotation a:annotations) {
            System.out.println(a);
        }

    }
}

關於反射的面試題:

1.反射的應用場景

反射是框架設計的靈魂,平時模塊化的開發,通過反射調用對應的字節碼;動態代理設計模式也采用了反射機制日常使用的spring框架也大量使用到了反射。
舉例:

1.我們在使用JDBC連接數據庫使用Class.forName()通過反射加載數據庫的驅動程序;

2.Spring框架也用到了很多反射機制,xml配置模式。Spring通過XML配置模式裝載Bean的過程:

1.將程序內所有XML或Properties配置文件加載入內存中·;

2.Java類里面解析XML或properties里面的內容,得到的對應實體類的字節碼字符串以及相關的屬性信息。

3.使用反射機制,根據這個字符串獲得某個類的Class實例。

4.動態配置實例的屬性

2.反射是否破壞了面向對象的封裝性?

封裝是為了提高代碼的安全性,

反射目的就是為了動態性,

反射有反射的意義,

封裝有封裝的意義。
封裝性是指對外隱藏對象的屬性和實現細節,僅對外提供公共的訪問方式。反射是通過對象找到類,既然找到類了,那么我們就可以得到這個類的成員結構了,例如這個類的屬性和方法,即使是private的也能得到,你想,現在這個類我都得到了,那么這個類中的所以東西我肯定是都得到了,我現在只是得到了這個類的成員,並沒有說是在外部訪問這個類的private的東西。這並沒有破壞面向對象的封裝性


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM