反射
先简简单单的说下类的加载
类的加载
在程序运行后,首次使用某个类,会把这个类的字节码文件读取到内存,并且会将这个类的所有信息存到一个Class对象中。
简单步骤:
1:jvm执行代码,例如读到 new Student()
2 : 读取Student的字节码文件
3:把字节码文件的内容(读取成字节码对象)存储到方法区(以前的说法),1.8以后叫做元空间(貌似)
4:最后类加载完成以后,在堆空间创建内存空间。
类的加载时机(下面六种)
- 创建类的实例。(new 一个类)
- 类的静态变量,或者为静态变量赋值。
- 类的静态方法。
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
- 初始化某个类的子类。
- 直接使用java.exe命令来运行某个主类。(编译 javac 类名.java, 运行 java.类名)
今天的反射也属于类加载时机的一种
先上代码:
public class Student {
public static int thinging;
private int id;
private String name;
public static void get(){
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
static {
System.out.println("使用了静态代码块");
}
}
类加载时机代码演示
/**
* // 1. 创建类的实例。 静态代码块随着类的加载而加载,并且只执行一次。
*/
@Test
public void checkNew(){
new Student();
}
/**
*
* 2. 类的静态变量,或者为静态变量赋值。
*/
@Test
public void checkStaticFiled(){
Student.thinging=1;
}
/**
* 3. 类的静态方法。
*/
@Test
public void checkStaticMethod(){
Student.get();
}
/**
* 4 反射 通过 Class.forName("全类名") 进行反射
*/
@Test
public void checkReflect(){
try {
Class.forName("Student");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 5.通过子类实例
*/
@Test
public void checkExtends(){
new Reflect(); //这个类继承了Student
}
运行结果
都证明了这个stu类被加载了。
类加载器(暂时先了解了解)
下面说说类加载器,类加载器是负责将实际空间中的某个class文件读取到内存中并且生成Class的对象。
Java中有三种类加载器,它们分别用于加载不同种类的class:
Java中有三种类加载器,它们分别用于加载不同种类的class:
-
启动类加载器(Bootstrap ClassLoader):用于加载系统类库<JAVA_HOME>\bin目录下的class,例如:rt.jar。
-
扩展类加载器(Extension ClassLoader):用于加载扩展类库<JAVA_HOME>\lib\ext目录下的class。
-
应用程序类加载器(Application ClassLoader):用于加载我们自定义类的加载器。
进去代码看看
我们是如何获得类加载器的
在点进getClassLoader()方法看看jdk是怎么描述的
图中看到,一些实现可能使用null来表示引导类加载器,可能在此类中的实现返回null.
举个例子。
好了,接下来该回到反射的主题
反射的概念
它一种机制,利用这个机制就可以在程序中对类进行解析,并且可以操作类中所有成员(变量,方法,构造方法)。
使用反射需要一个前提,获取需要反射的那个类的字节码对象。
Class对象的获取方式
- 通过类名.class获得
- 通过对象名.getClass()方法获得
- 通过Class类的静态方法获得: static Class forName(“类全名”)
上代码
/**
* 通过类名.class来获取。
*/
@Test
public void getClassByDotClass(){
Class<Student> studentClass = Student.class;
try {
studentClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/**
*
* 通过对象名.getClass()来获取
*/
@Test
public void getClassByGetClass(){
Student student = new Student();
student.getClass();
}
/**
*
* 通过Class的静态方法ClassForName("全类名")
*/
@Test
public void getClassByForName(){
try {
Class.forName("Student");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
Tips:每个类的Class对象都只有一个
@Test
public void isTheSame(){
try {
System.out.println(Student.class==Class.forName("Student"));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
Class类常用的方法
String getSimpleName(); 获取类名字符串
String getName(); 获取全类名字符串
/**
*
* 获取类名:getSimpleName()
* 获取全类名:getName()
*/
@Test
public void ClassApi(){
Class<Student> studentClass = Student.class;
System.out.println("Class的类名是:"+studentClass.getSimpleName());
System.out.println("Class的全类名是:"+studentClass.getName());
}
创建类的对象
1.T newInstance(): 创建Class对象关联类的对象。
/**
*
* T newInstance() ; 创建Class对象关联类的对象
*/
@Test
public void getStudentByInstance(){
try {
Student student = Student.class.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
2.通过Constructor类来创建对象
首先要获取构造器类
Class类中与Constructor相关的方法
-
Constructor getConstructor(Class… parameterTypes)
* 根据参数类型获得对应的Constructor对象。getConstructor()的源码描述 ```Java @CallerSensitive public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); return getConstructor0(parameterTypes, Member.PUBLIC); } ``` 上个使用的代码 ```Java /** * 先用getConstructor() 获取Constructor<T>类对象 * 再通过这个对象实例T。 没有参数的用法 * @throws Exception */ @Test public void getPublicConstructorWithoutParameter() throws Exception { Class<Student> studentClass = Student.class; //先获取无参构造类对象。 Constructor<Student> constructor=studentClass.getConstructor(); //再通过这个实例方法,实例这个类 Student student = constructor.newInstance(); } /** * 先用getConstructor() 获取Constructor<T>类对象 * 再通过这个对象实例T。 有参数的用法 * @throws Exception */ @Test public void getPublicConstructor() throws Exception { Class<Student> studentClass = Student.class; //获取有参构造类 Constructor<Student> constructor=studentClass.getConstructor(int.class); //输入参数实例化Student类 Student student = constructor.newInstance(1); } 如图所述,如果传入了没有相关参数重载的构造器,会报NoSuchMethodException异常
-
Constructor getDeclaredConstructor(Class… parameterTypes)
* 根据参数类型获得对应的Constructor对象 * 可以是public、protected、(默认)、private修饰符的构造方法。 * 如果是被protected、(默认)、private修饰符修饰的构造器,需要确定本身能否访问到,如果不能访问到就要设置权限,用setAccessible(true)。
/** * 暴力获取构造器,能获取包括public protected default private * 通过getDeclaredConstructor()暴力获取Student类中除了public其他修饰符的构造器,但这个构造类对象需要设置权限,setAccessible(true) * 具体要不要设置,得看这个修饰符本身能否访问到相关资源。 */ @Test public void getAnotherAccessConstructor() throws Exception { Class<Student> studentClass = Student.class; Constructor<Student> constructor = studentClass.getDeclaredConstructor(String.class); constructor.setAccessible(true); Student student = constructor.newInstance("PETER"); System.out.println(student); }
图上是个反例。
-
Constructor[] getConstructors()
获得类中的所有构造方法对象,只能获得public的上代码:
/** * * 通过getConstructors()获取所有public修饰的构造器类对象。 * */ @Test public void getAllPublicConstructor(){ Class<Student> studentClass = Student.class; Constructor<Student>[] constructors = (Constructor<Student>[]) studentClass.getConstructors(); for (Constructor<Student> constructor : constructors) { System.out.println(constructor); } }
遍历打印的结果:
-
Constructor[] getDeclaredConstructors()
获得类中的所有构造方法对象可以是public、protected、(默认)、private修饰符的构造方法。
/** * * 通过getDeclaredConstructors()获取所有p修饰的构造器类对象。 * */ @Test public void getAllAccessConstructor(){ Class<Student> studentClass = Student.class; Constructor<Student>[] constructors = (Constructor<Student>[]) studentClass.getDeclaredConstructors(); for (Constructor<Student> constructor : constructors) { System.out.println(constructor); } }
遍历打印的结果:
#### 获取成员方法
接下来的部分就不放代码演示了,就提供下api。
1 **Method getMethod(String name,Class...args);**
方法名和参数类型获得对应的成员方法对象,只能获得public的
2 **Method getDeclaredMethod(String name,Class...args);**
根据方法名和参数类型获得对应的成员方法对象,包括public、protected、(默认)、private的
3 **Method[] getMethods();**
获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的
4**Method[] getDeclaredMethods();**
获得类中的所有成员方法对象,返回数组,只获得本类的,包括public、protected、(默认)、private的
#### 通过反射访问成员变量
Method对象常用方法
* **Object invoke(Object obj, Object... args)**
* 调用指定对象obj的该方法
* args:调用方法时传递的参数
* void setAccessible(true)
设置"暴力访问"——是否取消权限检查,true取消权限检查,false表示不取消
#### 通过反射获取类的成员变量
Class类中与Field相关的方法
* **Field getField(String name);**
* 根据成员变量名获得对应Field对象,只能获得public修饰
* **Field getDeclaredField(String name);**
* 根据成员变量名获得对应Field对象,包括public、protected、(默认)、private的
* **Field[] getFields();**
* 获得所有的成员变量对应的Field对象,只能获得public的
* **Field[] getDeclaredFields();**
* 获得所有的成员变量对应的Field对象,包括public、protected、(默认)、private的
#### 通过反射访问成员变量
Field对象常用方法
**void set(Object obj, Object value)**
**void setInt(Object obj, int i)**
**void setLong(Object obj, long l)**
**void setBoolean(Object obj, boolean z)**
**void setDouble(Object obj, double d)**
**Object get(Object obj)
int getInt(Object obj)**
**long getLong(Object obj)**
**boolean getBoolean(Object ob)**
**double getDouble(Object obj)**
**void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。**
**Class getType(); 获取属性的类型,返回Class对象。**
今天的学习就到这里了,如果有理解的错误地方,请各位大佬提醒我哦。