作为一种重要特性,Java反射机制在很多地方会用到。在此做一小结,供朋友们参考。

首先从一个问题开始着手。

可恶的问题又来了,NoSuchFieldException,如下图所示:

完全不知道这个question是从哪里来的。以前也遇到过这样的问题,后来解决了,但是没有写文档,再次相遇这样的问题,傻了。

经过上网一番查找,发现遇到这个问题的小盆友还真不少,这个问题是属于java反射机制里的。

这是一个反射对象时候的异常,已经被捕获了的。这个报错代码是混淆了的,是不是这个question对象被混淆成其他名了。。

源代码如下:

public static <T> List<T> findMoreRefResult(String sql, List<Object> params,

Class<T> cls) throws Exception {

//加载数据库驱动

new MysqlUtil();

//连接数据库

MysqlUtil.GetConnection();

// 构造一个初始容量为 10 的空列表。

List<T> list = new ArrayList<T>();

// 表示占位符的第一个位置

int index = 1;

pstmt = connection.prepareStatement(sql);

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

// 判断所填充的占位符是否有值;判断集合的标准方式

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

for (int i = 0; i < params.size(); i++) {

// 使用给定对象设置指定参数的值。第二个参数必须是Object类型

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

}

}

// 返回查询结果

resultset = pstmt.executeQuery();

// 获取列的相关信息

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

// 获取列数

int col_lenth = metdata.getColumnCount();

while (resultset.next()) {

// 通过反射机制创建一个实例(生成对象)

T resultObject = cls.newInstance();

for (int i = 0; i < col_lenth; 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的私有访问权限

field.setAccessible(true);

field.set(resultObject, cols_value);

}

list.add(resultObject);

}

//关闭数据库

MysqlUtil.releaseConn();

return list;

}

调用以上方法的方法如下:

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<Object> params = new ArrayList<Object>();

String sql = "SELECT * FROM question ";

try {

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

} catch (Exception e) {

e.printStackTrace();

}

}

在调用findMoreRefResult()方法的时候,请注意红色背景部分,先获取数据库表中字段名,然后依据该字段名来反射此 Class 对象所表示的类或接口的指定已声明字段。也就是说数据库中的字段名应该与类中的属性字段名一一对应才对,这是符合“建数据表时应与类一一对应”原则的。

下面,我们来深入学习一下Java的反射机制,只有做到深入了解某件事情,当这件事情发生问题时,我们自然就会懂得解决之道。

注:下面的内容只了解基本的原理就行,代码可忽略。

一、反射的基础---Class

Class是所有java类的一个总称,就好像各式各样的人都可以用Person来称呼,每一个类被加载之后都会在内存中生存一个Class对象,这个对象我们通常称之为字节码,而我们通过调用一个类创造的对象其实都是字节码搞出来的,一个类只会产生一份字节码。

那么我们怎么获得一个类的Class呢?有三种方式:

1.调用某个类的class属性

2.使用Class的forName()静态方法

3.调用某个对象的getClass()方法。

下面我们通过一个实例来展示下上面两点:

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是同一份字节码吗?"+(cl1==cl2));

System.out.println("通过Class.forName()获得的字节码与String.class一样吗?"+(cl1==cl3));

}

}

通过查看JDK文档,我们可以发现,Class有许多方法,通过这些方法我们可以得到java类的相关信息,Constructor,Method,Field等,具体的大家可以参考JDK文档。

二、反射的应用

那么什么是反射呢?曾经有人说过,反射就是把java类中的各种成分映射成相应的java类。为什么呢?从上一个讲解中大家是否发现,通过Class我们可以解析出一个java类的各种成分,他们返回的也是一个类,所以这句话还是很有道理的。

下面让我们来看看反射的一些应用:

1.使用反射生成对象

通过反射来生成对象有两种方式:

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

b.使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建Class对应类的实例。

实例: 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对象,再调用Constructor对象的newInstance()方法来创建Class对应类的实例。

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

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

}

}

2.成员变量的反射

接上实例大家看看:

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));

因为a是私有成员变量

*/

//我们可以通过暴力反射来获取它的值

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

//设置为可以访问

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;

}

}

如果将上面的搞懂,那么我们不妨来做一道经典的题目:将一个类中所有String类型的成员变量,其中含有的‘a’替换为‘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初步

