Java反射机制 Reflection

        Java 反射机制是 Java 语言的一个重要特性。在学习 Java 反射机制前,大家应该先了解两个概念:编译期和运行期。
        编译期:是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作。比如:检查语法错误。
        运行期:是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。
        Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。

Class类

        Class类是一个特殊类,它用于表示JVM运行时类或接口的信息。
        Class类提供很多方法用于获取类的各种信息,比如获取类名、判断该类是否是一个接口还是普通类等等。
        在Java中枚举类是一种类,而注解是一个接口,数组也是一个类;Java原始类型(boolean, byte, char, short, int, long, float, and double)和关键字void也被表示为Class的对象。

        一个类只有被JVM加载后才能使用,当类被虚拟机加载后都会在内存中创建一个该类的Class对象,用于存储该类的各种信息。

        Class类是java中的一个类和其他非继承类一样,默认的父类也是Object。

public class Demo01 {
	
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
		
		// 创建Student类的对象
		// 编译期确定对象的类型(Student)
		Student stu1 = new Student();
		
		// 反射使用场景
		// 运行期确定对象的类型()
//		Student stu2 = (Student)(Class.forName("?").newInstance());
//		System.out.println(stu2);
		
		
		// 创建(获取)Class对象
		// Student类/Person类的信息
		Class cls1 = Person.class;
		Class cls2 = Person.class;
		Class cls3 = Person.class;
		
		// 一个类只会被加载1次,加载后产生1个该类的Class对象
		System.out.println(cls1.hashCode()); // 哈希值相同
		System.out.println(cls2.hashCode());
		System.out.println(cls3.hashCode());
		
		
		
	}


}

class Person{}

class Student{}

这种通过Class实例获取class信息的方法称为反射(Reflection)。

以下为Class对象的三种创建方式:

	// Class对象的三种创建方式
public class Demo02 {
	
	public static void main(String[] args) throws ClassNotFoundException {
		
		// 方式1:通过类名访问class
		Class stringCls1 = String.class;
		
		// 方式2:通过实例访问getClass()
		String s = " ";
		Class stringCls2 = s.getClass();
		
		// 方式3:通过Class类的静态方法forName(类名)
		Class stringCls3 = Class.forName("java.lang.String");
		
		// 相同的Class对象
		System.out.println(stringCls1.hashCode());
		System.out.println(stringCls2.hashCode());
		System.out.println(stringCls3.hashCode());
		
	}

}

Class常用的方法


import java.time.DayOfWeek;


import com.sun.xml.internal.bind.v2.schemagen.xmlschema.List;
import com.sun.xml.internal.ws.api.message.Packet;

public class Demo03 {
	
	public static void main(String[] args) {
		
		Class clssss1 = Person.class;
		Class clssss2 = Integer.class;
		Class clssss3 = List.class;
		
		Class clssss4 = DayOfWeek.class;
		
		Class clssss = String.class;
		printClassInfo(clssss);
		
	}
	
	public static void printClassInfo(Class cls) {
		
		// 类名
		System.out.println("类(接口)的名称:" + cls.getName());
		System.out.println("完全限定名:" + cls.getSimpleName());
		System.out.println("类(接口)的类型名称:" + cls.getTypeName());
		
		// 父类
		Class superCls = cls.getSuperclass();
		System.out.println("类的父类:" + superCls.toString());
		
		// 接口实现类
		Class[] interfaceCls = cls.getInterfaces();
		System.out.println("当前类实现的接口:");
		for (Class icls : interfaceCls) {
			System.out.println(icls);
		}
		
		// packet包
		Package pct = cls.getPackage();
		if (pct != null) {
			System.out.println("类所在的包的名称:" + pct.getName());
		}
		
		
		// 判断类型
		System.out.println("是否为接口:" + cls.isInterface());
		System.out.println(cls.isArray());
		System.out.println(cls.isEnum());
		
		
	}



}

 以下为创建Class对象来获取其他类的信息:


import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.UUID;

public class Demo04 {
	
	public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
		
		// 读取类的名称
		String className = Files.readAllLines(Paths.get("e://test//relf.txt")).get(0);
		
		// 得到该类的Class对象
		Class cls = Class.forName(className);
		
		// 创建三个对象
		// 调用newInstance()相当于:Order o1 = new Order()
		// 执行无参构造方法
		Object obj1 = cls.newInstance();
		Object obj2 = cls.newInstance();
		Object obj3 = cls.newInstance();
		
		System.out.println(obj1);
		System.out.println(obj2);
		System.out.println(obj3);
		
		
	}
	

class Order{
	
	private String orderNo;
	private LocalDateTime creationTime;
		
