反射一 :反射应用、反射与类操作、反射与简单Java类 - Java高级特性 13

目录

反射机制简介

认识反射机制

class类对象的三种实例化模式

反射应用案例

反射实例化对象

反射与工厂设计模式

反射与单例设计模式

反射与类操作

反射获取类的基本信息

反射调用构造方法

反射调用普通方法

反射调用成员

Unsafe工具类

反射与简单Java类

传统属性赋值的弊端

属性自动赋值实现思路

单级属性配置

设置多种数据类型

级联对象实例化

级联属性赋值


学习笔记。

只要能编写级联属性赋值的代码(级联属性部分中最后的代码) ,  he  fan she yu单例   工厂的关系,就表示掌握本部分。

反射机制简介

认识反射机制

在Java语言里面之所以会有如此众多的开源技术支撑,很大的一部分是来自于Java最大的一个特征,这个特征就是:反射机制。如果不能够灵活的去使用反射机制进行项目的开发与设计,那么可以说你并未接触到Java的精髓所在。

所有的技术实现的目标只有一点:重用性。

对于反射技术首先来考虑“反”与“正”的操作,所谓的“正”操作指的是当我们要使用一个类的时候,一定能够要先导入程序所在的包,而后根据类进行对象实例化,并且依靠对象调用类中的方法。但是如果说“反”,根据实例化对象反推出其类型。

范例:正向操作

package cn.ren.demo;
import java.util.Date;    // 1、导入程序所在的包.类,知道对象的出处
public class JavaAPIDemo {
	public static void main(String[] args) {
		Date data = new Date() ;    // 2、通过类产生实例化对象
		System.out.println(data.getTime());   // 3、 根据对象调用类中方法
	}
}

如果要想实现“反”的处理操作,那么首先要采用的是Object类中提供的一个操作方法:

  • 获取Class对象信息:public final Class<?> getClass​()

范例:观察Class对象的使用

package cn.ren.demo;

import java.util.Date;    // 1、导入程序所在的包.类,知道对象的出处

public class JavaAPIDemo {
	public static void main(String[] args) {
		Date data = new Date() ;    // 2、通过类产生实例化对象
		System.out.println(data.getClass());   // 3、 根据实例化对象,找到对象的所属类型
	}
}

getClass()可以帮助使用者找到对象的根源。

class类对象的三种实例化模式

反射之中所有对象的核心操作都是通过Class类对象展开的,可以说Class类是反射的根源所在,但是这个类如果要想获取它的实例化对象,可以采用三种方式完成,首先来观察java.lang.Class类的定义。

public final class Class<T>
       extends Object
       implements Serializable, GenericDeclaration, Type, AnnotatedElement

从JDK1.5开始Class类再定义的时候可以使用泛型进行标记,这样的用法主要是希望可以避免所谓的向下转型。下面通过具体的操作讲解三种实例化的形式。

1、 【Object 类支持】Object类可以根据实例化对象获取Class对象:public final Class<?> getClass​()

  • 这种方式有一个不是缺点的缺点:如果现在只是想获得Class类对象,则必须产生指定类后才能获得
package cn.ren.demo;

class Person {} // 自定义的程序类
public class JavaAPIDemo {
	public static void main(String[] args) {
		Person per = new Person() ;  	//已经存在有指定类的实例化对象
		Class<? extends Person> cls = per.getClass() ;
		System.out.println(cls.getName());   // 获取的是类的完整名称
	}
}

2、【JVM直接支持】采用“类.class”的形式实例化

  • 特点:如果要想采用此种模式,则必须导入程序所对应的开发包
package cn.ren.demo;

class Person {} // 自定义的程序类
public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<? extends Person> cls = Person.class ;
		System.out.println(cls.getName());   // 获取的是类的完整名称
	}
}

3、【Class类支持】在Class类里面提供有一个static方法:

package cn.ren.demo.vo;

public class Person {

}
package cn.ren.demo;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = Class.forName("cn.ren.demo.vo.Person") ;
		System.out.println(cls.getName());   // 获取的是类的完整名称
	}
}

这种模式最大的特点是可以直接采用字符串定义要使用的类型,并且程序中不需要编写任何的import语句。连导包的耦合都没有了。此时如果要使用的程序类不存在则会抛出“ java.lang.ClassNotFoundException”异常。

反射应用案例

经过一系列分析之后,可以发现虽然获得了Class类实例化对象,但是它的作用在哪?下面通过几个案例进行说明(都是在实际开发之中一定会使用到的)。

反射实例化对象

获取Class对象之后最大的意义并不是在于只是一个对象实例化的操作形式,更重要的是Class里面提供有对象反实例化的方法:

范例:通过newInstance()方法实例化Person类对象

package cn.ren.demo.vo;

public class Person {
	public Person() {
		System.out.println("****** Person 类构造方法 *********");
	}
	@Override
	public String toString() {
		return "我是一只小小鸟!" ;
	}
}
package cn.ren.demo;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = Class.forName("cn.ren.demo.vo.Person") ;
		Object obj = cls.newInstance() ;  // 实例化对象,但是JDK1.9后被废除
		System.out.println(obj);     // 输出对象调用toStirng()
	}
}

cls.newInstance()  等价于 关键字new

现在通过反射实现的对象实例化处理,依然要调用类中的无参构造方法,其本质等价于 “类 对象 = new 类()”。相当于隐含了关键字new,而直接使用字符串进行了替代。

范例:从JDK1.9之后newInstance()被替代了

package cn.ren.demo;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = Class.forName("cn.ren.demo.vo.Person") ;
		Object obj = cls.getDeclaredConstructor().newInstance() ;  // 实例化对象,但是JDK1.9后被废除
		System.out.println(obj);     // 输出对象调用toStirng()
	}
}

因为默认的Class类中的newInstance()方法只能够调用无参构造,所以很多开发者会认为其描述的不准确将其变换了形式(构造方法会说)。

反射与工厂设计模式

如果要想进行对象实例化的处理,除了可以使用关键字new之外,还可以使用反射机制来完成,那么为什么要提供一个反射的实例化?到底是使用关键字new还是个使用反射呢?

如果要想更好的理解此类问题,最好的解释方案就是通过工厂设计模式来解决。工厂实际模式的最大特点:客服端的程序类不直接牵扯到对象的实例化管理,只与接口发生关联,通过工厂类获取指定接口的实例化对象。

范例:传统的设计模式

package cn.ren.demo;
interface IMessage {
	public void send() ; 	//  消息发送
}
class NetNessage implements IMessage {
	public void send () {
		System.out.println("【网络消息发送】ren");
	}
}
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		IMessage msg = new NetNessage() ;		// 如果直接实例化则一定会耦合问题
	}
}

