JAVA基础编程——反射机制

本文详细介绍了Java反射中的核心概念,包括如何通过getClass获取类信息,Class类对象的实例化,使用forName方法查找类,以及如何利用反射实例化对象、调用构造方法和普通方法。特别强调了反射在工厂模式中的应用和安全性注意事项。
摘要由CSDN通过智能技术生成

反射是JAVA中重要的特性,大部分的开发框架以及应用技术中都是基于反射技术的应用。

什么是反射

在构建类的实例化对象过程中,需要先确定使用的类,然后使用new进行实例化对象构建。但是如果要通过对象取得此对象所在类的信息,就需要通过Object类中的getClass方法实现。

public final Class<?> getClass()

这里的?就是不明确指明类是哪个。

public class Demo {
	public static void main(String[] args) throws Exception {
		String str = "Hello world";
		
		System.out.println(str.getClass());
	}
}

执行结果为:

class java.lang.String

该方法的返回值为Class<?>,然后又进行了打印。

Class类对象实例化

当时用getClass方法时,返回的类型为java.lang.Class。既然该方法的返回值为Class<?>,那么就可以用该返回值实例化对象,但是具体要怎么构建实例化对象。

调用Object类中的getClass方法

使用此类操作必须要有实例化对象。

public class Demo {
	public static void main(String[] args) throws Exception {
		String str = "Hello world";
		
		Class<?> cls = str.getClass();
				
		System.out.println(cls.getName());
	}
}

执行结果为:

java.lang.String

使用“类.class”取得

此时可以不需要通过指定类的实例化对象取得。

public class Demo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = java.lang.String.class;
				
		System.out.println(cls.getName());
	}
}

执行结果为:

java.lang.String

调用Class类提供的方法

public static Class<?> forName(Module module, String name)

示例代码为:

public class Demo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = Class.forName("java.lang.String");
				
		System.out.println(cls.getName());
	}
}

执行结果为:

java.lang.String

使用Class类中的forName方法前提是此类确实存在,如果不存在则会抛出“ClassNotFoundException”异常。

反射实例化对象

在Class类中存在如下几种常用方法:

static Class<?> forName(Module module, String name) // Returns the Class with the given binary name in the given module.
static Class<?> forName(String className) // Returns the Class object associated with the class or interface with the given string name.
static Class<?> forName(String name, boolean initialize, ClassLoader loader) // Returns the Class object associated with the class or interface with the given string name, using the given class loader.
Class<?>[] getInterfaces() // Returns the interfaces directly implemented by the class or interface represented by this Class object.
String getName() // Returns the name of the entity (class, interface, array class, primitive type, or void) represented by this Class object.
Package getPackage() // Gets the package of this class.
String getSimpleName() // Returns the simple name of the underlying class as given in the source code.
Class<? super T> getSuperclass() // Returns the Class representing the direct superclass of the entity (class, interface, primitive type or void) represented by this Class.
boolean isArray() // Determines if this Class object represents an array class.
boolean isEnum() // Returns true if and only if this class was declared as an enum in the source code.
boolean isInterface() // Determines if this Class object represents an interface type.
T newInstance() // Deprecated. This method propagates any exception thrown by the nullary constructor, including a checked exception.

通过上述的几种方法可以看出,在反射操作中类或接口都是利用Class来进行包装的,同时利用Class类可以表示任意类、枚举、接口、数组等引用操作的类型。

而知道了实例化对象的类信息后,是要构建实例化对象的,此时使用newInstance方法可以构建实例化对象,而不用使用new关键字。

class A {
    public A() {
	    System.out.println("Hello world");
	}
}

public class Demo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = Class.forName("A");
		
		Object obj = cls.newInstance();
	}
}

执行结果为:

Hello world

上面的代码中,没有使用new进行实例化对象构建,反而使用Class的newInstance方法进行实例化对象,此时默认调用类A的无参构造方法。同时从上面代码可以看到,newInstance方法返回的对象类型是Object,若有需要也可进行向下转型。