	public Order(){
			this.orderNo = UUID.randomUUID().toString();
			this.creationTime = LocalDateTime.now();
			
			try {
				Thread.sleep(1000);
				
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
	}
}

}

调用构造方法

        为了调用任意的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息,可以创建一个实例。Constructor对象是一个构造方法,调用结果总是返回实例。

通过Constructor对象来调用:


import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

// 反射的方式调用构造方法
public class Demo05 {
	
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
		
		Class cls = Example.class;
		
		// 调用无参构造方法创建Example类型的对象
		Example ex1 = (Example)cls.newInstance();
		System.out.println(ex1);
		
		// 获取所有的构造方法
		System.out.println("所有的构造方法:");
		Constructor[] constructArray = cls.getConstructors();
		for (Constructor construct : constructArray) {
			System.out.println(construct);
		}
		
		
		System.out.println("------------------------------");
		
		// 调用有参构造方法创建Example类型的对象
		// 1、获取指定参数类型的构造方法
		// 无参构造
//		Constructor construct = cls.getConstructor();
		
		// 有一个参数的构造方法
//		Constructor construct = cls.getConstructor(int.class);
		
		// 有两个参数的构造方法
		Constructor construct = cls.getConstructor(int.class,double.class);
		System.out.println(construct);
		
		
		// 第二步:执行构造方法,创建Example类型的对象
		Example ex2 = (Example)construct.newInstance(1024,3.1415926);
		System.out.println(ex2);
		
		
	}
	

}



class Example{
	
	private Example(String s) {
		System.out.printf("Example类的有参构造: s=%s\n",s);
	}
	
	protected Example(float f) {
		System.out.printf("Example类的有参构造: f=%f\n",f);
	}
	
	public Example() {
		System.out.println("Example类的无参构造!");
	}
	
	public Example(int a) {
		System.out.printf("Example类的有参构造: a=%d\n",a);
	}
	
	public Example(int a, double b) {
		System.out.printf("Example类的有参构造: a=%d,b=%f\n",a,b);
	}
	
}


通过 Constructor对象的 .getConstructors() 方法来获取所有的public共有的构造方法;

通过 Constructor对象的  .getDeclaredConstructors() 方法来获取所有的构造方法;

通过 privateConstruct.setAccessible(true); 来打破私有的封装,从而获取构造方法。


import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

// 反射的方式调用构造方法
public class Demo06 {
	
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
		
		Class cls = Example.class;
		System.out.println("所有的构造方法:");	
		
		// 获取所有public共有的构造方法
//		Constructor[] constructArray = cls.getConstructors();
//		for (Constructor construct : constructArray) {
//			System.out.println(construct);
//		}
		
		// 获取所有定义的构造方法
		Constructor[] constructArray = cls.getDeclaredConstructors();
		for (Constructor construct : constructArray) {
			System.out.println(construct);
		}
		
		
		// 获取一个私有的构造方法
		Constructor privateConstruct = cls.getDeclaredConstructor(String.class);
		
		// 调用一个私有的构造方法
		privateConstruct.setAccessible(true);
		Example ex = (Example)privateConstruct.newInstance("luyujiang");
		System.out.println(ex);

		
		
		
		
	}
	

}

获取继承关系

通过Class对象可以获取继承关系:
Class getSuperclass():获取父类类型;
Class[] getInterfaces():获取当前类实现的所有接口。


import java.io.FileInputStream;

// 反射操作 :获取继承关系

public class Demo07 {
	
	public static void main(String[] args) {
		
		// 父类
		Class superClass = FileInputStream.class.getSuperclass();
		System.out.println("父类:" + superClass.getName());
		System.out.println("父类的父类:" + superClass.getSuperclass().getName());
		
		// 接口
		System.out.println("实现接口列表:");
		Class[] interfaceClassArray = MyStringComparator.class.getInterfaces();
		for (Class inf : interfaceClassArray) {
			System.out.println(inf.getName());
		}
		
	}

}


class MyStringComparator implements Comparable{

	@Override
	public int compareTo(Object o) {
		// TODO Auto-generated method stub
		return 0;
	}
	
	
} 

通过Class对象的isstanceof()方法可以判断 “引用” 和 “类型” 之间的关系;
通过Class对象的isAssignableFrom()方法可以判断  “类型” 和 “类型” 之间的关系;


// instanceof 运算符
// isAssignableFrom() 方法

public class Demo08 {
	
