JavaSE-反射机制

概述:

在未使用反射之间,创建类型对象都是用已知确定的类型,使用new关键字创建类型对象。为了使代码模块化,与特定类型解耦,引入了反射机制,处理运行时类。

也就是说对于某一模块,向其不确定的传入不确定类型,即可完成对这些类型对象的处理,称为运行时类。

反射之前: 引入需要类的包类名称---->new关键字实例化------>取得实例化对象

反射:        实例化对象------>getClass()方法------->得到完整的包类名称

故起反射之名。

反射的作用:

可以创建对象、为属性赋值、调用方法等一切关于类型有关的信息以及动态代理 

使用反射机制,使用到的包类:

java.lang.Class

java.lang.reflection.Field

java.lang.reflecion.Method

java.lang.reflection.Constructor

......

简单看一下反射的使用:

	//在没有反射时,创建类对象以及属性赋值、方法调用的过程
	@Test
	public void test1(){
		Person person = new Person();
		person.setName("yuchen");
		person.setAge(23);
		
		person.show();
		person.display("China");
	}
	
	@Test
	public void testReflection() throws Exception{
		//获得Class类型对象
		Class clazz = Person.class;
		//使用Class对象的newInstance()创建类对象
		Person person = (Person) clazz.newInstance();
		
		//使用Filed类型,为属性赋值,如果属性是public,可直接赋值
		Field name = clazz.getField("name");
		name.set(person, "yuchen");
		//如果属性是private,使用getDeclaredField方法获取,并设置setAccessible(true)
		Field age = clazz.getDeclaredField("age");
		age.setAccessible(true);
		age.set(person, 23);
		
		//使用Method类型,调用对象的方法
		//无参的方法调用
		Method show = clazz.getMethod("show");
		show.invoke(person);
		
		//有参数的方法调用,需指定参数类型,传入参数
		Method display = clazz.getMethod("display", String.class);
		display.invoke(person, "China");
		
		System.out.println(person);
	}

2、反射的入口Class类

Class是一种类型,该类型可以看做是.class文件被加载到JVM内存后的表示,就是标识不同的.class缓存区域。

我们编写的.java文件被javac.exe编译后成为.class文件,后被JVM加载到内存中,就成为运行时类,存在于缓存中,这个运行时类,我们就可以用Class的不同对象代表。

	@Test
	public void testClass(){
		Class clazz = Person.class;
		System.out.println(clazz);
		//输出:class com.cqupt.javase.reflection.Person
	}
可以看出,其实Class对象其实就是指向类型的

通过Class对象,我们就可以对对应的类型进行各种各样的操作:创建类、属性赋值、调用方法等

同样一个类型只有在使用时,才被加载到虚拟机JVM内存中,且只加载一次。
获取Class对象的方法有四种:

1、类型本身的class属性

2、对象的getClass()方法

3、Class类型的静态放过forName("className") ,className是全类名

4、类加载器ClassLoader的静态方法LoadClass("className")方法, className是全类名

	/*
	 * 四种方法获取Class对象
	 */
	@Test
	public void testClass() throws ClassNotFoundException{
		//1、利用类型自身的class属性
		Class clazz1 = Person.class;
		System.out.println(clazz1);
		
		//2、利用对象的getClass()方法
		Person person = new Person();
		Class clazz2 = person.getClass();
		System.out.println(clazz2);
		
		//3、利用Class类型的静态方法forName("className")方法,className是类的全类名
		String className = "com.cqupt.javase.reflection.Person";
		Class clazz3 = Class.forName(className);
		System.out.println(clazz3);
		
		//4、(了解)通过类加载器ClassLoader的静态方法loadClass("className"),同样是全类名
		ClassLoader classLoader = this.getClass().getClassLoader();
		Class clazz4 = classLoader.loadClass(className);
		System.out.println(clazz4);
		
		//证明加载类型只有一次,同一类型的Class对象地址都是相同的
		System.out.println(clazz1 == clazz2);
		System.out.println(clazz1 == clazz3);
		System.out.println(clazz1 == clazz4);
	}