而至于为什么使用反射机制,因为使用这种反射机制重新实例化对象好像有点麻烦,这是因为使用new需要明确指明构造方法,尤其是构造方法重载。而使用反射机制则只需要将字符串形式的类名传入即可,然后对于反射得到的实例化对象进行向下转型即可。比如在工厂设计模式中,就可以使用反射机制将工厂类中的代码利用反射机制统一,而不用在每次新增接口子类时都需要更新工厂类的判断。

反射调用构造

之前利用Class的newInstance方法进行实例化对象,但是该操作只能使用无参构造。而如果想要使用有参构造方法时,就需要通过java.lang.reflect.Constructor类实现对象的反射实例化操作。

public Constructor<?>[] getConstructors() throws SecurityException
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException,SecurityException

 返回的类型为:

public final class Constructor<T> extends Executable

利用这两种方法可以获得java.lang.reflect.Constructor,其常用方法为:

Class<?>[] getExceptionTypes() // Returns an array of Class objects that represent the types of exceptions declared to be thrown by the underlying executable represented by this object.
int getModifiers() // Returns the Java language modifiers for the executable represented by this object.
String getName() // Returns the name of this constructor, as a string.
int getParameterCount() // Returns the number of formal parameters (whether explicitly declared or implicitly declared or neither) for the executable represented by this object.
Class<?>[] getParameterTypes() // Returns an array of Class objects that represent the formal parameter types, in declaration order, of the executable represented by this object.
T newInstance(Object... initargs) // Uses the constructor represented by this Constructor object to create and initialize a new instance of the constructor's declaring class, with the specified initialization parameters.

这里newInstance方法定义了可变参数的形式,这样的话就可以先找到执行参数类型的构造方法,再利用该方法进行实例化对象的参数来调用指定的构造方法。

而从上面的方法来看,getModifiers方法和getParameterCount方法返回的都是int值,这是因为JAVA用数字的形式来表示修饰符,同时在java.lang.reflect.Modifer中也存在将数字转换为指定修饰符的toString方法。

import java.lang.reflect.Constructor;

class A {
	private String name;
	private int num;
	
    public A() {
	    System.out.println("Hello world");
	}
	
	public A(String name) {
	    this.name = name;
	}
	
	public A(String name,int num) {
	    this.name = name;
		this.num = num;
	}
	
	@Override
	public String toString() {
	    return "Name is " + name + ", num is " + num;
	}
}

public class Demo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = Class.forName("A");
		
		Constructor<?> con1 = cls.getConstructor(String.class, int.class);
		Constructor<?> con2 = cls.getConstructor(String.class);
		
		Object obj1 = con1.newInstance("Tom",10);
		System.out.println(obj1);
		
		Object obj2 = con2.newInstance("Tom");
		System.out.println(obj2);
	}
}

执行结果为:

Name is Tom, num is 10
Name is Tom, num is 0

上面的代码中,通过Class类获取了类本身,之后通过getConstructor分别获取两种构造方法,然后使用newInstance调用该构造方法。

反射调用方法

既然可以反射调用构造方法,那么反射调用方法也是可以的,其原理都是类似的。可以利用Class类取得方法:

Method getMethod(String name, Class<?>... parameterTypes) // Returns a Method object that reflects the specified public member method of the class or interface represented by this Class object.
Method[] getMethods() // Returns an array containing Method objects reflecting all the public methods of the class or interface represented by this Class object, including those declared by the class or interface and those inherited from superclasses and superinterfaces.

 返回的类型为:

public final class Method extends Executable

也就是说反射调用方法可以通过java.lang.reflect.Method类表示,Method类的常用方法有:

int getModifiers() // Returns the Java language modifiers for the executable represented by this object.
Class<?> getReturnType() // Returns a Class object that represents the formal return type of the method represented by this Method object.
int getParameterCount() // Returns the number of formal parameters (whether explicitly declared or implicitly declared or neither) for the executable represented by this object.
Class<?>[] getParameterTypes() // Returns an array of Class objects that represent the formal parameter types, in declaration order, of the executable represented by this object.
Object invoke(Object obj, Object... args) // Invokes the underlying method represented by this Method object, on the specified object with the specified parameters.
Class<?>[] getExceptionTypes() // Returns an array of Class objects that represent the types of exceptions declared to be thrown by the underlying executable represented by this object.

通过Method类的常用方法来看,方法和方法的基本格式都是一致的,比如返回值、参数、抛出异常。和之前通过newInstance方法调用构造方法一样,调用普通方法则需要通过invoke方法实现。

而在调用invoke方法中的第一个参数也是类的实例化对象,而该实例化对象也是Object。

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

class A {
	private String name;
	private int num;
	
    public A() {
	    System.out.println("Hello world");
	}
	
	public A(String name) {
	    this.name = name;
	}
	
	public A(String name,int num) {
	    this.name = name;
		this.num = num;
	}
	
	@Override
	public String toString() {
	    return "Name is " + name + ", num is " + num;
	}
	
	public void func(String tmp) {
	    System.out.println("Func " + tmp);
	}
}

public class Demo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = Class.forName("A");
		
		Constructor<?> con = cls.getConstructor(String.class, int.class);
		Object obj = con.newInstance("Tom",10);

		Method m = cls.getMethod("func",String.class);
		
		m.invoke(obj,"abc");
	}
}

执行结果为:

Func abc

从上面的代码可以看出,反射调用方法的逻辑和调用构造方法的逻辑都差不多。

反射调用成员

既然可以利用反射调用构造方法,也可以反射调用普通方法,那么反射调用成员应该也是可以的,逻辑也是相似的。Class类对此也存在相关的方法:

Field getDeclaredField(String name) // Returns a Field object that reflects the specified declared field of the class or interface represented by this Class object.
Field[] getDeclaredFields() // Returns an array of Field objects reflecting all the fields declared by the class or interface represented by this Class object.
Field getField(String name) // Returns a Field object that reflects the specified public member field of the class or interface represented by this Class object.
Field[] getFields() // Returns an array containing Field objects reflecting all the accessible public fields of the class or interface represented by this Class object.

返回类型为:

public final class Field extends AccessibleObject implements Member

而Field的相关方法为:

Class<?> getType() // Returns a Class object that identifies the declared type for the field represented by this Field object.
Object get(Object obj) // Returns the value of the field represented by this Field, on the specified object.
void set(Object obj, Object value) // Sets the field represented by this Field object on the specified object argument to the specified new value.
void setAccessible(boolean flag) // Set the accessible flag for this reflected object to the indicated boolean value.

这里比较特殊的方法是setAccessible,因为类中的属性大部分都是私有的,因此如果想要在外部进行访问,就应该首先取消该属性的封装。

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

class A {
	private String name;
	private int num;
	
    public A() {
	    System.out.println("Hello world");
	}
	
	public A(String name) {
	    this.name = name;
	}
	
	public A(String name,int num) {
	    this.name = name;
		this.num = num;
	}
	
	@Override
	public String toString() {
	    return "Name is " + name + ", num is " + num;
	}
	
	public void func(String tmp) {
	    System.out.println("Func " + tmp);
	}
}

public class Demo {
	public static void main(String[] args) throws Exception {
		Class<?> cls = Class.forName("A");
		
		Constructor<?> con = cls.getConstructor(String.class, int.class);
		Object obj = con.newInstance("Tom",10);

		Method m = cls.getMethod("func",String.class);
		m.invoke(obj,"abc");
		
		Field f = cls.getDeclaredField("name");
		f.setAccessible(true);
		f.set(obj,"Jerry");
		
		System.out.println(obj);
	}
}

执行结果为:

Func abc
Name is Jerry, num is 10

上边的代码示例并不严谨,因为属性封装正是为了不被外界访问,这里直接进行修改赋值的操作可能会带来其它的风险。因此最好还是通过setter、getter方法进行获取修改赋值操作。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值