	public static void main(String[] args) {
		
		// instanceof 运算符:判断 “引用” 和 “类型” 之间的关系
		
		Object obj1 = Integer.valueOf(123);
		
		System.out.println("是否为Integer类型:" + (obj1 instanceof Integer));
		System.out.println("是否为Number类型:" + (obj1 instanceof Number));
		System.out.println("是否为Double类型:" + (obj1 instanceof Double));
		System.out.println("是否为String类型:" + (obj1 instanceof String));
		
		System.out.println("-----------------------");
		
		
		Object obj2 = String.valueOf(123);
		
		System.out.println("是否为Object类型:" + (obj2 instanceof Object));
		System.out.println("是否为Integer类型:" + (obj2 instanceof Integer));
		System.out.println("是否为Number类型:" + (obj2 instanceof Number));
		System.out.println("是否为Double类型:" + (obj2 instanceof Double));
		System.out.println("是否为String类型:" + (obj2 instanceof String));
		
		System.out.println("-----------------------");
		
		
		// isAssignableFrom() 方法:判断 “类型” 和 “类型” 之间的关系
		System.out.println("Integer <= Integer ?   " + (Integer.class.isAssignableFrom(Integer.class)));
		System.out.println("Integer <= Number ?   " + (Integer.class.isAssignableFrom(Number.class)));
		System.out.println("Number <= Integer ?   " + (Number.class.isAssignableFrom(Integer.class)));
		System.out.println("Double <= Integer ?   " + (Double.class.isAssignableFrom(Integer.class)));
		System.out.println("Comparable <= Integer ?   " + (Comparable.class.isAssignableFrom(Integer.class)));
		
		
		
	}

}

访问字段

Java的反射API提供的Field类封装了字段的所有信息:
通过Class实例的方法可以获取Field实例:getField(),getFields(),getDeclaredField(),getDeclaredFields();
通过Field实例可以获取字段信息:getName(),getType(),getModifiers();
通过Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true)来访问非public字段。
通过反射读写字段是一种非常规方法,它会破坏对象的封装。


import java.lang.reflect.Field;
import java.lang.reflect.Modifier;


// 反射操作:访问字段(成员变量)

public class Demo09 {
	
	public static void main(String[] args) {
		
		Class cls = Book.class;
		
		// 所有public访问修饰符定义的字段
		Field[] fields1 = cls.getFields();
		for (Field field : fields1) {
			System.out.println("所有public访问修饰符定义的字段");
			System.out.println("成员变量访问修饰符(int):" + field.getModifiers());
			System.out.println("成员变量访问修饰符:" + Modifier.toString(field.getModifiers()));
			System.out.println("成员变量类型:" + field.getType());
			System.out.println("成员变量名称:" + field.getName());
			System.out.println("----------------------------------------");
		}
		
		
		// 所有定义的字段
		Field[] fields2 = cls.getDeclaredFields();
		for (Field field : fields2) {
			System.out.println("所有定义的字段");
			System.out.println("成员变量访问修饰符(int):" + field.getModifiers());
			System.out.println("成员变量访问修饰符:" + Modifier.toString(field.getModifiers()));
			System.out.println("成员变量类型:" + field.getType());
			System.out.println("成员变量名称:" + field.getName());
			System.out.println("----------------------------------------");
		}
		

		
	}

}


class Book{


	public String bookName;
	public int stock;
	private double sale;
	private boolean isShelf;
	
		@Override
	public String toString() {
		return "Book [bookName=" + bookName + ", stock=" + stock + ", sale=" + sale + ", isShelf=" + isShelf + "]";
	}

	public String getBookName() {
		return bookName;
	}
	
	public void setBookName(String bookName) {
		this.bookName = bookName;
	}
	
	public int getStock() {
		return stock;
	}
	
	public void setStock(int stock) {
		this.stock = stock;
	}
	
	public double getSale() {
		return sale;
	}
	
	public void setSale(double sale) {
		this.sale = sale;
	}
	
	public boolean isShelf() {
		return isShelf;
	}
	
	public void setShelf(boolean isShelf) {
		this.isShelf = isShelf;
	}
	
	
	
}

案例1:


import java.lang.reflect.Field;

// 使用反射的方式,完成成员变量的访问
public class Demo10 {
	
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
		
		// 编译期完成对象类型和成员变量的访问
//		Book book = new Book();
//		book.bookName = "葫芦娃大战迪迦";
		
		// 运行期
		// 使用反射的方式,完成成员变量保存值
		Class cls = Book.class; // 获取Class类型对象
		Object obj = cls.newInstance(); // 通过反射创建Book类的对象
		
		// 按照字段名称,获取指定字段
		Field field = cls.getDeclaredField("bookName");
		
		// 参数1:目标Book对象
		// 参数2:存入成员变量的值
		field.set(obj, "葫芦娃大战赛罗");
		
		System.out.println(obj);
		
		
		
		
		// sale字段(private)
		// 按照字段名称,获取指定字段
		Field field2 = cls.getDeclaredField("sale");
		field2.setAccessible(true); // 访问私有成员变量
		field2.setDouble(obj,0.8);
		
		System.out.println(obj);
	}

}

