4年前 (2020-10-23)  Java系列 |   抢沙发  296 
文章评分 0 次,平均分 0.0
[收起] 文章目录

Java反射提供了检查和修改应用程序运行时行为的能力。Java中的反射是核心Java的前沿课题之一。使用java反射,我们可以在运行时检查类、接口、枚举,获得它们的结构、方法和字段信息,即使类在编译时是不可访问的。我们还可以使用反射来实例化一个对象,调用它的方法,更改字段值。

Java反射的用途系列一

Java中的反射

Java中的反射是一个非常强大的概念,它在普通编程中用处不大,但它是大多数Java、J2EE框架的主干。使用java反射的一些框架包括:

  • JUnit–使用反射来解析@Test注释以获取测试方法,然后调用它。
  • Spring–依赖注入,请阅读Spring依赖注入
  • Tomcat web容器通过解析web.xml文件文件和请求URI。
  • 方法名的Eclipse自动完成
  • Struts
  • Hibernate

列表是无穷无尽的,它们都使用java反射,因为所有这些框架都没有用户定义的类、接口、它们的方法等的知识和访问权限。

由于以下缺点,我们不应该在已经可以访问类和接口的普通编程中使用反射。

  1. 性能差–由于java反射动态地解析类型,因此它涉及到诸如扫描类路径以查找要加载的类的处理,导致性能低下。
  2. 安全限制–反射需要运行时权限,这些权限对于在安全管理器下运行的系统可能不可用。这可能导致应用程序在运行时因安全管理器而失败。
  3. 安全问题——使用反射,我们可以访问不应该访问的部分代码,例如,我们可以访问类的私有字段并更改其值。这可能是一个严重的安全威胁,并导致应用程序异常运行。
  4. 高维护性——反射代码很难理解和调试,而且代码中的任何问题在编译时都找不到,因为类可能不可用,这使得它不太灵活,也很难维护。

类的Java反射

在java中,每个对象要么是一个基元类型 primitive type,要么是一个引用。所有的类、枚举、数组都是引用类型并继承自java.lang.Object. 基本类型是–booleanbyteshortintlongcharfloatdouble

java.lang.Class是所有反射操作的入口点。对于每种类型的对象,JVM实例化java.lang.Class它提供了检查对象的运行时属性和创建新对象、调用其方法和获取/设置对象字段的方法。

为了方便起见,我们在创建类和类的一些方法时,我们将在这一节中讨论一些重要的继承和方法。

package com.journaldev.reflection;

public interface BaseInterface {
	
	public int interfaceInt=0;
	
	void method1();
	
	int method2(String str);
}
package com.journaldev.reflection;

public class BaseClass {

	public int baseInt;
	
	private static void method3(){
		System.out.println("Method3");
	}
	
	public int method4(){
		System.out.println("Method4");
		return 0;
	}
	
	public static int method5(){
		System.out.println("Method5");
		return 0;
	}
	
	void method6(){
		System.out.println("Method6");
	}
	
	// inner public class
	public class BaseClassInnerClass{}
		
	//member public enum
	public enum BaseClassMemberEnum{}
}
package com.journaldev.reflection;

@Deprecated
public class ConcreteClass extends BaseClass implements BaseInterface {

	public int publicInt;
	private String privateString="private string";
	protected boolean protectedBoolean;
	Object defaultObject;
	
	public ConcreteClass(int i){
		this.publicInt=i;
	}

	@Override
	public void method1() {
		System.out.println("Method1 impl.");
	}

	@Override
	public int method2(String str) {
		System.out.println("Method2 impl.");
		return 0;
	}
	
	@Override
	public int method4(){
		System.out.println("Method4 overriden.");
		return 0;
	}
	
	public int method5(int i){
		System.out.println("Method4 overriden.");
		return 0;
	}
	
	// inner classes
	public class ConcreteClassPublicClass{}
	private class ConcreteClassPrivateClass{}
	protected class ConcreteClassProtectedClass{}
	class ConcreteClassDefaultClass{}
	
