java反射(三)--反射与操作类

一.反射与操作类
  在反射机制的处理过程之中不仅仅只是一个实例化对象的处理操作,更多的情况下还有类的组成的操作,任何一个类的基本组成结构:父类(父接口),包,属性,方法(构造方法,普通方法)
--获取类的基本信息
  一个类的基本信息主要包括的是所在的包名称,父类的定义,父接口的定义.
--范例:定义一个程序类

1 package 反射.反射与操作类;
2 
3 /**
4  * @author : S K Y
5  * @version :0.0.1
6  */
7 public interface IMessageStrvice {  //消息服务
8     void send();
9 }
1 package 反射.反射与操作类;
2 
3 /**
4  * @author : S K Y
5  * @version :0.0.1
6  */
7 public interface IChannelService {
8     boolean connect();
9 }
1 package 反射.反射与操作类;
2 
3 /**
4  * @author : S K Y
5  * @version :0.0.1
6  */
7 public class AbstractBase {
8 }
 1 package 反射.反射与操作类;
 2 
 3 /**
 4  * @author : S K Y
 5  * @version :0.0.1
 6  */
 7 public class Person extends AbstractBase implements IMessageStrvice, IChannelService {
 8     @Override
 9     public boolean connect() {
10         return true;
11     }
12 
13     @Override
14     public void send() {
15         if (this.connect()) {
16             System.out.println("消息发送");
17         }
18     }
19 }

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

 1 public class PersonDemo {
 2     public static void main(String[] args) {
 3         Class<Person> personClass = Person.class;       //获取指定类的class对象
 4         Package aPackage = personClass.getPackage();    //获取指定类的包定义
 5         System.out.println("获取包名称: " + aPackage.getName());
 6         Class<? super Person> superclass = personClass.getSuperclass();     //获取父类
 7         System.out.println("Person获取父类: " + superclass.getName()) ;
 8         System.out.println("superclass获取父类: " + superclass.getSuperclass().getName());
 9         Class<?>[] interfaces = personClass.getInterfaces();        //获取父接口
10         for (int i = 0; i < interfaces.length; i++) {
11             System.out.println("获取父接口" + (i + 1) + ": " + interfaces[i].getName());
12         }
13     }
14 }

--运行结果

获取包名称: 反射.反射与操作类
Person获取父类: 反射.反射与操作类.AbstractBase
superclass获取父类: java.lang.Object
获取父接口1: 反射.反射与操作类.IMessageStrvice
获取父接口2: 反射.反射与操作类.IChannelService

Process finished with exit code 0

-获取一个类的Class对象之后就以为这这个对象可以获取类之中的一切继承结构信息,因此我们可以知道Class可以描述普通类,抽象类,接口

二.反射调用构造方法

  在一个类之中除了有继承的关系之外,最为重要的操作就是类中的结构处理了,类中的中首先需要关注的就是构造方法的使用问题,实际上在之前通过反射实例化对象的使用就已经接触到了构造方法的问题了,及newInstance()方法,所有类的构造方法的获取都可以直接通过Class类来完成,该类中定义有如下的几个方法:
  获取指定构造方法(根据参数类型): Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)  
  获取所有构造方法: Constructor<?>[] getDeclaredConstructors()  
  获取所有构造方法: Constructor<?>[] getConstructors()  
  获取指定构造方法: Constructor<T> getConstructor(Class<?>... parameterTypes)  

 1 package 反射.反射与操作类;
 2 
 3 /**
 4  * @author : S K Y
 5  * @version :0.0.1
 6  */
 7 public class AbstractBase {
 8     public AbstractBase(){}
 9     public AbstractBase(String name){}
10 }

--修改Person类的定义

 1 package 反射.反射与操作类;
 2 
 3 /**
 4  * @author : S K Y
 5  * @version :0.0.1
 6  */
 7 public class Person extends AbstractBase implements IMessageStrvice, IChannelService {
 8     public Person() {
 9     }
10 
11     public Person(String name, int age) {
12     }
13 
14     @Override
15     public boolean connect() {
16         return true;
17     }
18 
19     @Override
20     public void send() {
21         if (this.connect()) {
22             System.out.println("消息发送");
23         }
24     }
25 }

 --范例:获取全部构造方法

 1 class ConstructorDemo{
 2     public static void main(String[] args) {
 3         Class<Person> personClass = Person.class;       //获取指定类的class对象
 4         Constructor<?>[] constructors = personClass.getDeclaredConstructors();      //获取全部构造
 5         for (Constructor<?> constructor : constructors) {
 6             System.out.println(constructor);
 7         }
 8         System.out.println("========================");
 9         constructors = personClass.getConstructors();
10         for (Constructor<?> constructor : constructors) {
11             System.out.println(constructor);
12         }
13     }
14 }

