引言
介绍java的反射部分,这个部分的知识很有必要学习。
类加载器(了解)
什么是类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,
初始化三步来实现对这个类进行初始化。
一个类在加载过程中的三部曲:
1.加载
就是指将class文件读入内存,并为之创建一个Class对象.
任何类被使用时系统都会建立一个Class对象。
2.连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
3.初始化 就是我们以前讲过的初始化步骤
类的加载时机(掌握)
1、创建类的实例
2、访问类的静态变量,或者为静态变量赋值
3、调用类的静态方法
4、使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5、初始化某个类的子类
6、直接使用java.exe命令来运行某个主类
加载器分类
类加载起的作用?
负责将.class文件加载到内在中,并为之生成对应的Class对象。
类加载器的分类
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
Sysetm ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件
反射
Student.java–Student.class(字节码文件)–看成一个对象,这个对象就叫字节码文件对象–对应的类Class
什么是反射?
答:通过字节码文件对象去使用成员。
图解如下:
注意:每一个类会对应一个字节码文件对象,而这个字节码文件对象就是这个类的原型,每一个类有且仅有一个字节码文件对象。
获取字节码文件对象的三种方式:
A:Object类的getClass()方法
B:数据类型的静态class属性
C:Class类的静态方法forName()
注意:
在平常写案例的时候,我们直接使用第二种最方便。
但是实际开发中,我们一般用的都是第三种。是因为第三种接收的是一个字符串类型的参数,
我们可以把这个参数作为配置文件的内容进行配置,这样就实现了一个变化的内容。
案例:使用上述三种方式获取类的Class对象
//person
package com.stu_02;
public class Person {
private String name;
private int age;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address
+ "]";
}
public Person(String name, int age, String address) {
super();
this.name = name;
this.age = age;
this.address = address;
}
public Person() {
super();
// TODO Auto-generated constructor stub
}
//创建一个成员方法
public void method(){
System.out.println("method");
}
void function(int price){
System.out.println(price);
}
private void show(String husband,String wife){
System.out.println(husband+"--"+wife);
}
}
//测试
package com.stu_02;
/**
* 获取字节码文件对象的三种方式:
A:Object类的getClass()方法
B:数据类型的静态class属性
C:Class类的静态方法forName()
注意:
在平常写案例的时候,我们直接使用第二种最方便。
但是实际开发中,我们一般用的都是第三种。是因为第三种接收的是一个字符串类型的参数,
我们可以把这个参数作为配置文件的内容进行配置,这样就实现了一个变化的内容。
案例:使用上述三种方式获取类的Class对象
*/
public class ReflectStu {
public static void main(String[] args) throws ClassNotFoundException {
//A:Object类的getClass()方法
Person p1 = new Person();
Person p2 = new Person();
Class c1 = p1.getClass();
Class c2 = p2.getClass();
System.out.println(p1==p2);//false
System.out.println(c1==c2);//true
//B:数据类型的静态class属性
Class c3= Person.class;
System.out.println(c3==c2);//true
//C:Class类的静态方法forName()
Class c4 = Class.forName("com.stu_02.Person");
System.out.println(c4==c3);//true
}
}
运行结果在注释中显示
反射的使用步骤
Class:
成员变量 Field
构造方法 Constructor
成员方法 Method
反射:
class字节码文件对象 – 去得到对应的成员对象 – 通过该成员的对象调用方法使用
1.通过反射获取构造方法
public Constructor[] getConstructors() 获取公共的构造方法
public Constructor[] getDeclaredConstructors() 获取所有的构造方法(包括私有)
public Constructor getConstructor(Class… parameterTypes) 根据构造参数获取公共的指定构造
public Constructor getDeclaredConstructor(Class… parameterTypes) 根据构造参数获取指定构造(包括私有,但是私有在使用的时候需要取消访问限制)
2.通过反射获取构造方法并创建对象
public T newInstance(Object… initargs)
3.通过反射获取成员变量并使用
public Field[] getFields()获取公有的成员变量
public Field[] getDeclaredFields()获取全部的成员变量,包括私有
public Field getDeclaredField(String name) 传入变量名称返回指定的成员变量对象,包括私有
public Field getField(String name)传入变量名称返回指定的成员变量对象,仅可获取共有的
public void set(Object obj,Object value)给一个对象的一个字段设置一个值
参数1:需要设置的对象
参数2:需要给这个对象设置什么值
4.通过反射获取成员方法并使用
public Method[] getMethods()获取所有公共成员方法
public Method[] getDeclaredMethods()获取所有成员方法,包括私有
public Method getMethod(String name, Class… parameterTypes)参数一:方法名 参数二:方法参数类型.class 获取指定的公共方法
public Method getDeclaredMethod(String name,Class… parameterTypes)
参数二:方法参数类型.class 获取指定的方法,包括私有
Object invoke(Object obj, Object… args) 让某一个对象使用这个方法,并且传入参数
在这里,简单的举一个通过反射获取构造方法的例子:
package com.stu_03;
import java.lang.reflect.Constructor;
/**
* 1.通过反射获取构造方法
public Constructor[] getConstructors() 获取公共的构造方法
public Constructor[] getDeclaredConstructors() 获取所有的构造方法(包括私有)
public Constructor getConstructor(Class... parameterTypes) 根据构造参数获取公共的指定构造
public Constructor getDeclaredConstructor(Class<?>... parameterTypes) 根据构造参数获取指定构造(包括私有,但是私有在使用的时候需要取消访问限制)
2.通过反射获取构造方法并创建对象
public T newInstance(Object... initargs)
*/
public class ConstractStu {
public static void main(String[] args) throws Exception {
//public Constructor[] getConstructors() 获取公共的构造方法
Class<?> c1 = Class.forName("com.stu_02.Person");
// Constructor[] cons = c1.getConstructors();
// for (Constructor con : cons) {
// System.out.println(con);//public com.stu_02.Person()
// }
//public Constructor[] getDeclaredConstructors() 获取所有的构造方法(包括私有)
// Constructor[] cons2 = c1.getDeclaredConstructors();
// for (Constructor<?> con : cons2) {
// System.out.println(con);
// }
/*
* public com.stu_02.Person()
private com.stu_02.Person(java.lang.String,int,java.lang.String)
protected com.stu_02.Person(java.lang.String,int)
com.stu_02.Person(java.lang.String)
*/
// public Constructor getConstructor(Class... parameterTypes) 根据构造参数获取公共的指定构造
// Constructor cons3 = c1.getConstructor();
// System.out.println(cons3);
// Object obj = cons3.newInstance();
// System.out.println(obj);
/*
* public com.stu_02.Person()
Person [name=null, age=0, address=null]
*/
//public Constructor getDeclaredConstructor(Class<?>... parameterTypes)
//根据构造参数获取指定构造(包括私有,但是私有在使用的时候需要取消访问限制)
// Constructor cons4 = c1.getDeclaredConstructor(String.class,int.class,String.class);
// cons4.setAccessible(true);
// Object obj2 = cons4.newInstance("薛之谦",35,"上海");
// System.out.println(obj2);
//Person [name=薛之谦, age=35, address=上海]
Constructor cons5 = c1.getDeclaredConstructor(String.class,int.class);
cons5.setAccessible(true);
Object obj3 = cons5.newInstance("黄晓明",36);
System.out.println(obj3);//Person [name=黄晓明, age=36, address=null]
}
}
相关运行结果在代码中有注释