输出结果:

class com.cqupt.javase.reflection.Person
class com.cqupt.javase.reflection.Person
class com.cqupt.javase.reflection.Person
class com.cqupt.javase.reflection.Person
true
true
true

3、类加载器ClassLoader

类加载器是加载.class文件到JVM内存中的模块,有三种级别的类加载器:

引导类加载器:C++编写,负责java平台的核心库,是无法获取的

扩展类加载器:负责jre下库的加载

系统类加载器:负责自定义类型的加载,是最常用的类加载器

系统类加载器的父类是扩展类加载器,扩展类加载器的父类是引导类加载器

	@Test
	public void testClassLoader() throws ClassNotFoundException{
		//获得系统类加载器
		ClassLoader classLoader = ClassLoader.getSystemClassLoader();
		System.out.println(classLoader);//输出:sun.misc.Launcher$AppClassLoader@2a139a55
		
		ClassLoader classLoaderEx = classLoader.getParent();
		System.out.println(classLoaderEx);
		//系统类加载器的父类是扩展类加载器:sun.misc.Launcher$ExtClassLoader@2503dbd3
		
		ClassLoader classLoaderExParent = classLoaderEx.getParent();
		System.out.println(classLoaderExParent);
		//扩展类加载器的父类是引导类加载器,是获取不到的:null
		
		String className = "java.lang.Object";
		ClassLoader classLoader1= Class.forName(className).getClassLoader();
		System.out.println(classLoader1);
		//加载Object类型的是引导类加载器,引导类加载器获取不到:null
		
		//自己创建的类型,都是使用系统类加载器进行加载的
		Class clazz = Person.class;
		ClassLoader classLoader2 = clazz.getClassLoader();
		System.out.println(classLoader2);//输出:sun.misc.Launcher$AppClassLoader@2a139a55
	}

类加载器不但可以加载类,还可以用来加载文件,使用ClassLoader的getResourceAsStream("path")的方法,返回InputStream

程序开发中有两种加载文件的方法:

1、就是使用ClassLoader的getResourceAsStream("path")的方法

2、使用FileInputStream

区别就是ClassLoader的方法加载,更好用,它可以加载src下不同文件夹下的文件(加载不了Project根目录的文件),而FileInputStream只能加载Project根目录的文件

	@Test
	public void testClassLoader2() throws IOException{
		ClassLoader classLoader = this.getClass().getClassLoader();
		
		//ClassLoader的getResourceAsStream的方法可以加载在src文件夹下不同位置的文件
//		InputStream input= classLoader.getResourceAsStream("jdbc.properties");
		InputStream input= classLoader.getResourceAsStream("com/cqupt/javase/reflection/jdbc.properties");
		Properties properties = new Properties();
		properties.load(input);
		System.out.println(properties.getProperty("driver"));
		
		//FileInputStream只能加载Project根目录下的文件
		FileInputStream file = new FileInputStream("jdbc.properties");
		properties.load(file);
		System.out.println(properties.getProperty("url"));
	}

4、创建运行时类的对象newInstance()

只要获得了Class对象,就打开了反射之门,通过Class对象完成运行时类对象的创建。

通过Class对象的newInstance()方法创建运行时类的对象

有两个前提:

1.运行时类的定义必须有无参构造器

2.必须要有无参构造器的访问权限(public 或者是默认的)

	//定义类型时,尽量要有无参构造器,方便以后进行反射使用
	@Test
	public void testNewInstance() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
		String className = "com.cqupt.javase.reflection.Person";
		Class clazz = Class.forName(className);
		
		Person person = (Person) clazz.newInstance();
		System.out.println(person);
		//输出:Peson Constructor()...
		//Person [name=null, age=0]
	}

5、获取运行时类的结构

Class类对象包含了所对应类型的结构信息,我们可以利用Class对象获取对应类型的:

1、属性相关信息:权限修饰符、类型、属性名