--运行结果

public 反射.反射与操作类.Person()
public 反射.反射与操作类.Person(java.lang.String,int)
========================
public 反射.反射与操作类.Person()
public 反射.反射与操作类.Person(java.lang.String,int)

Process finished with exit code 0

--此时获取的是类之中的全部构造方法,但是也可以获取一个指定参数的构造

 1 package 反射.反射与操作类;
 2 
 3 /**
 4  * @author : S K Y
 5  * @version :0.0.1
 6  */
 7 public class Person extends AbstractBase implements IMessageStrvice, IChannelService {
 8     private String name;
 9     private int age;
10     public Person() {
11     }
12 
13     public Person(String name, int age) {
14         this.name = name;
15         this.age = age;
16     }
17 
18     @Override
19     public boolean connect() {
20         return true;
21     }
22 
23     @Override
24     public void send() {
25         if (this.connect()) {
26             System.out.println("消息发送");
27         }
28     }
29 
30     @Override
31     public String toString() {
32         return "Person{" +
33                 "name='" + name + '\'' +
34                 ", age=" + age +
35                 '}';
36     }
37 }

--需要调用类的有参构造方法,那么我们应该传入参数,观察java.lang.reflect.Constructor类的方法
  实例化方法: public T newInstance(Object... initargs)throws InstantiationException,IllegalAccessException,IllegalArgumentException,InvocationTargetException
--范例:调用指定构造实例化对象

1 class OneConstructorDemo {
2     public static void main(String[] args) throws Exception {
3         Class<Person> personClass = Person.class;       //获取指定类的class对象
4         Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
5         Person person = constructor.newInstance("张三", 56);
6         System.out.println(person);
7     }
8 }

--运行结果

Person{name='张三', age=56}

Process finished with exit code 0

--虽然程序代码本身允许开发者调用有参构造处理,但是如果从实际开发来讲,所有的类中最好提供有无参构造(特别是使用反射的类中),这样的实例化能够达到很高的统一性
--我们观察Constructor类的结构发现在JDK1.8之后提供了一个Executable类作为Constructor类的父类对象

三.反射调用普通方法
  在进行反射处理的时候,也可以通过反射获取类中的全部方法,但是需要提醒的是,如果要想通过反射调用这些方法,必须有一个前提条件,类之中要提供有实例化对象
--在Class类中提供有如下的操作可以获取方法对象:
  获取全部方法: public Method[] getMethods()throws SecurityException
  获取指定方法: Method getMethod(String name, Class<?>... parameterTypes)  
  获取本类全部方法:Method[] getDeclaredMethods()  
  获取本类指定方法: Method getDeclaredMethod(String name, Class<?>... parameterTypes)  
--范例:获取全部方法

 1 class MethodDemo{
 2     public static void main(String[] args) {
 3         Class<Person> personClass = Person.class;       //获取指定类的class对象
 4         {   //获取全部方法
 5             Method[] methods = personClass.getMethods();
 6             for (Method method : methods) {
 7                 System.out.println(method);
 8             }
 9         }
10     }
11 }

--运行结果

public java.lang.String 反射.反射与操作类.Person.toString()
public boolean 反射.反射与操作类.Person.connect()
public void 反射.反射与操作类.Person.send()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

Process finished with exit code 0

--可以发现将父类Object中的方法也获取到了,如果只是想获取本地的方法

 1 class MethodDemo{
 2     public static void main(String[] args) {
 3         Class<Person> personClass = Person.class;       //获取指定类的class对象
 4         {   //获取全部方法
 5             Method[] methods = personClass.getMethods();
 6             for (Method method : methods) {
 7                 System.out.println(method);
 8             }
 9         }
10         System.out.println("=========================");
11         {   //获取全部方法
12             Method[] methods = personClass.getDeclaredMethods();
13             for (Method method : methods) {
14                 System.out.println(method);
15             }
16         }
17     }
18 }