类加载器负责加载所有的类,系统为所有被载入内存中的类生成一个java.lang.Class实例。一旦一个类被载入到JVM中,同一个类就不会再次被载入了,这是针对同一个加载器,不同的加载器还是可以加载同一个类的,不同加载器加载同一个类在JVM中是不同的。因为在JVM中用类的全限定类名加类加载器作为其唯一标识。

在JVM启动时,会形成有三个类加载器组成的初始类加载器层次结构:

-->Bootstrap ClassLoader:根类加载器

-->Extension ClassLoader:扩展类加载器

-->System ClassLoader:系统类加载器

Bootstrap ClassLoader是使用C++写的,我们是无法得到它的源码,它是java的核心Loader,比如我们通过String.class.getClassLoader()是获得不到它的名字的,返回的是空值。

如果父类加载器加载了某个类,子类就不会加载了。

ClassLoader动态加载:

a.并非一次性加载

b.需要运行时才加载

c.可以观察类的具体加载过程:java -verbose:class  在eclipse中只要配置后面的-verbose:class

d.static语句块在加载后执行一次。

e.dynamic语句块每次new新的对象都会执行

等同于构造方法中的语句。

用的比较少。

具体看参见:http://gzcj.iteye.com/blog/394644

二、使用反射实现JDK动态代理

在java中提供了Proxy类和一个InvocationHandler接口,通过他们俩我们就可以创建JDK动态代理,下面让我们通过一个实例来看看如何使用吧:

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("正在运行的方法:"+method);

if(args!=null){

System.out.println("执行该方法传入的参数:");

for(Object arg:args){

System.out.println(arg);

}

}else {

System.out.println("该方法没有传入参数!");

}

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接口的代理类实例

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

person.walk();

person.sayHello("ITEYE");

}

}

通过Proxy类的newProxyInstance()方法我们可以获得一个指定接口的代理类实例,在MyInvocationHandler中会自动执行invoke方法,执行结果如下:

正在运行的方法:public abstract void com.lyl.reflect.Person.walk()

该方法没有传入参数!

正在运行的方法:public abstract void com.lyl.reflect.Person.sayHello(java.lang.String)

执行该方法传入的参数:

ITEYE

首先必须明一点 Field类主要是用来辅助获取和操作类的属性的!

1.怎么通过反射获取类的属性

先来看JDK提供的方法有如下几种:

a)Class.getDeclaredField(String name);

返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。

b)Class.getDeclaredFields();

返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。

c)Class.getField(String name);

返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。

d)Class.getField();

返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。

来一个例子来说明一下 :

实体类:

import java.util.Date;

/**

* @ClassName: Student

* @Description: 学生实体

* @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.进行属性获取、更改

得到这个Field之后你就可以获取他的值或者设置他的值了。

获取它的值用get类型方法,针对常见类型提供了对应get方法,这里就不一一列举了。

值得注意的是获取私有属性的时候必须先设置Accessible为true,然后才能获取。

同理设置的时候调用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

*/

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));

//私有变量必须先设置Accessible为true

field.setAccessible(true);

//获取用get类方法。

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

}

