类加载过程
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
类加载
- 类加载就是指将class文件从硬盘读入内存,并为之创建一个Class对象
- 任何类被使用时系统都会建立一个Class对象(Class对象就是类的字节码,字节码中包括类的属性、构造、方法等)
连接
- 验证:是否有正确的内部结构,并和其他类协调一致
- 准备:负责为类的静态成员分配内存,并设置默认初始化值
- 解析:将类的二进制数据中的符号引用替换为直接引用
初始化
-
对象初始化
类加载的时机
- 创建类的实例
- 使用类的静态变量,或者为静态变量赋值
- 使用类的静态方法
- 使用反射方式来强制创建某个类或接口对应的
java.lang.Class
对象
- 使用反射方式来强制创建某个类或接口对应的
- 初始化某个类的子类
- 直接使用
java.exe
命令来运行某个主类
- 直接使用
类加载器
java.lang.ClassLoader
类是类的加载器。类加载器负责将.class文件加载到内在中,并为之生成对应的Class对象。
类加载后,在JVM的元空间中就有了类的Class对象(类的字节码),Class对象是供我们来使用的。最常用的使用方式是new对象,还可以通过反射来使用Class对象。
反射
AVA反射机制是在<font color='red'>运行时</font>,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象。
Class类
Class类是java.lang
包中的类,Class对象表示类的字节码,每个加载到元空间中的.class文件都是一个Class对象。
获得Class对象的三种方法:
- 通过Object类的getClass()获得对象
语法:obj.getClass()
User user = new User();
Class clazz = user.getClass();//通过对象,获取User类的Class对象
- 通过类名获得Class对象
语法: 类名.class
Class clazz = User.class;//通过类名,获取User类的Class对象
- 通过Class.forName()获得Class对象
语法:Class.forName(类路径)
Class clazz = Class.forName("com.sunmer.User");
通过反射,获取构造方法
在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor类表示。可通过Class类中提供的方法获取构造方法:
返回一个构造方法 | |
---|---|
Constructor<T> |
getConstructor(类<?>... parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法 |
Constructor<T> |
getDeclaredConstructor(类<?>... parameterTypes) 获取所有修饰, 指定参数类型所对应的构造方法 |
-
getConstructor
:根据参数匹配,获取public的构造函数 -
getDeclaredConstructor
:根据参数匹配,获取构造函数,包括private的
返回多个构造方法 | |
---|---|
Constructor<T>[] |
getConstructors() 获取public修饰的所有构造方法 |
Constructor<T>[] |
getDeclaredConstructors() 获取所有修饰的构造方法 |
-
getConstructors
:获取public的所有构造函数 -
getDeclaredConstructors
:获取所有的构造函数,包括private的
如下:
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//获取Class对象
Class c = Class.forName("com.sunmer.reflect.Person");//包名.类名
System.out.println("---获取所有的构造方法,包含private的---");
Constructor[] cons1 = c.getDeclaredConstructors();
for (Constructor con : cons1) {
System.out.println(con);
}
System.out.println();
System.out.println("---获取所有的构造方法,不包含private的---");
Constructor[] cons2 = c.getConstructors();
for (Constructor con : cons2) {
System.out.println(con);
}
System.out.println();
System.out.println("---获取一个无参构造方法---");
Constructor con3 = c.getConstructor(null);
System.out.println(con3);
System.out.println();
System.out.println("---获取一个String参数的构造方法---");
Constructor con4 = c.getConstructor(String.class);
System.out.println(con4);
System.out.println();
System.out.println("---获取private的,有String和int参数的构造方法---");
Constructor con5 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(con5);
System.out.println();
System.out.println("---获取private的,有String、int、String参数的构造方法---");
Constructor con6 = c.getDeclaredConstructor(String.class, int.class, String.class);
System.out.println(con6);
}
}
通过反射,获取构造方法,创建对象
步骤如下:
- 获取到Class对象
- 获取指定的构造方法
- 通过构造方法类Constructor中的方法,创建对象
public T newInstance(Object... initargs)
/*
* 通过反射,获取构造方法,创建对象
*/
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//1:获取到Class对象
Class c = Class.forName("com.sunmer.reflect.Person");//包名.类名
//2:获取构造方法
Constructor con = c.getConstructor(String.class, int.class, String.class);
//3,通过构造方法,创建对象
Object obj = con.newInstance("扈三娘", 22, "扈家庄");
//4:显示对象
System.out.println(obj);
}
}
运行结果
带有String, int, String的构造方法
Person [name=扈三娘, age=22, address=扈家庄]
通过反射,获取私有构造方法,创建对象
步骤如下:
获取到Class对象
获取指定的构造方法
暴力访问, 通过setAccessible(boolean flag)方法
-
通过构造方法类Constructor中的方法,创建对象
public T newInstance(Object... initargs)
/*
* 通过反射,获取私有构造方法,创建对象
*/
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//1:获取到Class对象
Class c = Class.forName("com.sunmer.reflect.Person");//包名.类名
//2:获取私有构造方法
Constructor con = c.getDeclaredConstructor(String.class, int.class);
//3:暴力访问, 取消 Java 语言访问检查,因为默认private不允许类外访问
con.setAccessible(true);
//4:通过构造方法,创建对象
Object obj = con.newInstance("扈三娘", 22);
//5:显示对象
System.out.println(obj);
}
}
运行结果
带有String,int的构造方法
Person [name=扈三娘, age=22, address=null]
通过反射,获取成员变量
在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量:
返回一个成员变量 | |
---|---|
Field |
getField(String name) 获取指定的 public修饰的成员变量 |
Field |
getDeclaredField(String name) 获取指定的任意成员变量 |
返回多个成员变量 | |
---|---|
Field[] |
getFields() 获取所有public 修饰的成员变量 |
Field[] |
getDeclaredFields() 获取所有的成员变量 (包含私有) |
代码演示:
/*
* 通过反射,获取成员变量
*/
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//1:获取到Class对象
Class c = Class.forName("com.sunmer.reflect.Person");//包名.类名
System.out.println("---获取多个成员变量,包含private的---");
Field[] fields1 = c.getDeclaredFields();
for (Field field : fields1) {
System.out.println(field);
}
System.out.println();
System.out.println("---获取多个public的成员变量---");
Field[] fields2 = c.getFields();
for (Field field : fields2) {
System.out.println(field);
}
System.out.println();
System.out.println("---获取一个public的成员变量---");
Field ageField = c.getField("age");
System.out.println(ageField);
System.out.println();
System.out.println("---获取private的成员变量---");
Field addressField = c.getDeclaredField("address");
System.out.println(addressField);
}
}
通过反射,对成员变量进行赋值与取值操作
步骤如下:
- 获取Class对象
- 获取构造方法
- 通过构造方法,创建对象
- 获取指定的成员变量(私有成员变量,通过setAccessible(boolean flag)方法暴力访问)
- 通过方法,给指定对象的指定成员变量赋值或者获取值
- public void set(Object obj, Object value)
在指定对象obj中,将此 Field 对象表示的成员变量设置为指定的新值 - public Object get(Object obj)
返回指定对象obj中,此 Field 对象表示的成员变量的值
- public void set(Object obj, Object value)
代码演示:
/*
* 通过反射,对成员变量进行赋值与取值操作
*/
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//1:获取到Class对象
Class c = Class.forName("com.sunmer.reflect.Person");//包名.类名
//2:获取构造方法
Constructor con = c.getConstructor(String.class);
//3:通过构造方法,创建对象
Object obj = con.newInstance("孙二娘");
//4:获取指定的成员变量
Field nameField = c.getField("name");
Field ageField = c.getField("age");
Field addressField = c.getDeclaredField("address");
addressField.setAccessible(true);
//5:通过方法,从指定对象的指定成员变量取值
System.out.println("-------------赋值前-----------");
System.out.println("name = "+ nameField.get(obj));
System.out.println("age = "+ ageField.get(obj));
System.out.println("address = "+ addressField.get(obj));
//5:通过方法,为指定对象的指定成员变量赋值
ageField.set(obj, 23);
addressField.set(obj, "十字坡");
System.out.println("-------------赋值后-----------");
System.out.println("name = "+ nameField.get(obj));
System.out.println("age = "+ ageField.get(obj));
System.out.println("address = "+ addressField.get(obj));
}
}
通过反射,获取成员方法
在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法。
返回一个成员方法 | |
---|---|
Method |
getMethod(String name, Class<?>... parameterTypes) 获取public 修饰的方法 |
Method |
getDeclaredMethod(String name, Class<?>... parameterTypes) 获取任意的方法,包含私有的 |
返回多个成员方法 | |
---|---|
Method[] |
getMethods() 获取本类与父类中所有public 修饰的方法 |
Method[] |
getDeclaredMethods() 获取本类中所有的方法(包含私有的) |
如下:
/*
* 通过反射,获取成员方法
*/
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//1:获取到Class对象
Class c = Class.forName("com.sunmer.reflect.Person");//包名.类名
System.out.println("---获取本类定义的所有的方法,包括private的---");
Method[] methods1 = c.getDeclaredMethods();
for (Method method : methods1) {
System.out.println(method);
}
System.out.println();
System.out.println("---获取本类及父类所有public的方法---");
Method[] methods2 = c.getMethods();
for (Method method : methods2) {
System.out.println(method);
}
System.out.println();
System.out.println("---获取无参方法---");
Method method1 = c.getMethod("method1", null);
System.out.println(method1);
System.out.println();
System.out.println("---获取有一个String参数的方法---");
Method method2 = c.getMethod("method4", String.class);
System.out.println(method2);
System.out.println();
System.out.println("---获取私有方法---");
Method method3 = c.getDeclaredMethod("method5", null);
System.out.println(method3);
}
}
通过反射,创建对象,调用指定的方法
步骤如下:
获取Class对象
获取构造方法
-
通过构造方法,创建对象
- 获取指定的方法
- 执行找到的方法
- public Object invoke(Object obj, Object... args)
执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。
代码演示:
/*
* 通过反射,创建对象,调用指定的方法
*/
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//1:获取到Class对象
Class c = Class.forName("com.sunmer.reflect.Person");//包名.类名
//2:获取构造方法
Constructor con = c.getConstructor(String.class, int.class, String.class);
//3:通过构造方法,创建对象
Object obj = con.newInstance("扈三娘", 22, "扈家庄");
//4:获取指定的方法
Method method = c.getMethod("method4", String.class);
//5:执行找到的方法
//method.invoke(obj, null);//若方法无参,则传入null
Object result = method.invoke(obj, "张清");
System.out.println("result = " + result);
}
}
运行结果
带有String, int, String的构造方法
有返回值,有参数的方法
result = 返回值:张清
通过反射,创建对象,调用指定的private 方法
步骤如下:
- 获取Class对象
- 获取构造方法
- 通过构造方法,创建对象
- 获取指定的方法
- 开启暴力访问
- 执行找到的方法
- public Object invoke(Object obj, Object... args)
执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。
- public Object invoke(Object obj, Object... args)
/*
* 通过反射,创建对象,调用指定的 private 方法
*/
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//1:获取到Class对象
Class c = Class.forName("com.sunmer.reflect.Person");//包名.类名
//2:获取构造方法
Constructor con = c.getConstructor(String.class, int.class, String.class);
//3:通过构造方法,创建对象
Object obj = con.newInstance("扈三娘", 22, "扈家庄");
//4:获取指定的方法
Method method = c.getDeclaredMethod("method5", null);
//5:开启暴力访问
method.setAccessible(true);
//6,执行找到的方法
method.invoke(obj, null);
}
}
泛型擦除
分析:泛型集合在添加元素时,是有类型检查的,字符串是不能添加到 Integer泛型的集合中的。
泛型集合的类型检查是在源码中进行的,在.class中已经没有泛型约束了,这种现象我们称为泛型的擦除。那么,我们可以通过反射技术,来完成向有泛型约束的集合中,添加任意类型的元素。
**如下:**
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
ArrayList<Integer> list = new ArrayList<Integer>();
//添加元素到集合
list.add(new Integer(110));
list.add(new Integer("120"));
list.add(119);
//list.add("时迁");//报错,因为有泛型类型的约束
System.out.println(list);
//通过反射技术,实现添加任意类型的元素
//1:获取字节码文件的Class对象
Class c = list.getClass();
//2:找到add()方法
Method addMethod = c.getMethod("add", Object.class);
//3:执行add()方法
addMethod.invoke(list, "时迁");// list.add("时迁");
//4:显示集合内容
System.out.println(list);
System.out.println(list.get(3));
}
}
[110, 120, 119]
[110, 120, 119, 时迁]
时迁