黑馬程序員:jdk1.5新特性3 (反射)

 

反射 ( reflect 反射不是JDK 1.5的新特性,是java1.2開始有的。)
 透徹分析反射的基礎-Class
  1 java 程序中的各個java類 屬於同一類事物,描述這類事物的java類名就是 Class。
  2  Class類 代表java類,它的各個實例對象分別對應什麼呢?
     ^對應各個類在內存中的字節碼,例如,Person類的字節碼,ArrayList類的字節碼....
     ^一個類被類加載器加載到內存中,佔用一片存儲空間,這個空間裏面的內容就是類的字節碼,不    同的類的字節碼是不同的,所以它們在內存中的內容是不同的,這一個個空間分別用一個個的對    象來表示,這些對象具有了相同的類型Class.
  3 如何得到各個字節碼對應的實例對象(Class類型)
    ^類名.class,例如 System.class
    ^對象。getClass(),例如new Daye().getClass
    ^Class.forName("類名"),例如,Class.forName("java.lang.Date");
     面試題:Class.forName("")的作用是什麼?
            答:返回字節碼。有兩種方式:
                1)如果字節碼在java虛擬機中已經存在,則在內存中找到並返回該字節碼
                2)如果字節碼在java虛擬機中不存在,則類加載器將其加載到內存後 返回該字節碼。
   4 九個預定義的Class對象
      八個基本類型 和void (int.class  、 void.class...)
       參看Class.isPrimitive方法的幫助 (是不是基本數據類型)
       Int.class==Integer.TYPE
   5 數組類型的Class實例對象
    Class.isArray()
 總之只要在源程序中出現的類型,都有各自的Class實例對象,例如:int[],void...

 反射 : 就是把java類中的各種成分映射成相應的java類


Constructor類 代表某個類中的一個構造方法
得到某個類的構造方法:
  Constructor [] constructor=
        Class.forName("java.lang.String").getConstructor(StringBuffer.class);
   //獲得方法時要用到類型
創建實例對象:
通常:String str=new String(new StringBuffer("abc"));
反射:String str=(String)constructor.newInstance(new StringBuffer("abc"));
     //調用方法時 要用到與上面相同類型的實例對象
Class.newInstance()方法:
  例子:String obj=(String)Class.forName("java.lang.String").newInstance();
  該方法內部先得到默認的構造方法(無參的構造方法被緩存了),然後用該構造方法創建實例對象。
  該方法用到了緩存機制來 保存默認的構造方法的實例對象。


Field 類  代表某個類中的一個成員變量
 問題:得到的Field對象是對應到類上的成員變量,還是對應到對象上的成員變量?
 類只有一個,而該類的實例對象有多個,所以Field對象關聯的是類上的成員變量。
  所以字段fieldX代表X的定義,而不是具體的X變量。
 
public class ReflectPoint {
  private int x;
  public int y;
  public ReflectPoint(int x, int y) {
 super();
 this.x = x;
 this.y = y;
}}
public class ReflectTest{
   public static void main(String[] args){
    try {
        ReflectPoint flect1=new ReflectPoint(4,6);
 Field fieldY=flect1.getClass().getField("y");
         //fieldY的值是多少?5錯:fieldY是類的變量,不是對象的。
         System.out.println(fieldY.get(flect1));
   /*Field fieldX=flect1.getClass().getField("x");
    * x是私有的外部類不能訪問,要想獲得X只能強制反射
    */
  Field fieldX=flect1.getClass().getDeclaredField("x");
  fieldX.setAccessible(true);
  System.out.println(fieldX.get(flect1));
  } catch (Exception e) {
     // TODO Auto-generated catch block
  e.printStackTrace();} } }  

作業:將任意一個對象中的所有String類型的成員變量所有對應的字符串內容中的“b”改爲“a”
import java.lang.reflect.Field;
public class ReflectTest{
    ReflectPoint flect1=new ReflectPoint();
    changeStringValue(flect1);
    System,out.print(flect1);
  public void changeStringValue(Object obj)throws Exception{
     Field[] fields=obj.getClass().getFields();
     for(Field field:fields){
            if(field.getType==String.class){
            String oldString=field.get(obj);
            String newString=oldString.replace("b","a");
            field.set(obj,newString);
                                            }
                             }
                                                           }
                         }       
public class ReflectPoint {
  private int x;
  public int y;
  public String str1="boy";
  public String str2="field";
  public String str3="hello";
  @Override
public String toString(){
   return str1+":"+str2+":"+str3; 
                        }
                          }


Method類 代表某個類中一個成員方法
得到類中某一個方法:
  Method methodCharAt=Class.forName("java.lang.String").getMethod("charAt",int.class);
