前言
反射是java開發進階的一項重要內容,在我們使用的框架如spring中的aop中就有其最佳實踐的案例,我們有必要熟悉其Api,並且深刻理解它的作用,今天就讓我們一起來看看java的反射-reflect吧。
正篇
一:java反射載入類的三種方式
首先建立測試類:
介面類
package com.lly.springtest1.reflect;
/**
* @ClassName IPerson
* @Description TODO
* @Author lly
* @Date 2019/2/21 2:42 PM
* @Version 1.0
**/
public interface IPerson {
void sayHello();
}
複製程式碼
實現類
package com.lly.springtest1.reflect;
/**
* @ClassName ChineseEntity
* @Description TODO
* @Author lly
* @Date 2019/2/20 3:24 PM
* @Version 1.0
**/
public class ChineseEntity implements IPerson{
private String name;
private int age;
private String phone;
public String addres;
public ChineseEntity() {
}
public ChineseEntity(String name, int age, String phone) {
this.name = name;
this.age = age;
this.phone = phone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "ChineseEntity{" +
"name='" + name + '\'' +
", age=" + age +
", phone='" + phone + '\'' +
'}';
}
public String getAddres() {
return addres;
}
public void setAddres(String addres) {
this.addres = addres;
}
private void getTestName(){
}
@Override
public void sayHello() {
System.out.println("Chinese say hello");
}
public void test(String word) {
System.out.println("testWord:"+word);
}
}
複製程式碼
package com.lly.springtest1.reflect;
/**
* @ClassName ChineseEntity
* @Description TODO
* @Author lly
* @Date 2019/2/20 3:24 PM
* @Version 1.0
**/
public class AmericanEntity implements IPerson{
private String name;
private int age;
private String phone;
public String addres;
public AmericanEntity() {
}
public AmericanEntity(String name, int age, String phone) {
this.name = name;
this.age = age;
this.phone = phone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "ChineseEntity{" +
"name='" + name + '\'' +
", age=" + age +
", phone='" + phone + '\'' +
'}';
}
public String getAddres() {
return addres;
}
public void setAddres(String addres) {
this.addres = addres;
}
private void getTestName(){
}
@Override
public void sayHello() {
System.out.println("American say hello");
}
}
複製程式碼
1:Class.forName()
public static void typeOne() {
try {
log.info("通過類的全路徑獲取類");
// 動態載入類,在需要的時候才載入所需要的類
Class<?> pClass = Class.forName("com.lly.springtest1.reflect.ChineseEntity");
log.info("獲取該類所有的屬性");
Arrays.asList(pClass.getDeclaredFields()).stream().forEach(e -> System.out.println(e.getName()));
log.info("獲取該類所有的所有公開屬性");
Arrays.asList(pClass.getFields()).stream().forEach(e -> System.out.println(e.getName()));
log.info("獲取類的例項");
ChineseEntity person = (ChineseEntity) pClass.newInstance();
person.sayHello();
person.setAge(10);
log.info("列印類的一個屬性");
System.out.println("年齡:" + person.getAge());
log.info("返回某個類的所有公用(public)方法包括其繼承類的公用方法,當然也包括它所實現介面的方法");
Arrays.asList(pClass.getMethods()).stream().forEach(e -> {
System.out.print(e.getName() + "(");
Arrays.asList(e.getParameterTypes()).stream().forEach(e1 -> System.out.print(e1.getName() + ","));
System.out.println(")");
return;
});
log.info("類或介面宣告的所有方法,包括公共、保護、預設(包)訪問和私有方法,但不包括繼承的方法。當然也包括它所實現介面的方法");
Arrays.asList(pClass.getDeclaredMethods()).stream().forEach(e -> System.out.println(e.getName()));
log.info("獲取指定的方法");
Method test = pClass.getDeclaredMethod("test", String.class);
//方法有返回值,返回實際的值.沒有返回值返回null
Object person1 = test.invoke(person, "person");
System.out.println(person1);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
複製程式碼
2:Object.class
public static void typeTwo() {
// 靜態載入需要的類
Class<ChineseEntity> pClass = ChineseEntity.class;
Arrays.asList(pClass.getDeclaredFields()).stream().forEach(e -> System.out.println(e.getName()));
}
複製程式碼
3:new Object()
public static void typeThree() {
// new 物件是靜態載入類,在編譯時刻就需要載入所有需要可能的類
ChineseEntity chineseEntity = new ChineseEntity();
Class<? extends ChineseEntity> aClass = chineseEntity.getClass();
Arrays.asList(aClass.getDeclaredFields()).stream().forEach(e -> System.out.println(e.getName()));
}
複製程式碼
執行結果
15:33:58.553 [main] INFO com.lly.springtest1.reflect.ReflectTest - 通過類的全路徑獲取類
15:33:58.561 [main] INFO com.lly.springtest1.reflect.ReflectTest - 獲取該類所有的屬性
name
age
phone
addres
15:33:58.687 [main] INFO com.lly.springtest1.reflect.ReflectTest - 獲取該類所有的所有公開屬性
addres
15:33:58.688 [main] INFO com.lly.springtest1.reflect.ReflectTest - 獲取類的例項
Chinese say hello
15:33:58.688 [main] INFO com.lly.springtest1.reflect.ReflectTest - 列印類的一個屬性
年齡:10
15:33:58.688 [main] INFO com.lly.springtest1.reflect.ReflectTest - 返回某個類的所有公用(public)方法包括其繼承類的公用方法,當然也包括它所實現介面的方法
toString()
getName()
setName(java.lang.String,)
test(java.lang.String,)
sayHello()
setAge(int,)
getAge()
setPhone(java.lang.String,)
getAddres()
getPhone()
setAddres(java.lang.String,)
wait(long,int,)
wait(long,)
wait()
equals(java.lang.Object,)
hashCode()
getClass()
notify()
notifyAll()
15:33:58.691 [main] INFO com.lly.springtest1.reflect.ReflectTest - 類或介面宣告的所有方法,包括公共、保護、預設(包)訪問和私有方法,但不包括繼承的方法。當然也包括它所實現介面的方法
toString
getName
setName
test
sayHello
setAge
getAge
setPhone
getAddres
getPhone
setAddres
getTestName
15:33:58.691 [main] INFO com.lly.springtest1.reflect.ReflectTest - 獲取指定的方法
testWord:person
null
Process finished with exit code 0
複製程式碼
二:java反射的實際應用
瞭解過設計模式的同學肯定知曉工廠模式,該模式是幫助我們獲取一個類的例項,那麼其中實現的原理是什麼呢,沒錯,就是java的反射,通過反射獲取到具體類的例項,上面我們已經定義了2個實體類中國人和美國人,並且實現了IPerson介面,這樣我們在獲取其中一個國家人的例項的時候,通常我們會用
ChineseEntity chineseEntity = new ChineseEntity();
複製程式碼
這種寫法,但是如果我們以後要獲取到美國人或者其他國家的人恩,是不是就需要修改程式碼了,那麼我們就用了介面的有點,實現統一的標準,方便擴充套件,再利用工廠類來獲取我們指定的例項就大大優化了我們打程式碼,下面我們來實現以下
工廠類
package com.lly.springtest1.reflect;
/**
* @ClassName PersonFactory
* @Description persion工廠類
* @Author lly
* @Date 2019/2/22 2:44 PM
* @Version 1.0
**/
public class PersonFactory {
public static IPerson getiPersonInstance(String className) {
IPerson iPerson = null;
try {
iPerson = (IPerson) Class.forName(className).newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return iPerson;
}
}
複製程式碼
測試類
package com.lly.springtest1.reflect;
/**
* @ClassName ReflectAndFactoryTest
* @Description TODO
* @Author lly
* @Date 2019/2/22 2:50 PM
* @Version 1.0
**/
public class ReflectAndFactoryTest {
public static void main(String[] args) {
IPerson chinese = PersonFactory.getiPersonInstance("com.lly.springtest1.reflect.ChineseEntity");
chinese.sayHello();
IPerson american = PersonFactory.getiPersonInstance("com.lly.springtest1.reflect.AmericanEntity");
american.sayHello();
}
}
複製程式碼
結果
利用反射動態生成我們需要的例項物件,這裡有一點需要注意的使我們必須要知道類的全名,我們可以將全路徑類名和簡單類名做一個k-v的對映配置在java配置類中,這樣我們只需要修改配置類就可以了三:關於setAccessible()方法
首先我們來看看jdk中的解釋:
將此物件的 accessible 標誌設定為指示的布林值。值為 true 則指示反射的物件在使用時應該取消 Java 語言訪問檢查。值為 false 則指示反射的物件應該實施 Java 語言訪問檢查。並不是代表反射類的屬效能否訪問的開關,而是是否檢查語言訪問,我們來做個試驗
Method test = pClass.getDeclaredMethod("test", String.class);
//方法有返回值,返回實際的值.沒有返回值返回null
long stime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
test.invoke(person, "person");
}
long etime = System.currentTimeMillis();
log.info("時間:{}", etime - stime);
test.setAccessible(true);
long stime1 = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
test.invoke(person, "person");
}
long etime1 = System.currentTimeMillis();
log.info("時間:{}", etime1 - stime1);
複製程式碼
大家覺得結果是什麼呢,都執行10000次哪個更快呢,結果是
15:47:49.967 [main] INFO com.lly.springtest1.reflect.ReflectTest - 時間:101
15:47:50.038 [main] INFO com.lly.springtest1.reflect.ReflectTest - 時間:68
複製程式碼
事實勝於雄辯,原來是由於JDK的安全檢查耗時較多.所以通過setAccessible(true)的方式關閉安全檢查就可以達到提升反射速度的目的。
總結
反射的三種方式中第一種使我們經常使用的,例如我們在springAop,jdbc底層載入資料庫驅動包等等,其他2種由於我們已經知道了所學要的類,不存在動態載入,所以基本上不使用。