因为一个接口不可能只有一个子类,在实际开发之中接口的主要作用是为不同的层提供一个操作的标准。但是如果此时将一个子类设置为接口实例化操作,那么一定会有耦合问题,所以使用工厂模式来解决此类问题。

范例:利用工厂设计模式解决

package cn.ren.demo;
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		IMessage msg = Factory.getInstance("netmessage") ;
		msg.send();
	}
}
interface IMessage {
	public void send() ; 	//  消息发送
}
class NetMessage implements IMessage {
	public void send () {
		System.out.println("【网络消息发送】ren");
	}
}
class Factory {
	private Factory() {}   // 没有产生实例化的意义,所以构造方法私有化
	public static IMessage getInstance (String className) {
		if ("netmessage".equals(className)) {
			return new NetMessage() ;
		}
		return null;
	}
}

此种工厂设计模式属于静态工厂设计模式,也就是说如果现在要追加一个子类,则意味着工厂类一定要做出修改,如果不追加这种判断是无法获取指定接口对象的。

范例:为IMessage追加一个子类

package cn.ren.demo;
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		IMessage msg = Factory.getInstance("clouldmessage") ;
		msg.send();
	}
}
interface IMessage {
	public void send() ; 	//  消息发送
}
class CloudMessage implements IMessage {
	@Override
	public void send() {
		System.out.println("【云消息】57");
	}
}
class NetMessage implements IMessage {
	public void send () {
		System.out.println("【网络消息发送】ren");
	}
}
class Factory {
	private Factory() {}   // 没有产生实例化的意义,所以构造方法私有化
	public static IMessage getInstance (String className) {
		if ("netmessage".equals(className)) {
			return new NetMessage() ;
		} else if ("clouldmessage".equals(className)) {
			return new CloudMessage() ;
		}
		return null;
	}
}

工廠設計模式最有效解决的是子类与客户端的耦合问题,解决的核心思想是提供有一个工厂类作为过度段,可是随着项目的进行,你的IMesagel类有可能会有更多的子类,而且随着时间的推移子类产生越来越多,这就意味着你的工厂类永远你都要进行修改。

那么这个时候最好的解决方案就是不使用关键字new来完成,因为关键字new在使用的时候需要有一个明确的类存在。而newInstance()的方法只需要一个明确表示类名称的字符串即可应用。 