調用方法:
   通常:System.out.print(str.charAt(1));
   反射:System.out.print(methodCharAt.invoke(str,1));
 如果傳遞給Method對象的invoke()方法的第一個參數爲null,說明該Method對象對應的是一個靜態方法
(因爲靜態方法調用時 對象還沒有產生)System.out.print(methodCharAt.invoke(null,1));  
jdk1.4和 jdk1.5的區別:
   jdk1.5: public Object invoke(Object obj,Object ... args)
   jdk1.4: public Object invoke(Object obj,Object[] args)
 按jdk1.4的語法需要將一個數組作爲參數傳遞給invoke方法時,數組中的每個元素分別對應被調用方法的一個參數,所有 調用charAt方法的代碼也可以用jdk1.4改寫methodCharAt(str,new Object[]{1});
 

對接收數組參數的成員方法進行反射
 用反射的方式執行某個類中的main方法:
  目標:寫一個程序,這個程序能夠根據用戶提供的類名,去執行該類的main()。那麼我們爲什麼要用反        射的方式調用呢?
  問題:啓動java程序的main方法的參數時一個字符串數組,通過反射調用main方法時,如何爲invoke         方法傳遞參數呢?按jdk1.5,整個數組是一個參數;按jdk1.4,數組中的每個元素對應一個參數        jdk1.5肯定要兼容jdk1.4的語法,把數組打散成若干個單獨的參數。所以給main方法傳遞參數時        不能使用代碼。

 解決辦法:
  1 mainMethod.invoke(null,new Object[]{new String[]{"xxx"}})
    將數組作爲另一個數組的元素。
  2 mainMethod.invoke(null,(object)new String[]{"xxx"});
   編譯器會作特殊處理,編譯時不把參數當做數組看待。
目標程序:(運行這段代碼時 我們在運行對話框裏 給類傳遞參數(要運行main方法類的完整名稱)這樣主          函數才能找到類名進行反射)
import java.lang.reflect.Method;
public class MainMethodRunTest {
    public static void main(String[] args) {
 String mainStaticMethod = args[0];
  try {
    Method mainMethod=
 Class.forName(mainStaticMethod).getMethod("main",String[].class);
    mainMethod.invoke(null,new Object[]{new String[]{"12","43","34"}}); 
  //mainMethod.invoke(null, (Object)new String[]{"12","43","34"});  
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();}}}
  class MainMethodTest {
 public static void main(String[] args) {
    // TODO Auto-generated method stub
          for(String arg:args){
           System.out.println(arg);
          } } }


數組與Object的關係及其反射類型
 1 具有相同維數和元素類型的數組屬於同一個類型,即具有相同的Class實例對象
 2 代表數組的Class實例對象的getSuperClass()方法返回的父類爲Object類對應的Class
 3 基本類型的一位數組可以被當做Object類型使用,不能當作Object[]類型使用;非基本類型的一維數組,既可以當作Object類型也可當作Object[]類型使用。
 4 Arrays.asList()方法處理int[]和String[]時的差異
    jdk1.4中Arrays.asList(Object[] a) 它將String[]的元素轉化爲List對象,就可以打出了;而
    int[] 屬於Object類型 不屬於Object[] 只能由jdk1.5處理。jdk1.5中,Arrays.asList(T ... a)
    int[] 被當作一個Object 所以不能打出int[]的各個元素。
  int [] a1=new int[]{1,2,8};
  String [] a2=new String[]{"a","b","d"};
  System.out.println(Arrays.asList(a1)); //結果[[I@C17164]
  System.out.println(Arrays.asList(a2));  //結果[a,b,d]
 5 Array工具類用於完成對數組的反射操作
 例:打印任意對象
 import java.util.Arrays;
 public class AyyayReflectTest{
  public static void main(String [] args){

     int [] a1=new int[]{1,5,8};
     String [] a2=new String[]{"v","fd","h"};
     printObject(a1);
     printObject(a2);
     printObject("xyz");
  }
 private static void printObject(Object obj) {
  // TODO Auto-generated method stub
  Class clazz=obj.getClass();
  if(clazz.isArray()){
    int len=Array.getLength(obj);
    for(int i=0;i<len;i++){
     System.out.println(Array.get(obj, i));   
    }
  }else{
   System.out.println(obj);
  }  }    }

ArrayList、HashSet的比較及Hashcode分析
ArrayList按順序存儲元素,可以重複
HashSet存儲的元素不可以重複,當存儲元素時先比較如果存在該元素則不存放。
     它們都可以實現Collection接口
