反射与类操作(阿里云开发者社区学习笔记)

反射获取类结构信息

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

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

范例:定义一个程序类

public interface IChannelService{
public boolean connect();
}
public interface IMessageService{
public void send();
}
public class Person5 extends AbstractBase implements IChannelService,IMssageService{
    public boolean connect(){
        return true;
    }

    @Override
    public void send() {
        if (this.connect()){
            System.out.println("[信息发送]www.baidu.com");
        }
    }
}

public abstract class AbstractBase{

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

        Class<?> [] clazz = cls.getInterfaces();
        for(int x =0; x<clazz.length;x++){
            System.out.println(clazz[x].getName());
        }
    }
}

如果此时要想获得类的一些基础信息则可以通过Class类中的如下方法:
‒ 获取包名称:public Package getPackage​()
‒ 获取继承父类:public Class<? super T> getSuperclass​() ‒ 获取实现父接口:public Class<?>[] getInterfaces​()

范例:获得包名称

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

范例:获取父类信息
public class JavaAPIDemo{
public static void main(String agrs[])throws Exception{
Class<?> cls = Person5.class; // 获取指定类的class对象
Class<?>parent = cls.getSuperclass();
System.out.println(parent.getName());
System.out.println(parent.getSuperclass().getName());
System.out.println(parent.getSuperclass().getSuperclass());
}
}

范例:获取父接口

public Class<?>[] getInterfaces​()
public Class<?>[] getInterfaces​()
public class JavaAPIDemo{
public static void main(String agrs[])throws Exception{
Class<?> cls = Person5.class; // 获取指定类的class对象
Class<?> [] clazz = cls.getInterfaces();
for(int x =0; x<clazz.length;x++){
System.out.println(clazz[x].getName());
}
}
}

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

反射调用构造方法

在一个类之中除了有继承的关系之外最为重要的操作就是类中的结构处理了,而类中的结构里面首先需要观察的就是构造方法的使用问题,实际上在之前通过反射实例化对象的时候就已经接触到了构造方法的问题了
‒ 实例化方法替代:clazz.getDeclaredConstructor().newInstance()
所有类的构造方法的获取都可以直接通过Class类来完成,该类中定义有如下的几个方法:
‒ 获取所有构造方法:public Constructor<?>[] getDeclaredConstructors​()throws SecurityException ‒ 获取指定构造方法:public Constructor getDeclaredConstructor​(Class<?>… parameterTypes)throws NoSuchMethodException,SecurityException
‒ 获取所有构造方法:public Constructor<?>[] getConstructors​()throws SecurityException ‒ 获取指定构造方法:public Constructor getConstructor​(Class<?>… parameterTypes)throws NoSuchMethodException,SecurityException

范例:修改Person类的定义

public abstract class AbstractBase {
    public AbstractBase(){};
    public AbstractBase(String msg){};
}

范例:修改Person类的定义

public class Person5 extends AbstractBase implements IChannelService, IMessageService {
    private String name ;
    private int age ;
    public Person5(){}
    public Person5(String name,int age){
        // 其他操作略
        this.name = name;
        this.age = age ;
    }
    public String toString(){
        return "姓名:"+this.name + "年龄:" + this.age ;
    }
    @Override
    public boolean connect() {
        return false;
    }
    @Override
    public void send() {
    }
}

范例:获取构造


import java.lang.reflect.Constructor;

public class JavaAPIDemo26 {
    public static void main(String[] args) {
        Class<?> cls = Person5.class; // 获取指定类的class对象
        Constructor<?>[]constructors=cls.getDeclaredConstructors();// 获取全部构造
// Constructor<?> []constructors=cls.getConstructors();// 获取全部构造
        for(Constructor<?> cons:constructors){
            System.out.println(cons);
        }
    }
}


```
此时获取的是类之中的全部构造方法,但是也可以获取一个指定参数的构造,例如:现在的Person类之中提供有两个构造方法
```java
public class Person5 extends AbstractBase implements IChannelService, IMessageService {
    private String name ;
    private int age ;
    public Person(){}
    public Person(String name,int age){
    // 其他操作略
        this.name = name;
        this.age = age ;
    }
    public String toString(){
        return "姓名:"+this.name + "年龄:" + this.age ;
    }
    @Override
    public boolean connect() {
        return false;
    }
    @Override
    public void send() {
    }
}
```
此时程序打算调用Person类之中的有参构造方法进行Person类对象的实例化处理,这个时候就必须指明要调用的构造,而后通过Constructor类之中提供的实例化方法操作:public T newInstance​(Object... initargs)throws InstantiationException,IllegalAccessException,IllegalArgumentException,InvocationTargetException

范例:调用指定构造实例化对象
```java
import java.lang.reflect.Constructor;

