廢話很少說,直切進入正題。先來一張反射機制的圖-->java
import java.util.Date;//先有類
public class ReflectTest1 {
public static void main(String[] args) {
Date date = new Date();//後有對象
System.out.println(date);
}
}複製代碼
import java.util.Date;
public class ReflectTest2 {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date.getClass());
}
}
"class java.util.Date"
複製代碼
發現調用了getClas()方法後的輸出,就輸出了類的完整名稱,等於找到了對象的出處。bash
java.lang.Class是一個類,這個類是反射操做的源頭,即:全部的反射都要從類開始進行,而最關鍵的是這個類有三種實例化方法:app
1.調用Object類中的getClass()方法:ide
import java.util.Date;
public class ReflectTest3 {
public static void main(String[] args) {
Date date = new Date();
Class<?> cls = date.getClass();
System.out.println(cls);
}
}
"class java.util.Date"
複製代碼
2. 使用「類.class」取得:ui
import java.util.Date;
public class ReflectTest4 {
public static void main(String[] args) {
Class<?> cls = Date.class;
System.out.println(cls);
}
}
"class java.util.Date"
複製代碼
以前是在實例化了Date對象以後取得的Class類對象,可是如今並無實例化對象的產生。這種方法用在Hibernate、Mybatis、Spring等。this
3. 調用Class類提供的一個方法--實例化Class對象(在java.lang.Object下public static spa
Class<?> forName(String className) throws ClassNotFoundException):code
public class ReflectTest5 {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> cls = Class.forName("java.util.Date");
System.out.println(cls);
}
}
"class java.util.Date"
複製代碼
此時能夠不使用import語句導入一個明確的類,而類的名稱是採用字符串的形式進行描述的。cdn
當拿到一個類的時候,確定要直接使用關鍵字new 進行對象實例化操做這屬於咱們習慣性的作法。若是拿到Class類對象,那麼就能夠作到利用反射來實例化對象操做:對象
public T newInstance() throws InstantiationException,IllegalAccessException
範例: 利用反射來實例化對象
/*正常實例化*/
class Book{
public Book(){
System.out.println("*** Book的構造方法 ***");
}
@Override
public String toString() {
return "--- 這是Book方法 ---";
}
}
public class ReflectTest6 {
public static void main(String[] args) throws ClassNotFoundException {
Book b = new Book();
System.out.println(b);
}
}
"*** Book的構造方法 *** --- 這是Book方法 ---"複製代碼
package com.jkx.lzh.test;
/*反射實例化*/
class Book{
public Book(){
System.out.println("*** Book的構造方法 ***");
}
@Override
public String toString() {
return "--- 這是Book方法 ---";
}
}
public class ReflectTest7 {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException{
Class<?> cls = Class.forName("com.jkx.lzh.test.Book");
Object obj = cls.newInstance();
}
}
"*** Book的構造方法 ***"複製代碼
class Book{
public Book(){
System.out.println("*** Book的構造方法 ***");
}
@Override
public String toString() {
return "--- 這是Book方法 ---";
}
}
public class ReflectTest8 {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException{
Class<?> cls = Class.forName("com.jkx.lzh.test.Book");
Object obj = cls.newInstance();
Book b = (Book) obj;
System.out.println(b);
}
}
"*** Book的構造方法 *** --- 這是Book方法 ---"複製代碼
有了反射以後,之後進行對象化實例化的操做不在只是單獨的依靠關鍵字new完成了,反射也一樣能夠,可是這並不表示new就被徹底取代了。
這樣有人就會說:「下面兩行代碼至關於:Book b = new Book();這樣不是更復雜了,麼「。
Class<?> cls = Class.forName("com.jkx.lzh.test.Book");
Object obj = cls.newInstance();
複製代碼
PS: 在任何的開發之中,new是形成耦合的最大元兇。一切的耦合都起源於new。
範例:觀察工廠模式-->
interface Fruit{
void eat();
}
class Apple implements Fruit{
@Override
public void eat() {
System.out.println("* 吃蘋果 *");
}
}
class Factory{
public static Fruit getInstance(String className){
if("apple" == className){
return new Apple();
}
return null;
}
}
public class TestFactory {
public static void main(String[] args) {
Fruit f = Factory.getInstance("apple");
f.eat();
}
}
"* 吃蘋果 *"
複製代碼
代碼如上,可是此時,若是咱們要增長一個Fruit接口子類「orange」,就意味着咱們就要修改工廠模式的方法。
class Orange implements Fruit{
@Override
public void eat() {
System.out.println("* 吃橘子 *");
}
}
class Factory{
public static Fruit getInstance(String className){
if("apple" == className){
return new Apple();
}else if("orange" == className){
return new Orange();
}
return null;
}
}複製代碼
因而可知,每增長一個Fruit接口子類,就要修改工廠類,那麼若是隨時須要增長子類呢?
由於如今工廠類都是new關鍵字直接實例化的,因此new就形成了全部問題的關鍵點。要想解決這一問題,就只能依靠反射完成。
修改工廠模式的方法以下:
package com.jkx.lzh.test;
interface Fruit{
void eat();
}
class Apple implements Fruit{
@Override
public void eat() {
System.out.println("* 吃蘋果 *");
}
}
class Orange implements Fruit{
@Override
public void eat() {
System.out.println("* 吃橘子 *");
}
}
class Factory{
public static Fruit getInstance(String className){
Fruit f = null;
try {
f = (Fruit) Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return f;
}
}
public class TestFactory {
public static void main(String[] args) {
Fruit f = Factory.getInstance("com.jkx.lzh.test.Apple");
f.eat();
}
}複製代碼
此時的程序就真正的完成了解耦合的目的,並且可擴展性很是強。
在以前所編寫的代碼中,咱們都默認調用了類中的無參構造方法。但是類中也有可能不提供無參構造方法。
範例:觀察當前程序的問題
Book.java
package com.jkx.lzh.po;
public class Book {
private String title;
private double price;
public Book(String title,double price){
this.title = title;
this.price = price;
}
public String toString(String title,double price) {
return "書名:"+ this.title + "價格: " + this.price ;
}
}複製代碼
ReflectTest9.java
package com.jkx.lzh.test;
public class ReflectTest9 {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException{
Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
Object obj = cls.newInstance();
System.out.println(obj);
}
}複製代碼
以上代碼因爲Book沒有無參構造方法(<init>()),因此拋出如下異常:
Exception in thread "main" java.lang.InstantiationException: com.jkx.lzh.po.Book
at java.lang.Class.newInstance(Class.java:427)
at com.jkx.lzh.test.ReflectTest1.main(ReflectTest1.java:7)
Caused by: java.lang.NoSuchMethodException: com.jkx.lzh.po.Book.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 1 more複製代碼
那麼怎麼解決這個問題呢?
在Class類中提供了這麼一個構造方法能夠取得構造:
以上兩個方法返回都是「java.lang.reflect.Constructor」類的對象。在這個類中咱們提供一個明確傳遞有參構造內容實例化對象方法:
public T newInstance(Object... initargs) throws InstantiationException,IllegalAccessException,IllegalAccessException,IllegalArgumentException,InvocationTargetException;
範例:明確調用類中的構造方法
ReflectTest9.java修改以下:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectTest9 {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException, NoSuchMethodException,
SecurityException, IllegalArgumentException, InvocationTargetException{
Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
//public Book(String title,double price);Constructor傳的是有參構造的參數類型
Constructor<?> con = cls.getConstructor(String.class,double.class);
Object obj = con.newInstance("JAVA 反射,",82.22);
System.out.println(obj);
}
}
"書名:JAVA 反射價格: 82.22"
複製代碼
以上給我一個警示:在咱們的簡單java類的開發中無論提供多少構造方法,咱們至少要保留無參構造方法。
類中普通方法只有在這個類實例化對象以後才能被調用。而且實例化方法有三種(new,conle,反射)
範例:定義一個類
public class Book {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}複製代碼
這個類有無參構造方法,因此實例化對象的時候能夠直接利用Class提供的newInstance方法
在Class類裏面提供有如下取得類中Method()的操做:
1. 取得一個類中的所有方法: public Method[] getMethods() throws SecurityException;
public Method getMethod(String name,Class<?> ... ParameterTypes) throws NoSuchMethodException,SecurityException;
以上兩個操做返回的是「java.lang.reflect.Method」類對象,在這裏類裏面咱們重點關注一個方法:調用方法-->
public Object invoke(Object obj,Object args) throws IllegalAccessException,IllegalArgumentException,InvocationTargetException;
範例:反射調用方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectTest10 {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException, NoSuchMethodException,
SecurityException, IllegalArgumentException, InvocationTargetException{
String fileName = "title";//要操做的成員
Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
Object obj = cls.newInstance();//必須實例化對象
Method setMet = cls.getMethod("set"+ inticap(fileName) , String.class);
Method getMet = cls.getMethod("get"+ inticap(fileName));
setMet.invoke(obj, "JAVA 反射");
System.out.println(getMet.invoke(obj));
}
public static String inticap(String str){
return str.substring(0,1).toUpperCase() + str.substring(1);
}
}
"JAVA 反射"複製代碼
此時咱們徹底看不到操做類型,也就是說利用反射能夠實現任意類的指定方法調用。
1. 取得所有成員:public Filed[] getDeclaredFileds() throws SecurityException;
2. 取得指定成員:public Filed getDeclaredFiled(String name) throws NoSuchMethodException,SecurityException;
1. 取得屬性的內容:public Object getObject(Object obj) throws IllegalAccessException,IllegalArgumentException
2. 設置屬性的內容:public void Object getObject(Object obj,Object values) throws IllegalAccessException,IllegalArgumentException
1. Executable下面繼承了Constructor,Method;
2. Filed: 在這個類中提供有一個方法:public void setAccessible(boolean flag) throws SecurityException;設置是否取消封裝
範例:如今提供以下類
package com.jkx.lzh.po;
public class Book {
private String title;
}複製代碼
這個類中定義了一個私有屬性,按照咱們原始的作法,此時它必定沒法被外部所使用。
範例: 可是使用setAccessible取消封裝就可使用了
import java.lang.reflect.Field;
public class ReflectTest11 {
public static void main(String[] args) throws NoSuchFieldException,
SecurityException, ClassNotFoundException,
InstantiationException, IllegalAccessException {
Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
Object obj = cls.newInstance();//必須實例化對象
Field titleField = cls.getDeclaredField("title");
titleField.setAccessible(true);
titleField.set(obj, "JAVA 反射");//至關於:Book對象.title = "JAVA 反射";
System.out.println(titleField.get(obj));//至關於:Book對象.title;
}
}
"JAVA 反射"複製代碼
構造方法和普通方法一樣能夠取消,只不過咱們不多這樣去作,並且對於屬性的訪問仍是建議使用settr、gettr