package cn.ren.demo;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		IMessage msg = Factory.getInstance("cn.ren.demo.NetMessage") ;
		msg.send();
	}
}
interface IMessage {
	public void send() ; 	//  消息发送
}
class CloudMessage implements IMessage {
	@Override
	public void send() {
		System.out.println("【云消息】57");
	}
}
class NetMessage implements IMessage {
	public void send () {
		System.out.println("【网络消息发送】ren");
	}
}
class Factory {
	private Factory() {}   // 没有产生实例化的意义,所以构造方法私有化
	public static IMessage getInstance (String className) {
		IMessage instance = null ;
		try {
			instance = (IMessage) Class.forName(className).getDeclaredConstructor().newInstance() ;
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return instance;
	}
}

这个时候可以发现,利用反射机制实现的工厂设计模式最大的优势在于:对于接口子类的扩充将不再影响到工厂类的定义。

但是现在依然需要思考,因为项目的实际开发过程之中,有可能会存在大量的接口,并且这些接口,都可能需要通过工厂类实例化,以此时的工厂设计模式不应该只为了一个IMessage接口服务,应该为所有的接口服务。只能依赖泛型,如下:

package cn.ren.demo;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		IMessage msg = Factory.getInstance("cn.ren.demo.NetMessage", IMessage.class) ;
		msg.send();
		IService service = Factory.getInstance("cn.ren.demo.HouseService", IService.class) ;
		service.service();
	}
}
interface IService {
	public void service() ;
}
class HouseService implements IService {
	public void service() {
		System.out.println("【服务】为你的住宿提供服务");
	}
}
interface IMessage {
	public void send() ; 	//  消息发送
}
class NetMessage implements IMessage {
	public void send () {
		System.out.println("【网络消息发送】ren");
	}
}
class Factory<T> {
	private Factory() {}   // 没有产生实例化的意义,所以构造方法私有化
	/**
	 * 获取接口实例化对象
	 * @param <T>
	 * @param className  接口的子类
	 * @param clazz  描述一个接口的类型
	 * @return		如果子类存在返回接口实例化对象
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getInstance (String className, Class<T> clazz) {
		T instance = null ;
		try {
			instance = (T) Class.forName(className).getDeclaredConstructor().newInstance() ;
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return instance;
	}
}

此时的工厂设计模式将不在受限于指定的接口,可以为所有的接口提供实例化的服务。

反射与单例设计模式

单例设计模式的核心本质在于:类内部的构造方法私有化,在类的内部产生实例化对象之后通过static方法获取实例化对象,进行类中的结构调用,单例设计模式一共有两类:懒汉式、饿汉式,下面主要讨论懒汉式的单例。

范例:观察懒汉式单例的问题 

package cn.ren.demo;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Singleton sinA = Singleton.getInstance() ;
		sinA.print();
	}
}
class Singleton {
	private static Singleton instance = null ;
	private Singleton () {}
	public static Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton() ;
		}
		return instance ;		
	}
	public void print() {
		System.out.println("ren");
	}
}
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Singleton sinA = Singleton.getInstance() ;
		sinA.print();
		Singleton sinB = Singleton.getInstance() ;
		sinB.print();
	}
}

此时的操作是在单线程下执行的,那么在多线程呢?

package cn.ren.demo;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		for (int x = 0; x < 3; x ++) {
			new Thread(()->{
				Singleton.getInstance().print();
			},"单例消费端-" + x).start() ; 
		}
	}
}
class Singleton {
	private static Singleton instance = null ;
	private Singleton () {
		System.out.println("【"+Thread.currentThread().getName()+"】******** 实例化Singleton类对象********");
	}
	public static Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton() ;
		}
		return instance ;		
	}
	public void print() {
		System.out.println("ren");
	}
}

单例设计模式的最大特点是在整体的运行过程之中,只允许产生一个实例化对象,这个时候会发现有了若干个线程之后实际上当前的程序就可以产生多个实例化对象,那么此时就不是单例设计模式。此时问题造成的关键在于代码本身出现了代码不同步的情况,而要想解决的关键核心在于需要解决同步处理,同步自然想到synchronized的处理。

范例:在getInstance()前加上synchronized关键字

public static synchronized Singleton getInstance() {
	if (instance == null) {
		instance = new Singleton() ;
	}
	return instance ;		
}

这个时候的确式进行同步处理,但是这个同步代价有些大,因为效率会低,例如:同时有三十二个线程在进行访问,但是只有一个能进行。因为整体代码里面实际上只有一块代码需要同步:instance对象的实例化部分。instance需要与主内存中的数据保持一致。

范例:更加合理的进行同步处理(标准的单例设计模式)

class Singleton {
	private static volatile Singleton instance = null ;
	private Singleton () {
		System.out.println("【"+Thread.currentThread().getName()+"】******** 实例化Singleton类对象********");
	}
	public static Singleton getInstance() {
		if (instance == null) {
			synchronized (Singleton.class) {
				if (instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance ;		
	}
	public void print() {
		System.out.println("ren");
	}
} 

为什么要在加两遍:假设一万个线程第一次来获取对象,加哪里都一样,但是这一万个线程第二次来获取时,这样的代码中instance不为空就不会执行同步了。

不能使用this作为同步对象,因为这里是static方法。如果使用会出现以下错误。

面试题:请编写单例设计模式

  • 【100%】直接编写一个以前的饿汉式单例设计模式, 并且构造方法私有化;
  • 【120%】在Java中那里使用到单例设计模式了?Runtime类、Spring框架;
  • 【200%】懒汉式单例设计的问题?如上

反射与类操作

在反射机制的处理过程中不仅仅是一个实例化对象的操作,更多情况还有类的组成结构,任何一个类的基本组成结构:父类(父接口)、包、属性、方法(构造方法、普通方法)。

反射获取类的基本信息

一个类的基本信息主要包括类所在的包名称、父类的定义、父接口的定义。

范例 :定义一个程序类

package cn.ren.service;

public interface IMessageService {
	public void send() ;
}

package cn.ren.service;

public interface IChannelService {
	public boolean connect() ;
}
package cn.ren.abs;

public abstract class AbstractBase {
	
}
package cn.ren.vo;

import cn.ren.abs.AbstractBase;
import cn.ren.service.IChannelService;
import cn.ren.service.IMessageService;

public class Person extends AbstractBase implements IMessageService, IChannelService {

	@Override
	public boolean connect() {
		return true ;
	}

	@Override
	public void send() {
		if (this.connect()) {
			System.out.println("【信息发送】ren57");
		}

	}

}

如果此时要想获得一些基础信息则可以通过Class中的如下方法:

  • 获取包名称:public Package getPackage​()
  • 获取继承父类:public Class<? super T> getSuperclass​()
  • 获取实现父接口:public Class<?>[] getInterfaces​()

范例:获得包名称

package cn.ren.demo;

import cn.ren.vo.Person;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = Person.class ; 		// 获取指定类的class对象 
		Package pack = cls.getPackage() ; 	// 获取指定类的包定义
		System.out.println(pack.getName());
	}
}

范例:获取父类信息

package cn.ren.demo;

import cn.ren.vo.Person;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = Person.class ; 		// 获取指定类的class对象 
		Class<?> parent = cls.getSuperclass() ;
		System.out.println(parent.getName());
		System.out.println(parent.getSuperclass().getName());
	}
}

范例:获取父接口 

package cn.ren.demo;

import cn.ren.vo.Person;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = Person.class ; 		// 获取指定类的class对象 
		Class<?> clazz[] = cls.getInterfaces() ;
		for (Class<?> temp : clazz) {
			System.out.println(temp.getName());
		}
	}
}

当获取一个类的Class对象之后,就意味着这一对象可以获取类之中的一切继承结构信息。

反射调用构造方法

在一个一个类之中除了有继承的关系之外最为重要的就是类中的结构处理了,而类中结构里面首先需要观察的就是构造方法的使用问题,实际上在之前通过反射实例化的时候就已经接触到构造方法的问题了:

  • 实例化方法替代:clazz.getDeclaredConstructor().newInstance()

所有类的构造方法的获取都可以直接通过Class类完成,该类中定义有如下的几个方法:

范例:修改Person类的定义

package cn.ren.abs;

public abstract class AbstractBase {
	public AbstractBase() {}
	public AbstractBase(String name) {}	
}
package cn.ren.vo;

import cn.ren.abs.AbstractBase;
import cn.ren.service.IChannelService;
import cn.ren.service.IMessageService;

public class Person extends AbstractBase implements IMessageService, IChannelService {
	public Person() {}
	public Person(String name, int age){}
	// 其它操作略...
}

范例:获取构造

package cn.ren.demo;
import java.lang.reflect.Constructor;
import cn.ren.vo.Person;
public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = Person.class ; 		// 获取指定类的class对象 
		Constructor<?>[] constructors = cls.getConstructors() ;		//获取全部构造
		for (Constructor<?> cons : constructors) {
			System.out.println(cons);
		}
	}
}

此时获取的是类之中的全部构造方法,但是也可以获取指定参数的构造。这里加Declared没区别,在多级时有区别。例如:现在的Person类之中提供有两个个构造方法:

public class Person extends AbstractBase implements IMessageService, IChannelService {
	private String name ;
	private int age ;
	public Person() {}
	public Person(String name, int age){
		this.name = name ;
		this.age = age ;
	}
	@Override
	public String toString() {
		return "姓名:" + this.name + "、年龄:" + this.age ;
	}
    // 其它操作略
}

此时调用Person类之中的有参构造方法进行Person类对象的实例化处理,这个时候就必须指明要调用的构造,而后通过Constructor类之中提供的实例化方法操作:public T newInstance​(Object... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException

范例:通过调用指定构造实例化对象

package cn.ren.demo;
import java.lang.reflect.Constructor;
import cn.ren.vo.Person;
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = Person.class ; 		// 获取指定类的class对象 
		Constructor<?> constructor = cls.getConstructor(String.class, int.class) ;
		Object obj = constructor.newInstance("张三", 89);
		System.out.println(obj);
	}
}

虽然程序代码之中允许开发者调用有参构造处理,但是如果从实际的开发来讲,所有的使用反射的类中最好使用无参构造,因为这样的实例化可以达到统一性。

反射调用普通方法

在进行反射处理的时候也可以通过反射获取类之中的全部方法,如果要想通过反射调用这些方法,必须有一个前提条件:类之中要提供实例化对象。

在Class类里面 提供有如下的操作可以获取方法对象:

范例:获取全部方法 

package cn.ren.demo;
import java.lang.reflect.Method;

import cn.ren.vo.Person;
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = Person.class ; 		// 获取指定类的class对象 
		{ // 获取全部方法 (包括父类中的方法)
			Method methods [] = cls.getMethods() ;
			for (Method met : methods) {
				System.out.println(met);
			}
		}
		System.out.println("--------------------------------------------------");
		{ // 获取本类方法
			Method methods [] = cls.getDeclaredMethods() ;
			for (Method met : methods) {
				System.out.println(met);
			}
		}
	}
}

但是需要注意的是,这个时候的方法信息的获取是依靠Method类提供的toString()方法完成的,很多时候可以有用户自己拼凑方法的信息。

范例:自定义方法信息显示(了解)

package cn.ren.demo;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import cn.ren.vo.Person;
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = Person.class ; 		// 获取指定类的class对象 
		Method methods[] = cls.getMethods();
		for (Method met : methods) {
			int mod = met.getModifiers() ;     // 修饰符
			System.out.print(Modifier.toString(mod) + " ");
			System.out.print(met.getReturnType().getName() + " ");
			System.out.print(met.getName() + "(");
			Class<?> params [] = met.getParameterTypes() ; // 获取参乎上类型
			for (int x = 0; x < params.length; x ++) {
				System.out.print(params[x].getName() + " " + "args-" + x );
				if (x < params.length -1) {
					System.out.print(", ");
				}
			}
			System.out.print(")");
			Class<?> exp[] = met.getExceptionTypes();
			if (exp.length > 0) {
				System.out.print(" throws ");
				for (int x = 0; x < exp.length; x++) {
					System.out.print(exp[x].getName());
					if (x < exp.length - 1) {
						System.out.print(", ");
					}
				}
			}
			System.out.println(); // 换行
		}	
	}
}

这种代码只需要清楚可以根据反射获取方法的结构即可,不需要做过多的了解,但是在Mehtod类里面有一个重要的方法:public Object invoke​(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException

在Person类里面为name属性追加有setter、getter方法。

public class Person extends AbstractBase implements IMessageService, IChannelService {
	private String name ;
	private int age ;
	public Person() {}
	public Person(String name, int age){
		this.name = name ;
		this.age = age ;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
    // 其它操作代码略
}

需要通过反射机制来实现Person类之中的setter、getter的调用处理。

范例:在不导入指定类开发包的情况下实现属性的配置

package cn.ren.demo;

import java.lang.reflect.Method;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = Class.forName("cn.ren.vo.Person") ; 		// 获取指定类的class对象 
		String value = "张三" ; 				//要设置的属性内容	
		// 1、 任何情况下如果要想保存类中的属性或调用类中的方法,都需要保证具有实例化对象,既然不允许导入包,那么就反射实例化
		Object obj = cls.getDeclaredConstructor().newInstance() ; // 调用无参构造实例化
		// 2、如果要进行方法的调用,那么一定要获取方法的名称
		String setMethodName = "setName" ;  // 方法名称
		Method setMethod = cls.getDeclaredMethod(setMethodName, String.class) ; // 获取指定的方法
		setMethod.invoke(obj, value) ;   // 等价于 : Person对象.setName(value) ;
		String getMethodName = "getName" ;
		Method getMethod = cls.getDeclaredMethod(getMethodName) ;
		System.out.println(getMethod.invoke(obj));   // 等价于 Person对象.getName.		
	}
}

任何情况下如果要想保存类中的属性或调用类中的方法,都需要保证具有实例化对象,既然不允许导入包,那么就反射实例化。利用此类操作整体的形式上不会有任何明确的类对象产生,一切都是依靠反射机制处理的,这样的处理避免了某一个类的耦合问题。

反射调用成员

类结构中最后的一部分组成就是(Field),大部分情况下对会将其称为成员属性,对于成员属性的获取也是通过Classl类完成的,在这个类中提供有如下两组方法:

范例:修改要操作的父类结构

package cn.ren.abs;

public abstract class AbstractBase {
	protected static final String BASE = "ren" ;
	private String info = "hello" ;
	public AbstractBase() {}
	public AbstractBase(String name) {}	
}
package cn.ren.service;

public interface IChannelService {
	public static final String NAME = "renjava" ;
	public boolean connect() ;
}

范例:获取类中成员 

package cn.ren.demo;

import java.lang.reflect.Field;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = Class.forName("cn.ren.vo.Person") ; 		// 获取指定类的class对象 
		{ // 获取父类中的公共成员信息
			Field fields[] = cls.getFields(); // 获取成员
			for (Field fie : fields) {
				System.out.println(fie);
			}
		}
		System.out.println("-------------------------------------------");
		{ // 获取本类中定义的成员信息
			Field fields[] = cls.getDeclaredFields(); // 获取成员
			for (Field fie : fields) {
				System.out.println(fie);
			}
		}
	}
}

但是在Filed类里面最重要的操作形式并不是获取全部成员,而是如下的三个方法:

所有成员是是在对象实例化之后进行空间分配的,所以此时一定要先有实例化对象之后才可以进行成员的操作

范例:直接调用Person类中的name私有成员 

package cn.ren.demo;

import java.lang.reflect.Field;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = Class.forName("cn.ren.vo.Person") ; 	 	// 获取指定类的class对象 
		Object obj = cls.getDeclaredConstructor().newInstance() ; // 实例化对象 ,分配成员空间
		Field nameField = cls.getDeclaredField("name") ; 	// 获取成员对象
		nameField.setAccessible(true); 	//解除封装
		nameField.set(obj, "李四");   // 等价于 obj.setName("李四") ;
		System.out.println(nameField.get(obj));   // 等价于 Person对象.ame();
	}
}

通过一系列的分析可以发现类之中的构造、方法、成员属性都可以通过反射实现调用,但是对于成员的调用很少这样直接处理,大部分操作应该通过setter、getter完成,所以对于以上的代码是反射的特色,但是不具备有实际的使用能力,而对于Field只有一个方法最为常用:

  • 获取成员类型:public Class<?> getType​()

范例:获取Person类中的name成员类型

package cn.ren.demo;

import java.lang.reflect.Field;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = Class.forName("cn.ren.vo.Person") ; 	 	// 获取指定类的class对象 
		Object obj = cls.getDeclaredConstructor().newInstance() ; // 实例化对象 ,分配成员空间
		Field nameField = cls.getDeclaredField("name") ; 	// 获取成员对象
		System.out.println(nameField.getType());
		System.out.println(nameField.getType().getName());		// 获取完整类名称:包.类
		System.out.println(nameField.getType().getSimpleName());  	// 获取类名称
	}
}

在以后开发中进行反射处理的时候。往往会使用Field与Method类实现setter、getter方法的调用。

Unsafe工具类

反射式Java第一大的特点,一旦打开了反射的大门就可以有了更加丰富的类设计形式。处理JVM本身支持的反射处理之外,在Java里面也提供了sun.misc.Unsafe类(不安全的操作),这个类的主要特点是可以利用反射获取对象,并且直接利用底层的c++来代替JVM执行,即可以绕过JVM的相关的对象的管理机制,如果你一旦使用了Unsafe类,那么你的项目之中将无法使用内存管理机制以及垃圾回收处理。

但是如果想要使用Unsafe类首先就需要确认一下这个类之中定义的构造方法与常量问题:

构造方法:private Unsafe() {}
私有常量:private static final Unsafe theUnsafe = new Unsafe();

但是需要注意的是,在这个Unsafe类里面并没有提供static方法,即:不能够通过类似于传统的单例设计模式之中样式来进行操作,如果要想获得这个类的对象,就必须利用反射机制来完成。

package cn.ren.demo;
import java.lang.reflect.Field;
import sun.misc.Unsafe;
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Field field = Unsafe.class.getDeclaredField("theUnsafe") ;
		field.setAccessible(true); 		// 解除封装处理
		Unsafe unsafeObject = (Unsafe) field.get(null) ;  	 	// static 属性不需要传递实例化对象
	}
}

在传统的开发之中,一个程序类必须通过实例化对象后才可以调用普通方法,尤其是以单例设计模式为例。

范例:使用Unsafe类绕过实例化对象的管理

package cn.ren.demo;
import java.lang.reflect.Field;
import sun.misc.Unsafe;
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Field field = Unsafe.class.getDeclaredField("theUnsafe") ;
		field.setAccessible(true); 		// 解除封装处理
		Unsafe unsafeObject = (Unsafe) field.get(null) ;  	 	// static 属性不需要传递实例化对象
		// 利用Unsafe类绕过了JVM的管理机制,可以在没有实例化对象的情况下获取一个Singleton类的对象
		Singleton instance = (Singleton) unsafeObject.allocateInstance(Singleton.class) ;
		instance.print();
	}
}

class Singleton {		
	private Singleton() {
		System.out.println("******* Singleton类构造 *********");
	}
	public void print() {
		System.out.println("hello ren");
	}
}

Unsafe只能够说为开发提供了一些更加方便的处理机制,但是这种操作不受JVM的管理,所以不是必须的情况下不建议使用,而这个类可以帮助巩固对于反射的理解,同时,在面试的时候有人问到你也可以将以上内容讲出。

反射与简单Java类

简单Java类的主要特征是由属性组成,并且提供相应的setter、getter处理方法,同时简单Java类通过对象保存相应的属性内容。但是如果使用传统的简单Java类开发,那么会面临非常麻烦的困难。

传统属性赋值的弊端

范例:观察简单Java类的操作

class Emp {
	private String ename ;
	private String job ;
	public void setEname(String ename) {
		this.ename = ename;
	}
	public void setJob(String job) {
		this.job = job;
	}
	public String getEname() {
		return ename;
	}
	public String getJob() {
		return job;
	}
}

为了方便理解,本次Emp之中定义的ename\job属性类型使用的是String类型。按照传统的做法,首先实例化Emp对象,而后通过实例化对象进行setter方法的调用以设置属性内容。

范例:传统的调用   

package cn.ren.demo;
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Emp emp = new Emp() ;     // 更多情况下开发者关注的是内容的设置
		emp.setEname("张三");
		emp.setJob("程序员");
		// 使用为对象设置之后
		System.out.println("姓名:" + emp.getEname() + "、职位:" + emp.getJob());
	}
}

在整个进行Emp对象实例化并设置数据的操作过程之中,设置数据的部分最麻烦,如果现在Emp类里面提供有50个属性,那么对于整个程序而言将是一堆的Setter方法调用。在实际开发之中,简单Java类的个数是非常的多,那么所有的简单Java类都牵扯到属性赋值的时候,这个情况下代码编写的重复性将会非常的高。

按照传统编程方法所带来的问题是代码会带来大量的重复操作。如果要想解决对象的重复操作,解决方案就是反射机制,反射机制最大的特征可以根据其自身的特点(Object类直接操作,可以操作属性和方法),实现相同功能类的重复操作的抽象处理。

属性自动赋值实现思路

经过分析之后已经确认简单Java类所存在的问题,而对于开发者而言就需要想办法通过一种解决方案来实现属性内容的自动设置,那么这个时候的设置强烈建议采用字符串的形式描述对应的类型。

1、在进行程序开发的时候String字符串可以描述的内容很多,并且可以由开发者自行定义字符串的结构,下面采用:“属性:内容|属性|内容”的形式来为简单Java类中的属性初始化。 

2、类设计的基本结构:应该由一个专门的ClassInstanceFactory类负责所有的反射处理,即:接收反射对象与要设置的属性内容,同时可以获取指定类的实例化对象。

3、 设计的基本结构:

class ClassInstanceFactory {
	private ClassInstanceFactory() {}
	/**
	 * 实例化对象的创建方法,该对象可以根据传入字符串结构”属性:内容|属性:内容“
	 * @param <T>
	 * @param clazz  要进行反射实例化的class类对象,有Class就可以反射实例化对象
	 * @param value  要设置给对象的属性内容
	 * @return 一个已经配置好的Java类对象
	 */
	public static <T> T creat(Class<?> clazz, String value) {
		return null ;
	}
}

