一.类加载器
-
类加载:
就是将class文件读入内存,并为之创建一个java.lang.Class对象
任何类被使用时,系统都会为之建立一个java.lang.Class对象 -
类的连接:
· 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
· 准备阶段:负责为类的变量分配内存,并设置默认初始化值
· 解析阶段:将类的二进制数据中的符号引用替换为直接引用 -
类的初始化:
在该阶段,主要就是对类变量进行初始化
类的初始化步骤:
(1) 假如类还未被加载和连接,则程序先加载类并连接该类
(2) 假如类的直接父类还未被初始化,则先初始化其直接父类
(2) 假如类中有初始化语句,则系统依次执行这些初始化语句
注意:在执行第二个步骤时,系统对直接父类的初始化步骤也遵循初始化步骤1-3 -
类的初始化时机:
(1) 创建类的实例
(2) 调用类的类方法
(3) 访问类或者接口的类变量,或者为该类变量赋值
(4) 使用反射的方式来强制创建某个类或接口对应的java.lang.Class对象
(5) 初始化某个类的子类
(6) 直接使用java.exe命令来运行某个主类 -
类加载的作用:
负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象
样例:
//ClassLoader中的两个方法
public class ClassLoaderDemo {
public static void main(String[] args) {
//static ClassLoader getSystemClassLoader() 返回用于委派的类系统加载器
ClassLoader c = ClassLoader.getSystemClassLoader();
System.out.println(c);
// CLassLoader getParent() 返回父类加载器
ClassLoader c2 = c.getParent();
System.out.println(c2);
ClassLoader c3 = c2.getParent();
System.out.println(c3);
}
}
输出:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@330bedb4
null
二.反射(Reflect)
2.1 获取Class的方式
- 使用类的class属性来获取该类对应的Class对象
- 调用对象的getClass()方法,返回该对象所属类对应的Class对象,
该方法是Object类中的方法,所有的Java对象都可以调用该方法; - 使用Class类中的静态方法forName(String className),该方法需传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径。
首先准备一个Student类作为测试类
public class Student {
private String name;
int age;
public String address;
public Student() {
}
private Student(String name) {
this.name = name;
}
Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
private void function(){
System.out.println("function");
}
public void method1(){
System.out.println("method");
}
public void method2(String s){
System.out.println("method:" + s);
}
public String method3(String s,int i){
return s + "," + i;
}
public void study(){
System.out.println("I am student");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
//1.使用类的class属性来获取该类对应的Class对象
Class<Student> c1 = Student.class;
System.out.println(c1);
Class<Student> c2 = Student.class;
System.out.println(c1==c2);
System.out.println("--------");
//2.调用对象的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("review.reflect.Student");
System.out.println(c1==c4);
}
}
输出:
class review.reflect.Student
true
--------
true
--------
true
2.2 通过反射获取构造方法
-
获取构造方法
(1) Constructor<?>[] getConstructors() 返回一个包含Constructor对象的数组,Constructor对象反映了由该Class对象表示的类所有公共构造函数;
(2) Constructor<?>[] getDeclaredConstructors()返回由该Class对象表示的类声明的所有构造函数的Constructor对象的数组;
(3) Constructor<T> getConstructor(Class<?>… parameterTypes) 返回一个Constructor对象,该对象反映了由该Class对象表示的类的指定公共构造函数;
(4) Constructor<T> getDeclaredConstructor(Class<?>… parameterTypes) 返回一个Constructor对象,该对象反映了由该Class对象表示的类或接口的指定构造函数(参数:要获取的构造方法的参数的个数和数据类型对应的字节码文件对象)。
-
使用 :创建对象
(1) T newInstance(Object… initargs) 使用由此Constructor对象表示的构造函数,使用指定初始化参数来创建和初始化构造函数
这里的传的initargs参数相当于使用常规的的创建对象 Student s = new Student(“姓名”,年龄,“住址”) 里需要传递的参数,若创建的对象不需要参数,则无需传参。
public class ReflectDemo {
public static void main(String[] args) throws Exception {
//获取Class对象
Class<?> c = Class.forName("review.reflect.Student");
//Constructor<?>[] getConstructors()
Constructor<?>[] cons = c.getConstructors();
for (Constructor con:cons) {
System.out.println(con);
}
System.out.println("--------");
//Constructor<?>[] getDeclaredConstructors()
Constructor<?>[] dcons = c.getDeclaredConstructors();
for (Constructor con:dcons) {
System.out.println(con);
}
System.out.println("--------");
//Constructor<T> getConstructor(Class<?>... parameterTypes)
// Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
Constructor<?> con = c.getConstructor();
//Constructor提供了一个类的单个构造函数的信息和访问权限
//T newInstance(Object... initargs)
Object obj = con.newInstance();
System.out.println(obj);
}
}
输出:
public review.reflect.Student(java.lang.String,int,java.lang.String)
public review.reflect.Student()
--------
public review.reflect.Student(java.lang.String,int,java.lang.String)
review.reflect.Student(java.lang.String,int)
private review.reflect.Student(java.lang.String)
public review.reflect.Student()
--------
Student{name='null', age=0, address='null'}
2.3 通过反射获取成员变量
1.获取成员变量
(1) Field[] getFields() 返回一个包含Field对象的数组,Field对象反应由该Class对象表示的类或接口的所有可访问的公共字段
(2) Field[] getDeclaredFields() 返回一个Field对象的数组,反映了由该Class对象表示的类或接口声明的所有字段
(3) Field getField(String name) 返回一个Field对象,该对象反应由该Class对象表示的类或接口的指定的可访问的公共字段
(4) Field getDeclaredField(String name) 返回一个Field对象,该对象反应由该Class对象表示的类或接口的指定的字段
2.为参数赋值
(1) void set(Object obj,Object value) 将指定的对象参数中由此Field对象表示的字段设置为指定的新值
public class ReflectDemo {
public static void main(String[] args) throws Exception {
//获取Class对象
Class<?> c = Class.forName("review.reflect.Student");
//Field[] getFields()
//Field[] getDeclaredFields()
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("--------");
//Field[] getField(String name)
//Field[] getDeclaredField(String name)
Field addressField = c.getField("address");
//获取无参构造方法创建对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//Field提供由关类或接口的单个字段的信息和动态访问
//void set(Object obj,Object value)
addressField.set(obj,"郑州"); //给obj这个对象的成员变量addressField赋值
System.out.println(obj);
}
}
输出:
private java.lang.String review.reflect.Student.name
int review.reflect.Student.age
public java.lang.String review.reflect.Student.address
--------
Student{name='null', age=0, address='郑州'}
2.4 通过反射获取成员方法
1.获取成员方法
(1) Method[] getMethods() 返回一个包含Method对象的数组,Method对象反应由该Class对象表示的类或接口的公共方法,包括由类或接口声明的对象以及从超类和超级接口继承的类;
(2) Method[] getDeclaredMethods() 返回一个包含Method对象的数组,Method对象反应由该Class对象表示的类或接口的所有方法;
(3) Method getMethod(String name,Class<?>… parameterTypes) 返回一个Method对象,Method对象反应由该Class对象表示的类或接口指定的公共方法;
(4) Method getDeclaredMethod(String name,Class<?>… parameterTypes) 返回一个Method对象,Method对象反应由该Class对象表示的类或接口指定的所有方法。
2.通过反射使用成员方法
(1) Object invoke(Object obj,Object… args) 在具有指定参数的指定对象上调用此Method对象表示的基础方法;
Object : 返回值类型 obj : 调用方法的对象 args : 方法需要的参数
public class ReflectDemo {
public static void main(String[] args) throws Exception{
//获取Class对象
Class<?> c = Class.forName("review.reflect.Student");
//Method[] getMethods()
//Method[] getDeclaredMethods()
//Method[] methods = c.getMethods();
Method[] methods = c.getDeclaredMethods();
for (Method method:methods) {
System.out.println(method);
}
System.out.println("---------");
//Method getMethod(String name,Class<?>... parameterTypes)
//Method getDeclaredMethod(String name,Class<?>... parameterTypes)
Method m = c.getMethod("method1");
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//调用方法
m.invoke(obj);
}
}
输出:
public java.lang.String review.reflect.Student.toString()
private void review.reflect.Student.function()
public java.lang.String review.reflect.Student.method3(java.lang.String,int)
public void review.reflect.Student.study()
public void review.reflect.Student.method2(java.lang.String)
public void review.reflect.Student.method1()
---------
method
2.5 暴力反射
就是通过反射的来获取Class类的私有构造方法,成员变量,成员方法,
将 public void setAccessible(boolean flag) 的值设置为true,即可取消访问检查,访问此类的所有构造方法,成员变量,成员方法。
//暴力反射
public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("review.reflect.Student");
//获取私有构造方法
Constructor<?> con = c.getDeclaredConstructor(String.class);
con.setAccessible(true);
Object obj = con.newInstance("洛基");
System.out.println(obj);
System.out.println("-----------------------");
//获取私有成员变量
Field name = c.getDeclaredField("name");
name.setAccessible(true);
name.set(obj,"索尔");
System.out.println(obj);
System.out.println("-----------------------");
//获取私有成员方法
Method function = c.getDeclaredMethod("function");
function.setAccessible(true);
function.invoke(obj);
}
}
输出:
Student{name='洛基', age=0, address='null'}
-----------------------
Student{name='索尔', age=0, address='null'}
-----------------------
function
正常情况下,无法将字符串元素通过集合的add()方法将其添加至ArrayList<Integer>类型的集合中,但是通过反射调用List集合中的私有方法便可将字符串插入。
//将字符串添加至ArrayList<Integer>中
public class ReflectDemo02 {
public static void main(String[] args) throws Exception {
ArrayList<Integer> array = new ArrayList<>();
array.add(10);
array.add(20);
// array.add("hello");
System.out.println(array);
Class<? extends ArrayList> c = array.getClass();
Method m = c.getMethod("add", Object.class);
m.invoke(array,"hello");
m.invoke(array,"java");
System.out.println(array);
}
}
输出:
[10, 20]
[10, 20, hello, java]
2.6 通过配置文件运行类中的方法
这样做的好处就是可以方便的维护代码,提高代码的执行效率,解决了要运行不同类的方法时来回更改代码的操作,只需更改配置文件的参数即可使程序运行指定的类中的方法。
在项目的src目录下创建文件class.txt
className=review.reflect.Student
methodName=study
Code:
public class ReflectDemo {
public static void main(String[] args) throws Exception {
//加载数据
Properties prop = new Properties();
FileReader fr = new FileReader("src\\class.txt");
prop.load(fr);
fr.close();
String className = prop.getProperty("className");
String methodName = prop.getProperty("methodName");
//通过反射来使用
Class<?> c = Class.forName(className);
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
Method m = c.getMethod(methodName);
m.invoke(obj);
}
}
输出:
I am student