2、方法相关信息:注解、权限修饰符、返回类型、方法名、形参列表、异常

3、构造器的相关信息

4、其他信息的获取:父类、带泛函的父类、父类的泛函类型、类的注解、接口、所在的包

测试类及其实现的父类、接口:

父类:

public class Creature <T> {

	private double weight;
	public void breath(){
		System.out.println("breath...");
	}
}
接口:

public interface MyInterface extends Serializable{

}
注解

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)//必须是RUNTIME,生命周期长,可以被获取到
public @interface MyAnnotation {

	String value();
}

运行时类:Person

@MyAnnotation(value = "cqupt")
public class Person extends Creature <String> implements Comparable ,MyInterface{

	Person() {
		super();
		System.out.println("Peson Constructor()...");
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	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;
	}
	private void display(String nation,int i) throws Exception{
		System.out.println("我的国籍是:"+nation);
	}
	@MyAnnotation(value = "abc123")
	public void show(){
		System.out.println("我是一个人!");
	}
	public static void info(){
		System.out.println("中国人!");
	}
	public String name;
	private int age;
	String address;
	@Override
	public int compareTo(Object arg0) {
		// TODO Auto-generated method stub
		return 0;
	}
	
	class Bird{
		
	}

}

属性相关信息的获取

Class类型中Field getField()方法得到Field类对象

获取权限修饰符:Field类对象的int getModifiers()方法

类型:Field类对象的Class getType()方法

属性名:Field类对象的getName

getField()与getDeclaredField()方法的区别

public class FieldTest {

	//获取运行时类的属性
	@Test
	public void test1(){
		Class clazz = Person.class;
		
		//getField()只能获运行时类中及其父类中取权限为public的属性
		Field[] fields = clazz.getFields();
		for(Field field:fields){
			System.out.println(field);
		}
		
		//只能获取到运行时类中所有声明的属性
		fields = clazz.getDeclaredFields();
		for(Field field:fields){
			System.out.println(field.getName());
		}
	}
	
	//获取运行时类属性的 权限修饰符、变量类型、变量名
	@Test
	public void test2(){
		Class clazz = Person.class;
		
		Field[] fields = clazz.getFields();
		for(Field field:fields){
			//获取属性权限修饰符:getModifiers,返回值为int
			int modifier = field.getModifiers();
			System.out.print(Modifier.toString(modifier)+" ");
			
			//获取属性变量类型:getType
			Class type = field.getType();
			System.out.print(type.getSimpleName()+" ");
			
			//获取属性名
			System.out.println(field.getName());
			
			//输出结果:public String name
		}
		
		System.out.println();
		fields = clazz.getDeclaredFields();
		for(Field field:fields){
			//获取属性权限修饰符:getModifiers,返回值为int
			int modifier = field.getModifiers();
			System.out.print(Modifier.toString(modifier)+" ");
			
			//获取属性变量类型:getType
			System.out.print(field.getType().getSimpleName()+" ");
			
			//获取属性名
			System.out.println(field.getName());
			
			/*输出结果:
			public String name
			private int age
			 String address
			 */
			
		}
	}
}

方法相关信息的获取

同获取属性信息类似,先获取Method对象,再利用Method对象的方法获取方法相关信息
public class MethodTest {