那么在当前的开发中,留给用户完善的就是ClassInstanceFactory.creat()处理方法,

单级属性配置

对于此时的Emp类里面会发现所给出的数据类型没有其它引用关联了,只是描述Emp本类的对象,所以这样的设置称为单级设置处理,所以此时应该处理两件事情:

  • 需要通过反射进行指定类对象的实例化处理;
  • 进行内容的设置(需要知道成员的属性类型、方法的名称、要设置的内容);

1、定义StringUtils工具类,该工具类主要实现首字母大写 

class StringUtils {
	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) ;
		}
	}
}

2、定义BeanUtils工具类,该工具类主要实现属性的一个设置, 

class BeanUtils { // 进行Bean处理的类
	private BeanUtils() {
	}

	/**
	 * 实现属性对象的设置
	 * 
	 * @param obj   要进行反射操作的实例化对象
	 * @param value 包含有指定内容的字符串、格式:”属性:内容|属性:内容“
	 */
	public static void setValue(Object obj, String value) {
		String results[] = value.split("\\|");
		for (int x = 0; x < results.length; x++) { // 循环设置属性内容
			String[] attval = results[x].split(":"); // 获取属性名称和属性内容
			try { // 出错原因属性找不到,但是希望能够向下继续setValue,如果向外抛出程序就会停止
				Field field = obj.getClass().getDeclaredField(attval[0]);
				Method setMethod = obj.getClass().getDeclaredMethod("set" + StringUtils.initcap(attval[0]),
						field.getType());
				setMethod.invoke(obj, attval[1]) ;  // 调用setter方法设置内容
			} catch (Exception e) {}
		}
	}
}

