作为一种重要特性,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. 初识 Runtime

    前言 之前在看一些第三方源码的时候,时不时的能碰到一些关于运行时相关的代码.于是乎,就阅读了一些关于运行时的文章,感觉写的都不错,写此篇文章为了记录一下,同时也重新学习一遍. Runtime简介 Ru ...

  2. MYSQL 表左连接 ON AND 和ON WHERE 的区别

    首先是针对左右连接,这里与inner join区分 在使用left join时,on and 和on where会有区别 1. on的条件是在连接生成临时表时使用的条件,以左表为基准 ,不管on中的条 ...

  3. jenkins部署.net平台自动化构建

    在引入自动化部署工具的时候,我们对比了jenkins和gitlab CI,jenkins有非常丰富的插件,配置起来方便.gitlab CI更倾向于脚本配置,当然jenkins也可以使用pipeline ...

  4. Go 语言 goto 语句

    Go 语言的 goto 语句可以无条件地转移到过程中指定的行. goto语句通常与条件语句配合使用.可用来实现条件转移, 构成循环,跳出循环体等功能. 但是,在结构化程序设计中一般不主张使用goto语 ...

  5. 配置 docker0 网桥

    Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络. Docker 默认 ...

  6. RDO Stack: Failed connect to server

    Issue: When you create an instance, but cannot connect to the VNC Server because of the error messag ...

  7. Freemarker商品详情页静态化服务调用处理

    --------------------------------------------------------------------------------------------- [版权申明: ...

  8. linux网络编程之一-----多播(组播)编程

    什么是多播 组播(Multicast)是网络一种点对多(one to many)的通信方式,通过报文复制完成网络中一台server对应多台接收者的高效数据传 送.对其形象的比喻就是类似于广播电台和电视 ...

  9. 如何找出Xcode中不同版本Swift的路径

    我们知道Xcode中可能包含不知一个Swift的版本,那么我们如何找到它们对应的路径呢? 熟悉unix shell命令的童鞋都知道有一个find指令,在我们已知Xcode路径时,我们可以在其中找到Sw ...

  10. Java异常处理-----非运行时异常(受检异常)

    非运行时异常(受检异常) 如果出现了非运行时异常必须进行处理throw或者try{}catch(){}处理,否则编译器报错. 1:IOException 使用要导入包import java.io.IO ...