	@Test
	public void test1(){
		Class clazz = Person.class;
		//获取运行时类及其父类中所有的public方法
		Method[] methods = clazz.getMethods();
		for(Method method:methods){
			System.out.println(method);
		}
		/*
		 * 	public java.lang.String com.cqupt.javase.reflection.Person.toString()
			public int com.cqupt.javase.reflection.Person.compareTo(java.lang.Object)
			public java.lang.String com.cqupt.javase.reflection.Person.getName()
			public void com.cqupt.javase.reflection.Person.setName(java.lang.String)
			public int com.cqupt.javase.reflection.Person.getAge()
			public void com.cqupt.javase.reflection.Person.setAge(int)
			public void com.cqupt.javase.reflection.Person.show()
			public void com.cqupt.javase.reflection.Creature.breath()
			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()
		 */
		System.out.println();
		//只能获取运行时类中所有的方法
		methods = clazz.getDeclaredMethods();
		for(Method method:methods){
			System.out.println(method);
		}
		/*
		 * 	public java.lang.String com.cqupt.javase.reflection.Person.toString()
			public int com.cqupt.javase.reflection.Person.compareTo(java.lang.Object)
			public java.lang.String com.cqupt.javase.reflection.Person.getName()
			public void com.cqupt.javase.reflection.Person.setName(java.lang.String)
			public int com.cqupt.javase.reflection.Person.getAge()
			public void com.cqupt.javase.reflection.Person.setAge(int)
			private void com.cqupt.javase.reflection.Person.display(java.lang.String,int) throws java.lang.Exception
			public void com.cqupt.javase.reflection.Person.show()
		 */
	}
	
	//获取方法的完整结构:注解、权限修饰符、返回类型、方法名、形参列表、异常
	@Test
	public void test2(){
		Class clazz = Person.class;
		Method[] methods = clazz.getDeclaredMethods();
		
		for(Method method:methods){
			
			//获取注解
			Annotation[] annotations = method.getAnnotations();
			for(Annotation annotation:annotations){
				System.out.println(annotation);
			}
			
			//获取权限修饰符
			int modifier = method.getModifiers();
			System.out.print(Modifier.toString(modifier)+" ");
			
			//获取返回类型
			Class returnType = method.getReturnType();
			System.out.print(returnType.getSimpleName()+" ");
			
			//获取方法名
			System.out.print(method.getName()+"(");
			
			//获取形参列表
			Parameter[] parameters = method.getParameters();
			for(int i=0;i< parameters.length; i++){
				System.out.print(parameters[i].getType().getSimpleName()+" arg"+i);
				if(i!=parameters.length-1)
					System.out.print(",");
				
			}
			System.out.print(") ");
			
			//获取异常
			Class[] exceptionTypes = method.getExceptionTypes();
			if(exceptionTypes.length!=0){
				System.out.print("throws ");
			}
			for(Class exceptionType:exceptionTypes){
				System.out.print(exceptionType.getSimpleName()+" ");
			}
			System.out.println();
			//输出结果:
			/*
			 * 	public String toString() 
				public int compareTo(Object arg0) 
				public String getName() 
				public void setName(String arg0) 
				public void setAge(int arg0) 
				public int getAge() 
				private void display(String arg0,int arg1) throws Exception 
				@com.cqupt.javase.reflection.MyAnnotation(value=abc123)
				public void show() 
			 */
		}
	}
}

构造器的相关信息

利用Class的方法getConstructor(),得到指定类型的构造器,可以不使用无参构造器就可以创建对象
public class ContructorTest {

	@Test
	public void test1() throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
		Class clazz = Person.class;
		
		//获取运行时类的public权限的构造器(缺省的获取不到)
		Constructor[] constructors = clazz.getConstructors();
		for(Constructor constructor:constructors){
			System.out.println(constructor);
			//输出:public com.cqupt.javase.reflection.Person(java.lang.String,int)
		}
		
		System.out.println();
		
		//获取运行时类的所有构造器
		constructors = clazz.getDeclaredConstructors();
		for(Constructor constructor:constructors){
			System.out.println(constructor);
			//输出:com.cqupt.javase.reflection.Person()
			//public com.cqupt.javase.reflection.Person(java.lang.String,int)
		}
		
		//获取运行时类指定的构造器,并创建对象,可以不使用无参构造器
		Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);
		Person p = (Person) constructor.newInstance("yuchen", 25);
		System.out.println(p);
		//输出:Person [name=yuchen, age=25]
	}
}

其他信息的获取

**着重注意父类泛函类型的获取**
public class OtherTest {

	//1.获取运行时类的父类
	@Test
	public void superclassTest(){
		Class clazz = Person.class;
		Class superclass = clazz.getSuperclass();
		System.out.println(superclass);
		//输出:class com.cqupt.javase.reflection.Creature
	}
	