public class JavaAPIDemo27 {
    public static void main(String agrs[])throws Exception{
        Class<?> cls = Person5.class; // 获取指定类的class对象
        Constructor<?> constructor=cls.getConstructor(String.class,int.class);
        Object obj = constructor.newInstance("小强",78); // 实例化对象
        System.out.println(obj);
    }
}
```
虽然程序代码本身允许开发者调用有参构造处理,但是如果从实际的开发来讲,所有使用反射的类中最好提供有无参构造,因为这样的实例化可以达到统一性.
# 反射调用普通方法
在进行反射处理的时候也可以通过反射来获取类中的全部方法,但是需要提醒的,如果要想通过反射调用这些方法,必须有一个前提条件:类之中要提供有实例化对象

在Class类里面提供有如下的操作可以获取方法对象:
‒ 获取全部方法:public Method[] getMethods​()throws SecurityException
‒ 获取指定方法:public Method getMethod​(String name,Class<?>... parameterTypes)throws NoSuchMethodException,SecurityException
‒ 获取本类全部方法:public Method[] getDeclaredMethods​()throws SecurityException
‒ 获取本类指定方法:public Method getDeclaredMethod​(String name,Class<?>... parameterTypes)throws NoSuchMethodException,SecurityException


范例:获取全部方法
```java
public class JavaAPIDemo{
public static void main(String args[]){
Class<?> cls = Person5.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()方法完成的,很多时候也可以由用户自己来拼凑方法信息的展示信息。


范例:自定义方法信息显示-了解即可
```java

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

public class JavaAPIDemo{
    public static void main(String args[]){
        Class<?> cls = Person5.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()+" "+"arg-"+x);
                if(x<params.length -1){
                    System.out.print(",");
                }
            }
            System.out.print(")");
            Class<?> exp[]=met.getExceptionTypes();
            for(int x =0;x<exp.length;x++) {
                if (exp.length > 0) {
                    System.out.print(" throws ");
                    for ( x = 0; x < exp.length; x++) {
                        System.out.print(exp[x].getName());
                        if (x < exp.length - 1) {
                            System.out.print(",");
                        }
                    }
                }
            }
            System.out.println();
        }
    }
}
```
这种代码你只需要清楚可以根据反射获取方法的结构即可,不需要做过多深入的了解,但是在Method类里面有一个致命的重要方法:public Object invoke​(Object obj,Object... args)throws IllegalAccessException,IllegalArgumentException,InvocationTargetExceptionPerson类里面为name属性追加有setter与getter方法
```java

public class Person5 extends AbstractBase implements IChannelService, IMessageService {
    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;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String toString(){
        return "姓名:-"+this.name + "年龄:" + this.age ;
    }
    @Override
    public boolean connect() {
        return false;
    }
    @Override
    public void send() {
    }
}
```

随后需要通过反射机制来实现Person类之中的setter与getter方法的调用处理

范例:在不导入指定类开发包的情况下实现属性的配置
```java
import java.lang.reflect.Method;


public class JavaAPIDemo{
    public static void main(String args[])throws Exception{
        Class<?> cls = Class.forName("Person5"); // 获取指定类的Class对象
        String attribute = "name";// 要操作的类属性
        String value = "小强子"; // 要设置的属性内容
        // 1.任何情况下如果要想保存类中的属性或者调用类中的方法都必须保证存在有实例化对象,既然不允许导入包,那么就反射实例化
        Object obj = cls.getDeclaredConstructor().newInstance(); // 调用无参构造实例化
        // 2.如果要想进行方法的调用,那么一定要获取方法的名称
        String setMethodName = "setName"; // 方法名称
        Method method= cls.getDeclaredMethod(setMethodName,String.class);  // 获取指定的方法
        method.invoke(obj,value); // 等价于:Person对象.setName(value);
        String getMethodName = "getName";
        Method getMethod = cls.getDeclaredMethod(getMethodName); // getter没有参数
        System.out.println(getMethod.invoke(obj)); //等价于:Person对象.getName()
    }
}
```
利用此类操作整体的形式上不会有任何的明确的类对象产生,一切都是依靠反射机制处理的,这样的处理避免了与某一个类的耦合问题

用这种方式进行类调用在一些更加复杂的设计上是非常方便的
# 反射调用成员
类结构之中的最后一个核心的组成就是成员(Field),大部分情况下都会将其称为成员属性,对于成员信息的获取也是通过Class类完成的,在这个类中提供有如下两组操作方法:
‒ 获取本类全部成员:public Field[] getDeclaredFields​()throws SecurityException
‒ 获取本类指定成员:public Field getDeclaredField​(String name)throws NoSuchFieldException,SecurityException
‒ 获取父类全部成员:public Field getDeclaredField​(String name)throws NoSuchFieldException,SecurityException
‒ 获取父类指定成员:public Field getField​(String name)throws NoSuchFieldException,SecurityException


范例:修改要操作父类的结构
```java
public abstract class AbstractBase{
protected static final String BASE = "www.baidu.com";
private String info = "Hello world";
public AbstractBase(){}
public AbstractBase(String msg){}
}


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

```


范例:获取类中的成员
````java
import java.lang.reflect.Method;


public class JavaAPIDemo{
    public static void main(String args[])throws Exception{
        Class<?> cls = Class.forName("Person5"); // 获取指定类的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);
}
}
    }
}
```
但是在Field类里面最为重要的操作并不是获取全部的成员,而是如下的三个方法:
‒ 设置属性内容:public void set​(Object obj,Object value)throws IllegalArgumentException,IllegalAccessException
‒ 获取属性内容:public Object get​(Object obj)throws IllegalArgumentException,IllegalAccessException
‒ 解除封装:public void setAccessible​(boolean flag)

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

范例:直接调用Person5类中的name私有成员
```java
import java.lang.reflect.Method;


