java笔记--反射进阶之总结与详解
一.反射进阶之动态设置类的私有域
"封装"是Java的三大特性之一,为了能更好保证其封装性,我们往往需要将域设置成私有的,
然后通过提供相对应的set和get方法来操作这个域。但是我们仍然可以用java的反射机制来
修改类的私有域,由于修改类的私有域会破坏Java"封装"的特性,故请慎重操作。
主要技术:
Field类提供有关类或接口的单个字段的信息,以及对它的动态访问权限。
访问的字段可能是一个类(静态)字段或实例字段。
常用方法:
set(Object obj,Object value)-----: 将指定对象变量上此Field对象表示的字段设置为指定的新值
setBoolean(Object obj,boolean z)-: 将字段使得设置为指定对象上的一个boolean值
setDouble(Object obj,double d)---: 将字段的值设置为指定对象上的一个double值
setInt(Object obj,int i)---------: 将字段的值设置为指定对象上的一个int值
setAccessible(boolean flag)------: 将此对象的accessible标志设置为指定的布尔值
注:对于私有域,外部类调用的时候一定要使用setAccessible()方法将其可见性设置为true才能设置新值,
否则将会抛出异。
--支持知识共享,转载请标注地址"http://www.cnblogs.com/XHJT/p/3922160.html "——和佑博客园,谢谢~~--
实例代码:
package com.xhj.reflection_excetion; import java.lang.reflect.Field; /**
* 动态修改类的私有域
*
* @author XIEHEJUN
*
*/
public class DynamicChangePrivate {
private String userName;
private int userAge;
private String userAddress;
private boolean userGender; public DynamicChangePrivate(String userName, int userAge,
String userAddress, boolean userGender) {
super();
this.userName = userName;
this.userAge = userAge;
this.userAddress = userAddress;
this.userGender = userGender;
} public DynamicChangePrivate() {
super();
} public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} public int getUserAge() {
return userAge;
} public void setUserAge(int userAge) {
this.userAge = userAge;
} public String getUserAddress() {
return userAddress;
} public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
} public boolean isUserGender() {
return userGender;
} public void setUserGender(boolean userGender) {
this.userGender = userGender;
} /**
* 根据获得的性别的真假,获取其String值: true--为男 false--为女
*
* @param userGender
* @return
*/
public String getGender(boolean userGender) {
if (userGender) {
return "男";
} else {
return "女";
}
} public static void main(String[] args) {
DynamicChangePrivate user = new DynamicChangePrivate("小黑", 21,
"北京路华西小区3单元446", true);
Class<?> clazz = user.getClass();
System.out.println("通过反射取得的对象全称为:" + clazz.getName());
try { // 获取要修改的类的字段名称
Field userName = clazz.getDeclaredField("userName");
Field userAge = clazz.getDeclaredField("userAge");
Field userAddress = clazz.getDeclaredField("userAddress");
Field userGender = clazz.getDeclaredField("userGender"); // 修改并输出新旧名称
System.out.print("原名称为:" + user.getUserName());
userName.set(user, "晓晓");
System.out.println("\t\t\t修改后的名称为:" + user.getUserName()); // 修改并输出新旧年龄
System.out.print("原年龄为:" + user.getUserAge());
userAge.set(user, 24);
System.out.println("\t\t\t修改后的年龄为:" + user.getUserAge()); // 修改并输出新旧地址
System.out.print("原地址为:" + user.getUserAddress());
userAddress.set(user, "石景山八角南里3单元506");
System.out.println("\t修改后的地址为:" + user.getUserAddress()); // 修改并输出新旧性别
System.out.print("原性别为:" + user.getGender(user.isUserGender()));
userGender.set(user, false);
System.out.println("\t\t\t修改后的性别为:"
+ user.getGender(user.isUserGender())); } catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} } }
结果为:
注:任何类型的域,都可以通过set(Object obj, Object value) 方法将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
但是Field本身也提供了其他类型相对应的set方法,如 setBoolean(Object obj, boolean z),setDouble(Object obj, double d)等。另外,通过Field也可以设置public.protected域,但一般情况下很少这么设置,尤其是public域。在这里特别要注意的是:一定
要明确修改的含义,不要轻易的通过反射来修改类的私有域,因为这破坏了java面向对象"封装"的特性。
二、反射进阶之动态调用类的方法
我们知道Java是一种面向对象的语言,对他而言,一切都是对象,因此要调用类的方法,只能通过建立类的对象来调用。当然如果是
静态的方法,那就可以直接通过类本身来调用,而不需要建立类的对象。那么还有没有其他可以调用类方法的方式呢??
在Java的反射的机制中,提供了比较另类的调用方式,既可以根据需要指定要调用的方法,而不必在编程时确定。调用的方法不仅权限于public
的,还可以是private的。
Method类提供类或接口上单独某个方法(以及如何访问该方法)的信息,所反映的方法可能是类方法或实例方法(包括抽象方法)。它允许在匹配
要调用的实参与底层方法的形参时进行扩展转换,但是如果要进行收缩转换,则会抛出"非法参数异常"--IllegalArgumentExcetion。使用invoke()
可以实现动态调用方法:
public Object invoke(Object obj,Object...args)throws IllegalArgumentException,IllegalAccessException,InvocationTargetExcetion
obj--要调用的方法的类对象
args--方法调用的参数
注:对于私有域,外部类调用的时候一定要使用setAccessible,并且设置为true。
实例代码:
package com.xhj.reflection_excetion; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; /**
* 实现动态调用类的方法
*
* @author XIEHEJUN
*
*/
public class DynamicCallMethod { public static void main(String[] args) {
try {
// 运行时动态调用Math的abs()静态方法
System.out.println("运行时动态调用Math的abs()静态方法");
Method meth = Math.class.getDeclaredMethod("abs", Integer.TYPE);
Integer a = (Integer) meth.invoke(null, new Integer(-1));
System.out.println("-1的绝对值为:" + a); // 运行时动态调用String的非静态方法contains()
System.out.println("运行时动态调用String的非静态方法contains()");
Method strMeth = String.class.getDeclaredMethod("contains",
CharSequence.class);
boolean str = (Boolean) strMeth.invoke(new String("xhjit"), "xhj");
System.out.println("xhjit中是否包含有xhj——" + str); } catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} } }
结果为:
三、反射进阶之动态实例化类
在Java中,要实例化一个类即创建一个类的对象,需要通过构造方法来实现。但是在Java中还可以使用另外一种方式来实现,
那就是通过反射来实例化一个类。
Constructor是Java中提供类的单个构造方法的信息以及访问权限的封装类。
它允许在将实参与带有底层构造方法的形参的newInstance()匹配时进行扩展转换,
但是如果发生收缩转换,则抛出IllegalArgumentExcetion。newInstance()方法可以
使用指定的参数来创建对象:
public T newInstance(Object...initargs)throws InstantiationException,IllegalAccessException,
IllegalArgumentException,InvocationTargetException
initargs: 将作为变量传递给构造方法调用的对象数组。
注:对于私有域,外部类调用的时候一定要使用setAccessible,并且设置为true。
代码实例为:
package com.xhj.reflection_excetion; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; /**
* 动态实例化类
*
* @author XIEHEJUN
*
*/
public class DynamiCreateClass { public static void main(String[] args) {
// TODO Auto-generated method stub try {
// 动态建立一个String对象,
Constructor<?> construtor = String.class
.getConstructor(String.class);
String str = (String) construtor.newInstance("000123");
System.out.println(Integer.parseInt(str)); // 动态建立一个txt文件,并将上面初始化后的String值写入文件的当中
construtor = File.class.getConstructor(String.class);
String url = "C:\\Users\\XIEHEJUN\\Desktop\\XHj.txt";
File file = (File) construtor
.newInstance(url);
file.createNewFile();
if (file.exists()) {
str += "---文件创建成功";
System.out.println(str);
OutputStream os = new FileOutputStream(url);
os.write(str.getBytes());
os.close();
} else {
System.out.println("创建文件失败");
}
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} } }
结果为:
控制台输出
生成的文件:
注:在java中有两种不用new就可以建立类对象的方法,即Class.newInstance()和Constructor.newInstance();
区别在于:前者只能调用无参构造方法,而后者却能调用有参构造方法;
且前者需要被调用的构造方法可见,后者则在特定情况下运行调用不可见的构造方法
四、反射进阶之自定义可变数组工具类
在Java中,要创建可变数组可通过ArraryList类来实现。除此之外,我们也可以用自定义的方法来实现可变数组。
这里,我们将使用Java的反射机制实现一个工具类,通过这个工具类,我们就能实现可变数组的创建。
主要技术:
Array类提供了动态创建和访问Java数组的方法。它允许在执行get,set操作期间进行扩展转换,但若发生收缩
转换将抛出IllegalArgumentExcetion。为了创建新的数组对象,需要使用newInstance()方法,它可以根据指定
的元素类型和长度创建新的数组:
public static Object newInstance(Class<?> componentType,int length)throws NegativeArraySizeException
componentType: 表示新数组的组件类型的Class对象
length: 新数组的长度
注:Java 中的数组不管是几维的,都是Object类型的s
代码实例:
可变数组工具类的实现:
package com.xhj.reflection_excetion; import java.lang.reflect.Array; /**
* 用反射机制实现可变数组工具类
*
* @author XIEHEJUN
*
*/
public class VariableArrayUtil {
/**
* 增加的数组长度值
*/
private int addLength;
/**
* 需要增加长度的原数组
*/
private Object array; public int getaddLength() {
return addLength;
} public Object getArray() {
return array;
} /**
* 可变数组初始化
* @param addLength 需要增加的数组的长度
* @param array 需要增加长度的原数组
*/
public VariableArrayUtil(int addLength, Object array) {
super();
this.addLength = addLength;
this.array = array;
} /**
* 可变数组的实现
*
* @return
*/
public Object newArrary() {
Class<?> clazz = array.getClass();
if (clazz.isArray()) {
Class<?> type = clazz.getComponentType();
int length = Array.getLength(array);
Object new_Array = Array.newInstance(type, length + addLength);
System.arraycopy(array, 0, new_Array, 0, length);
return new_Array;
}
return null;
} }
测试类:
package com.xhj.test; import com.xhj.reflection_excetion.VariableArrayUtil; /**
* 可变数组工具类的测试类
*
* @author XIEHEJUN
*
*/
public class Test { public static void main(String[] args) {
int[] a = new int[10];
System.out.println("原数组为:");
for (int i = 0; i < 10; i++) {
a[i] = i;
System.out.print(" " + a[i]);
}
System.out.println("\n数组长度为:" + a.length);
VariableArrayUtil util = new VariableArrayUtil(5, (Object) a);
int[] b = (int[]) util.newArrary();
System.out
.println("==================================================\n"
+ "更改后的数组长度为:" + b.length);
for (int i = 10; i < 15; i++) {
b[i] = i;
}
System.out.println("更改后的数组为:");
for (int i : b) {
System.out.print(" " + i);
}
} }
结果为:
五、反射进阶之重写toString方法
为了方便输出对象,在Object中定义了toString的方法,其默认值由类名和哈希码组成。但是很多时候,为了能更好的满足我们的需求,
我们都是需要重写这个方法的。下面我们将利用Java的反射机制,重写这个方法,并输出类的相关信息。
代码实例:
package com.xhj.reflection_excetion; import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type; /**
* 利用反射重写Object的toString方法
*
* @author XIEHEJUN
*
*/
public class RewriteToString {
public String toString(Object obj) {
Class<?> clazz = obj.getClass();
// 建立一个容器用来存储类的信息
StringBuilder strBuilder = new StringBuilder();
strBuilder.append("以下是类的信息:");
// 类名
String className = clazz.getSimpleName();
// 包名
Package packageName = clazz.getPackage();
// 公共构造方法
Constructor<?>[] constructors = clazz.getConstructors();
// 公共域
Field[] fields = clazz.getFields();
// 接口
Type[] interfaces = clazz.getInterfaces();
// 公共方法
Method[] methods = clazz.getMethods(); strBuilder.append("\n此类的简单名称为--" + className); strBuilder.append("\n此类的包名为--" + packageName); strBuilder.append("\n此类的公共构造方法有:");
if (constructors.length > 0) {
for (Constructor<?> constructor : constructors) {
strBuilder.append("\n\t" + constructor);
}
} else {
strBuilder.append("空");
} strBuilder.append("\n此类的公共域有:");
if (fields.length > 0) {
for (Field field : fields) {
strBuilder.append("\n\t" + field);
}
} else {
strBuilder.append("空");
} strBuilder.append("\n此类的接口有:");
if (fields.length > 0) {
for (Type type : interfaces) {
strBuilder.append("\n\t" + type);
}
} else {
strBuilder.append("空");
} strBuilder.append("\n此类的公共方法有:");
if (methods.length > 0) {
for (Method method : methods) {
strBuilder.append("\n\t" + method);
}
} else {
strBuilder.append("空");
}
return strBuilder.toString();
} public static void main(String[] args) {
RewriteToString rts = new RewriteToString();
System.out.println(rts.toString(new StringBuilder()));
} }
结果为:
注:在实际开发当中,一般而言都是需要重写toString方法的,为了更好的开发,可使用Commons Lang 组件提供的工具类来重写此方法。
六、反射进阶之动态代理
代理是Java中很重要的一个特性,使用代理可以在程序运行时创建一个实现指定接口的新类。一般而言,我们只有在程序编译时
无法确定需要使用哪些接口时才会使用代理机制。代理更多的是用在系统的开发上,它可以为工具类提供更加灵活的特性。
InvocationHandle 接口是代理实例的调用处理程序实现的接口。每个代理实例都具有一个关联的调用处理程序。
对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的invoke()方法:
Object invoke(Object proxy,Method method,Object[]args)throws Throwable
proxy:代理类
method: 代理实例上要被调用的方法
args: 代理实例上方法调用的参数数组
Proxy接口提供了用于创建动态代理类和实例的静态方法,它是所有动态代理类的父类。
获得一个指定接口的代理类实例:
public static Object newProxyInstance(ClassLoader loader,Class<?> [] interfaces,InvocationHandle h)throws IllegalArgumentException
loader:定义代理类的类加载器
interfaces:代理类要实现的接口列表
h:指派方法调用的代理处理程序
代码实例:
接口:
package com.xhj.reflection_excetion.dynamicProxy.bean; /**
* 代理类和被代理类的共同接口
*
* @author XIEHEJUN
*
*/
public interface DoBusiness {
/**
* 商品交易方式
*/
public void trading();
}
被代理类:
package com.xhj.reflection_excetion.dynamicProxy.service; import com.xhj.reflection_excetion.dynamicProxy.bean.DoBusiness; /**
* 被代理类--厂家
*
* @author XIEHEJUN
*
*/
public class Product implements DoBusiness { @Override
public void trading() {
System.out.println("厂家直销");
} }
代理处理器:
package com.xhj.reflection_excetion.dynamicProxy.proxy; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; /**
* 代理处理器--商家代理
*
* @author XIEHEJUN
*
*/
public class DynamicProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("商家代理定点销售");
return null;
}
}
测试类:
package com.xhj.reflection_excetion.dynamicProxy; import java.lang.reflect.Proxy; import com.xhj.reflection_excetion.dynamicProxy.bean.DoBusiness;
import com.xhj.reflection_excetion.dynamicProxy.proxy.DynamicProxy;
import com.xhj.reflection_excetion.dynamicProxy.service.Product; public class Test { public static void main(String[] args) {
DoBusiness p = new Product();
System.out.print("没有启用代理模式---");
p.trading();
ClassLoader loader = p.getClass().getClassLoader();
p = (DoBusiness) Proxy.newProxyInstance(loader,
Product.class.getInterfaces(), new DynamicProxy());
System.out.print("启用动态代理模式---");
p.trading();
} }
结果为:
注:
java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Method method, Object[] args)。
在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的trading(),args为该方法的参数数组。这个抽象方法在代理类(代理处理类)中动态实现。
Proxy:该类即为动态代理类。Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):
返回代理类的一个实例,返回后的代理类可以当作被代理类使用 (可使用被代理类的在接口中声明过的方法)。总结:
动态代理类是一个实现在创建类并运行时指定接口列表的类,
1.代理接口是代理类所实现的一个接口。
2.代理实例是代理类的一个实例。
3.每一个代理实例都有一个关联的调用处理程序对象,它可以实现接口InvocationHandler。
4.通过调用代理处理器中的Invoke方法实现代理,并传递代理实例,识别调用方法以及方法上的参数数组。
5.调用对象加载器以及代理处理器中的方法,并以代理实例为结果返回。
java笔记--反射进阶之总结与详解的更多相关文章
- Java基础-反射(reflect)技术详解
Java基础-反射(reflect)技术详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.类加载器 1>.JVM 类加载机制 如下图所示,JVM类加载机制分为五个部分 ...
- Scala进阶之路-反射(reflect)技术详解
Scala进阶之路-反射(reflect)技术详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Scala中的反射技术和Java反射用法类似,我这里就不一一介绍反射是啥了,如果对 ...
- Java 反射 设计模式 动态代理机制详解 [ 转载 ]
Java 反射 设计模式 动态代理机制详解 [ 转载 ] @author 亦山 原文链接:http://blog.csdn.net/luanlouis/article/details/24589193 ...
- Java工程师 基础+实战 完整路线图(详解版)
Java工程师 基础+实战 完整路线图(详解版) Java 基础 Java 是一门纯粹的面向对象的编程语言,所以除了基础语法之外,必须得弄懂它的 oop 特性:封装.继承.多态.此外还有泛型.反射 ...
- Go语言备忘录:反射的原理与使用详解
目录: 预备知识 reflect.Typeof.reflect.ValueOf Value.Type 动态调用 通过反射可以修改原对象 实现类似“泛型”的功能 1.预备知识: Go的变量都是静态类 ...
- J2EE进阶(四)Spring配置文件详解
J2EE进阶(四)Spring配置文件详解 前言 Spring配置文件是用于指导Spring工厂进行Bean生产.依赖关系注入(装配)及Bean实例分发的"图纸".Java EE程 ...
- 转 Java虚拟机5:Java垃圾回收(GC)机制详解
转 Java虚拟机5:Java垃圾回收(GC)机制详解 Java虚拟机5:Java垃圾回收(GC)机制详解 哪些内存需要回收? 哪些内存需要回收是垃圾回收机制第一个要考虑的问题,所谓“要回收的垃圾”无 ...
- Go语言备忘录(2):反射的原理与使用详解
本文内容是本人对Go语言的反射原理与使用的备忘录,记录了关键的相关知识点,以供翻查. 文中如有错误的地方请大家指出,以免误导!转摘本文也请注明出处:Go语言备忘录(2):反射的原理与使用详解,多谢! ...
- 在java poi导入Excel通用工具类示例详解
转: 在java poi导入Excel通用工具类示例详解 更新时间:2017年09月10日 14:21:36 作者:daochuwenziyao 我要评论 这篇文章主要给大家介绍了关于在j ...
随机推荐
- Android开发的那些坑和小技巧
1.android:clipToPadding 意思是控件的绘制区域是否在padding里面.默认为true.如果你设置了此属性值为false,就能实现一个在布局上事半功陪的效果.先看一个效果图. 上 ...
- [AaronYang]C#人爱学不学[2]
1. 记事本写C#,脱离vs 新建记事本,名字为 helloworld.cs using System; namespace Hello{ public class HelloWorldSay{ st ...
- 调研Android平台开发环境的发展演变
Android是Google推出的开源手机操作系统,主要以开发应用为主,要进行Android开发首先得搭建好开发平台.最近在搭建Android的开发环境,发现往往一个小问题都能花费你大半天时间,从刚开 ...
- jsp+oracle实现数据库内容以表格形式在前台显示(包含分页)
jsp+oracle实现数据库内容以表格形式在前台显示(包含分页) 在文件夹内新增list_emp.jsp 代码如下: <%@ page contentType="text/html& ...
- 图书封面制作-ps图片处理使用教程
这是一个实用而有趣的教程,在这个PS教程中,我们来学习如何使用photoshop制作一个立体的软件产品盒子. 1.首先运行photoshop创建一个500*500像素的新文件,背景颜色为白色,如图所示 ...
- JS模式:Mixin混合模式,=_=!就是常见的Object.create()或者_extend()
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 安装Eclipse插件
安装Eclipse插件 从eclipse 3.6开始,eclipse有一个marketplace,这个类似现在手机的app store一样,可以在其中检索相关插件,直接安装,打开help--> ...
- HP 电脑装 纯净版的win7
新买的 HP 电脑,自带 Win10 的操作系统,今天把它改成 装win7 系统 在安装的过程中遇到的问题 1.数字证书错误.您安装的操作系统来源不明之类的错误,具体没有记下来 2.磁盘的格式不是NT ...
- FastDFS在centos上的安装配置与使用
FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储.文件同步.文件访问(文件上传.文件下载)等,解决了大容量存储和负载均衡的问题.特别适合以文件为载体的在线服务.(百 ...
- UTL_FILE详解
包UTL_FILE 提供了在操作系统层面上对文件系统中文件的读写功能.非超级用户在使用包UTL_FILE中任何函数或存储过程前必须由超级用户授予在这个包上的EXECUTE权限.例如:我们使用下列命令对 ...