--运行结果

public java.lang.String 反射.反射与操作类.Person.toString()
public boolean 反射.反射与操作类.Person.connect()
public void 反射.反射与操作类.Person.send()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
=========================
public java.lang.String 反射.反射与操作类.Person.toString()
public boolean 反射.反射与操作类.Person.connect()
public void 反射.反射与操作类.Person.send()

Process finished with exit code 0

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

 1 class MyMethodDemo {
 2     public static void main(String[] args) {
 3         Class<Person> personClass = Person.class;       //获取指定类的class对象
 4         Method[] methods = personClass.getMethods();
 5         for (Method method : methods) {
 6             String modifier = Modifier.toString(method.getModifiers());//获取访问修饰符
 7             String returnType = method.getReturnType().getName();     //获取方法的返回类型
 8             Class<?>[] parameterTypes = method.getParameterTypes();     //获取参数类型
 9             StringBuffer buffer = new StringBuffer();
10             buffer.append("(");
11             for (int i = 0; i < parameterTypes.length; i++) {
12                 buffer.append(parameterTypes[i].getName());
13                 buffer.append(" arg").append(i);
14                 if (i < parameterTypes.length - 1) {
15                     buffer.append(",");
16                 }
17             }
18             buffer.append(")");
19             String parameterType = buffer.toString();
20             Class<?>[] exceptionTypes = method.getExceptionTypes();     //获取异常类型
21             buffer = new StringBuffer();
22             if (exceptionTypes.length > 0) {
23                 buffer.append(" throws ");
24                 for (int i = 0; i < exceptionTypes.length; i++) {
25                     buffer.append(exceptionTypes[i].getName());
26                     buffer.append(" e").append(i);
27                     if (i < exceptionTypes.length - 1) {
28                         buffer.append(",");
29                     }
30                 }
31             }
32             String exceptionType = buffer.toString();
33             System.out.println(modifier + " " + returnType + " " + method.getName() + parameterType + "" + exceptionType);
34         }
35     }
36 }

--运行结果

public java.lang.String toString()
public boolean connect()
public void send()
public final void wait() throws java.lang.InterruptedException e0
public final void wait(long arg0,int arg1) throws java.lang.InterruptedException e0
public final native void wait(long arg0) throws java.lang.InterruptedException e0
public boolean equals(java.lang.Object arg0)
public native int hashCode()
public final native java.lang.Class getClass()
public final native void notify()
public final native void notifyAll()

Process finished with exit code 0

--在Method类中有一个重要的方法:  public Object invoke(Object obj,Object... args)throws IllegalAccessException,IllegalArgumentException,InvocationTargetException,用于反射调用方法,在Person类中有name属性追加有setter与getter方法:

 1 package 反射.反射与操作类;
 2 
 3 /**
 4  * @author : S K Y
 5  * @version :0.0.1
 6  */
 7 public class Person extends AbstractBase implements IMessageStrvice, IChannelService {
 8     private String name;
 9     private int age;
10     public Person() {
11     }
12 
13     public Person(String name, int age) {
14         this.name = name;
15         this.age = age;
16     }
17 
18     @Override
19     public boolean connect() {
20         return true;
21     }
22 
23     @Override
24     public void send() {
25         if (this.connect()) {
26             System.out.println("消息发送");
27         }
28     }
29 
30     public String getName() {
31         return name;
32     }
33 
34     public void setName(String name) {
35         this.name = name;
36     }
37 
38     @Override
39     public String toString() {
40         return "Person{" +
41                 "name='" + name + '\'' +
42                 ", age=" + age +
43                 '}';
44     }
45 }