3、ClassInstanceFactory负责实例化对象并且调用BeanUtils类实现属性内容的设置

package cn.ren.demo;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		String value = "enames:smith|job:Clerk";
		Emp empObject = ClassInstanceFactory.creat(Emp.class, value);
		System.out.println("姓名:" + empObject.getEname() + "、职位:" + empObject.getJob());
	}
}

class ClassInstanceFactory {
	private ClassInstanceFactory() {
	}

	/**
	 * 实例化对象的创建方法,该对象可以根据传入字符串结构”属性:内容|属性:内容“
	 * 
	 * @param <T>
	 * @param clazz 要进行反射实例化的class类对象,有Class就可以反射实例化对象
	 * @param value 要设置给对象的属性内容
	 * @return 一个已经配置好的Java类对象
	 */
	public static <T> T creat(Class<?> clazz, String value) {
		try {// 如果要想采用反射进行简单Java类对象属性设置的时候,类中必须要有无参构造
			Object obj = clazz.getDeclaredConstructor().newInstance();
			BeanUtils.setValue(obj, value); // 通过反射设置属性
			return (T) obj; // 返回对象
		} catch (Exception e) {
			e.printStackTrace(); // 如果此时真的出现这种错误,本质上抛出异常也没用
			return null;
		}
	}
}

