一.类加载器的介绍
描述:
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化
加载:
就是指将class文件读入内存,并为之创建一个 java.lang.Class 对象
任何类被使用时,系统都会为之建立一个 java.lang.Class 对象
连接:
验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
准备阶段:负责为类的类变量分配内存,并设置默认初始化值
解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化:
在该阶段,主要就是对类变量进行初始化
初始化步骤:
- 假如类还未被加载和连接,则程序先加载并连接该类
- 假如该类的直接父类还未被初始化,则先初始化其直接父类
- 假如类中有初始化语句,则系统依次执行这些初始化语句
- 注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
类的初始化时机:
- 创建类的实例
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
作用:
负责将.class文件加载到内存中,并为之生成对应的 java.lang.Class 对象。虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行!
二.Java中的内置类加载器
JVM内置的类加载器,有Bootstrap加载器、ExtClassLoader加载器和AppClassLoader加载器 三种,分别负责加载不同目录下的.class文件
- Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null ,并且没有父null
- Platform class loader:平台类加载器可以看到所有平台类 ,平台类包括由平台类加载器或其祖先定义的Java SE平台API,其实现类和JDK特定的运行时类
- System class loader:它也被称为应用程序类加载器 ,与平台类加载器不同。 系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类
- 类加载器的继承关系:System的父加载器为Platform,而Platform的父加载器为Bootstrap
其中Classloader中的两个方法介绍
示例代码:
public class ClassLoaderDemo {
public static void main(String[] args) {
//static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器
ClassLoader c = ClassLoader.getSystemClassLoader();
System.out.println(c); //AppClassLoader
//ClassLoader getParent():返回父类加载器进行委派
ClassLoader c2 = c.getParent();
System.out.println(c2); //PlatformClassLoader
ClassLoader c3 = c2.getParent();
System.out.println(c3); //null
}
}
三.反射
概述:加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
获取Class类对象的三种方式:
类名.class属性
对象名.getClass()方法
Class.forName(全类名)方法
示例代码:
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
//使用类的class属性来获取该类对应的Class对象
Class<Student> c1 = Student.class;
System.out.println(c1);
Class<Student> c2 = Student.class;
System.out.println(c1 == c2);
System.out.println("--------");
//调用对象的getClass()方法,返回该对象所属类对应的Class对象
Student s = new Student();
Class<? extends Student> c3 = s.getClass();
System.out.println(c1 == c3);
System.out.println("--------");
//使用Class类中的静态方法forName(String className)
Class<?> c4 = Class.forName("com.itheima_02.Student");
System.out.println(c1 == c4);
}
}
反射获取构造方法:
Constructor类用于创建对象的方法:
反射获取成员变量:
Field类用于给成员变量赋值的方法:
反射获取成员方法:
Method类用于执行方法的方法:
这里总结一下以上所有方法以及注意事项:
package com.fanshe.Demo1;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author BaBa
* @Version 1/0
*/
public class ReflectDemo02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
Class<?> c1 = Class.forName("com.fanshe.Demo1.Student");
// System.out.println("================================================");
//所谓反射就是通过Class.forName去得到它的字节码对象,再通过该字节码对象的构造方法去创建对象,
// 再通过前一个创建的对象去使用它的方法设置它内部的变量的值,最后再获取每个值
//获取构造方法方法并使用
//Constructor<?>[] getConstructors() 返回一个包含 Constructor对象的数组, Constructor对象反映了由该 Class对象表示的类的所有公共构造函数
// Constructor<?>[] cons = c.getConstructors();
//Constructor<?>[] getDeclaredConstructors() 返回反映由该 Class对象表示的类声明的所有构造函数的 Constructor对象的数组
// Constructor<?>[] cons = c1.getConstructors();
//Constructor<T> getConstructor(Class<?>... parameterTypes) 返回一个 Constructor对象,该对象反映由该 Class对象表示的类的指定公共构造函数
//Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回一个 Constructor对象,该对象反映由此 Class对象表示的类或接口的指定构造函数
//参数:你要获取的构造方法的参数的个数和数据类型对应的字节码文件对象
// for (Constructor con : cons){
// System.out.println(con);
// }
//获取它所有的私有公有方法
// Constructor<?>[] co = c1.getDeclaredConstructors();
// for (Constructor cc : co){
// System.out.println(cc);
//
// }
//获取单个的方法
// Constructor<?> ccc = c1.getConstructor();
// System.out.println(con);
//T newInstance(Object... initargs) 使用由此 Constructor对象表示的构造函数,
// 使用指定的初始化参数来创建和初始化构造函数的声明类的新实例
// Constructor<?> c = c1.getDeclaredConstructor();
// Object o = c.newInstance();
// System.out.println(o);
//和以下该方法创建的对象一样
// Student s = new Student();
// System.out.println(s);
// System.out.println("================================================");
//通过反射获取成员变量并使用
//基本数据类型也可以通过.class得到对应的Class类型
// Constructor<?> con = c1.getConstructor(String.class, int.class, String.class);
// Object o = con.newInstance("周强", 21, "云南");
// System.out.println(o);
// Constructor<?> con1 = c1.getDeclaredConstructor(String.class, int.class);
// Object o1 = con1.newInstance("我是傻逼", 88);
// System.out.println(o1);
//私有的构造方法不能直接设置,可以通过暴力设置
// .IllegalAccessException:
// Class com.fanshe.Demo1.ReflectDemo02 can not access a member of
// class com.fanshe.Demo1.Student with modifiers "private"
// Constructor<?> con2 = c1.getDeclaredConstructor(String.class);
// Object o2 = con2.newInstance("大冤种");
// System.out.println(o2);
//暴力反射
// Constructor<?> con2 = c1.getDeclaredConstructor(String.class);
// con2.setAccessible(true);
// Object o2 = con2.newInstance("靓仔");
// System.out.println(o2);
System.out.println("================================================");
//通过反射获取成员变量并使用
//Field[] getFields() 返回一个包含 Field对象的数组, Field对象反映由该 Class对象表示的类或接口的所有可访问的公共字段
//Field[] getDeclaredFields() 返回一个 Field对象的数组,反映了由该 Class对象表示的类或接口声明的所有字段
//公有的成员变量
// Field[] f = c1.getFields();
// for (Field ff:f){
// System.out.println(ff);
// }
//私有的和公有成员变量
// Field[] fields = c1.getDeclaredFields();
// for (Field f :fields){
// System.out.println(f);
// }
// //获取公共的变量名称
// Field field = c1.getField("address");
//obj.addressField = "西安";
//Field提供有关类或接口的单个字段的信息和动态访问
// //void set(Object obj, Object value) 将指定的对象参数中由此 Field对象表示的字段设置为指定的新值
// Constructor<?> cons = c1.getConstructor();
// Object o = cons.newInstance();
// field.set(o,"苦海无涯");//给o的成员变量field赋值为苦海无涯
// System.out.println(o);
//获取私有和公共的变量名称
// Constructor<?> cons = c1.getConstructor();
// Object o = cons.newInstance();
//
//
// Field name = c1.getDeclaredField("name");
// //使用暴力反射
// name.setAccessible(true);
// name.set(o,"警花警犬");
// System.out.println(o);
//
// Field age = c1.getDeclaredField("age");
// //使用暴力反射
// age.setAccessible(true);
// age.set(o,21);
// System.out.println(o);
//
// Field address = c1.getDeclaredField("address");
// //使用暴力反射
// address.setAccessible(true);
// address.set(o,"焕颜");
// System.out.println(o);
// System.out.println("================================================");
//获取成员方法并使用
//Method[] getMethods() 返回一个包含 方法对象的数组, 方法对象反映由该 Class对象表示的类或接口的所有公共方法,
// 包括由类或接口声明的对象以及从超类和超级接口继承的类
//Method[] getDeclaredMethods() 返回一个包含 方法对象的数组, 方法对象反映由 Class对象表示的类或接口的所有声明方法,
// 包括public,protected,default(package)访问和私有方法,但不包括继承方法
// Method[] m = c1.getMethods();
// Method[] m = c1.getDeclaredMethods();
// for (Method method:m){
// System.out.println(method);
// }
//Method getMethod(String name, Class<?>... parameterTypes) 返回一个 方法对象,该对象反映由该 Class对象表示的类或接口的指定公共成员方法
//Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 Class对象
// Method method = c1.getMethod("method1");
// Constructor<?> con = c1.getConstructor();//创建对象
// obj.m();
//在类或接口上提供有关单一方法的信息和访问权限
//Object invoke(Object obj, Object... args) 在具有指定参数的指定对象上调用此 方法对象表示的基础方法
//Object:返回值类型
//obj:调用方法的对象
//args:方法需要的参数
// method.invoke(con);
Constructor<?> cons = c1.getConstructor();
Object o = cons.newInstance();
Method method = c1.getMethod("method3", String.class, int.class);
// method.setAccessible(true);
Object o1 = method.invoke(o, "我命由我不由天", 21);
System.out.println(o1);
Method method2 = c1.getMethod("method2", String.class);
// method2.setAccessible(true);
method2.invoke(o, "我自横刀向天笑");
Method function = c1.getDeclaredMethod("function");
function.setAccessible(true);
function.invoke(o);
}
}
作业展示:
利用通过反射修改私有成员变量
1. 定义PrivateTest类,有私有name属性,并且属性值为hellokitty,只提供name的getName的公有方法
2. 创建带有main方法ReflectTest的类,利用Class类得到私有的name属性
3. 修改私有的name属性值,并调用getName()的方法打印name属性值
package com.fanshe.Demo3;
/**
* @author BaBa
* @Version 1/0
*/
public class PrivateTest {
private String name = "hellokitty";
public void getName(String name) {
System.out.println(name);
}
// public PrivateTest() {
// System.out.println(this.name);
// }
}
package com.fanshe.Demo3;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author BaBa
* @Version 1/0
*/
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> c = Class.forName("com.fanshe.Demo3.PrivateTest");
Constructor<?> con = c.getConstructor();
Object o = con.newInstance();
Method name = c.getMethod("getName", String.class);
name.invoke(o,"天道");
System.out.println(o);
// Field name = c.getDeclaredField("name");
// name.set(o, "天道");
// System.out.println(name.getName());
}
}
利用反射和File完成以下功能
1. 利用Class类的forName方法得到File类
2. 在控制台打印File类的所有构造器
3. 通过newInstance的方法创建File对象,并创建D:\mynew.txt文件
package com.fanshe.Demo4;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author BaBa
* @Version 1/0
*/
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> c = Class.forName("java.io.File");
Constructor<?>[] cons = c.getDeclaredConstructors();
for (Constructor constructor : cons){
System.out.println(constructor);
}
Constructor<?> CON = c.getConstructor(String.class);
Object o = CON.newInstance("D:\\mynew.txt");
Method createNewFile = c.getMethod("createNewFile");
createNewFile.invoke(o);
}
}