--通过反射机制来实现person类之中的setter与getter方法的调用处理,并且不进行person类的包导入操作

 1 class GetterAndSetterDemo {
 2     public static void main(String[] args) throws Exception {
 3         //在不导入开发包的情况下实现属性的配置
 4         Class<?> aClass = Class.forName("反射.反射与操作类.Person");//获取指定类的class对象
 5         String attribute = "name";  //要操作的类属性
 6         String value = "张三";    //要设置的属性内容
 7         //1.任何情况写想要调用类中的属性或者调用类中的方法都必须保证存在有实例化对象
 8         Object obj = aClass.newInstance();      //调用无参构造实例化
 9         //2.如果想要进行方法的调用,一定要获取方法的名称
10         String setMethodName = "setName";
11         Method method = aClass.getDeclaredMethod(setMethodName, String.class);
12         method.invoke(obj, value);   //等价于 person.setName("小强");
13         System.out.println("toString: " + obj);
14         String getMethodName = "getName";
15         Method getName = aClass.getDeclaredMethod(getMethodName);
16         System.out.println("getName: " + getName.invoke(obj));  //等价于 person.getName();
17     }
18 }

--运行结果

toString: Person{name='张三', age=0}
getName: 张三

Process finished with exit code 0

--利用此类操作整体的形式不会有任何的明确的类对象产生,这样的处理避免了某一个类的耦合问题.

四.反射调用成员
  类结构之中的最后一个核心的组成就是成员(Field),大部分情况下都会将其称为成员属性,对成员属性的获取也是通过Class类来完成的,在这个类中提供有如下两组操作方法:
  获取本类全部成员: Field[] getDeclaredFields()  
  获取本类指定成员: Field getDeclaredField(String name)  
  获取父类全部成员: Field[] getFields()  
  获取父类指定成员: Field getField(String name) 
--修改AbstractBase类

 1 package 反射.反射与操作类;
 2 
 3 /**
 4  * @author : S K Y
 5  * @version :0.0.1
 6  */
 7 public class AbstractBase {
 8     public static final String BASE = "123456789";
 9     private String info = "hello world";
10     protected static final String ABC = "888";
11     public AbstractBase() {
12     }
13 
14     public AbstractBase(String name) {
15     }
16 }
 1 package 反射.反射与操作类;
 2 
 3 /**
 4  * @author : S K Y
 5  * @version :0.0.1
 6  */
 7 public class Person extends AbstractBase implements IMessageStrvice, IChannelService {
 8     public static final String NAME = "小张三";
 9     private String name;
10     private int age;
11     public Person() {
12     }
13 
14     public Person(String name, int age) {
15         this.name = name;
16         this.age = age;
17     }
18 
19     @Override
20     public boolean connect() {
21         return true;
22     }
23 
24     @Override
25     public void send() {
26         if (this.connect()) {
27             System.out.println("消息发送");
28         }
29     }
30 
31     public String getName() {
32         return name;
33     }
34 
35     public void setName(String name) {
36         this.name = name;
37     }
38 
39     @Override
40     public String toString() {
41         return "Person{" +
42                 "name='" + name + '\'' +
43                 ", age=" + age +
44                 '}';
45     }
46 }

--范例:获取类中的成员

 1 class GetFieldDemo{
 2     public static void main(String[] args) throws Exception {
 3         Class<?> aClass = Class.forName("反射.反射与操作类.Person");//获取指定类的class对象
 4         Field[] fields = aClass.getFields();
 5         for (Field field : fields) {
 6             System.out.println(field);
 7         }
 8         System.out.println("======================");
 9         fields = aClass.getDeclaredFields();
10         for (Field field : fields) {
11             System.out.println(field);
12         }
13     }
14 }

--运行结果

public static final java.lang.String 反射.反射与操作类.Person.NAME
public static final java.lang.String 反射.反射与操作类.AbstractBase.BASE
======================
public static final java.lang.String 反射.反射与操作类.Person.NAME
private java.lang.String 反射.反射与操作类.Person.name
private int 反射.反射与操作类.Person.age

Process finished with exit code 0

--从上述结果我们可以得知getFields()可以获得父类及子类的公有成员,而getDeclaredFields()可以获得子类的所有成员,但是在Field类中最为重要的操作形式并不是获取全部的成员,而是如下的三个方法:
  设置属性内容: public void set(Object obj,Object value)throws IllegalArgumentException,IllegalAccessException
  获取属性内容: public Object get(Object obj)throws IllegalArgumentException,IllegalAccessException
  解除封装: public static void setAccessible(AccessibleObject[] array,boolean flag)throws SecurityException