class StringUtils {
	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);
		}
	}
}

class BeanUtils { // 进行Bean处理的类
	private BeanUtils() {
	}

	/**
	 * 实现属性对象的设置
	 * 
	 * @param obj   要进行反射操作的实例化对象
	 * @param value 包含有指定内容的字符串、格式:”属性:内容|属性:内容“
	 */
	public static void setValue(Object obj, String value) {
		String results[] = value.split("\\|");
		for (int x = 0; x < results.length; x++) { // 循环设置属性内容
			String[] attval = results[x].split(":"); // 获取属性名称和属性内容
			try { // 出错原因属性找不到,但是希望能够向下继续setValue,如果向外抛出程序就会停止
				Field field = obj.getClass().getDeclaredField(attval[0]);
				Method setMethod = obj.getClass().getDeclaredMethod("set" + StringUtils.initcap(attval[0]),
						field.getType());
				setMethod.invoke(obj, attval[1]) ;  // 调用setter方法设置内容
			} catch (Exception e) {}
		}
	}
}

class Emp {
	private String ename;
	private String job;

	public void setEname(String ename) {
		this.ename = ename;
	}

	public void setJob(String job) {
		this.job = job;
	}

	public String getEname() {
		return ename;
	}

	public String getJob() {
		return job;
	}
}

即使现在类中的属性再多,那么也可以轻松的实现setter的调用(类对象实例化处理)

设置多种数据类型

现在已经成功的实现了单级的属性配置,但是这里面依然需要考虑一个实际情况,当前给定的数据类型只是String,但是在实际开发之中面对简单的Java类中的属性类型一般可选的方案:long(Long)、int(Integer)、double(Double)、String、Date(日期、日期时间),所以这个时候对于当前的程序代码就必须做出修改,要求可以实现各种类型的配置。

既然要求可以实现不同类型的内容设置,并且BeanUtils类主要是完成属性赋值处理的,那么可以在这个类之中,追加有一系列的处理方法。

package cn.ren.demo;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		String value = "empno:5207|ename:smith|job:Clerk|salary:55.00|hiredate:2020-8-9";
		Emp empObject = ClassInstanceFactory.creat(Emp.class, value);
		System.out.println("编号:" + empObject.getEmpno() + "、姓名:" + empObject.getEname() + "、职位:" + empObject.getJob() 
							+ "、薪资:" + empObject.getSalary() + "、入职时间:" + empObject.getHiredate());
	}
}

class ClassInstanceFactory {
	private ClassInstanceFactory() {
	}

	/**
	 * 实例化对象的创建方法,该对象可以根据传入字符串结构”属性:内容|属性:内容“
	 * 
	 * @param <T>
	 * @param clazz 要进行反射实例化的class类对象,有Class就可以反射实例化对象
	 * @param value 要设置给对象的属性内容
	 * @return 一个已经配置好的Java类对象
	 */
	public static <T> T creat(Class<?> clazz, String value) {
		try {// 如果要想采用反射进行简单Java类对象属性设置的时候,类中必须要有无参构造
			Object obj = clazz.getDeclaredConstructor().newInstance();
			BeanUtils.setValue(obj, value); // 通过反射设置属性
			return (T) obj; // 返回对象
		} catch (Exception e) {
			e.printStackTrace(); // 如果此时真的出现这种错误,本质上抛出异常也没用
			return null;
		}
	}
}

class StringUtils {
	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);
		}
	}
}

class BeanUtils { // 进行Bean处理的类
	private BeanUtils() {
	}

	/**
	 * 实现属性对象的设置
	 * 
	 * @param obj   要进行反射操作的实例化对象
	 * @param value 包含有指定内容的字符串、格式:”属性:内容|属性:内容“
	 */
	public static void setValue(Object obj, String value) {
		String results[] = value.split("\\|");
		for (int x = 0; x < results.length; x++) { // 循环设置属性内容
			String[] attval = results[x].split(":"); // 获取属性名称和属性内容
			try { // 出错原因属性找不到,但是希望能够向下继续setValue,如果向外抛出程序就会停止
				Field field = obj.getClass().getDeclaredField(attval[0]);
				Method setMethod = obj.getClass().getDeclaredMethod("set" + StringUtils.initcap(attval[0]),
						field.getType());    // 方法名
				Object convertVal = BeanUtils.convertAttributeValue(field.getType().getName(), attval[1]) ; 
				setMethod.invoke(obj, convertVal) ;  // 调用setter方法设置内容
			} catch (Exception e) {}
		}
	}
	/**
	 * 实现属性类型转换处理
	 * @param type 属性类型 ,通过Field获取的
	 * @param value  属性的内容,传入的都是字符串,需要将其变为指定类型
	 * @return 转换后的数据
	 */	
	private static Object convertAttributeValue(String type, String value) {
		if ("long".equals(type) || "java.lang.Long".equals(type)) { // 长整型
			return Long.parseLong(value);
		} else if ("int".equals(type) || "java.lang.Integer".equals(type)) {
			return Integer.parseInt(value);
		} else if ("double".equals(type) || "java.lang.Double".equals(type)) {
			return Double.parseDouble(value);
		} else if ("java.util.Date".equals(type)) {
			SimpleDateFormat sdf = null;
			if (value.matches("\\d{4}-\\d{2}-\\d{2}")) {
				sdf = new SimpleDateFormat("yyyy-MM-dd");
			} else if (value.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")) {
				sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			} else {
				return new Date(); // 返回当前时间
			}
			try {
				return sdf.parse(value);
			} catch (ParseException e) {
				return new Date();
			}
		} else {
			return value ; //字符串
		}
	}
}

