反射基础详解

类加载器

概述

当我们要使用一个类的时候,这个类还未被加载到内存中,就会启动类加载器

类加载器是负责加载类的对象。将class文件(硬盘)加载到内存生成Class对象。

类加载器会做3件事情:

​ 1.加载

​ a.会把要使用的类(Person.class文件)加载到内存中

​ b.会为class文件,创建一个对象,这个对象叫class文件对象

​ ⒉连接

​ 3.类的初始化

类的初始化

  1. 创建类的实例

  2. 类的静态变量,或者为静态变量赋值

  3. 类的静态方法

  4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

  5. 初始化某个类的子类

  6. 直接使用java.exe命令来运行某个主类

在这里插入图片描述

类加载器的组成与继承关系

类加载器的组成

  • BootstrapClassLoader:根类加载器

    ​ 也被称为引导类加载器,负责Java核心类的加载

    ​ 比如System,String等。

  • ExtClassLoader:扩展类加载器

    ​ 负责JRE的扩展目录中jar包的加载。

    ​ 在JDK中JRE的lib目录下ext目录

  • AppClassLoader:系统类加载器

    ​ 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

类加载器的继承关系

  • 类加载器之间的继承关系:

    AppClassLoader extends ExtClassLoader extends BootstrapClassLoader(c语言) extends ClassLoader

  • 所有的类加载器都是 java.lang.ClassLoader 的子类

在这里插入图片描述

  • 类加载器加载机制:全盘负责委托机制

    ​ 全盘负责:A类如果要使用B类(在内存中不存在),A类加载器必须负责加载B类。

    在这里插入图片描述

    委托机制:A类加载器如果要加载资源B,必须询问父类加载是否加载。

    ​ 如果加载,将直接使用。

    ​ 如果没有加载,自己再加载。

  • 采用全盘负责委托机制保证一个class文件只会被加载一次,形成一个Class对象。

类加载器的获取

java.lang.ClassLoader:类加载器是负责加载类的对象。负责把类加载到内存中,并为类创建一个class文件对象

获取类加载器的方式:

/* Class类中的方法 */
ClassLoader getClassLoader()	 // 返回该类的类加载器

/* ClassLoader类中的成员方法 */
ClassLoader getParent() 		// 返回委托的父类加载器

示例

import sun.security.ec.SunEC;

public class Demo01ClassLoader {
    public static void main(String[] args) {
    	//直接获取根类加载器
        ClassLoader c = String.class.getClassLoader();
		System.out.println(c);//null

        //直接获取扩展类加载器
        ClassLoader c = SunEC.class.getClassLoader();
        System.out.println(c);//sun.misc.Launcher$ExtClassLoader@7ea987ac
        
        //获取类加载器
        Class clazz = Demo01ClassLoader.class;
        ClassLoader c1 = clazz.getClassLoader();
        System.out.println(c1);//sun.misc.Launcher$AppClassLoader@18b4aac2

        ClassLoader c2 = c1.getParent();
        System.out.println(c2);//sun.misc.Launcher$ExtClassLoader@4554617c

        ClassLoader c3 = c2.getParent();
        System.out.println(c3);//null 根类加载器,不是java语言编写的,获取不到
    }
}

反射

概述

反射前提:类已经进入到内存中生成class文件对象

反射:使用class文件对象,对class文件进行解剖获取class文件中的成员变量、成员方法、构造方法

反射的好处:提高代码的复用性。对于任意的一个类,都可以使用相同的方法进行解剖。框架的底层大量的使用了反射技术 ==> 框架灵魂

获取class文件对象的方式

class文件对象是由类加载器创建的,我们无权创建和销毁,我们可以获取class文件对象,使用class文件对象中的方法 ==> 实现反射

技术反射:使用class文件对象中的方法对内存中的class文件进行解剖

​ 获取class文件中的成员变量,成员方法,构造方法,这就是反射

**获取class文件对象有三种方式:**class文件对象是由类加载器创建的,具有唯一性,我们通过3种方式获取的都是同一个对象

  • 使用Object类中的方法getClass获取

    Class<?> getClass() 	// 返回此 Object 的运行时类。
    
  • java会为每种数据类型都赋予一个class属性,这个class属性返回的就是class文件对象

    基本数据类型: int.class,double.class,char.class,boolean.class…

    引用数据类型: int[].class,ArrayList.class,String.class,Person.class

  • 可以使用Class类中的静态方法forName获取

    static Class<?> forName(String className) 		// 返回与带有给定字符串名的类或接口相关联的 Class 对象
    /* 参数:
    		String className:全类名(包名+类名) 可以确定类的唯一性
    		com.cormorant.demo01init.Person
       注:数据库 jdbc技术:使用Class类中的forName方法,获取class文件对象,会执行类中的静态代码块
    */
    

Class类常用方法

String getSimpleName() 			// 获取类名
String getName() 				// 获取全类名(包名+类名)
ClassLoader getClassLoader() 	// 返回该类的类加载器

    
// 获取类的指定公共构造方法
Constructor<T> getConstructor(Class<?>... parameterTypes)
// 获取类或接口的指定构造方法。(包含公共、保护、默认(包)访问和私有方法)
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
/* 参数:
		...可变参数:调用的方法,参数是可变参数,实际参数可以传递任意个(不传递,1,2,3,4,...)
		Class<?>... parameterTypes:传递构造方法参数列表的class文件对象
			public Person() ==>() 没有参数不传递
            public Person(String name, int age, String sex)==>(String.class,int.class,String.class)
            private Person(String name, int age)==>(String.class,int.class)
   
    注意:类中没有指定的构造方法,会抛出NoSuchMethodException:没有这个方法异常
*/
Constructor<?>[] getConstructors()				// 获取类的所有公共构造方法
Constructor<?>[] getDeclaredConstructors()  	// 获取类声明的所有构造方法(包含公共、保护、默认(包)访问和私有方法)