	//member enum
	enum ConcreteClassDefaultEnum{}
	public enum ConcreteClassPublicEnum{}
	
	//member interface
	public interface ConcreteClassPublicInterface{}

}

让我们看看一些重要的方法:

获取类对象

我们可以用三种方法得到一个对象的类——通过静态变量类,使用objectjava.lang.Class.forName(String fullyClassifiedClassName) 。对于基元类型和数组,我们可以使用静态变量类。包装类提供另一个静态变量类型来获取类。

// Get Class using reflection
Class<?> concreteClass = ConcreteClass.class;
concreteClass = new ConcreteClass(5).getClass();
try {
	// below method is used most of the times in frameworks like JUnit
	//Spring dependency injection, Tomcat web container
	//Eclipse auto completion of method names, hibernate, Struts2 etc.
	//because ConcreteClass is not available at compile time
	concreteClass = Class.forName("com.journaldev.reflection.ConcreteClass");
} catch (ClassNotFoundException e) {
	e.printStackTrace();
}
System.out.println(concreteClass.getCanonicalName()); // prints com.journaldev.reflection.ConcreteClass

//for primitive types, wrapper classes and arrays
Class<?> booleanClass = boolean.class;
System.out.println(booleanClass.getCanonicalName()); // prints boolean

Class<?> cDouble = Double.TYPE;
System.out.println(cDouble.getCanonicalName()); // prints double

Class<?> cDoubleArray = Class.forName("[D");
System.out.println(cDoubleArray.getCanonicalName()); //prints double[]

Class<?> twoDStringArray = String[][].class;
System.out.println(twoDStringArray.getCanonicalName()); // prints java.lang.String[][]

getCanonicalName()返回基础类的规范名称。注意到java.lang.Class使用泛型,它帮助框架确保检索到的类是框架基类的子类。查看Java泛型教程,了解泛型及其通配符。

获得超类 Super Class

类对象上的getSuperclass()方法返回类的超级类。如果该类表示对象类、接口、基元类型或void,则返回null。如果此对象表示数组类,则返回表示该对象类的类对象。

Class<?> superClass = Class.forName("com.journaldev.reflection.ConcreteClass").getSuperclass();
System.out.println(superClass); // prints "class com.journaldev.reflection.BaseClass"
System.out.println(Object.class.getSuperclass()); // prints "null"
System.out.println(String[][].class.getSuperclass());// prints "class java.lang.Object"

获取公共成员类

对象的类表示形式的getClasses()方法返回一个数组,该数组包含表示该类对象所表示的类的所有公共类、接口和枚举的类对象。这包括从超类继承的公共类和接口成员,以及由类声明的公共类和接口成员。如果该类对象没有公共成员类或接口,或者该类对象表示基元类型、数组类或void,则此方法返回长度为0的数组。

Class<?>[] classes = concreteClass.getClasses();
//[class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicClass, 
//class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicEnum, 
//interface com.journaldev.reflection.ConcreteClass$ConcreteClassPublicInterface,
//class com.journaldev.reflection.BaseClass$BaseClassInnerClass, 
//class com.journaldev.reflection.BaseClass$BaseClassMemberEnum]
System.out.println(Arrays.toString(classes));

获取声明类 Declared Classes

getDeclaredClasses()方法返回一个类对象数组,该数组反映声明为该类对象表示的类的成员的所有类和接口。返回的数组不包括在继承的类和接口中声明的类。

//getting all of the classes, interfaces, and enums that are explicitly declared in ConcreteClass
Class<?>[] explicitClasses = Class.forName("com.journaldev.reflection.ConcreteClass").getDeclaredClasses();
//prints [class com.journaldev.reflection.ConcreteClass$ConcreteClassDefaultClass, 
//class com.journaldev.reflection.ConcreteClass$ConcreteClassDefaultEnum, 
//class com.journaldev.reflection.ConcreteClass$ConcreteClassPrivateClass, 
//class com.journaldev.reflection.ConcreteClass$ConcreteClassProtectedClass, 
//class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicClass, 
//class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicEnum, 
//interface com.journaldev.reflection.ConcreteClass$ConcreteClassPublicInterface]
System.out.println(Arrays.toString(explicitClasses));