class Emp {
	private long empno ;
	private String ename;
	private String job;
	private double salary ;
	private Date hiredate ;
	
	public long getEmpno() {
		return empno;
	}
	public void setEmpno(long empno) {
		this.empno = empno;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
	public Date getHiredate() {
		return hiredate;
	}
	public void setHiredate(Date hiredate) {
		this.hiredate = hiredate;
	}
	public void setEname(String ename) {
		this.ename = ename;
	}
	public void setJob(String job) {
		this.job = job;
	}

	public String getEname() {
		return ename;
	}

	public String getJob() {
		return job;
	}
}

此时只是列举出了常用的数据类型,如果要想其作为一个产品推广必须考虑所有可能的类型,所有可能出现的日期格式也需要考虑。

级联对象实例化

如果说现在给定类的对象之中存在有其它引用的级联关系的情况下,称之为多级设置,例如:一个雇员属于一个部门,一个部门属于一个公司,所以对于简单Java类的基本关系如下:

 Compy.javaDept.javaEmp.java
class Company {
	private String name ;
	private Date createdate ;
	
}

 

class Dept {
	private String dname ;
	private String loc ;
	private Company company ;
}

 

class Emp {
	private long empno ;
	private String ename;
	private String job;
	private double salary ;
	private Date hiredate ;
	private Dept dept ;
}

 

添加所有的setter、getter.

如果要通过Emp进行操作,应该通过"."作为操作的级联关系处理

dept.dname:财务部Emp实例化对象.getDept().getsetDname("财务部")
dept.company.name:renEmp实例化对象.getDept().getCompany().setName("ren")

但是考虑代码的简洁性,所以应该考虑可以通过级联的配置自动实现类中属性的实例化。下面需要解决如何进行对象的实例化。

String value = "empno:5207|ename:smith|job:Clerk|salary:55.00|hiredate:2020-8-9" + 
						"dept.dname:财务部|dept.company.name:REN";

现在的属性存在有多级的关系,那么就需要与单级的关系区分开,有“.”就是多级。 

class BeanUtils { // 进行Bean处理的类
	private BeanUtils() {
	}

	/**
	 * 实现属性对象的设置 
	 * @param obj   要进行反射操作的实例化对象
	 * @param value 包含有指定内容的字符串、格式:”属性:内容|属性:内容“
	 */
	public static void setValue(Object obj, String value) {
		String results[] = value.split("\\|");
		for (int x = 0; x < results.length; x++) { // 循环设置属性内容
			String[] attval = results[x].split(":"); // 获取属性名称和属性内容
			try { // 出错原因属性找不到,但是希望能够向下继续setValue,如果向外抛出程序就会停止
				if (attval[0].contains(".")) { // 多级配置
					String temp[] = attval[0].split("\\.");
					Object currentObject = obj ;
					
					// 最后一位是指定类中的属性名称,所以不在本次的实例化范围之内
					for (int y = 0; y < temp.length - 1; y ++ ) {  //实例化
						// 调用相应的Getter方法,如果getter方法返回了空,表示该对象未实例化
						Method getMethod = currentObject.getClass().getDeclaredMethod("get" + StringUtils.initcap(temp[y])) ;
						Object tempObject = getMethod.invoke(currentObject) ; 
						if (tempObject == null) { // "."后面的对象并没有实例化
							Field field = currentObject.getClass().getDeclaredField(temp[y]) ;   // 获取本类中"."后面的对象的属性类型
							Method method = currentObject.getClass().getDeclaredMethod("set" + StringUtils.initcap(temp[y]), field.getType()) ;
							Object newObject = field.getType().getDeclaredConstructor().newInstance() ; //  "."后面的对象实例化 
							method.invoke(currentObject, newObject) ;
							currentObject = newObject ;
						} else { // 已经实例化
							currentObject = tempObject ;
						}
					}
					
				} else {
					Field field = obj.getClass().getDeclaredField(attval[0]);   // 获取成员属性,obj需要获取Class对象,才能进行反射操作
					Method setMethod = obj.getClass().getDeclaredMethod("set" + StringUtils.initcap(attval[0]),
							field.getType()); // 获取方法
					Object convertVal = BeanUtils.convertAttributeValue(field.getType().getName(), attval[1]);
					setMethod.invoke(obj, convertVal); // 调用setter方法设置内容
				}
			} catch (Exception e) {}
		}
	}
	/**
	 * 实现属性类型转换处理
	 * @param type 属性类型 ,通过Field获取的
	 * @param value  属性的内容,传入的都是字符串,需要将其变为指定类型
	 * @return 转换后的数据
	 */	
	private static Object convertAttributeValue(String type, String value) {
		if ("long".equals(type) || "java.lang.Long".equals(type)) { // 长整型
			return Long.parseLong(value);
		} else if ("int".equals(type) || "java.lang.Integer".equals(type)) {
			return Integer.parseInt(value);
		} else if ("double".equals(type) || "java.lang.Double".equals(type)) {
			return Double.parseDouble(value);
		} else if ("java.util.Date".equals(type)) {
			SimpleDateFormat sdf = null;
			if (value.matches("\\d{4}-\\d{2}-\\d{2}")) {
				sdf = new SimpleDateFormat("yyyy-MM-dd");
			} else if (value.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")) {
				sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			} else {
				return new Date(); // 返回当前时间
			}
			try {
				return sdf.parse(value);
			} catch (ParseException e) {
				return new Date();
			}
		} else {
			return value ; //字符串
		}
	}
}

这些自动化的级联配置实例化的操作,在以后的项目编写中,一定会使用到。

级联属性赋值

现在已经成功的实现了级联对象实例化的处理,现在就需要考虑级联的属性设置,在之前考虑级联对象实例化的时候,循环的时候都是少了一位的。

for (int y = 0; y < temp.length - 1; y ++ ) {  //实例化
	// 调用相应的Getter方法,如果getter方法返回了空,表示该对象未实例化
	Method getMethod = currentObject.getClass().getDeclaredMethod("get" + StringUtils.initcap(temp[y])) ;
	Object tempObject = getMethod.invoke(currentObject) ; 
	if (tempObject == null) { // "."后面的对象并没有实例化
		Field field = currentObject.getClass().getDeclaredField(temp[y]) ;   // 获取本类中"."后面的对象的属性类型
		Method method = currentObject.getClass().getDeclaredMethod("set" + StringUtils.initcap(temp[y]), field.getType()) ;
		Object newObject = field.getType().getDeclaredConstructor().newInstance() ; //  "."后面的对象实例化 
		method.invoke(currentObject, newObject) ;
		currentObject = newObject ;
	} else { // 已经实例化
		currentObject = tempObject ;
	}
}

当此时代码循环处理完成之后,currentObject表示就是可以进行setter方法调用的对象了,并且该对象一定不可能为空,随后就可以按照之前的方式利用对象进行setter方法调用。

范例:实现对象的级联属性设置


//进行属性内容的设置
Field field = currentObject.getClass().getDeclaredField(temp[temp.length-1]);   // 获取成员属性,obj需要获取Class对象,才能进行反射操作
Method setMethod = currentObject.getClass().getDeclaredMethod("set" + StringUtils.initcap(temp[temp.length-1]),
		field.getType()); // 获取方法
Object convertVal = BeanUtils.convertAttributeValue(field.getType().getName(), attval[1]);
setMethod.invoke(currentObject, convertVal); // 调用setter方法设置内容

部分完整代码:

package cn.ren.demo;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		String value = "empno:5207|ename:smith|job:Clerk|salary:55.00|hiredate:2020-8-9" + 
						"|dept.dname:财务部|dept.company.name:REN";
		Emp empObject = ClassInstanceFactory.creat(Emp.class, value);
		System.out.println("编号:" + empObject.getEmpno() + "、姓名:" + empObject.getEname() + "、职位:" + empObject.getJob() 
							+ "、薪资:" + empObject.getSalary() + "、入职时间:" + empObject.getHiredate() + 
							"、部门:" + empObject.getDept().getDname() + "、公司:" + empObject.getDept().getCompany().getName());
		System.out.println(empObject.getDept());
		System.out.println(empObject.getDept().getCompany());
		
	}
}

