反射
反射概念:
對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的東西。這並沒有破壞面向對象的封裝性