获取声明类 Declaring Class

getDeclaringClass() 方法返回类对象,该对象表示在其中声明它的类。

Class<?> innerClass = Class.forName("com.journaldev.reflection.ConcreteClass$ConcreteClassDefaultClass");
//prints com.journaldev.reflection.ConcreteClass
System.out.println(innerClass.getDeclaringClass().getCanonicalName());
System.out.println(innerClass.getEnclosingClass().getCanonicalName());

正在获取包名称

getPackage() 方法返回此类的包。该类的类装入器用于查找包。我们可以调用包的getName()方法来获取包的名称。

//prints "com.journaldev.reflection"
System.out.println(Class.forName("com.journaldev.reflection.BaseInterface").getPackage().getName());

获取类修饰符

getModifiers()方法返回类修饰符的int表示,我们可以使用java.lang.reflect.Modifier.toString()方法获取源代码中使用的字符串格式。

System.out.println(Modifier.toString(concreteClass.getModifiers())); //prints "public"
//prints "public abstract interface"
System.out.println(Modifier.toString(Class.forName("com.journaldev.reflection.BaseInterface").getModifiers()));

获取类型参数

如果有任何类型参数与类关联,则getTypeParameters()返回TypeVariable的数组。参数的声明顺序与参数的声明顺序相同。

//Get Type parameters (generics)
TypeVariable<?>[] typeParameters = Class.forName("java.util.HashMap").getTypeParameters();
for(TypeVariable<?> t : typeParameters)
System.out.print(t.getName()+",");

获取实现的接口

getGenericInterfaces()方法返回由具有泛型类型信息的类实现的接口数组。我们还可以使用getInterfaces()来获取所有实现接口的类表示。

Type[] interfaces = Class.forName("java.util.HashMap").getGenericInterfaces();
//prints "[java.util.Map<K, V>, interface java.lang.Cloneable, interface java.io.Serializable]"
System.out.println(Arrays.toString(interfaces));
//prints "[interface java.util.Map, interface java.lang.Cloneable, interface java.io.Serializable]"
System.out.println(Arrays.toString(Class.forName("java.util.HashMap").getInterfaces()));

获取所有公共方法

getMethods()方法返回类的公共方法数组,包括它的超类和超级接口的公共方法。

Method[] publicMethods = Class.forName("com.journaldev.reflection.ConcreteClass").getMethods();
//prints public methods of ConcreteClass, BaseClass, Object
System.out.println(Arrays.toString(publicMethods));

获取所有公共构造函数

getConstructors() 方法返回对象的类引用的公共构造函数的列表。

//Get All public constructors
Constructor<?>[] publicConstructors = Class.forName("com.journaldev.reflection.ConcreteClass").getConstructors();
//prints public constructors of ConcreteClass
System.out.println(Arrays.toString(publicConstructors));

获取所有公共字段

getFields() 方法返回类的公共字段数组,包括其超类和超级接口的公共字段。

//Get All public fields
Field[] publicFields = Class.forName("com.journaldev.reflection.ConcreteClass").getFields();
//prints public fields of ConcreteClass, it's superclass and super interfaces
System.out.println(Arrays.toString(publicFields));

获取所有注解

getAnnotations()方法返回元素的所有注释,也可以与类、字段和方法一起使用。请注意,只有反射可用的注释才具有运行时的保留策略。

java.lang.annotation.Annotation[] annotations = Class.forName("com.journaldev.reflection.ConcreteClass").getAnnotations();
//prints [@java.lang.Deprecated()]
System.out.println(Arrays.toString(annotations));

关于Java反射机制的讲解可以参考这篇文章:https://javakk.com/682.html

 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/696.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册