zoukankan      html  css  js  c++  java
  • Java反射详解

    一、反射概述

    1. java.lang.Class:是反射的源头

    我们创建一个类,通过编译,生成对应的.calss文件,之后使用java.exe加载(jvm的类加载器)此.class文件,此.class文件加载到内存以后,就是一个运行时类,存在缓存区,那么这个运行时类的本身就是一个class的实例

    • 每一个运行时类只加载一次
    • 有了Class实例以后,我们才可以进行如下的操作:
      • 创建对应的运行时类的对象(重点)
      • 可以获取对象的运行时类的完整结构(属性、方法、构造器、内部类、、、)(理解)
      • 调用对应的运行时类的指定的结构(属性、方法)(重点)

    在反射以前,如何创建一个类的对象,并调用其中的方法属性

    public void test1() {
    		Person p = new Person();
    		p.setAge(10);
    		p.setName("AA");
    		p.show();
    		System.out.println(p);
    	}
    
    

    有了反射,可以通过反射创建一个类的对象,并调用其中的方法,下面详细说

    public void test2() throws Exception {
    		
    		Class clazz = Person.class;
    		
    		//1.创建clazz对应的运行时类Person类的对象
    		Person p = (Person)clazz.newInstance();
    		System.out.println(p);
    		
    		//2.通过反射调用运行时类的指定属性,public name的修改方式
    		Field f1 = clazz.getField("name");
    		f1.set(p, "LiuDaHua");
    		System.out.println(p);
    		
    		//private age的方式
    		Field f2 = clazz.getDeclaredField("age");
    		f2.setAccessible(true);
    		f2.set(p, 20);
    		System.out.println(p);
    		
    		//3.通过反射调用运行时类的指定方法 public修饰的
    		Method m1 = clazz.getMethod("show");
        	// 执行
    		m1.invoke(p);
        	// 带参数的方法
    		Method m2 = clazz.getMethod("display", String.class);
    		m2.invoke(p, "cn");
    		
    	}
    	
    

    二、如何获取Class的实例

    1.调用运行时类本身的.class属性

    Class clazz = Person.class;
    System.out.println(clazz.getName());
    		
    Class clazz1 = String.class;
    System.out.println(clazz1.getName());
    
    

    2.通过运行时类的对象获取

    Person p = new Person();
    Class clazz2 = p.getClass();
    System.out.println(clazz.getName());
    
    

    3.通过Class的静态方法获取,通过此方式,体会反射的动态性

    String className = "com.atguigu.java.Person";
    		
    Class clazz4 = Class.forName(className);	   
    System.out.println(clazz4);
    
    
    

    4.通过类的加载器

    ClassLoader classLoader = this.getClass().getClassLoader();
    Class clazz5 = classLoader.loadClass(className);
    System.out.println(clazz5.getName());
    

    整个代码

    public void test4() throws Exception {
    		//1.调用运行时类本身的.class属性
    		Class clazz = Person.class;
    		System.out.println(clazz.getName());
    		
    		Class clazz1 = String.class;
    		System.out.println(clazz1.getName());
    		
    		//2.通过运行时类的对象获取
    		Person p = new Person();
    		Class clazz2 = p.getClass();
    		System.out.println(clazz.getName());
    		
    		//3.通过Class的静态方法获取,通过此方式,体会反射的动态性
    		String className = "com.atguigu.java.Person";
    		
    		Class clazz4 = Class.forName(className);
    		System.out.println(clazz4);
    		
    		
    		//4.通过类的加载器
    		ClassLoader classLoader = this.getClass().getClassLoader();
    		Class clazz5 = classLoader.loadClass(className);
    		System.out.println(clazz5.getName());
    	}
    	
    
    

    三、创建运行时类对象

    1. 获取Class的实例

    通常直接使用类名.class , 例如Person.class

    当然了,上面的几种方法都是可以的

    String className = "com.atguigu.java.Person";
    Class clazz = Class.forName(className);
    

    2.创建运行时类对象

    创建对应的运行时类的对象,使用的是newInstance(),实际上就是运用了运行时类的空参数的构造器

    要想能够创建成功

    • 要求对应的运行时类要有空参数的构造器
    • 构造器的权限修饰符的权限要足够
    Object obj = clazz.newInstance();//调用的是空参构造器
    Person p = (Person)obj;
    System.out.println(p);
    
    

    全部代码

    public void test1() throws Exception {
    	// 获取Class实例
       		Class clazz = Person.class;
    		
    		//创建对应的运行时类的对象,使用的是newInstance(),实际上就是运用了运行时类的空参数的构造器
    		//要想能够创建成功,①要求对应的运行时类要有空参数的构造器,②构造器的权限修饰符的权限要足够
    		Object obj = clazz.newInstance();//调用的是空参构造器
    		Person p = (Person)obj;
    		System.out.println(p);		
    	}
    

    四、通过反射获取类的完整结构

    1.获取运行时类的属性

    1.getFields() 返回 :表示公共字段的 Field 对象的数组,只能获取运行时类中以及父类中声明的为public的属性

    Field[] fiels = clazz.getFields();
    for(int i=0;i<fiels.length;i++) {
        System.out.println(fiels[i]);
    	}
    

    2.getDeclaredFields() :获取运行时类本身声明的所有的属性,包括私有的

    Field[] fiels1 = clazz.getDeclaredFields();
    for(int i=0;i<fiels1.length;i++) {
    	System.out.println(fiels1[i].getName());
    	}
    		//增强for循环
    for(Field f:fiels1) {
    	System.out.println(f.getName());
    }
    
    

    2.获取属性的各个部分的内容

    权限修饰符 变量类型 变量名

    1.获取每个属性的权限修饰符

    Field[] field = clazz.getDeclaredFields();
    	for(Field i:field) {
    	//1.获取每个属性的权限修饰符
    		int a = i.getModifiers();
    		String str1 = Modifier.toString(a);
    		System.out.print(str1+"   ");
    	}
    

    2.获取属性的变量类型

    Field[] field = clazz.getDeclaredFields();
    		for(Field i:field) {
    		//2.获取属性的变量类型
    		Class type = i.getType();
    		System.out.print(type+"  ");
    	}
    

    3.获取属性名

    Class clazz = Person.class;
    		Field[] field = clazz.getDeclaredFields();
    		for(Field i:field) {
    		//3.获取属性名
    		System.out.print(i.getName());
    		System.out.println();
    	}
    	
    

    3.获取运行时类的方法(重点)

    1.getMethods() 获取运行时类及其父类中所有声明为public的方法

    Class clazz = Person.class;
    Method[] m1 = clazz.getMethods();
    	for(Method m:m1) {
    	System.out.println(m);
    	}
    

    2.getDeclaredMethods() 获取运行时类本身声明的所有的方法

    Method[] methods = clazz.getDeclaredMethods();
    	for(int i=0;i<methods.length;i++) {
    		System.out.println(methods[i]);
    	}
    

    4.获取方法的各个部分的内容

    注解 权限修饰符 返回值类型 方法名 形参列表 异常

    1.注解

    Class clazz = Person.class;
    		
    	Method[] m1 = clazz.getMethods();
    	for(Method m:m1) {
    	Annotation[] an = m.getAnnotations();
    	for(Annotation a:an) {
    	    System.out.println(a);
    	}
    }
    

    2.权限修饰符

    int a = m.getModifiers();
    String str1 = Modifier.toString(a);
    System.out.print(str1+"  ");
    
    

    3.返回值类型

    Class return1 = m.getReturnType();
    System.out.print(return1+"  ");
    
    

    4.方法名

    System.out.print(m.getName()+"   ");
    
    

    5.形参列表

    System.out.print("(");
    Class[] params = m.getParameterTypes();
    for(Class p : params) {
    	System.out.print(p.getName());
    }
    System.out.println(")"+"   ");
    
    

    6.抛的异常

    Class[] ex = m.getExceptionTypes();
    	for(Class e:ex) {
    		System.out.print(e.getName());
    	}
     //   for(int i=0;i<ex.length;i++) {
    //	System.out.print(ex[i].getName());
     //       }
    
    

    5.获取构造器

    	@Test
    	public void test5() throws Exception {
    		
    		Class clazz = Class.forName("com.atguigu.java.Person");
    		
    		Constructor[] cons = clazz.getDeclaredConstructors();
    		for(Constructor c : cons) {
    			System.out.println(c);
    		}
    		
    	
    	}
    
    

    6.获取运行时类的父类

    @Test
    	public void test6() {
    		Class clazz = Person.class;
    		Class super1 = clazz.getSuperclass();
    		System.out.println(super1);
    	}
    
    

    7.获取带泛型的父类

    @Test
    	public void test7() {
    		Class clazz = Person.class;
    		Type type1 = clazz.getGenericSuperclass();
    		System.out.println(type1);
    	}
    
    

    8.获取父类的泛型(重点)

    @Test
    	public void test8() {
    		Class clazz = Person.class;
    		Type type1 = clazz.getGenericSuperclass();
    		
    		ParameterizedType param= (ParameterizedType)type1;
    		Type[] ars = param.getActualTypeArguments();
    		System.out.println((Class)ars[0]);
    	}
    
    

    9.获取实现的接口

    @Test
    	public void test9() {
    		Class clazz = Person.class;
    		Class[] i = clazz.getInterfaces();
    		for(Class a:i) {
    			System.out.println(a);
    		}
    	}
    
    

    10.获取所在的包

    @Test
    	public void test10() {
    		Class clazz = Person.class;
    		Package p = clazz.getPackage();
    		System.out.println(p);
    	}
    
    

    全部代码如下:

    package com.atguigu.java;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    
    import org.junit.Test;
    
    /**
     * 通过反射获取类的完整结构
     * 
     * @author MD
     *
     */
    public class TestField {
    
    	
    	/**
    	 * 1.获取运行时类的属性
    	 */
    	@Test
    	public void test1() {
    		Class clazz = Person.class;
    		
    		//1.getFields() 返回 :表示公共字段的 Field 对象的数组 
    		// 只能获取运行时类中以及父类中声明的为public的属性
    //		Field[] fiels = clazz.getFields();
    //		for(int i=0;i<fiels.length;i++) {
    //			System.out.println(fiels[i]);
    //		}
    		
    		//2.getDeclaredFields() :获取运行时类本身声明的所有的属性
    		Field[] fiels1 = clazz.getDeclaredFields();
    		for(int i=0;i<fiels1.length;i++) {
    			System.out.println(fiels1[i].getName());
    		}
    		//增强for循环
    		for(Field f:fiels1) {
    			System.out.println(f.getName());
    		}
    		
    				
    	}
    	
    	/**
    	 * 权限修饰符 变量类型  变量名
    	 * 2.获取属性的各个部分的内容
    	 * 
    	 */
    	
    	@Test
    	public void tset2() {
    		Class clazz = Person.class;
    		Field[] field = clazz.getDeclaredFields();
    		for(Field i:field) {
    			//1.获取每个属性的权限修饰符
    			int a = i.getModifiers();
    			String str1 = Modifier.toString(a);
    			System.out.print(str1+"   ");
    			//2.获取属性的变量类型
    			Class type = i.getType();
    			System.out.print(type+"  ");
    			//3.获取属性名
    			System.out.print(i.getName());
    			System.out.println();
    		}
    	}
    	
    	
    	
    	
    	
    	/**
    	 * 3.获取运行时类的方法
    	 */
    	@Test
    	public void test3() {
    		Class clazz = Person.class;
    		
    		//1.getMethods() 获取运行时类及其父类中所有声明为public的方法
    		
    //		Method[] m1 = clazz.getMethods();
    //		for(Method m:m1) {
    //			System.out.println(m);
    //		}
    			
    
    		//2.getDeclaredMethods() 获取运行时类本身声明的所有的方法
    		Method[] methods = clazz.getDeclaredMethods();
    		for(int i=0;i<methods.length;i++) {
    			System.out.println(methods[i]);
    		}
    	}
    	
    	/**
    	 * 4.获取方法的各个部分的内容
    	 * 注解  权限修饰符   返回值类型	方法名    形参列表    异常
    	 * 
    	 */
    	@Test
    	public void test4() {
    		Class clazz = Person.class;
    		
    		Method[] m1 = clazz.getMethods();
    		for(Method m:m1) {
    			//1.注解
    			Annotation[] an = m.getAnnotations();
    			for(Annotation a:an) {
    				System.out.println(a);
    			}
    						
    			//2.权限修饰符
    			int a = m.getModifiers();
    			String str1 = Modifier.toString(a);
    			System.out.print(str1+"  ");
    			
    			//3.返回值类型
    			Class return1 = m.getReturnType();
    			System.out.print(return1+"  ");
    			
    			
    			//4.方法名
    			System.out.print(m.getName()+"   ");
    						
    			//5.形参列表
    			System.out.print("(");
    			Class[] params = m.getParameterTypes();
    			for(Class p : params) {
    				System.out.print(p.getName());
    			}
    			System.out.println(")"+"   ");
    			
    			//6.抛的异常
    			Class[] ex = m.getExceptionTypes();
    			for(Class e:ex) {
    				System.out.print(e.getName());
    			}
    //			for(int i=0;i<ex.length;i++) {
    //				System.out.print(ex[i].getName());
    //			}
    			
    			System.out.println();
    		}
    		
    		
    	}
    
    	/**
    	 * 5.获取构造器
    	 * @throws Exception 
    	 */
    	
    	@Test
    	public void test5() throws Exception {
    		
    		Class clazz = Class.forName("com.atguigu.java.Person");
    		
    		Constructor[] cons = clazz.getDeclaredConstructors();
    		for(Constructor c : cons) {
    			System.out.println(c);
    		}
    	}
    
    	/**
    	 * 
    	 * 6.获取运行时类的父类
    	 */
    	@Test
    	public void test6() {
    		Class clazz = Person.class;
    		Class super1 = clazz.getSuperclass();
    		System.out.println(super1);
    	}
    	
    	
    	/**
    	 * 7.获取带泛型的父类
    	 */
    	
    	@Test
    	public void test7() {
    		Class clazz = Person.class;
    		Type type1 = clazz.getGenericSuperclass();
    		System.out.println(type1);
    	}
    	
    	
    	/**
    	 * 8.获取父类的泛型
    	 */
    	@Test
    	public void test8() {
    		Class clazz = Person.class;
    		Type type1 = clazz.getGenericSuperclass();
    		
    		ParameterizedType param= (ParameterizedType)type1;
    		Type[] ars = param.getActualTypeArguments();
    		System.out.println((Class)ars[0]);
    	}
    	
    	/**
    	 * 9.获取实现的接口
    	 */
    	@Test
    	public void test9() {
    		Class clazz = Person.class;
    		Class[] i = clazz.getInterfaces();
    		for(Class a:i) {
    			System.out.println(a);
    		}
    	}
    	
    	/**
    	 * 10.获取所在的包
    	 */
    	@Test
    	public void test10() {
    		Class clazz = Person.class;
    		Package p = clazz.getPackage();
    		System.out.println(p);
    	}
    }
    
    
    

    五、调用运行时类的指定结构

    1.调用运行时类指定的属性并赋值

    1. 获取指定的属性

    getField(String fieldName):获取运行时类中声明为public的指定的属性名为fieldName的属性

    Field name = clazz.getField("name");
    

    2.创建运行时类的对象

    Person p = (Person) clazz.newInstance();
    System.out.println(p);
    
    

    3.将运行时类的指定属性赋值

    name.set(p, "Jerry");
    System.out.println(p);
    
    

    给age赋值,private需要注意

    getDeclareField(String fieldName):获取运行时类中指明为filedName的属性

    	Field age = clazz.getDeclaredField("age");
    	//由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作
    	age.setAccessible(true);//私有的设置成可以访问的
    	age.set(p,1);
    	System.out.println(p);
    
    

    给id赋值,默认的修饰符

    	Field id = clazz.getDeclaredField("id");
    	id.set(p,10);
    	System.out.println(p);
    
    

    2.调用运行时类中指定的方法

    1.getMethod(String methodName,Class...params)获取指定的public方法,方法名,参数列表

    Class clazz = Person.class;
    Method m1 = clazz.getMethod("show");
    
    

    2.创建运行时类的对象

    Person p =(Person)clazz.newInstance();
    
    

    3.和属性相似,这里是invoke关键字里面是对象和参数列表,或许还有返回值,用Object接收

    Object returnVal = m1.invoke(p);
    System.out.println(returnVal);//没返回值的打印为null
    
    

    4.获取toString()有返回值的

    Method m2 = clazz.getMethod("toString");
    Object returnVal1 = m2.invoke(p);
    System.out.println(returnVal1);
    
    

    5.获取display()带参数的

    Method m3 = clazz.getMethod("display",String.class);
    m3.invoke(p, "china");
    
    

    6.获取info()静态的方法

    Method m4 = clazz.getMethod("info");
    m4.invoke(Person.class);
    
    

    7.获取Test() 私有的带参数的有返回值的

    Method m5 = clazz.getDeclaredMethod("Test",String.class,Integer.class);
    m5.setAccessible(true);
    Object o = m5.invoke(p,"测试",5);
    System.out.println(o);
    
    

    3.调用指定的构造器,创建类对象

    public void test3() throws InstantiationException, Exception {
    		Class clazz = Person.class;
    		Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
    		cons.setAccessible(true);
    		Person p = (Person)cons.newInstance("迪丽热巴",20);
    		System.out.println(p);
    
    		
    	}
    
    

    全部代码

    package com.atguigu.java;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    import org.junit.Test;
    
    /**
     * ******调用对应的运行时类的指定的结构(属性、方法)
     * @author MD
     *
     */
    public class TestField1 {
    	/**
    	 * 调用运行时类指定的属性
    	 * @throws Exception 
    	 * @throws NoSuchFieldException 
    	 */
    	
    	@Test
    	public void test1() throws Exception {
    		Class clazz = Person.class;
    		
    		//1.获取指定的属性
    		//getField(String fieldName):获取运行时类中声明为public的指定的属性名为fieldName的属性
    		Field name = clazz.getField("name");
    		//2.创建运行时类的对象
    		Person p = (Person) clazz.newInstance();
    		System.out.println(p);
    		//3.将运行时类的指定属性赋值
    		name.set(p, "Jerry");
    		System.out.println(p);
    		
    		//给age赋值,private需要注意
    		//getDeclareField(String fieldName):获取运行时类中指明为filedName的属性
    		Field age = clazz.getDeclaredField("age");
    		//由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作
    		age.setAccessible(true);//私有的设置成可以访问的
    		age.set(p,1);
    		System.out.println(p);
    				
    		//给id赋值,默认的修饰符
    		Field id = clazz.getDeclaredField("id");
    		id.set(p,10);
    		System.out.println(p);
    		
    	}
    		
    	/**
    	 * 调用运行时类中指定的方法
    	 * @throws Exception 
    	 * @throws NoSuchMethodException 
    	 */
    	@Test
    	public void test2() throws NoSuchMethodException, Exception {
    		Class clazz = Person.class;
    		
    		//getMethod(String methodName,Class...params)获取指定的public方法,方法名,参数列表
    		Method m1 = clazz.getMethod("show");
    		
    		//创建运行时类的对象
    		Person p =(Person)clazz.newInstance();
    		
    		//和属性相似,这里是invoke关键字里面是对象和参数列表,或许还有返回值,用Object接收
    		Object returnVal = m1.invoke(p);
    		System.out.println(returnVal);
    				
    		//获取toString()有返回值的
    		Method m2 = clazz.getMethod("toString");
    		Object returnVal1 = m2.invoke(p);
    		System.out.println(returnVal1);
    		
    		//获取display()带参数的
    		Method m3 = clazz.getMethod("display",String.class);
    		m3.invoke(p, "china");
    				
    		//获取info()静态的方法
    		Method m4 = clazz.getMethod("info");
    		m4.invoke(Person.class);
    				
    		//获取Test() 私有的带参数的有返回值的
    		Method m5 = clazz.getDeclaredMethod("Test",String.class,Integer.class);
    		m5.setAccessible(true);
    		Object o = m5.invoke(p,"测试",5);
    		System.out.println(o);	
    	}
    	
    	
    	/**
    	 * 调用指定的构造器,创建类对象
    	 * @throws Exception 
    	 * @throws InstantiationException 
    	 */
    	
    	@Test
    	public void test3() throws InstantiationException, Exception {
    		Class clazz = Person.class;	
    		Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
    		cons.setAccessible(true);
    		Person p = (Person)cons.newInstance("迪丽热巴",20);
    		System.out.println(p);
    	}
    }
    

    六、ClassLoader

    类加载器是用来把类(class)装载进内存

    1.获取一个系统类加载器

    ClassLoader loader1 = ClassLoader.getSystemClassLoader();
    System.out.println(loader1);
    

    2.获取系统类加载器的父类加载器,即扩展类加载器

    ClassLoader loader2 = loader1.getParent();
    System.out.println(loader2);
    

    3.获取扩展类加载器的父类加载器,即引导类加载器,加载的是核心库,打印为null

    ClassLoader loader3 = loader2.getParent();
    System.out.println(loader3);
    

    4.测试当前类由哪个类加载器进行加载

    Class clazz1 = Person.class;
    ClassLoader loader4 = clazz1.getClassLoader();
    System.out.println(loader4);//系统类加载器
    

    1. 描述一下JVM加载class文件的原理机制?

    JVM中类的装载是由ClassLoader和它的子类来实现的,

    Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。

    作者:山丘!

    -------------------------------------------

    你闻讯而来,我大喜过望,我在这等你,你又在哪呢?喜欢的话加一个“关注”呗!

    如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!

  • 相关阅读:
    try和catch
    获取地址栏参数(E积分项目)
    正则验证,只能输入数字,每四位隔一个空格。
    E积分项目总结(绑卡页 第一步)
    本地存储localStorage用法详解
    python os 模块介绍
    生成器迭代器
    python 魔法方法
    匿名函数
    python自定义函数和内置函数
  • 原文地址:https://www.cnblogs.com/mengd/p/13400005.html
Copyright © 2011-2022 走看看