案例2:


import java.lang.reflect.Field;

// 反射操作:访问对象中的成员变量(字段)值
public class Demo11 {
	
	public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
		
		Book book = new Book();
		book.setBookName("书剑恩仇录");
		book.setSale(0.75);
		book.setShelf(true);
		book.setStock(451);
		
		printInfo(book);
		
		
		
		
	}

	private static void printInfo(Object obj) throws IllegalArgumentException, IllegalAccessException {
		// 输出参数对象的所有成员变量和值
		Class cls = obj.getClass();
		Field[] fields = cls.getDeclaredFields();
		
		for (Field field : fields) {
			System.out.println("成员变量名称:" + field.getName());
			
			// 判断是否可以访问
			if(!field.isAccessible()) {
				field.setAccessible(true); // 设置私有成员变量允许访问
			}
			
			System.out.println("成员变量内容:" + field.get(obj));
			System.out.println();
		}
		
	}

}

调用方法

Java的反射API提供的Method对象封装了方法的所有信息:
通过Class实例的方法可以获取Method实例:getMethod(),getMethods(),getDeclaredMethod(),getDeclaredMethods();
通过Method实例可以获取方法信息:getName(),getReturnType(),getParameterTypes(),getModifiers();
通过Method实例可以调用某个对象的方法:Object invoke(Object instance, Object... parameters);
通过设置setAccessible(true)来访问非public方法;
通过反射调用方法时,仍然遵循多态原则。


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

// 反射操作:获取方法
// 每一个方法都会被封装成一个Method对象
public class Demo12 {
	
	public static void main(String[] args) {
		
		Class cls = Book.class;

		// 获取所有public方法(包含父类)
//		Method[] methods = cls.getMethods();
		
		// 获取所有定义的方法(仅包含当前类)
		Method[] methods = cls.getDeclaredMethods();
		
		for (Method method : methods) {
			
			System.out.println("方法的访问修饰符:" + Modifier.toString(method.getModifiers()));
			System.out.println("方法的返回值类型:" + method.getReturnType());
			System.out.println("方法的名称:" + method.getName());
			System.out.println("--------------------");
			
			// 获取所有的参数类型
//			System.out.println("方法的参数类型:");
//			Class[] paramTypes = method.getParameterTypes();
			
			// 获取所有的参数对象
			Parameter[] params = method.getParameters();
			
			for (Parameter p : params) {
				
				System.out.println(p.getName());
				System.out.println(p.getType());
				System.out.println("-------------------------");
				
			}
			
			System.out.println();
			
		}
		
		
		
		
	}

}

        如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null。


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


// 反射操作:调用方法

public class Demo13 {
	
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
		
//		Base b = new Base();
//		b.create(1000);
	
		
		// 获取Class对象
		Class cls = Base.class;
		Object obj = cls.newInstance(); // 创建Base对象
		
		// 按照方法名称和“参数类型”获取Method方法
		// create()
//		Method method = cls.getMethod("create");
		
		// create(int x)
		Method method = cls.getMethod("create",int.class);
		
		
		
		// Method对象的invoke()作用:
		// 以反射的方式执行create()方法
		
		// create()
//		int r = (int) method.invoke(obj);
//		System.out.println(r);
		
		// create(int x)
		int r = (int) method.invoke(obj, 1000);
		System.out.println(r);
		
	}

}

class Base{
	
	public Object create() {
		int i = (int) (Math.random()*10);
		return i;
	}
	
	public  Object create(int x) {
		int i = (int) (Math.random()*x);
		return i;
	}
}

案例1:


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

// 以反射的方式调用静态方法

public class Demo14 {
	
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		
		// 计算10以底的对数
		// 获取某个指定数字的位数
		System.out.println((int)Math.log10(123456) + 1);
		
		// 反射调用
		Class cls = Math.class;
		Method methodLog10 = cls.getMethod("log10", double.class);
		
		int size = Double.valueOf((double)methodLog10.invoke(null, 123456)).intValue() + 1;
		System.out.println(size);
		
	}

}

        我们来考察这样一种情况:一个Person类定义了hello()方法,并且它的子类Student也覆写了hello()方法,那么,从Person.class获取的Method,作用于Student实例时,调用的方法到底是哪个?


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Demo15 {
	
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		
		// 获取Person1的Hello方法
		Method h = Person1.class.getMethod("hello");
		// 对Student1调用Hello()方法
		h.invoke(new Student1());
		
		// 执行逻辑相当于
		Person1 p = new Student1();
		p.hello();
		
	}

}


class Person1{
	
	public void hello() {
		System.out.println("Person:Hello");
	}
	
	
}

class Student1 extends Person1{
	
	public void hello() {
		System.out.println("Student:Hello");
	}
	
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值