所有的成员都是在对象实例化之后进行空间分配的,所以此时一定要先有实例化对象之后,才可以进行成员的操作.
--范例:直接调用person类中的name私有成员:

 1 class UseNameDemo{
 2     public static void main(String[] args) throws Exception {
 3         Class<?> aClass = Class.forName("反射.反射与操作类.Person");//获取指定类的class对象
 4         Object obj = aClass.getConstructor().newInstance();
 5         Field name = aClass.getDeclaredField("name");
 6         name.setAccessible(true);       //解除封装
 7         name.set(obj,"张三");
 8         System.out.println(name.get(obj));
 9     }
10 }

--运行结果

张三

Process finished with exit code 0

--通过一系列的分析可以发现,类之中的构造方法,成员属性都可以通过反射实现调用,但是对于成员的反射调用很少这样直接处理,大部分操作都应该通过getter和setter处理,所以对于以上的代码只是能够说明反射具有这样的能力,而对于Field类在实际的开发之中只有一个方法最为常用:
  获取成员类型: public Class<?> getType()
--范例:获取Person类中的name成员的类型

1 class UseNameDemo{
2     public static void main(String[] args) throws Exception {
3         Class<?> aClass = Class.forName("反射.反射与操作类.Person");//获取指定类的class对象
4         Object obj = aClass.getConstructor().newInstance();
5         Field name = aClass.getDeclaredField("name");
6         System.out.println(name.getType().getName());   //获取完整类名称
7         System.out.println(name.getType().getSimpleName());
8     }
9 }

--运行结果

java.lang.String
String

Process finished with exit code 0

--在开发中进行反射处理的时候,往往会利用Field与Method类实现类中的setter方法的调用.
五.Unsafe工具类
  反射是java的第一大特点,一旦打开了反射的大门,就可以有更加丰富的类的设计形式,除了JVM本身支持的反射处理之外,在java里面也提供有一个Unsafe(不安全操作),这个类的主要特点是可以利用反射来获取对象,并且直接使用底层的c++来代替JVM执行,即可以绕过JVM的相关的对象的管理机制,如果项目中使用了Unsafe类,那么项目之中将无法进行JVM的内存管理机制以及垃圾回收处理.
--如果要想使用Unsafe类首先就需要确认一下这个类之中定义的构造方法与常量问题
  构造方法: private Unsafe(){}
  私有常量:  private static final Unsafe theUnsafe;
但是需要注意的是在这个Unsafe的类中并没有提供static的方法,即不能通过类似与传统的单例设计模式中提供的样式进行操作,如果要想获得这个类的对象,就必须利用反射机制来完成:

1 class GetUnsafeDemo{
2     public static void main(String[] args) throws Exception{
3         Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
4         theUnsafe.setAccessible(true);  //解除封装处理
5         Unsafe unsafe = (Unsafe) theUnsafe.get(null);     //static属性不需要传递实例化对象
6     }
7 }

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

 1 class Singleton {
 2     private static final Singleton INSTANCE = new Singleton();
 3 
 4     private Singleton() {
 5     }
 6 
 7     public static Singleton getInstance() {
 8         return INSTANCE;
 9     }
10 
11     public void print() {
12         System.out.println("实例化对象输出内容");
13     }
14 }

--利用Unsafe类绕过JVM的管理机制使用print()方法:

 1 class GetUnsafeDemo {
 2     public static void main(String[] args) throws Exception {
 3         Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
 4         theUnsafe.setAccessible(true);  //解除封装处理
 5         Unsafe unsafe = (Unsafe) theUnsafe.get(null);     //static属性不需要传递实例化对象
 6         Singleton singleton = (Singleton) unsafe.allocateInstance(Singleton.class);
 7         singleton.print();
 8     }
 9 }
10 
11 class Singleton {
12     private static final Singleton INSTANCE = new Singleton();
13     private Singleton(){
14         System.out.println("构造方法");
15     }
16     public void print() {
17         System.out.println("实例化对象输出内容");
18     }
19 }

--运行结果

构造方法
实例化对象输出内容

Process finished with exit code 0

--Uusafe只能说为开发提供了一些更加方便的处理机制,但是这种操作由于不受JVM的管理所以如果不是必须的情况下不建议使用

posted @ 2019-08-28 22:50  灰色天空_graySky  阅读(316)  评论(0编辑  收藏  举报