类加载器
概述
当我们要使用一个类的时候,这个类还未被加载到内存中,就会启动类加载器。
类加载器是负责加载类的对象。将class文件(硬盘)加载到内存生成Class对象。
类加载器会做3件事情:
1.加载
a.会把要使用的类(Person.class文件)加载到内存中
b.会为class文件,创建一个对象,这个对象叫class文件对象
⒉连接
3.类的初始化
类的初始化
- 创建类的实例
- 类的静态变量,或者为静态变量赋值
- 类的静态方法
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用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类的私有成员方法!
}
}