反射与类操作

本文详细介绍了Java反射机制,包括获取类结构信息(包名、父类、接口)、反射调用构造方法、普通方法及成员属性。通过反射,可以动态创建对象、调用方法和修改属性,甚至绕过常规的实例化过程。文中还提到了Unsafe工具类,它可以避开JVM的某些管理机制,但使用时需谨慎。
摘要由CSDN通过智能技术生成

反射与类操作

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

1、反射获取类结构信息

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

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

  • 获取包名称:public Package getPackage()

  • 获取继承父类:public Class<? super T> getSuperclass()

获取包名称

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Class<Person> personClass = Person.class;
      Package aPackage = personClass.getPackage();
      System.out.println(aPackage.getName());
   }
}

获取父类信息

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Class<Person> personClass = Person.class;
      Class<? super Person> superclass = personClass.getSuperclass();
      System.out.println(superclass.getName());
   }
}

获取父接口

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Class<Person> personClass = Person.class;
      Class<?>[] interfaces = personClass.getInterfaces();
      for (Class<?> temp : interfaces) {
         System.out.println(temp.getName());
      }
   }
}

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

2、反射调用构造方法

在一个类之中除了有继承的关系之外最为重要的操作就是类中的结构处理,而类中的结构里面首先需要观察的就是构造方法的使用问题

  • 实例化方法替代: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类之中的有参构造方法进行Person类对象的实例化处理,这个时候就必须指明要调用的构造,通过Constructor类之中提供的实例化方法操作:

public T newInstance(Object… initargs) throws InstantiationException,IllegalAccessException, IllegalArgumentException, InvocationTargetException

调用指定构造实例化对象

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Class<Person> personClass = Person.class;
      Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
      Object obj = constructor.newInstance("linlin", 18);
      System.out.println(obj);
   }
}
public class Person {
   
   private String name;
   private int age;
   
   public Person() {
      System.out.println("这里是Person的构造方法");
   }
   public Person(String name , int age) {
      System.out.println("这里是Person的构造方法");
   }
   
   @Override
   public String toString() {
      return "这里是Person的toString方法";
   }
}

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

3、反射调用普通方法

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

在 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

获取全部方法

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Class<Person> personClass = Person.class;
      {   //获取全部方法(包括父类中的方法)
         Method[] methods = personClass.getMethods();
         for (Method method : methods) {
            System.out.println(method);
         }
      }
      System.out.println("*******************************");
      {   //获取本类方法
         Method[] methods = personClass.getDeclaredMethods();
         for (Method method : methods) {
            System.out.println(method);
         }
      }
   }

但是需要注意的是,这个时候的方法信息的获取是依靠Method类提供的toString方法完成的.

Method类中一个极其重要的方法

public Object invoke(Object obj,
                     Object... args)
              throws IllegalAccessException,
                     IllegalArgumentException,
                     InvocationTargetException

完善Person类中的setter和getter方法

public class Person {
   
   private String name;
   private int age;
   
   public Person() {
      System.out.println("这里是Person的构造方法");
   }
   public Person(String name , int age) {
      System.out.println("这里是Person的构造方法");
   }
   
   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;
   }
   
   @Override
   public String toString() {
      return "这里是Person的toString方法";
   }
}

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

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

public class JavaDemo {
	public static void main(String[] args) throws Exception {
		Class<?> aClass = Class.forName("com.xialuote.domain.Person");
		String value = "linlin";
		//1.反射实例化
		Object obj = aClass.getDeclaredConstructor().newInstance(); //无参构造实例化
		//2.获取方法的名称
		String setMethodName = "setName";   //方法名称
		Method setMethod = aClass.getDeclaredMethod(setMethodName, String.class); //获取指定方法
		setMethod.invoke(obj , value);  //等价于Person对象.setName(value)
		String getMethodName = "getName";
		Method getMethod = aClass.getDeclaredMethod(getMethodName);    //getter没有参数
		System.out.println(getMethod.invoke(obj));  //等价于Person对象.getName()
	}
}
//这里是Person的构造方法
//linlin