	//2.获取运行时类的带泛型的父类
	@Test 
	public void genericSuperclassTest(){
		Class clazz = Person.class;
		Type type = clazz.getGenericSuperclass();
		System.out.println(type);
		//输出:com.cqupt.javase.reflection.Creature<java.lang.String>
	}
	
	//3.获取运行时类的带泛型父类的泛型
	//Class是接口Type的实现类
	@Test
	public void genericTest(){
		Class clazz = Person.class;
		Type type = clazz.getGenericSuperclass();
		ParameterizedType para = (ParameterizedType)type;
		Type[] types = para.getActualTypeArguments();
		System.out.println(((Class)types[0]).getName());
		//输出:java.lang.String
	}
	
	//4.获取实现的接口
	@Test
	public void interfaceTest(){
		Class clazz  = Person.class;
		//只能获取运行时类的实现的接口,父类的实现的接口 和 实现接口的父接口就获取不到了
		Class[] ifaces = clazz.getInterfaces();
		for(Class iface : ifaces){
			System.out.println(iface.getName());
		}
		//输出:java.lang.Comparable
		//com.cqupt.javase.reflection.MyInterface
	}
	
	//5.获取所在的包
	@Test
	public void packageTest(){
		Class clazz  = Person.class;
		Package pack = clazz.getPackage();
		System.out.println(pack.getName());
		//输出:com.cqupt.javase.reflection
	}
	//6.获取注解
	@Test
	public void annotationTest(){
		Class clazz  = Person.class;
		Annotation[] annotations = clazz.getAnnotations();
		for(Annotation annotation:annotations){
			System.out.println(annotation);
		}
		//输出:@com.cqupt.javase.reflection.MyAnnotation(value=cqupt)
	}
	
}

6、调用属性及方法、构造器

属性调用:Filed field = clazz.getField("fieldName"),clazz.getDeclaredField("age");
属性设置:field.set(person, value)
遇到private的属性,需设置setAccessible(true)

	//调用指定属性,并赋值
	@Test
	public void testField() throws NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{
		Class<Person> clazz = Person.class;
		Person person = clazz.newInstance();
		
		//指定获取public声明的属性
		Field name = clazz.getField("name");
		name.set(person, "yuchen");
		System.out.println(person);
		//输出:Person [name=yuchen, age=0]
		
		//指定获取运行时类的private声明的属性
		Field age = clazz.getDeclaredField("age");
		//缺省权限修饰符的属性不需要设置setAccessible就可赋值
		age.setAccessible(true);
		age.set(person, 25);
		System.out.println(person);
		//输出:Person [name=yuchen, age=25]
	}
方法获取:Method method = clazz.getMethod("methodName", Class ... ), clazz.getDeclaredMethod("display", Class ...),指定形参列表的类型获取方法

方法调用:method.invoke(person,args...)

构造器的调用:

	//调用指定方法
	@Test
	public void testMethod() throws Exception{
		Class<Person> clazz = Person.class;
		Person person =  clazz.newInstance();
		
		//1.获取指定名的无参方法,public声明
		Method show = clazz.getMethod("show");
		//调用无参方法
		show.invoke(person);
		
		//2.获取指定名的有参方法,private声明
		//注意int.class是可行的,与Integer.class是不同的,Integer.class不能在此调用
		Method display = clazz.getDeclaredMethod("display", String.class, int.class);
		display.setAccessible(true);
		display.invoke(person, "CHN",10);
		
		//3.获取指定名的静态方法,invoke时直接传入类的Class对象
		Method info = clazz.getMethod("info");
		info.invoke(Person.class);
		
		//4.调用指定的构造器
		Constructor<Person> constructor = clazz.getConstructor(String.class,int.class);
		Person person2 = constructor.newInstance("yuchen",24);
		System.out.println(person2);
		//Person [name=yuchen, age=24]
	}






  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值