public class JavaAPIDemo{
    public static void main(String args[])throws Exception{
        Class<?> cls = Class.forName("Person5"); // 获取指定类的Class对象
Object obj = cls.getConstructor().newInstance(); // 实例化对象(分配成员空间)
Field nameField=cls.getDeclaredField("name"); // 获取成员对象
nameField.setAccessible(true); // 没有封装了
nameField.set(obj,"番茄强"); // 等价于:Person5对象.name = "番茄强";
System.out.println(nameField.get(obj)); // 等价于:person5对象.name
    }
}
```
通过一系列的分析可以发现,类之中的构造,方法,成员属性都可以通过反射实现调用,但是对于成员的反射调用很少这样直接处理,大部分操作都应该通过setter或getter处理,所以对于以上的代码只能说是反射的特色,不具备实际的使用能力,而对于Field类在实际开发之中只有一个方法最为常用:
‒ 获取成员类型:public Class<?> getType​()

范例:获取Person5类中的name成员类型
```java
import java.lang.reflect.Method;


public class JavaAPIDemo{
    public static void main(String args[])throws Exception{
        Class<?> cls = Class.forName("Person5"); // 获取指定类的Class对象
Field nameField = cls.getDeclaredField("name"); // 获取成员对象
System.out.println(nameField);
System.out.println(nameField.getType().getName()); // 获取完整类名称"包.类"
System.out.println(nameField.getType().getSimpleName()); // 获取类名称
    }
}
```
在以后开发中进行反射处理的时候,往往会利用FieldMethod类实现类中的setter方法的调用
# Unsafe工具

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

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

构造方法:private Unsafe() {}

私有常量:private static final Unsafe theUnsafe = new Unsafe();

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

```java

import sun.misc.Unsafe;

import java.lang.reflect.Field;

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属性不需要传递实例化对象
    }
}
```
在传统的开发之中,一个程序类必须要通过实例化对象后才可以调用类中的普通方法,尤其是以单例设计模式为例。
```java
public class JavaAPIDemo{
    public static void main(String args[])throws Exception{
        Singleton.getInstance().print();
    }
}

class Singleton{
    private final static Singleton  INSTANCE = new Singleton();
    private Singleton(){
        System.out.println("********Singgleton类构造**********");
    }
    public static Singleton getInstance(){
        return INSTANCE;
    }
    public void print(){
        System.out.println("www.baidu.com");
    }
}
```
范例:采用Unsafe绕过实例化对象管理机制
```java

import sun.misc.Unsafe;

import java.lang.reflect.Field;

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("www.baidu.com");
    }
}
```
Unsafe只能说为我们的开发提供了一些更加方便的处理机制,但是这种操作由于不受JVM的管理所以如果不是必须的情况下不建议使用,而讲解这个类主要的目的是帮助大家巩固对于反射的理解,同时也帮助大家在笔试的时候如果有人问到你单例设计模式的情况下,也可以追加一个Unsafe以加深你对这一概念的理解,从而获得一个比较好的对比效果
# 学习资料
阿里云开发者社区
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值