利用此类操作整体的形式上不会有任何的明确的类对象产生,一切都是依靠反射机制处理的,这样的处理避免了与某一个类的耦合问题

4、反射调用成员

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

  • 获取本类全部成员:public Field[] getDeclaredFields() throws SecurityException
  • 获取本类指定成员:public Field getDeclaredField(String name) throws NoSuchFieldException,SecurityException
  • 获取父类全部成员:public Field[] getFields() throws SecurityException
  • 获取父类指定成员:public Field getField(String name) throws NoSuchFieldException, SecurityException

获取类的成员信息

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Class<?> aClass = Class.forName("com.xialuote.domain.Student");
      {
         Field [] fields  = aClass.getFields();
         for (Field field : fields) {
            System.out.println(field);
         }
      }
      System.out.println("---------------------------------");
      {
         Field [] fields = aClass.getDeclaredFields();
         for (Field field : fields) {
            System.out.println(field);
         }
      }
   }
}
//public java.lang.String com.xialuote.domain.Person.school
//---------------------------------
//private double com.xialuote.domain.Student.grade

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

  • 获取属性内容:public Object get(Object obj) throws IllegalArgumentException,IllegalAccessException
  • 设置属性内容:public void set(Object obj,Object value) throws IllegalArgumentException,IllegalAccessException
  • 解除封装:public void setAccessible(boolean flag)

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

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

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Class<?> aClass = Class.forName("com.xialuote.domain.Person");
      Object obj = aClass.getConstructor().newInstance();//实例化对象
      Field nameField = aClass.getDeclaredField("name");  //获取成员对象
      nameField.setAccessible(true);  //解除封装
      nameField.set(obj , "linlin");
      System.out.println(nameField.get(obj));
   }
}
//这里是Person的构造方法
//linlin

类之中的构造、方法、成员属性都可以通过反射实现调用,但是对于成员的反射调用一般不会这样处理,一般情况下都是应该通过setter或getter处理

而对于Field类在实际开发之中只有一个方法最为常用:

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

获取Person类中的name成员类型

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Class<?> aClass = Class.forName("com.xialuote.domain.Person");
      Object obj = aClass.getConstructor().newInstance();//实例化对象
      Field nameField = aClass.getDeclaredField("name");  //获取成员对象
      Class<?> type = nameField.getType();
      System.out.println(type.getName());
      System.out.println(type.getSimpleName());
   }
}
//这里是Person的构造方法
//java.lang.String
//String

在开发中进行反射处理的时候,往往会利用Field 与 Method类实现类中setter方法的调用

4、Unsafe工具类

java中提供有一个Unsafe(不安全操作), 这个类的主要特点是可以利用反射来获取对象,并且直接使用底层的C++来来代替J八VM执行,即:可以绕过JVM的相关的对象的管理机制,如果你一旦使用了Unsafe类,那么项目之中就无法使用JVM的内存管理机制以及垃圾回收处理

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

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

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); //static属性不需要传递实例化对象
   }
}

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

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

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); //static属性不需要传递实例化对象
      
      //利用Unsafe类绕过了JVM的管理机制,可以在没有实例化对象的情况下获取Singleton类实例化对象
      Singleton instance = (Singleton) unsafeObject.allocateInstance(Singleton.class);
      instance.print();
   }
}

class Singleton {
	//private static final Singleton INSTANCE = new Singleton();
	private Singleton() {
		System.out.println("Singleton构造方法");
	}
	
	//public static Singleton getInstance() {
	//    return INSTANCE;
	//}
	public void print(){
		System.out.println("输出消息");
	}
}

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

在面试的时候,如果遇到单例设计模式的情况下,也可以追加一个Unsafe以加深概念

下一篇:反射与简单Java类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MyRedScarf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值