**分析Hashcode:
 如果要查找一個集合中是否有某個對象,通常我們會逐一取出每個元素與查找的對象比較,直到找到那個用equals方法比較相等的值,停止查找並返回肯定的信息,如果有一萬個元素並且不包含要查找的對象,那麼equals方法會被調用一萬次。有人發明了哈希算法,計算出每個元素的哈希值,並將集合分爲若干區域,這樣查找對象時 只要計算出它的哈希值所對應的區域 在對這個區域進行查找 ,大大的提高了程序的運行效率。當然Hashcode只對用哈希算法存儲的集合有價值,例如HashSet.

注意(內存泄露):當一個對象被存入HashSet集合以後,就不能修改這個對象中的那些參與計算哈希值    的字段了,否則,對象修改後的哈希值與最初存入的不同,這時即使在contains方法使用該對象當前引   用作爲參數去HashSet集合檢索對象,也將 返回 找不到對象的結果,這也會導致無法從HashSet集合中   單獨刪除當前對象,造成內存泄露。

反射的作用——>實現框架功能
  框架與工具類的區別:工具類被用戶調用;框架則是調用用戶提供的類
  框架要解決的核心問題:寫程序時無法知道要被調用的類名,所以在程序中無法直接new某個類的實例對象,而要用反射方式來做
綜合案例:採用配置文件夾反射的方式創建ArrayList和HashSet的實例對象。
   工作間workspacethree 類名ReflectPoint.java和ReflectTest2.java  配置文件config.properties  *   配置文件一定要用完整的目錄,這個目錄不是硬編碼,是運算出來的。
 *   在javaWeb中  我們通過getRealPath();來獲得絕對路徑 將配置文件放在一個目錄下,之後讓用戶 *   配置這個目錄
 *   另一個獲得資源文件的方式,用類加載器
ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast.day1/config.properties")
ReflectTest2.class.getResourceStream("config.peoperties")


內省-->瞭解JavaBean
IntroSpector(內省)-->JavaBean-->特殊的java類
JavaBean:是一種特殊的java類主要用於傳遞數據信息,這種java類中的方法主要用於訪問私有的字段,           且方法名符合某種命名規則
 如果在兩個模塊之間傳遞多個信息,可以將這些信息封裝到一個JavaBean中,這種JavaBean的實例對象通常稱之爲值對象(Value Object簡稱VO),這些信息在類中用私有字段來存儲,稱爲類的屬性,
屬性是由方法來判斷的,去掉get或set後 就是JavaBean的屬性,如果第二個字母是小寫則第一個字母變成小寫getTime-->time、gettime-->time、getCPU-->CPU

 JavaBean類的好處:
   1 在javaEE開發中,經常要使用JavaBean,很多環境要求按JavaBean的方式操作。
   2 JDK中提供了對JavaBean進行操作的一些API,這套API 稱爲內省,使用內省操作JavaBean比普通類       的方式更方便
寫一段代碼 讀取javaBean屬性,然後再設置javaBean屬性
   代碼1: 用PropertyDescriptor類更簡單(workspace2/javaenhance/cn/itcast/day1/IntroSpectTest.java)
  代碼2:採用遍歷BeanInfo的所有屬性方式查找和設置某個ReflectPoint對象的x屬性。在程序中把一個類當作javaBean來看,就是調用IntroSpector.getBeanInfo(),得到的BeanInfo對象封裝了把這個類當作JavaBean看的結果信息

**BeanUtils工具包
  由於javaBean的屬性設置和獲取使用的非常多,有人開發出了Beanutils工具包。
  將beanutils包的最大jar包commons-beanutils-1.8.3放入工程中(在工程裏新建一個文件夾lib),之  後build path;因爲beanutils包使用到了commons-logging.jar,所以將這個jar同樣的導入工程中。
 
(1)BeanUtils(獲得設置javabean屬性)
   get()返回的屬性結果爲字符串,set可接受任意類型對象,通常爲字符串。
     BeanUtils.setProperty(Object,propertyName,value)
     BeanUtils.getProperty(object,peopertyName)
例:
     BeanUtils.setProperty(rp, "x", "8");
     BeanUtils.getProperty(rp,"x");

 *操作符合屬性,支持屬性的級聯操作
  BeanUtils.setProperty(rp,"birthday.time" ,"111");
  System.out.println(BeanUtils.getProperty(rp, "birthday.time"));

 *java7新特性:beanutils可以對MAP進行操作。提供了Map與javabean 的轉換
Map map={name:"lisi",age:17};
BeanUtils.setProperty(map,"name","wangsan");
 
(2)PropertyUtils(另一個獲得設置javabean屬性的簡單類 )
get()返回的屬性爲bean本身類型,set只接受bean屬性的本身類型。
    PropertyUtils.setProperty(bean,propertyName ,value);
    System.out.println(PropertyUtils.getProperty(bean, propertyName));
 例如:
    PropertyUtils.setProperty(rp, "x", 5);
    System.out.println(PropertyUtils.getProperty(rp, "x"));

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章