Java進階學習之反射與動態繫結(5)
阿新 • • 發佈:2022-12-01
目錄
1.反射
1.1.概述
Java的反射機制是指在程式的執行狀態時,可以構建任何一個類的物件,對任何一個物件可以瞭解其所屬的成員變數和方法,這種動態獲取程式資訊以及動態呼叫物件的功能稱為Java語言的反射機制。
1.2.實現方式
在最初的 helloworld案例 中,我們知道原始碼會編譯成位元組碼檔案.class,然後交給JVM執行,這裡的位元組碼檔案其實就是類的模板,所有的物件都需要通過這個模板進行建立。所以在使用反射之前需要獲取位元組碼檔案。
1.2.1.獲取Class
方式一:通過物件獲取
方式二:通過 類.class 獲取
方式三:通過Class類的靜態方法forName(String className)獲取
例子:
實體類:
public class Person { private String name; public Integer age; public Person() {} public Person(String name) { this.name = name; } public Person(String name, Integer age) { this.name = name; this.age = age; } 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; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } public String say(String name) { return "hello, " + name; } private String secretlySay(Integer time) { return "I love you " + time + " thousand times!"; } }
測試類:
public class TestReflection { public static void main(String[] args) { //方式一 Person person = new Person("zhangsan", 22); Class clazz1 = person.getClass(); System.out.println(clazz1); //方式一 Class clazz2 = Person.class; System.out.println(clazz2); //方式三 try { Class clazz3 = Class.forName("com.liquor.reflection.Person"); System.out.println(clazz3); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
執行結果:
我們知道這裡的Class物件其實就是類的模板,因此有且只有一個。
1.2.2.通過反射獲取屬性、設定屬性
以下都是用第一種方式獲取Class的。
public class TestReflection {
public static void main(String[] args) {
Person person = new Person("zhangsan", 22);
Class clazz1 = person.getClass();
System.out.println(clazz1);
System.out.println("---------------------------");
//獲取public屬性
Field[] fields = clazz1.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
System.out.println("---------------------------");
//獲取包含private的所有屬性
Field[] declaredFields = clazz1.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println(field.getName());
}
try {
//獲取指定name的public屬性
Field age = clazz1.getField("age");
System.out.println(age);
//獲取指定name的包含private的屬性
Field name = clazz1.getDeclaredField("name");
System.out.println(name);
//獲取物件
Object o = clazz1.newInstance();
//設定屬性不校驗
name.setAccessible(true);
//設定屬性值,私有屬性需要關閉校驗
name.set(o, "lisi");
age.set(o, 33);
System.out.println(o);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
執行結果:
1.2.3.通過反射獲取方法、執行方法
public class TestReflection {
public static void main(String[] args) {
Person person = new Person("zhangsan", 22);
Class clazz1 = person.getClass();
//獲取類及其父類的public方法
Method[] methods = clazz1.getMethods();
for (Method method : methods) {
System.out.print(method.getName() + " ");
}
System.out.println();
//獲取類包含private的所有方法
Method[] methods2 = clazz1.getDeclaredMethods();
for (Method method : methods2) {
System.out.print(method.getName() + " ");
}
System.out.println();
try {
//獲取指定name的public方法
Method m1 = clazz1.getMethod("say", String.class);
System.out.println(m1);
//獲取指定name的包含private的方法
Method m2 = clazz1.getDeclaredMethod("say", String.class);
System.out.println(m2);
Method m3 = clazz1.getDeclaredMethod("secretlySay", Integer.class);
System.out.println(m3);
//獲取物件
Object o = clazz1.newInstance();
//設定方法不校驗
m3.setAccessible(true);
//呼叫方法
Object rel1 = m1.invoke(o, "lisi");
System.out.println(rel1);
Object rel2 = m3.invoke(o, 3);
System.out.println(rel2);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
執行結果:
1.2.4.通過反射獲取構造器並呼叫
public class TestReflection {
public static void main(String[] args) {
Person person = new Person("zhangsan", 22);
Class clazz1 = person.getClass();
//獲取類的public構造器
Constructor[] constructors = clazz1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("------------------------");
try {
//獲取指定name的public構造器
Constructor c1 = clazz1.getConstructor(String.class);
System.out.println(c1);
Constructor c2 = clazz1.getConstructor(String.class, Integer.class);
System.out.println(c2);
//通過構造器獲取物件
Object o1 = c1.newInstance("zhangsan");
System.out.println(o1);
Object o2 = c2.newInstance("lisi", 33);
System.out.println(o2);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
執行結果:
2.動態代理
2.1.代理模式
使用一個類代表另一個類的功能,是一種設計模式。
2.2.靜態代理
編譯時代理類已經生成。
例子:
介面HelloService.java
public interface HelloService {
String say(String name);
}
實際實現HelloServiceImpl
public class HelloServiceImpl implements HelloService {
@Override
public String say(String name) {
return "Hello, " + name;
}
}
代理類HelloStaticProxy.java
public class HelloStaticProxy implements HelloService {
private HelloService helloService = new HelloServiceImpl();
@Override
public String say(String name) {
System.out.println("--------------------------");
return helloService.say(name);
}
}
測試類:
public class TestProxy {
public static void main(String[] args) {
HelloStaticProxy helloStaticProxy = new HelloStaticProxy();
System.out.println(helloStaticProxy.say("zhangsan"));
}
}
執行結果:
靜態代理實現步驟:
- 建立介面
- 實現介面
- 建立代理類實現介面並且將原實現類引入,呼叫實現的方法
缺點:
- 由於實現同一個介面,介面變動則修改成本增加
- 如果介面實現方法很多的話再實現工作量很大
2.3.動態代理
相比靜態代理,動態代理就是執行時動態建立代理的方式。
介面和被代理類與前面一樣。
代理類:
public class HelloProxy implements InvocationHandler {
private Object obj;
public HelloProxy(Object obj) {
this.obj = obj;
}
public Object getNewInstance() {
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(obj, args);
return invoke;
}
}
測試類:
public class TestProxy {
public static void main(String[] args) {
HelloProxy helloProxy = new HelloProxy(new HelloServiceImpl());
HelloService helloService = (HelloService) helloProxy.getNewInstance();
System.out.println(helloService.say("zhangsan"));
}
}
執行結果:
這是使用JDK原始碼實現的動態代理,使用介面等實現的。而如何被代理類沒有實現介面呢?
被代理類HelloServiceClass.java
public class HelloServiceClass {
public String say(String name) {
return "Hello, " + name;
}
}
代理類:
public class HelloProxy implements InvocationHandler {
private Object obj;
public HelloProxy(Object obj) {
this.obj = obj;
}
public Object getNewInstance() {
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(obj, args);
return invoke;
}
}
測試類:
public class TestProxy {
public static void main(String[] args) {
HelloServiceClass helloServiceClass = (HelloServiceClass) new CglibInterceptor().getNewInstance(HelloServiceClass.class);
System.out.println(helloServiceClass.say("zhangsan"));
}
}
執行結果:
靜態代理相對於動態代理從程式碼的實現上更加容易理解和編寫,代理類中呼叫的是真正的實現類。動態代理中,當我們需要使用到時才會建立類的模板然後建立物件呼叫方法。很顯然反射在動態代理中舉足輕重。