Java進(jìn)階(六)Java反射機(jī)制可惡問題NoSuchFieldException

      網(wǎng)友投稿 1061 2025-04-02

      作為一種重要特性,Java反射機(jī)制在很多地方會用到。在此做一小結(jié),供朋友們參考。

      首先從一個問題開始著手。

      可惡的問題又來了,NoSuchFieldException,如下圖所示:

      完全不知道這個question是從哪里來的。以前也遇到過這樣的問題,后來解決了,但是沒有寫文檔,再次相遇這樣的問題,傻了。

      經(jīng)過上網(wǎng)一番查找,發(fā)現(xiàn)遇到這個問題的小盆友還真不少,這個問題是屬于java反射機(jī)制里的。

      這是一個反射對象時候的異常,已經(jīng)被捕獲了的。這個報錯代碼是混淆了的,是不是這個question對象被混淆成其他名了。。

      源代碼如下:

      public?static??List?findMoreRefResult(String?sql,?List?params,

      Class?cls)?throws?Exception?{

      //加載數(shù)據(jù)庫驅(qū)動

      new?MysqlUtil();

      //連接數(shù)據(jù)庫

      MysqlUtil.GetConnection();

      //?構(gòu)造一個初始容量為?10?的空列表。

      List?list?=?new?ArrayList();

      //?表示占位符的第一個位置

      int?index?=?1;

      pstmt?=?connection.prepareStatement(sql);

      System.out.println("MysqlUtil:"?+?params);

      //?判斷所填充的占位符是否有值;判斷集合的標(biāo)準(zhǔn)方式

      if?(params?!=?null?&&?!params.isEmpty())?{

      for?(int?i?=?0;?i?

      //?使用給定對象設(shè)置指定參數(shù)的值。第二個參數(shù)必須是Object類型

      pstmt.setObject(index++,?params.get(i));

      }

      }

      //?返回查詢結(jié)果

      resultset?=?pstmt.executeQuery();

      //?獲取列的相關(guān)信息

      java.sql.ResultSetMetaData?metdata?=?resultset.getMetaData();

      //?獲取列數(shù)

      int?col_lenth?=?metdata.getColumnCount();

      while?(resultset.next())?{

      //?通過反射機(jī)制創(chuàng)建一個實(shí)例(生成對象)

      T?resultObject?=?cls.newInstance();

      for?(int?i?=?0;?i?

      String?cols_name?=?metdata.getColumnName(i?+?1);

      Object?cols_value?=?resultset.getObject(cols_name);

      if?(cols_value?==?null)?{

      cols_value?=?"";

      }

      //?通過字段名獲得反射(返回一個?Field?對象,該對象反映此?Class?對象所表示的類或接口的指定已聲明字段)

      Field?field?=?cls.getDeclaredField(cols_name);

      //?打開javabean的私有訪問權(quán)限

      field.setAccessible(true);

      field.set(resultObject,?cols_value);

      }

      list.add(resultObject);

      }

      //關(guān)閉數(shù)據(jù)庫

      MysqlUtil.releaseConn();

      return?list;

      }

      調(diào)用以上方法的方法如下:

      public?static?void?main(String[]?args)?{

      //Question?question?=?new?Question();

      //

      //Field?property?=?null;

      //try?{

      //property?=?question.getClass().getDeclaredField("description");

      //}?catch?(NoSuchFieldException?e1)?{

      //e1.printStackTrace();

      //}?catch?(SecurityException?e1)?{

      //e1.printStackTrace();

      //}

      //????????System.out.println(property);

      List?params?=?new?ArrayList();

      String?sql?=?"SELECT?*?FROM?question?";

      try?{

      System.out.println(MysqlUtil.findMoreRefResult(sql,?params,?Question.class));

      }?catch?(Exception?e)?{

      e.printStackTrace();

      }

      }

      在調(diào)用findMoreRefResult()方法的時候,請注意紅色背景部分,先獲取數(shù)據(jù)庫表中字段名,然后依據(jù)該字段名來反射此?Class?對象所表示的類或接口的指定已聲明字段。也就是說數(shù)據(jù)庫中的字段名應(yīng)該與類中的屬性字段名一一對應(yīng)才對,這是符合“建數(shù)據(jù)表時應(yīng)與類一一對應(yīng)”原則的。

      下面,我們來深入學(xué)習(xí)一下Java的反射機(jī)制,只有做到深入了解某件事情,當(dāng)這件事情發(fā)生問題時,我們自然就會懂得解決之道。

      注:下面的內(nèi)容只了解基本的原理就行,代碼可忽略。

      一、反射的基礎(chǔ)---Class

      Class是所有java類的一個總稱,就好像各式各樣的人都可以用Person來稱呼,每一個類被加載之后都會在內(nèi)存中生存一個Class對象,這個對象我們通常稱之為字節(jié)碼,而我們通過調(diào)用一個類創(chuàng)造的對象其實(shí)都是字節(jié)碼搞出來的,一個類只會產(chǎn)生一份字節(jié)碼。

      那么我們怎么獲得一個類的Class呢?有三種方式:

      1.調(diào)用某個類的class屬性

      2.使用Class的forName()靜態(tài)方法

      3.調(diào)用某個對象的getClass()方法。

      下面我們通過一個實(shí)例來展示下上面兩點(diǎn):

      ClassDemo1.java

      package?com.lyl.exercise;

      public?class?ClassDemo1?{

      public?static?void?main(String[]?args)?throws?ClassNotFoundException{

      String?str="iteye";

      Class?cl1=String.class;

      Class?cl2=str.getClass();

      Class?cl3=Class.forName("java.lang.String");

      System.out.println("str對象與String是同一份字節(jié)碼嗎?"+(cl1==cl2));

      System.out.println("通過Class.forName()獲得的字節(jié)碼與String.class一樣嗎?"+(cl1==cl3));

      }

      }

      通過查看JDK文檔,我們可以發(fā)現(xiàn),Class有許多方法,通過這些方法我們可以得到j(luò)ava類的相關(guān)信息,Constructor,Method,Field等,具體的大家可以參考JDK文檔。

      二、反射的應(yīng)用

      那么什么是反射呢?曾經(jīng)有人說過,反射就是把java類中的各種成分映射成相應(yīng)的java類。為什么呢?從上一個講解中大家是否發(fā)現(xiàn),通過Class我們可以解析出一個java類的各種成分,他們返回的也是一個類,所以這句話還是很有道理的。

      下面讓我們來看看反射的一些應(yīng)用:

      1.使用反射生成對象

      通過反射來生成對象有兩種方式:

      a.使用Class對象的newInstance()方法

      b.使用Class對象獲取指定的Constructor對象,再調(diào)用Constructor對象的newInstance()方法來創(chuàng)建Class對應(yīng)類的實(shí)例。

      實(shí)例:?ClassDemo3.java

      package?com.lyl.exercise;

      import?java.lang.reflect.Constructor;

      public?class?ClassDemo3?{

      public?static?void?main(String[]?args)?throws?Exception{

      //使用Class對象的newInstance()方法

      String?str=(String)Class.forName("java.lang.String").newInstance();

      //使用Class對象獲取指定的Constructor對象,再調(diào)用Constructor對象的newInstance()方法來創(chuàng)建Class對應(yīng)類的實(shí)例。

      Constructor?constructor=String.class.getConstructor(StringBuffer.class);

      String?str2=(String)constructor.newInstance(new?StringBuffer("abc"));

      }

      }

      2.成員變量的反射

      接上實(shí)例大家看看:

      ReflectDemo1.java

      package?com.lyl.exercise;

      import?java.lang.reflect.Field;

      public?class?ReflectDemo1?{

      public?static?void?main(String[]?args)?throws?Exception?{

      ReflectHelper?rh=new?ReflectHelper("iteye",?"javaeye");

      Field?fieldb=rh.getClass().getField("b");

      //fieldb只是類的字段,不是對象的,所以要想獲得對象上的值,要使用get()方法

      System.out.println(fieldb.get(rh));

      //如果我們使用上面方法來訪問a的值呢?拋出java.lang.NoSuchFieldException

      /*Field?fielda=rh.getClass().getField("a");

      System.out.println(fieldb.get(rh));

      因?yàn)閍是私有成員變量

      */

      //我們可以通過暴力反射來獲取它的值

      Field?fielda=rh.getClass().getDeclaredField("a");

      //設(shè)置為可以訪問

      fielda.setAccessible(true);

      System.out.println(fielda.get(rh));

      }

      }

      ReflectHelper.java

      package?com.lyl.exercise;

      public?class?ReflectHelper?{

      private?String?a;

      public?String?b;

      public?ReflectHelper(String?a,String?b){

      this.a=a;

      this.b=b;

      }

      }

      如果將上面的搞懂,那么我們不妨來做一道經(jīng)典的題目:將一個類中所有String類型的成員變量,其中含有的‘a(chǎn)’替換為‘b’。

      這個題目很明顯通過反射可以輕易的完成,大家不妨來看看。

      package?com.lyl.exercise;

      import?java.lang.reflect.Field;

      public?class?ReflectDemo2?{

      public?static?void?main(String[]?args)?throws?Exception?{

      ReflectHelper?rh=new?ReflectHelper("abc",?"basketball");

      changeStringValue(rh);

      System.out.println(rh);

      }

      private?static?void?changeStringValue(Object?object)?throws?Exception{

      Field[]?fields=object.getClass().getFields();

      for(Field?field:fields){

      if(field.getType()==String.class){

      String?oldValue=(String)field.get(object);

      String?newValue=oldValue.replace('a','b');

      //將object的String類型的變量替換為newValue

      field.set(object,?newValue);

      }

      }

      }

      }

      這樣就搞定了,是不是很簡單?

      一、ClassLoader初步

      類加載器負(fù)責(zé)加載所有的類,系統(tǒng)為所有被載入內(nèi)存中的類生成一個java.lang.Class實(shí)例。一旦一個類被載入到JVM中,同一個類就不會再次被載入了,這是針對同一個加載器,不同的加載器還是可以加載同一個類的,不同加載器加載同一個類在JVM中是不同的。因?yàn)樵贘VM中用類的全限定類名加類加載器作為其唯一標(biāo)識。

      在JVM啟動時,會形成有三個類加載器組成的初始類加載器層次結(jié)構(gòu):

      -->Bootstrap?ClassLoader:根類加載器

      -->Extension?ClassLoader:擴(kuò)展類加載器

      -->System?ClassLoader:系統(tǒng)類加載器

      Bootstrap?ClassLoader是使用C++寫的,我們是無法得到它的源碼,它是java的核心Loader,比如我們通過String.class.getClassLoader()是獲得不到它的名字的,返回的是空值。

      如果父類加載器加載了某個類,子類就不會加載了。

      ClassLoader動態(tài)加載:

      a.并非一次性加載

      b.需要運(yùn)行時才加載

      c.可以觀察類的具體加載過程:java?-verbose:class??在eclipse中只要配置后面的-verbose:class

      d.static語句塊在加載后執(zhí)行一次。

      e.dynamic語句塊每次new新的對象都會執(zhí)行

      等同于構(gòu)造方法中的語句。

      用的比較少。

      具體看參見:http://gzcj.iteye.com/blog/394644

      二、使用反射實(shí)現(xiàn)JDK動態(tài)代理

      在java中提供了Proxy類和一個InvocationHandler接口,通過他們倆我們就可以創(chuàng)建JDK動態(tài)代理,下面讓我們通過一個實(shí)例來看看如何使用吧:

      Person接口

      package?com.lyl.reflect;

      public?interface?Person?{

      void?walk();

      void?sayHello(String?name);

      }

      MyInvocationHandler.java

      package?com.lyl.reflect;

      import?java.lang.reflect.InvocationHandler;

      import?java.lang.reflect.Method;

      public?class?MyInvocationHandler?implements?InvocationHandler?{

      @Override

      public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)

      throws?Throwable?{

      System.out.println("正在運(yùn)行的方法:"+method);

      if(args!=null){

      System.out.println("執(zhí)行該方法傳入的參數(shù):");

      for(Object?arg:args){

      System.out.println(arg);

      }

      }else?{

      System.out.println("該方法沒有傳入?yún)?shù)!");

      }

      return?null;

      }

      }

      ProxyTest.java

      package?com.lyl.reflect;

      import?java.lang.reflect.InvocationHandler;

      import?java.lang.reflect.Proxy;

      public?class?ProxyTest?{

      public?static?void?main(String[]?args)?{

      InvocationHandler?handler=new?MyInvocationHandler();

      //返回Person接口的代理類實(shí)例

      Person?person=(Person)Proxy.newProxyInstance(Person.class.getClassLoader(),?new?Class[]{Person.class},?handler);

      person.walk();

      person.sayHello("ITEYE");

      }

      }

      通過Proxy類的newProxyInstance()方法我們可以獲得一個指定接口的代理類實(shí)例,在MyInvocationHandler中會自動執(zhí)行invoke方法,執(zhí)行結(jié)果如下:

      正在運(yùn)行的方法:public?abstract?void?com.lyl.reflect.Person.walk()

      該方法沒有傳入?yún)?shù)!

      正在運(yùn)行的方法:public?abstract?void?com.lyl.reflect.Person.sayHello(java.lang.String)

      執(zhí)行該方法傳入的參數(shù):

      ITEYE

      首先必須明一點(diǎn)?Field類主要是用來輔助獲取和操作類的屬性的!

      1.怎么通過反射獲取類的屬性

      先來看JDK提供的方法有如下幾種:

      a)Class.getDeclaredField(String?name);

      返回一個?Field?對象,該對象反映此?Class?對象所表示的類或接口的指定已聲明字段。

      b)Class.getDeclaredFields();

      返回?Field?對象的一個數(shù)組,這些對象反映此?Class?對象所表示的類或接口所聲明的所有字段。

      c)Class.getField(String?name);

      返回一個?Field?對象,它反映此?Class?對象所表示的類或接口的指定公共成員字段。

      d)Class.getField();

      返回一個包含某些?Field?對象的數(shù)組,這些對象反映此?Class?對象所表示的類或接口的所有可訪問公共字段。

      來一個例子來說明一下?:

      實(shí)體類:

      import?java.util.Date;

      /**

      *?@ClassName:?Student

      *?@Description:?學(xué)生實(shí)體

      *?@author??***

      *?@date?2014-3-18?下午5:17:35

      *?@version?V1.0

      */

      public?class?Student

      {

      private?Long?id;

      private?String?name;

      private?Date?createdate;

      private?String?no;

      public?String?nickname;

      public?Long?getId()

      {

      return?id;

      }

      public?void?setId(Long?id)

      {

      this.id?=?id;

      }

      public?String?getName()

      {

      return?name;

      }

      public?void?setName(String?name)

      {

      this.name?=?name;

      }

      ................

      }

      測試類:

      import?java.lang.reflect.Field;

      import?java.util.Date;

      /**

      *?@ClassName:?ReflectFieldTest

      *?@Description:?反射Field測試。

      *?@author??***

      *?@date?2014-3-18?下午5:16:17

      *?@version?V1.0

      */

      public?class?ReflectFieldTest

      {

      public?static?void?main(String[]?args)

      {

      Student?stu=new?Student();

      stu.setId(1L);

      stu.setName("Josean");

      stu.setNo("201403185203344");

      stu.setCreatedate(new?Date());

      try

      {

      Field?property1=stu.getClass().getDeclaredField("name");

      //private?java.lang.String?com.cx.test.Student.name

      System.out.println(property1);

      Field?property3=stu.getClass().getField("nickname");

      //public?java.lang.String?com.cx.test.Student.nickname

      System.out.println(property3);

      //錯誤方法?getField系列方法只能獲取公共字段

      //Field?property2=stu.getClass().getField("name");

      //System.out.println(property2);

      //會拋java.lang.NoSuchFieldException

      }?catch?(SecurityException?e)

      {

      e.printStackTrace();

      }?catch?(NoSuchFieldException?e)

      {

      e.printStackTrace();

      }

      }

      }

      2.進(jìn)行屬性獲取、更改

      得到這個Field之后你就可以獲取他的值或者設(shè)置他的值了。

      獲取它的值用get類型方法,針對常見類型提供了對應(yīng)get方法,這里就不一一列舉了。

      值得注意的是獲取私有屬性的時候必須先設(shè)置Accessible為true,然后才能獲取。

      同理設(shè)置的時候調(diào)用set類型方法,這里也不一一列舉了,下面放代碼。

      import?java.lang.reflect.Field;

      import?java.util.Date;

      /**

      *?@ClassName:?ReflectFieldTest

      *?@Description:?反射Field測試。

      *?@author?JoseanLuo

      *?@date?2014-3-18?下午5:16:17

      *?@version?V1.0

      */

      Java進(jìn)階(六)Java反射機(jī)制可惡問題NoSuchFieldException

      public?class?ReflectFieldTest

      {

      public?static?void?main(String[]?args)?throws?Exception

      {????Student?stu=new?Student();

      stu.setId(1L);

      stu.setName("Josean");

      stu.setNo("201403185203344");

      stu.setCreatedate(new?Date());

      stu.setNickname("copyman");

      Field?property1=stu.getClass().getDeclaredField("name");

      //System.out.println(property1);

      //out:private?java.lang.String?com.cx.test.Student.name

      Field?property3=stu.getClass().getField("nickname");

      System.out.println(property3.get(stu));

      //System.out.println(property3);

      //out:public?java.lang.String?com.cx.test.Student.nickname

      //錯誤方法?getField系列方法只能獲取公共字段

      //Field?property2=stu.getClass().getField("name");

      //System.out.println(property2);

      //會拋java.lang.NoSuchFieldException

      Field?[]?prFields4=stu.getClass().getDeclaredFields();

      for(Field?field:prFields4)

      {

      System.out.println(field);

      System.out.println(field.equals(property1));

      //私有變量必須先設(shè)置Accessible為true

      field.setAccessible(true);

      //獲取用get類方法。

      System.out.println(field.get(stu));

      }

      //設(shè)置用set類方法

      property3.set(stu,?"名字被我改了,哈哈");

      System.out.println(stu.getNickname());

      }

      }

      這個是控制臺輸出:

      copyman

      private?java.lang.Long?com.cx.test.Student.id

      false

      1

      private?java.lang.String?com.cx.test.Student.name

      true

      Josean

      private?java.util.Date?com.cx.test.Student.createdate

      false

      Tue?Mar?18?18:19:39?CST?2014

      private?java.lang.String?com.cx.test.Student.no

      false

      201403185203344

      public?java.lang.String?com.cx.test.Student.nickname

      false

      copyman

      名字被我改了,哈哈

      Java 數(shù)據(jù)庫

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:如何用excel做地圖教程
      下一篇:WPS演示文稿中自定義動畫的設(shè)置方法
      相關(guān)文章
      日本亚洲视频在线| 亚洲国产日韩成人综合天堂| 亚洲裸男gv网站| 亚洲欧洲av综合色无码| 亚洲一区在线视频| 亚洲1区1区3区4区产品乱码芒果| 久久精品a亚洲国产v高清不卡| 亚洲爆乳无码专区| 亚洲AV综合色区无码一区| 亚洲av无码精品网站| 亚洲国产精品第一区二区| 亚洲国产精品一区| 久久亚洲日韩看片无码| 久久精品国产亚洲77777| 亚洲精品美女久久久久| 亚洲视频免费在线播放| 亚洲成av人片不卡无码| 亚洲制服丝袜第一页| 亚洲中文无码永久免| 亚洲Av永久无码精品黑人| 国产综合激情在线亚洲第一页| www.亚洲精品.com| 亚洲精品亚洲人成在线观看下载| 国产偷国产偷亚洲高清日韩 | 久久精品国产亚洲5555| 亚洲一区二区三区自拍公司| 亚洲国产精品久久久天堂| 亚洲一区综合在线播放| 亚洲日本在线播放| 亚洲中文字幕无码亚洲成A人片 | 亚洲色婷婷综合久久| 亚洲成亚洲乱码一二三四区软件| 亚洲精品成人av在线| 亚洲欧洲日产v特级毛片| 亚洲a在线视频视频| 亚洲AV无码久久精品蜜桃| 亚洲美女视频一区| 狠狠色香婷婷久久亚洲精品| 日韩亚洲人成网站| 亚洲五月综合缴情在线观看| 亚洲专区在线视频|