class ClassInstanceFactory {
	private ClassInstanceFactory() {
	}

	/**
	 * 实例化对象的创建方法,该对象可以根据传入字符串结构”属性:内容|属性:内容“
	 * 
	 * @param <T>
	 * @param clazz 要进行反射实例化的class类对象,有Class就可以反射实例化对象
	 * @param value 要设置给对象的属性内容
	 * @return 一个已经配置好的Java类对象
	 */
	public static <T> T creat(Class<?> clazz, String value) {
		try {// 如果要想采用反射进行简单Java类对象属性设置的时候,类中必须要有无参构造
			Object obj = clazz.getDeclaredConstructor().newInstance();
			BeanUtils.setValue(obj, value); // 通过反射设置属性
			return (T) obj; // 返回对象
		} catch (Exception e) {
			e.printStackTrace(); // 如果此时真的出现这种错误,本质上抛出异常也没用
			return null;
		}
	}
}

class StringUtils {
	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);
		}
	}
}

class BeanUtils { // 进行Bean处理的类
	private BeanUtils() {
	}

	/**
	 * 实现属性对象的设置 
	 * @param obj   要进行反射操作的实例化对象
	 * @param value 包含有指定内容的字符串、格式:”属性:内容|属性:内容“
	 */
	public static void setValue(Object obj, String value) {
		String results[] = value.split("\\|");
		for (int x = 0; x < results.length; x++) { // 循环设置属性内容
			String[] attval = results[x].split(":"); // 获取属性名称和属性内容
			try { // 出错原因属性找不到,但是希望能够向下继续setValue,如果向外抛出程序就会停止
				if (attval[0].contains(".")) { // 多级配置
					String temp[] = attval[0].split("\\.");
					Object currentObject = obj ;
					
					// 最后一位是指定类中的属性名称,所以不在本次的实例化范围之内
					for (int y = 0; y < temp.length - 1; y ++ ) {  //实例化
						// 调用相应的Getter方法,如果getter方法返回了空,表示该对象未实例化
						Method getMethod = currentObject.getClass().getDeclaredMethod("get" + StringUtils.initcap(temp[y])) ;
						Object tempObject = getMethod.invoke(currentObject) ; 
						if (tempObject == null) { // "."后面的对象并没有实例化
							Field field = currentObject.getClass().getDeclaredField(temp[y]) ;   // 获取本类中"."后面的对象的属性类型
							Method method = currentObject.getClass().getDeclaredMethod("set" + StringUtils.initcap(temp[y]), field.getType()) ;
							Object newObject = field.getType().getDeclaredConstructor().newInstance() ; //  "."后面的对象实例化 
							method.invoke(currentObject, newObject) ;
							currentObject = newObject ;
						} else { // 已经实例化
							currentObject = tempObject ;
						}
					}
					// 进行属性内容的设置
					Field field = currentObject.getClass().getDeclaredField(temp[temp.length-1]);   // 获取成员属性,obj需要获取Class对象,才能进行反射操作
					Method setMethod = currentObject.getClass().getDeclaredMethod("set" + StringUtils.initcap(temp[temp.length-1]),
							field.getType()); // 获取方法
					Object convertVal = BeanUtils.convertAttributeValue(field.getType().getName(), attval[1]);
					setMethod.invoke(currentObject, convertVal); // 调用setter方法设置内容
				} else {
					Field field = obj.getClass().getDeclaredField(attval[0]);   // 获取成员属性,obj需要获取Class对象,才能进行反射操作
					Method setMethod = obj.getClass().getDeclaredMethod("set" + StringUtils.initcap(attval[0]),
							field.getType()); // 获取方法
					Object convertVal = BeanUtils.convertAttributeValue(field.getType().getName(), attval[1]);
					setMethod.invoke(obj, convertVal); // 调用setter方法设置内容
				}
			} catch (Exception e) {}
		}
	}
	/**
	 * 实现属性类型转换处理
	 * @param type 属性类型 ,通过Field获取的
	 * @param value  属性的内容,传入的都是字符串,需要将其变为指定类型
	 * @return 转换后的数据
	 */	
	private static Object convertAttributeValue(String type, String value) {
		if ("long".equals(type) || "java.lang.Long".equals(type)) { // 长整型
			return Long.parseLong(value);
		} else if ("int".equals(type) || "java.lang.Integer".equals(type)) {
			return Integer.parseInt(value);
		} else if ("double".equals(type) || "java.lang.Double".equals(type)) {
			return Double.parseDouble(value);
		} else if ("java.util.Date".equals(type)) {
			SimpleDateFormat sdf = null;
			if (value.matches("\\d{4}-\\d{2}-\\d{2}")) {
				sdf = new SimpleDateFormat("yyyy-MM-dd");
			} else if (value.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")) {
				sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			} else {
				return new Date(); // 返回当前时间
			}
			try {
				return sdf.parse(value);
			} catch (ParseException e) {
				return new Date();
			}
		} else {
			return value ; //字符串
		}
	}
}

在以后的开发之中简单java类的赋值处理,将不在重复调用setter操作完成,而这种处理形式是在正规开发中普遍采用的方式。上述代码就是重点的重点。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值