重用性是面向对象设计的核心原则。为了进一步提升代码的重用性,Java提供了反射机制。反射技术首先考虑的是“反”与“正”的操作,所谓的“正”操作,是指当开发者使用一个类的时候,一定要先导入程序所在的包,而后根据类进行对象实例化,并且依靠对象调用类中的方法;而所谓的“反”操作,是指可以根据实例化对象反推出其类型。
Class类是反射机制的根源,可以通过Object类中所提供的方法获取一个Class实例。
获取Class实例化对象:public final Class<?> getClass();
package cn.uihtml.demo;
import java.util.Date;
class JavaDemo {
public static void main(String[] args) throws Exception {
Date date = new Date();
System.out.println(date.getClass());
}
}
Class类对象实例化
java.lang.Class类是反射机制操作的起源,为了适应不同情况下的反射机制操作,Java提供有3种Class类对象实例化方式。
利用Object类中提供的getClass()方法获取实例化对象
package cn.uihtml.demo;
class Memeber {}
class JavaDemo {
public static void main(String[] args) throws Exception {
Member m = new Member();
Class clazz = m.getClass();
System.out.println(clazz);
}
}
Object类是所有类的父类,这样所有类的实例化对象都可以直接利用getClass()方法获取Class类实例化对象。
使用“类.class”形式获取指定类或接口的Class实例化对象。
package cn.uihtml.demo;
class Member{}
class JavaDemo {
public static void main(String[] args) throws Exception {
Class clazz = Member.class;
System.out.println(clazz);
}
}
使用Class类内部提供的forName()方法,根据类的完整名称获取实例化对象。
package cn.uihtml.demo;
class Member{}
class JavaDemo {
public static void main(String[] args) throws Exception {
Class clazz = class.forName("cn.uihtml.demo.Member");
System.out.println(clazz);
}
}
反射机制与对象实例化
反射机制的设计可以方便地帮助开发者实现解耦和设计,并且可以帮助程序摆脱对关键字new的依赖,通过反射获取实例化对象。
反射Class类实例化对象
package cn.uihtml.demo;
class Member {
public Member() {
System.out.println('无参构造方法实例化Member类对象')
}
@Override
public String toString() {
return 'toString覆写'
}
}
class JavaDemo {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("cn.uithml.demo.Member");
Object obj = clazz.getDeclaredConstructor().newInstance();
System.out.println(obj);
}
}
反射与工厂设计模式
使用工厂设计模式的主要特点是解决接口与子类之间因直接使用关键字new所造成的耦合问题,但是传统的工厂设计操作中会存在两个严重的问题。
- 传统工厂设计属于静态工厂设计,需要根据传入的参数并结合大量的分支语句来判断所需要实例化的子类,当一个接口或抽象类扩充子类时必须修改工厂类结构,否则将无法获取新的子类实例。
- 工厂设计只能够满足一个接口或抽象类获取实例化对象的需求,如果有更多的接口或抽象类定义时将需要定义更多的工厂类或扩充工厂类中的static方法。
package cn.uihtml.demo;
interface IMessage {
public void send();
}
class CloudMessage implements IMessage {
@Override
public void send() {
System.out.println("云消息发送:www.uihtml.cn");
}
}
class NetMessage implements IMessage {
@Override
public void send() {
System.out.println("网路消息发送:www.uihtml.cn");
}
}
class Factory {
private Factory() {}
public static T getInstance(String className,Class clazz) {
T instance = nunll;
try {
instance = (T) Class.forName(className).getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
}
class JavaDemo {
public static void main(String[] args) throws Exception {
IMessage msg = Factory.getInstance("cn.uihtml.demo.NetMessage",IMessage.class);
msg.send();
}
}
本程序实现了一个全新的并且可用工厂类结构,为了让该工厂类适合于所有的类型,程序中结合反射机制于泛型获取指定类型的实例,这样可以避免向下转型所带来的安全隐患。
反射与单例设计模式
单例设计模式的核心本质在于:类内部的构造方法私有化,在类的内部产生实例化对象之后通过static方法获取实例化对象进行类中的结构调用。单例设计模式一共有两类:懒汉式和饿汉式。饿汉式的单例由于其在类加载的时候就已经进行了对象实例化处理,所以不涉及多线程的访问问题;但是懒汉式单例在多线程访问下却有可能出现多个实例化对象的产生的问题。
懒汉式单例设计与多线程访问
package cn.uihtml.demo;
class Singleton {
private static Singleton instance = null;
private Singleton() {
System.out.println("【" + Thread.currentThread().getName() + "】");
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public void print() {
System.out.println("www.uihtml.cn");
}
}
class JavaDemo {
public static void main(String[] args) throws Exception {
for (int x = 0; x < 3; x++) { new Thread(() -> {
Singleton.getInstance().print();
}, "单例消费端-" + x).start();
}
}
}
单例设计的核心在于Singleton类只允许有一个实例化对象,然而通过本程序的执行可以发现,此时产生了多个实例化对象,而这一操作的根源在于多线程访问不同步,即有多个线程对象在第一次使用时都通过了实例化对象的判断语句(if(instance == null)),多以此时只能够通过synchronized来进行同步处理。
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
反射机制与类操作
Java反射机制可以在程序运行状态下,自动获取并调用任意一个类中的组成结构(成员属性、方法等),这样的做法可以避免单一的程序调用模式,使代码开发变得更加灵活。
反射获取类结构信息
程序开发中,任何定义的类都存在继承关系,同时为了代码结构的清晰,也应该利用包保存不同功能的类。
反射获取类结构信息所用的方法
方法 | 类型 | 描述 |
---|---|---|
public Package getPackage() | 普通 | 获取包信息 |
public Class <? super T> getSuperclass() | 普通 | 获取继承父类 |
public Class?<?>[] getInterfaces() | 普通 | 获取实现接口 |
反射获取类结构信息
package cn.uihtml.demo;
interface IMessageService {
public void send();
}
inter IChannelService {
public boolean connect();
}
abstract class AbstractBase {}
class Mail extends AbstractBase implements IMessageService,IChannelService {
@Override
public boolean connect() {
return true;
}
@Override
public void send() {
if (this.connect()) {
System.out.println("消息发送:www.uihtml.cn");
}
}
}
class JavaDemo {
public static void main(String[] args) throws Exception {
Class cls = Mail.class;
Package pack = cls.getPackage();
System.out.println(pack.getName());
Class parent = cls.getSuperclass();
System.out.println(parent.getName());
System.out.println(parent.getSuperclass().getName());
Class clazz[] = cls.getInterfaces();
for(Class temp : clazz) {
System.out.println(temp.getName());
}
}
}
反射调用构造方法
Class类获取构造方法
方法 | 类型 | 描述 |
---|---|---|
public Constructor<?>[] getDelaredConstructors() throws SecurityException | 普通 | 获取指定类中所有的构造方法 |
public Constructor<T> getDelaredConstructor(Class<?>… parameterTypes) throws NoSuchMethodException,SecurityException | 普通 | 获取指定类中指定参数类型的构造方法 |
public Constructor<?>[] getConstructors() throws SecurityException | 普通 | 获取类中所有public权限的构造方法 |
public Constructor<T> getConstructor(Class<?>)… parameterTypes) throws NoSuchMethodException,SecurityException | 普通 | 获取指定类中指定参数类型并且访问权限为public的构造方法 |
使用Class类获取的所有构造方法都通过java.lang.reflect.Constructor类的对象来表示。
Constructor类常用方法
方法 | 类型 | 描述 |
---|---|---|
public T newInstance(Object… initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException | 普通 | 调用构造方法传入指定参数进行对象实例化 |
public String getName() | 普通 | 获取构造方法名称 |
public Type[] getGenericParameterTypes() | 普通 | 获取构造方法的参数类型 |
public Type[] getGenericExceptionTypes() | 普通 | 获取构造方法抛出的异常类型 |
public int getParameterCount() | 普通 | 获取构造方法的参数个数 |
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) | 普通 | 获取全部声明的Annotation |
public void setAccessible(boolean flag) | 普通 | 设置构造方法可见性 |
调用构造方法
package cn.uihtml.demo;
import java.lang.reflect.Constructor;
class Mail {
private String msg;
public Mail() {};
public Mail(String msg) {
System.out.println("调用单惨构造方法,实例化对象");
this.msg = msg;
}
@Override
public String toString() {
retrun "消息内容:" + this.msg;
}
}
class JavaDemo {
public static void main(String[] args) throws Exception {
Class cls = Mail.class;
Constructor[] constructors = cls.getDeclaredConstructors();
for (Constructor cons : constructors) {
System.out.println(cons);
}
// 获取单参构造方法并且参数类型为String的构造方法对象实例
Constructor cons = cls.getDeclaredConstructor(String.class);
Object obj = cons.newInstance("www.uihtml.cn");
System.out.println(obj);
}
}
本程序通过反射机制获取类中的全部构造方法进行信息展示,随后又获取了一个指定类型的构造方法并利用Constructor类的newInstance()方法实现了对象反射实例化操作。
反射调用方法
每个类都有不同的功能,所有的功能都可以通过方法进行定义。在Java中除了通过具体的实例化对象实现方法调用外,也可以利用反射基于实例化对象的形式事项方法调用。
Class获取方法信息的方法
方法 | 类型 | 描述 |
---|---|---|
public Method[] getDeclaredMethods() throws SecurityException | 普通 | 获取一个类中所有定义的方法 |
public Method getDeclaredMethod(String name, Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException | 普通 | 获取一个类中指定名称与指定类型的方法 |
public Method[] getMethods() throws SecurityException | 普通 | 获取一个类中所有public类型的方法 |
public Method getMethod(String name, Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException | 普通 | 获取类中指定名称、指定参数类型的public方法 |
通过Class类获取的每一个方法信息都使用java.lang.reflect.Method类实例描述,通过该类实例可以获取方法的相关信息,也可以实现方法的反射调用。
Method类常用方法
方法 | 类型 | 描述 |
---|---|---|
public Object invoke(Object obj,Object… args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException | 普通 | 方法调用,等价于“实例化对象.方法()” |
public Class<?> getReturnType() | 普通 | 获取方法返回值类型 |
public String getName() | 普通 | 获取构造方法名称 |
public Type[] getGenericParameterTypes() | 普通 | 获取构造方法的参数类型 |
public type[] getGenericExceptionTypes() | 普通 | 获取构造方法抛出的异常类型 |
public int getParameterCount() | 普通 | 获取构造方法的参数个数 |
public <T extends Annotation> T getAnnotation(Class <T> annotationClass) | 普通 | 获取全部声明的Annotation |
public int getModifiers() | 普通 | 获取方法修饰符 |
public void setAccessible(boolean flag) | 普通 | 设置方法可见性 |
获取类中的方法信息
package cn.uihtml.demo;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
class Mail {
public boolean connect() {
return true;
}
public void send() {
Systme.out.println("发送消息:www.uihtml.cn");
}
}
class JavaDemo {
public static void main(String[] args) throws Exception {
Class cls = Mail.class;
Method methods[] = cls.getMethods();
for(Method met : methods) {
int mod = met.getModifiers();
System.out.println(Modifier.toString(mod) + "、");
System.out.println(met.getReturnTypes().getName() + "、");
System.out.println(met.getName() + "(");
Class params[] = met.getParameterTypes();
for (int x = 0; x < params.length; x++) { System.out.print(params[x].getName() + " " + "arg-" + 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();
}
}
}
本程序通过反射机制获取了一个类中定义的所有方法,随后将获取到的每一个方法对象中的信息拼凑输出。
反射机制编程中除了获取类中的方法定义外,最为重要的功能就是可以利用Method类中的invoke()方法并结合实例化对象(Object类型即可)实现反射方法调用。
反射调用类中的setter、getter方法
package cn.uihtml.demo;
import java.lang.reflect.Method;
class Memeber {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
class javaDemo {
public static void main(String[] args) throws Exception {
Class cls = Member.class;
String value = "蜀国宰相-->诸葛孔明";
// 调用无参构造方法实例化对象
Object obj = cls.getDeclaredConstructor().newInstance();
// 反射调用方法需要明确地知道方法的名称以及方法中的参数类型
String setMethodName = "setName";
Method setMethod = cls.getDeclaredMethod(setMethodName, String.class);
setMethod.invoke(obj, value);
String getMethodName = "getName";
Method getMethod = cls.getDeclaerdMethod(getMethodName);
System.out.println(getMethod.invoke(obj));
}
}
通过反射实现的方法调用最大的特点是可以直接利用Object类型的实例化对象进行方法调用,但是在获取方法对象时需要明确知道方法名称以及方法的参数类型。
反射调用成员属性
成员属性保存着每一个对象的具体信息,Class类可以获取类中的成员信息,并提供又相应的操作方法。
Class类获取成员属性的操作方法
方法 | 类型 | 描述 |
---|---|---|
public Field[] getDeclaredFields() throws SecurityException | 普通 | 获取本类全部成员信息 |
public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException | 普通 | 获取指定成员属性信息 |
public Fieldp[] getFields() throws SecurityException | 普通 | 获取父类中定义的全部成员信息 |
public Field getField(String name) throws NoSuchFieldException, SecurityException | 普通 | 获取父类中定义的全部public成员信息 |
反射中成员通过java.lang.reflect.Field实例描述。
Field类常用的方法
方法 | 类型 | 描述 |
---|---|---|
public Class<?> getType() | 普通 | 获取成员属性类型 |
public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException | 普通 | 设置成员属性内容 |
public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException | 普通 | 获取成员属性内容 |
public int getModifiers() | 普通 | 获取成员属性修饰符 |
public void setAccessible(boolean flag) | 普通 | 设置成员属性可见性 |
获取类中的成员属性信息
package cn.uihtml.demo;
import java.lang.reflect.Field;
interface IChannelService {
public static final String NAME = "uihtml";
}
abstract class AbstractBase {
protected static final String BASE = "www.uihtml.cn";
private String info = "Hello UIHTML";
}
class Member extends AbstractBase implements IChannelService {
private String name;
private int age;
}
public class JavaDemo {
public static void main(String[] args) throws Exception {
Class cls = Member.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);
}
}
}
}
本程序获取了父亲继承而来的public成员属性以及本类定义的private成员属性信息,而获取Field成员属性对象的核心意义在于可以直接通过Field类并结合实例化对象实现属性赋值与获取。
反射操作成员属性内容
package cn.uihtml.demo;
import java.lang.reflect.Field;
class Member {
private String name;
}
public class JavaDemo {
public static void main(String[] args) throws Exception {
Class cls = Member.class;
Object obj = cls.getDeclaredConstructor().newInstance();
Field nameField = cls.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(obj,"蜀国宰相-->诸葛孔明");
System.out.println(nameField.get(obj));
}
}
本程序直接进行Member类中name成员属性的操作,由于name属性使用了private封装,所以在进行属性内容设置和取得前需要使用setAccessible(true)方法设置其为可见。
Unsafe工具类
为了进一步扩展反射操作的支持,在Java里提供一个sun.misc.Unsafe类(不安全操作)。Unsafe类的最大特点是可以利用反射来获取对象,并且直接使用底层的C++语言来代替JVM执行,即可以绕过JVM的相关对象的管理机制。一旦使用了Unsafe类,那么项目中将无法继续使用JVM的内存管理机制以及垃圾回收处理。
使用Unsafe类绕过实例化对象管理来获取对象实例
package cn.uihtml.demo;
import java.lang.reflect.Field;
import sun.misc.Unsafe;
class Singleton {
private Singleton() {
System.out.println("Singleton类构造**");
}
public void print() {
System.out.println("www.uihtml.cn");
}
}
public class JavaDemo {
public static void main(String[] args) throws Exception {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafeObject = (Unsafe) field.get(null);
// 利用Unsafe类绕过了JVM的管理机制,可以在没有实例化对象的情况下获取一个Singleton类实例化对象
Singleton instance = (Singleton) unsafeObject.allocateInstance(Singleton.class);
instance.print();
}
}
本程序利用Unsafe类绕过了JVM的类管理机制直接获取Singleton类的实例化对象,这样将不会调用类中的构造方法,并且也不受到系统GC回收管理。
原创文章,作者:ZERO,如若转载,请注明出处:https://www.edu24.cn/course/java/java-reflex.html