// 获取指定公共成员方法
Method getMethod(String name, Class<?>... parameterTypes)
// 获取指定已声明方法(包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法)
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
/*	参数:
		String name:要获取的方法的名称
		Class<?>... parameterTypes:方法参数列表的class文件对象

	注意:获取指定的方法不存在,会抛出NoSuchMethodException:没有这个方法异常
*/
Method[] getMethods() 			// 获取本类|父类|接口继承的所有公共的成员方法
Method[] getDeclaredMethods()  	// 获取本类声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法
    

Class<?>[] getInterfaces() 		// 确定此对象所表示的类或接口实现的接口。

使用反射技术实例化对象

java.lang.reflect.Constructor:描述构造方法的类

成员方法:

T newInstance(Object... initargs) 	// 使用构造方法实例化对象(创建)
/*	参数:
		Object... initargs:传递创建对象使用的实际参数  new Person("张三", 18, "男");
	返回值:
   		T:返回创建好的对象,类型默认使用Object类型   Object obj = new Person("张三", 18, "男");

	私有构造方法是没有权限使用的,要使用会抛出IllegalAccessException:非法访问异常
	解决:可以使用Constructor类的父类AccessibleObject类中的方法解决
		java.lang.reflect.AccessibleObject类
			void setAccessible(boolean flag) 将此对象的 accessible 标志设置为指示的布尔值。
			    值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
			    值为 false 则指示反射的对象应该实施 Java 语言访问检查。
	注意:暴力反射不推荐,破坏了类的封装性(私有,不让外界使用)
*/

示例

import java.lang.reflect.Constructor;

/*
	使用反射技术获取构造方法并实例化对象
*/
public class Demo02Constructor {
    public static void main(String[] args) throws Exception {
        /* 使用反射技术获取无参构造方法并实例化对象的简化方式 */
        //1.获取Person类的class文件对象
        Class clazz = Class.forName("com.cormorant.demo03reflect.Person");
        //Object obj = clazz.getConstructor().newInstance();
        //2.直接使用class文件对象中的方法newInstance实例化对象
        Object obj = clazz.newInstance();
        System.out.println(obj);//Person{name='null', age=0, sex='null'}
        //如果想要使用Person特有的方法,需要向下转型
        Person p = (Person)obj;
        p.setName("周华健");
        System.out.println(p.getName());
        
        /* 使用反射技术获取有参构造方法并实例化对象 */
		Constructor con2 = clazz.getConstructor(String.class, int.class, String.class);
        Object obj2 = con2.newInstance("张三", 18, "男");	//此方法就相当于 new Person("张三", 18, "男");
        System.out.println(obj2);	//Person{name='张三', age=18, sex='男'}
        
        
        /* 暴力反射:私有构造方法实例化对象 */
        Constructor con3 = clazz.getDeclaredConstructor(String.class, int.class);
        con3.setAccessible(true);	//取消java语言的访问检查==>暴力反射
        Object obj3 = con3.newInstance("老王", 60);
        System.out.println(obj3);	//Person{name='老王', age=60, sex='null'}
    }
}

使用反射技术运行类的成员方法

java.lang.reflect.Method:描述成员方法的类

String getName() 	// 以 String 形式返回此 Method 对象表示的方法名称
    
Object invoke(Object obj, Object... args)  运行成员方法
/*	参数:
		Object obj:传递一个对象,运行哪个类中的方法,就需要传递哪个类的对象
					运行Person类中的方法,需要传递Person对象(反射快速创建对象的方式获取)
		Object... args:运行方法需要传递的实际参数
	返回值:
		Object:成员方法的返回值
				方法的返回值类型不是void,Object就是方法的返回值
				方法的返回值类型是void,方法没有返回值,返回null
		
	私有方法没有权限运行,会抛出非法访问异常:IllegalAccessException
    解决:暴力反射,使用Method的父类AccessibleObject类中的方法setAccessible取消java的语言访问检查
*/

示例

package com.itheima.demo03reflect;

import java.lang.reflect.Method;

/*
    使用反射技术获取类中的成员方法,并运行获取到成员方法(重点)
 */
public class Demo03Method {
    public static void main(String[] args) throws Exception {
        //1.获取Person类的class文件对象
        Class clazz = Class.forName("com.itheima.demo03reflect.Person");

        //2.使用class文件对象中的方法getMethod|getMethods获取类中的(公共)成员方法Method
        //public String getName()
        Method getNameMethod = clazz.getMethod("getName");
        System.out.println(getNameMethod);	
        		/* public java.lang.String com.itheima.demo03reflect.Person.getName() */
        //public void setName(String name)
        Method setNameMethod = clazz.getMethod("setName", String.class);

        //3.使用成员方法Method类中的方法invoke,运行获取到的方法
        Object obj = clazz.newInstance();	//new Person();
        //public void setName(String name)
        Object v2 = setNameMethod.invoke(obj,"柳岩");//就相当于使用setName方法给成员变量name赋值
        System.out.println("v2:"+v2);	//v2:null setName方法没有返回值
        Object v3 = getNameMethod.invoke(obj);//就相当于使用getName方法获取成员变量name的值
        System.out.println("v3:"+v3);	//v3:柳岩

        
        /*
            使用暴力反射运行私有方法:使用Method的父类AccessibleObject类中的方法setAccessible取消java的语言访问检查
         */
        showMethod.setAccessible(true);
        showMethod.invoke(obj);	//Person类的私有成员方法!
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨鸦_Cormorant

大家喜欢的话可以点个关注投币哟

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值