//设置用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进阶(六)Java反射机制可恶问题NoSuchFieldException的更多相关文章

  1. Java进阶之reflection(反射机制)——反射概念与基础

    反射机制是Java动态性之一,而说到动态性首先得了解动态语言.那么何为动态语言? 一.动态语言 动态语言,是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结构上的变化.比如常见 ...

  2. Java进阶3. 内存回收机制

    Java进阶3. 内存回收机制 20131029 前言: 学过C++的都知道,C++中内存需要程序员自己维护.说道这里,很多开发的同学就感觉很痛苦,当他转向Java的时候,就会说你看Java多好啊,程 ...

  3. Java 核心类库之反射机制

    1:什么是反射机制? 2:反射机制它可以做什么呢? 3:反射机制对应的API又是什么? 1):通过反射机制来获取一个对象的全限定名称(完整包名),和类名: 2):实例化Class对象 3):获取对象的 ...

  4. 在JAVA中,关于反射机制的讨论

    一.什么是反射机制         简单的来说,反射机制指的是程序在运行时能够获取自身的信息.在java中,只要给定类的名字,     那么就可以通过反射机制来获得类的所有信息. 二.哪里用到反射机制 ...

  5. Java进阶 | Proxy动态代理机制详解

    一.Jvm加载对象 在说Java动态代理之前,还是要说一下Jvm加载对象的过程,这个依旧是理解动态代理的基础性原理: Java类即源代码程序.java类型文件,经过编译器编译之后就被转换成字节代码.c ...

  6. java.lang.Class<T> -- 反射机制

    1反射机制是什么 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为jav ...

  7. java工厂类与反射机制

    java 简单工厂类 2012-04-22 15:44:07|  分类: java |  标签:java工厂类  简单工厂类  |举报|字号 订阅     简单工厂模式需要由以下角色组成: 接口    ...

  8. Java SE之初探反射机制

    [Keywords]:Java,Hibernate,虚拟机,框架,SQL [Abstract]:   反射的概念:所谓的反射就是java语言在运行时拥有一项自观的能力,反射使您的程序代码能够得到装载到 ...

  9. JAVA(五)反射机制/Annotation

    成鹏致远 | lcw.cnblog.com |2014-02-04 反射机制 1.认识Class类 在正常情况下,必须知道一个类的完整路径之后才可以实例化对象,但是在 java中也允许通过一个对象来找 ...

随机推荐

  1. Mybatis之一级缓存,二级缓存

    一级缓存:Mybatis的一级缓存在session上,只要通过session查过的数据,都会放在session上,下一次再查询相同id的数据,都直接冲缓存中取出来,而不用到数据库里去取了. 二级缓存: ...

  2. 单选、多选框根据value值设置选中

    $("input:checkbox[value='video']").parent().addClass('checked'); $("input:radio[value ...

  3. 美链BEC合约漏洞技术分析

    这两天币圈链圈被美链BEC智能合约的漏洞导致代币价值几乎归零的事件刷遍朋友圈.这篇文章就来分析下BEC智能合约的漏洞 漏洞攻击交易 我们先来还原下攻击交易,这个交易可以在这个链接查询到. 我截图给大家 ...

  4. 安装Leanote极客范的云笔记

    前言 在这个互联网知识呈爆炸增长的时代,作为一个程序员要掌握的知识越来越多,然再好的记性也不如烂笔头,有了笔记我们就是可以时常扒拉扒拉以前的知识,顺便可以整理下自己的知识体系. 如今市面上云笔记产品, ...

  5. 【转载】RAID写惩罚(Write Penalty)与IOPS计算

    浅谈RAID写惩罚(Write Penalty)与IOPS计算 Character is what you are in the dark. 暗处最能反映一个人真正品格. ---------Apri ...

  6. redis在java客户端的操作

    redis高性能,速度快,效率高的特点,用来做缓存服务器是很不错的选择.(和memcache相似)redis在客户端的操作步骤: 1.redis单机版操作 1.1通过Jedis对象操作 (1)将安装r ...

  7. 剑指架构师系列-Redis安装与使用

    1.安装Redis 我们在VMware中安装CentOS 64位系统后,在用户目录下下载安装Redis. 下载redis目前最稳定版本也是功能最完善,集群支持最好并加入了sentinel(哨兵-高可用 ...

  8. 安卓高级6 玩转AppBarLayout,更酷炫的顶部栏 Toolbar

    原文大神地址:http://www.jianshu.com/p/d159f0176576 上一篇文章[<CoordinateLayout的使用如此简单 >]上一篇文章<Coordin ...

  9. [CSDN_Markdown] 使用CSDN Markdown编辑器

    简介 最近CSDN支持Markdown语法写博客了,甚是欢喜.前几天写了一篇实验了下,感觉不错.准备写几篇文章介绍一下如何使用CSDN的Markdown编辑器写博客,不求全面,但求够用,望大家批评指正 ...

  10. ROS机器人程序设计(原书第2版)补充资料 (柒) 第七章 3D建模与仿真 urdf Gazebo V-Rep Webots Morse

    ROS机器人程序设计(原书第2版)补充资料 (柒) 第七章 3D建模与仿真 urdf Gazebo V-Rep Webots Morse 书中,大部分出现hydro的地方,直接替换为indigo或ja ...