java反射操作类结构
- 反射操作构造方法
Class只是作为反射的操作源头,但是严格来讲,反射还有其他内容。反射给用户最大的方便有三点:
1.构造调用
2.方法调用
3.属性调用
在反射机制里面提供有java.lang.reflect包,包中最重要的三个类:
Constructor、Method、Field
构造方法调用
利用Class类中的forName()方法可以取得Class类的对象而后利用newInstance()进行对象的实例化处理操作。
但是这种操作最大的缺陷在于:类中必须要有提供有无参的构造方法;
因为此时的Emp类中不存在有无参构造方法,所以无法利用Class类的newInstance()方法来进行实例化操作。
要想解决此类问题,那么就必须依靠Constructor类完成,在Class类中定义有两个取得Constructor类的操作方法。
取得全部构造:
public Constructor [] getConstructors() throws SecurityException
取得指定参数类型构造:
取得全部构造:
package com.wanghaoxin.demo;
import java.lang.reflect.Constructor;
class Emp{
public Emp(){}
public Emp(String name ){}
public Emp(String name,double sal){}
}
public class TestClassDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.wanghaoxin.demo.Emp");
Constructor<?> cons [] = cls.getConstructors(); //取得全部的构造
for(int i=0;i<cons.length;i++){
System.out.println(cons[i]);
}
}
}
执行结果:
public com.wanghaoxin.demo.Emp()
public com.wanghaoxin.demo.Emp(java.lang.String)
public com.wanghaoxin.demo.Emp(java.lang.String,double)
这个时候取得构造方法信息依靠的是Constructor类中的toString()方法完成。
但是在Constructor中提供有如下方法:
取得方法名字
———public String getName()以字符串形式返回此构造方法的名称。它总是与构造方法的声明类的简单名称相同。
取得访问修饰符
———-public int getModifiers()以整数形式返回此 Constructor 对象所表示构造方法的 Java 语言修饰符。应该使用 Modifier 类对这些修饰符进行解码。
注意:所有的修饰符都是数字的叠加,如果要想将数字转换为可读懂的修饰符,要使用Modifier类来进行转换。
将数字转换为修饰符。
取得方法参数类型:
————–public Class<?>[] getParameterTypes()按照声明顺序返回一组 Class 对象,这些对象表示此 Constructor 对象所表示构造方法的形参类型。如果底层构造方法不带任何参数,则返回一个长度为 0 的数组。
代码:
package com.wanghaoxin.demo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
class Emp{
public Emp(){}
public Emp(String name ){}
public Emp(String name,double sal){}
}
public class TestClassDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.wanghaoxin.demo.Emp");
Constructor<?> cons [] = cls.getConstructors(); //取得全部的构造方法
for(int i=0;i<cons.length;i++){
System.out.println(Modifier.toString(cons[i].getModifiers()));//取得构造方法的修饰符
System.out.println(cons[i].getName());//取得方法的名字
Class<?> clsparams [] = cons[i].getParameterTypes();//取得方法参数类型
System.out.println("(");
for(int j=0;j<clsparams.length;j++){
System.out.println(clsparams[j].getSimpleName()+"---arg---"+j);
if(j<clsparams.length-1){//后面还有内容的时候输出逗号
System.out.println(",");
}
}
System.out.println(")");
//System.out.println(cons[i]);
}
}
}
以上的代码对于用户的直接开发意义不大, 但是可以利用这个功能对类中是否含有无参构造进行检测;
package com.wanghaoxin.demo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
class Emp{
public Emp(){}
public Emp(String name ){}
public Emp(String name,double sal){}
}
@SuppressWarnings("serial")
class NoParamException extends Exception{
public NoParamException(String msg){
super(msg);
}
}
public class TestClassDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.wanghaoxin.demo.Emp");
System.out.println(chekNoneParam(cls));
if(!chekNoneParam(cls)){
throw new NoParamException("没有无参构造");
}
}
public static boolean chekNoneParam(Class<?> cls){
Constructor<?> cons[] = cls.getConstructors();
for(int i=0;i<cons.length;i++){
if(cons[i].getParameterTypes().length==0){//没有参数
return true;
}
}
return false;
}
}
在反射的处理操作过程之中,最大的特点在于可以利用反射进行指定参数构造的调用来实现类对象的实例化操作。
实例化对象:
newInstance
public T newInstance(Object… initargs)
throws InstantiationException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
调用指定构造实例化对象:
package com.wanghaoxin.demo;
import java.lang.reflect.Constructor;
class EEmp{
private String name;
private double sal;
public EEmp(String name,double sal){
this.name=name;
this.sal=sal;
}
@Override
public String toString() {
return "姓名:"+this.name+" sal:"+this.sal;
}
}
public class TestDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.wanghaoxin.demo.EEmp");
Constructor<?> cons = cls.getConstructor(String.class,double.class);
Object obj = cons.newInstance("WANGHAOXIN",800);
System.out.println(obj);
}
}
虽然可以实现反射调用构造的方式实例化对象,但是实际开发中无法这样使用,你很难确定要使用的构造方法的参数类型。正因为如此在实际开发中,才要求简单javal类必须要提供有无参构造方法。
一般情况下,用构造调用无参实例化对象,这样Constructor使用的概率很低。
- 反射类操作普通方法—息息相关
在Class类里面定义有取得方法的操作:
取得全部的方法:
—-public Method[] getMethods()
throws SecurityException
取得指定方法:
public Method getMethod(String name,
Class<?>… parameterTypes)
throws NoSuchMethodException,
SecurityException
在Method类中定义有如下取得结构的方法:
取得方法的返回值类型:public Class<?> getReturnType();
取得全部抛出异常:
public Class<?> getExceptionTypes();
范例:取得全部方法:
package com.wanghaoxin.demo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
class EEEmp{
private String name;
private double sal;
public EEEmp(String name,double sal){
this.name=name;
this.sal=sal;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
}
public class TestClassMethod {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.wanghaoxin.demo.EEEmp");
Method meths [] = cls.getMethods();//取得全部方法
for(int i = 0;i<meths.length;i++){
System.out.print(Modifier.toString(meths[i].getModifiers())+" ");//取得方法的访问修饰符
System.out.print(meths[i].getReturnType().getName()+" ");//取得方法的返回值类型
System.out.print("(");
Class<?> clsparams [] = meths[i].getParameterTypes();//取得方法参数类型
for(int j=0;j<clsparams.length;j++){
System.out.print(clsparams[j].getSimpleName()+"---arg"+j);
if(j<clsparams.length-1){//后面还有内容的时候输出逗号
System.out.println(",");
}
}
System.out.print(")");
System.out.print(meths[i].getName());
Class<?> exs [] = meths[i].getExceptionTypes();//取得异常类型
if(exs.length>0){
System.out.print(" throws ");
}
for(int k=0;k<exs.length;k++){
System.out.print(exs[k]);
if(k<exs.length-1){//后面还有内容的时候输出逗号
System.out.print(", ");
}
}
System.out.println();
}
}
}
Method要比Constructor类要重要,一定要记住在Method类中提供有一个反射调用的方法
反射调用:
public Object invoke(Object obj,
Object… args)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
参数:
第一个参数表示类的实例化对象,只有实例化对象才可以调用方法
第二个参数:方法需要的参数类型
利用反射调用方法:
package com.wanghaoxin.demo;
import java.lang.reflect.Method;
class EEEmp{
private String name;
private double sal;
public EEEmp(){}
public EEEmp(String name,double sal){
this.name=name;
this.sal=sal;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
}
public class TestClassMethod {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.wanghaoxin.demo.EEEmp");
Object obj = cls.newInstance();
Method meth = cls.getMethod("setName", String.class);
meth.invoke(obj, "zhangsan");//等价于EEEmp对象setName("zhangsan")
Method getMeth = cls.getMethod("getName");
System.out.println(getMeth.invoke(obj));
}
}
这种方法的反射调用是可以解决提交参数与简单java类自动转换的处理操作。
invoke方法的使用
- 反射操作成员
首先在成员里面必须明确一点,它分为父类成员和子类成员两个部分。所以对于成员取得就有如下两种形式:
1.取得父类成员
—–取得全部成员:public Field getField(String name)
throws NoSuchFieldException,
SecurityException
—–取得指定成员:public Field[] getFields()
throws SecurityException
2.取得子类成员
—–取得全部成员:public Field getDeclaredField(String name) throws NoSuchFieldException,
SecurityException
—–取得指定成员:public Field[] getDeclaredFields()
throws SecurityException
范例:成员取得:
package com.wanghaoxin.demo;
import java.lang.reflect.Field;
import java.util.Arrays;
interface Person{
public static final String MSG="wanghaoxin";
}
class Empp implements Person{
private String job;
private Double sal;
}
public class TestMemberDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.wanghaoxin.demo.Empp");
{
Field fields [] = cls.getDeclaredFields();//获取子类成员
System.out.println(Arrays.toString(fields));
}
System.out.println("-------------------");{
Field fields [] = cls.getFields();//获取父类成员
System.out.println(Arrays.toString(fields));
}
}
}
父类指的是接口
只要说成员,大部分情况下我们只关心普通成员。
于是来观察Field类中定义的相关操作方法:
取得成员类型:
public Class<?> getType()
package com.wanghaoxin.demo;
import java.lang.reflect.Field;
import java.util.Arrays;
class Empp{
private String ename;
private String job;
private Double sal;
}
public class TestMemberDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.wanghaoxin.demo.Empp");
{
Field fields [] = cls.getDeclaredFields();//获取子类成员
for(int x=0;x<fields.length;x++){
System.out.println(fields[x].getName()+" , "
+fields[x].getType().getSimpleName());
}
}
}
}
除了取得全部的成员之外也可以取得指定名字的成员,取得成员之后,在Field类中提供有成员的直接操作
设置成员内容:public void set(Object obj,
Object value)
throws IllegalArgumentException,
IllegalAccessException
取得成员内容:public Object get(Object obj)
throws IllegalArgumentException,
IllegalAccessException
取消封装:public void setAccessible(boolean flag)
throws SecurityException
jdk1.8之前:
AccessibleObject下边有三个子类:Field、Method、Constructor
之后:
在set()于get()方法里边接受的Object就属于操作的实例化对象,成员只有在对象实例化之后才会分配空间。
范例:
package com.wanghaoxin.demo;
import java.lang.reflect.Field;
import java.util.Arrays;
class Empp{
private String ename;
private String job;
private Double sal;
}
public class TestMemberDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.wanghaoxin.demo.Empp");
Object obj = cls.newInstance();//实例化对象开辟堆内存
Field fieldsename = cls.getDeclaredField("ename");//获取指定子类成员
fieldsename.setAccessible(true);//取消封装。如果不取消封装,会报错,
// java.lang.IllegalAccessException: Class com.wanghaoxin.demo.TestMemberDemo can not access a member of class com.wanghaoxin.demo.Empp with modifiers "private"
fieldsename.set(obj, "wanghaoxin");
System.out.println(fieldsename.get(obj));
}
}
封装并不是万能的,关键是要通过反射才可以动态控制。
从实际开发来讲,直接访问成员,是一件绝对不可能做的事情。必须通过setter和getter来完成。
- *综合案例:反射设置属性
现在假设有一个Emp类,里面的结构组成如下:
简单java类:
class Emppp{
private Integer empno;
private String ename;
private String job;
private Double sal;
private Date hiredate;
//set、get方法略
}
现在对于类中所有的属性的内容都保存咋了字符串之中:
结构:”属性:内容| 属性:内容….”
String str = “empno:7369|ename:wanghaoxin|job:CK|sal:800.0|hiredate:2018-12-10”;
目的:将属性设置给对象
如果要想设置属性值,必须使用setter()和getter()方法,这就要求属性的第一个字字母必须大写,设置工具类
public class StringUtil {
public static String initcap(String str){
if(str == null || "".equals(str)){
return str;
}
if(str.length() == 1){
return str.toUpperCase();
}else{
return str.substring(0, 1).toUpperCase()+str.substring(1);
}
}
}
工具类:
package com.wanghaoxin.util;
public class ValidateData {
/**
* 验证字符串是否为空
* @param str
* @return 如果为空返回true,否则返回false
*/
public static boolean isEmpty(String str){
return str == null||"".equals(str);
}
/**
* 验证是否是整数
* @param str
* @return 是整数返回true
*/
public static boolean isNumber(String str){
if(!isEmpty(str)){//现在内容不是空
return str.matches("\\d+");
}
return false;
}
/**
* 验证是否是double数
* @param str
* @return
*/
public static boolean isDouble(String str){
if(!isEmpty(str)){//现在内容不是空
return str.matches("\\d+(\\.\\d+)?");
}
return false;
}
/**
* 验证是否是日期类
* @param str
* @return
*/
public static boolean isDate(String str){
if(!isEmpty(str)){//现在内容不是空
if(str.matches("\\d{4}-\\d{2}-\\d{2}")){
return true;
}else{
return str.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}");
}
}
return false;
}
}
范例:反射设置属性内容:
package com.wanghaoxin.demo;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.wanghaoxin.util.StringUtil;
import com.wanghaoxin.util.ValidateData;
@SuppressWarnings("serial")
class Emppp implements Serializable{
private Integer empno;
private String ename;
@Override
public String toString() {
return "Emppp [empno=" + empno + ", ename=" + ename + ", job=" + job + ", sal=" + sal + ", hiredate=" + hiredate
+ "]";
}
private String job;
private Double sal;
private Date hiredate;
public Integer getEmpno() {
return empno;
}
public void setEmpno(Integer empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
}
public class TestComDemo {
public static void main(String[] args) throws Exception {
String str = "empno:7369|ename:wanghaoxin|job:CK|sal:800.0|hiredate:2018-12-10";
//1.此时没有明确的Emppp对象,只有Object类对象,而且属性也使用了字符串描述,要取得操作类的class对象
Class<?> cls = Class.forName("com.wanghaoxin.demo.Emppp");
//2.实例化类对象,只有实例化后才会开辟堆内存
Object obj = cls.newInstance();
//3.需要拆分字符串获得属性名字和属性值,牵扯到字符串转型
String result[] = str.split("\\|");
for(int i = 0;i<result.length;i++){//要求循环设置内容
//4.进一步拆分内容,区分拿出属性和内容值
String temp[] = result[i].split(":");//相当于拆分了属性和内容
//5.取得具体操作setter方法名称
String methodName = "set" + StringUtil.initcap(temp[0]);
//6.为了确认setter方法中的参数类型,所以需要取出method的方法的参数类型
Field filed = cls.getDeclaredField(temp[0]);//取得属性
//7这样可以取得指定的方法对象
Method met = cls.getMethod(methodName,filed.getType());
//定义一个object类型,保存具体的数据
Object value = null;
//8、处理数据的转型
if("Integer".equals(filed.getType().getSimpleName())||"int".equals(filed.getType().getSimpleName())){
if(ValidateData.isNumber(temp[1])){
value = Integer.parseInt(temp[1]);
}else{
value = 0;
}
}else if("Double".equals(filed.getType().getSimpleName())||"double".equals(filed.getType().getSimpleName())){
if(ValidateData.isDouble(temp[1])){
value = Double.parseDouble(temp[1]);
}else{
value = 0;
}
}else if("Date".equals(filed.getType().getSimpleName())){
if(ValidateData.isDate(temp[1])){
if(temp[1].split(" ").length == 1){
value = new SimpleDateFormat("yyyy-MM-dd").parse(temp[1]);
}else{
value = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(temp[1]);
}
}else{
value = 0;
}
}else if("String".equals(filed.getType().getSimpleName())){
if(!ValidateData.isEmpty(temp[1])){
value = temp[1];
}
}
//10.反射设置具体内容
met.invoke(obj,value);
}
System.out.println(obj);
}
}
实际的开发里面都是采用此类模式动态设置属性内容的,所以为什么有严格的setter()和getter()也在于此。
- 反射取得父类结构
所谓父结构指的是类可以取得它所继承的父类以及所实现的所有接口。这些操作在Class类都有支持。
取得一个类的父类:
public Class<? super T> getSuperclass()
取得所有的实现父接口:
public Class<?>[] getInterfaces()
范例:观察父类取得:(为代理设计模式解决)
package com.wanghaoxin.demo;
import java.io.Serializable;
interface IMess{
}
@SuppressWarnings("serial")
class Dept implements IMess,Serializable,Cloneable{
}
public class TestFather {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.wanghaoxin.demo.Dept");
System.out.println("父类:"+cls.getSuperclass());//输出 父类:class java.lang.Object
Class<?> si [] = cls.getInterfaces();
for(int i = 0;i < si.length ; i++){
System.out.println(si[i]);
}
/* 输出
interface com.wanghaoxin.demo.IMess
interface java.io.Serializable
interface java.lang.Cloneable*/
}
}
获取父类接口此处操作是为了代理模式设计做铺垫的。
- 总结
所以反射机制最大的用处在于可以反射调用setter方法设置属性内容。
利用反射请求参数可自动转化为obj对象的操作。