类的加载
/*
类的加载 -- 参考 类的加载过程
当程序要使用某个类时,如果该类还未被加载到内存中,
则系统会通过类的加载,类的连接,类的初始化三步来实现对这个类进行初始化。
类的加载
就是指将class文件(字节码文件)读入内存,并为之创建一个Class对象。
任何类被使用时系统都会建立一个java.lang.Class对象。
读取class文件时,加载了(执行了)static
类的连接
验证:是否有正确的内部结构,并和其他类协调一致
准备:负责为类的静态成员分配内存,并设置默认初始化值
解析:将类的二进制数据中的符号引用(方法区中被加载的方法)替换为直接引用(指针、方法标记)
类的初始化
就是我们以前讲过的初始化步骤
*/
/*
类加载的注意事项和使用
注意事项:初始化
1.当类还未被加载和连接时,程序先加载并连接该类
2.该类的直接父类未被初始化,先初始化直接父类
3.假如类中有初始化语句(非static修饰)依次执行初始化语句
执行步骤2时,同样遵循1-3
使用
创建类的实例
操作类或相关的类变量
调用类方法
初始化子类
直接使用java.exe运行某个主类
用反射方式强制创建类或接口对应的Class对象
*/
理解
/*
在Java代码中,类型的加载、连接与初始化过程都是在程序运行期间完成的
类型:可以理解为一个class
加载:查找并加载类的二进制数据,最常见的情况是将已经编译完成的类的class文件从磁盘加载到内存中
连接:确定类型与类型之间的关系,对于字节码的相关处理
验证:确保被加载的类的正确性
准备:为类的静态变量分配内存,并将其初始化为默认值。但是在到达初始化之前,类变量都没有初始化为真正的初始值
解析:在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用转换为直接引用的过程
初始化:为类的静态变量赋予正确的初始值
使用:比如创建对象,调用类的方法等
卸载:类从内存中销毁
理解:public static int number = 666;
上面这段代码,在类加载的连接阶段,为对象number分配内存,并初始化为0;然后再初始化阶段在赋予正确的初始值:666
*/
类加载器
/*
Java加载类时,都是通过类加载器来进行加载
作用:
负责将class文件加载到内存中,并生成対应的Class对象
机制:
全盘负责:
当类加载器负责加载某一个class文件时,与之相关的
class文件也由该类加载器负责,除非显示由另一个加载器负责
父类委托:
加载某一个class文件时,先让父类的加载器试图加载该
class文件,只有在父类无法加载该class文件时,才尝试从自己的类
路径加载
缓存机制:
会把所有加载的class文件生成的Class对象进行缓存
当程序需使用某一个Class对象时,类加载器首先去缓存中搜索,
缓存中不存在时,才会将class文件加载到内存中,并生成对应的
Class对象
*/
反射
/*
反射就是在运行状态(测试类-main方法所在类)中,对于任意一个类,都能够知道这个类的所
有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。
而这也是Java被视为动态(或准动态,为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,
允许改变程序结构或变量类型,这种语言称为动态语言)语言的一个关键性质。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.
所以先要获取到每一个字节码文件对应的Class类型的对象.
*/
/*
Java反射机制原理探究
Class类:Java程序在编译完成后,会把所有class文件中所包含的类的基本元信息装载到JVM内存中,
以Class类的形式保存,每一个Class类对象代表一个具体类的基本元信息。我们的反射就是在Class类的基础上进行的,
Class类对象存储着类的所有相关信息,就像镜子,故称“反射”。
Field:即类或对象的域,就是属性值
Method:类或对象的方法
Constructor:类或对象的构造器,使用它可以构造出相应的类对象
反射基本原理
整体流程
调用反射的总体流程如下:
准备阶段:编译期装载所有的类,将每个类的元信息保存至Class类对象中,每一个类对应一个Class对象
获取Class对象:调用x.class/x.getClass()/Class.forName() 获取x的Class对象clz(这些方法的底层都是native方法,
是在JVM底层编写好的,涉及到了JVM底层,就先不进行探究了)
进行实际反射操作:通过clz对象获取Field/Method/Constructor对象进行进一步操作
整体过程中,需要注意的是进行实际反射操作的这个阶段,我们需要关注的点有:
我们是如何通过Class获取到Field/Method/Construcor的?
获取到的Field是如何具有对象属性值的?
获取到的Method是如何调用的?
*/
反射能做什么?
/*
我们知道反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,
包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。
那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;
还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!!
*/
通过反射获取源文件对应的Class对象
public class Student {
private String name;
private int age;
}
/**
* 在运行状态(main所在的类),可以获取任意一个类,当中的属性和方法
* 获取任意一个类,类的加载(加载的是class文件)加载成了一个与之对应的Class对象
* 可以通过当前Class对象获取class文件(源文件)的代码
* 反射可以通过代码创建对象
*/
public class Test {
public static void main(String[] args) throws Exception {
//反射获取源文件对应的Class对象(常用:方式1、3)
//1.方式一 类名.class属性
//类的加载 默认会生成一个与之对应的Class对象
//Class<Student> studentClass = Student.class;
// 2.方式2 对象的实例.getClass();
//Object类中:
//类<?> getClass() 返回此 Object的运行时类。
//Student student = new Student();
//Class<? extends Student> aClass = student.getClass();
//方式3 通过Class类中的forName()方法
//static 类<?> forName(String className)
//返回与给定字符串名称的类或接口相关联的类对象。
//className:类的路径从包名开始
Class<?> aClass = Class.forName("com.shujia.recl01.Student");
System.out.println(aClass);
// 通过三种方式获取类的Class对象,可以通过Class对象看到类中的代码(对象没有创建)
//Class类中:
//public T newInstance() 创建由此类对象表示的类的新实例。
Student o = (Student) aClass.newInstance();
//通过反射创建类的对象的实例
System.out.println(o);
}
}
反射与new创建实例的区别
![](https://img2020.cnblogs.com/blog/2648122/202201/2648122-20220103225310437-1835915229.png)
通过反射获取成员变量
public class F {
public String demo;
private String test;
}
public class Student extends F{
public String id;
private String name;
private int age;
public String sex;
}
/*
通过反射获取成员变量
Field[] getFields();获取所有公共成员变量的数组
Field[] getDeclaredFields();获取所有成员变量的数组
Field getField(String name);返回单个公共成员变量的对象
Field getDeclaredField(String name);返回单个成员变量的对象
*/
public class Test {
public static void main(String[] args) throws Exception{
//获取源文件对应的Class对象
Class<?> aClass = Class.forName("com.shujia.recl02.Student");
// Field[] getFields();获取所有公共成员变量的数组(可以获取父类中的公共属性)
// Field[] fields = aClass.getFields();
// for (Field field : fields) {
// System.out.println(field);
// }
// Field[] getDeclaredFields();
// 获取所有成员变量的数组(不能获取父类中的属性)
// Field[] declaredFields = aClass.getDeclaredFields();
// for (Field declaredField : declaredFields) {
// System.out.println(declaredField);
// }
// Field getField(String name);
// 返回单个公共成员变量的对象(可以获取父类中的公共属性)
// Field id = aClass.getField("id");
// System.out.println(id);
// Field name = aClass.getField("name");
// System.out.println(name); //获取不到私有的
Field demo = aClass.getField("demo");
System.out.println(demo);//可以获取到父类中的公共成员变量
// Field getDeclaredField(String name);
// 返回单个成员变量的对象(不能获取父类中的属性)
Field name = aClass.getDeclaredField("name");
System.out.println(name);//可以获取到自己的私有成员变量
Field demo = aClass.getDeclaredField("demo");
System.out.println(demo);//不可以获取到父类的公共成员变量
}
}
通过反射修改成员变量的值
public class Student {
private String name;
public String age;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
/*
Field类中:
通过反射赋值
void set(Object obj,Object value):给obj对象的成员赋值value
Accessable属性是继承自AccessibleObject类.功能是启用或禁用安全检查
绕过类型安全检测机制
setAccessible(true);
*/
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Exception{
//获取源文件对应的Class对象
Class<?> aClass = Class.forName("com.shujia.field.Student");
//通过反射创建实例
Student student = (Student) aClass.newInstance();
//获取成员
Field name = aClass.getDeclaredField("name");
System.out.println(name);
//通过反射赋值
//void set(Object obj,Object value):给obj对象的成员赋值value
//name.set(student,"test");//此时会报错--私有的成员变量不能赋值
//System.out.println(student);
//私有的成员不能赋值,那么公共的呢?
//获取成员
Field age = aClass.getDeclaredField("age");
System.out.println(age);
age.set(student,"test");//此时却可以
System.out.println(student);
//我既然能获取私有的成员变量,为啥不能赋值呢?
//想要赋值,就要在赋值之前,绕过类型安全检测机制
name.setAccessible(true);//绕过类型安全检测机制
name.set(student,"test");//之后就可以赋值了
System.out.println(student);
}
}
反射获取成员方法
public class User {
public void test1(){
System.out.println("test1");
}
public void test2(String name){
System.out.println("test2---1");
}
public void test2(int name){
System.out.println("test2---2");
}
private void test3(){
System.out.println("test3");
}
private void test4(Object name){
System.out.println("test4");
}
}
import java.lang.reflect.Method;
import java.util.ArrayList;
/*
获取所有方法
方法[] getMethods()
返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象,
包括那些由类或接口和那些从超类和超接口继承的声明。
方法[] getDeclaredMethods()
返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 类对象,
包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
获取单个方法
方法 getMethod(String name, 类<?>... parameterTypes)
返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象
方法 getDeclaredMethod(String name, 类<?>... parameterTypes)
返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 类对象。
*/
public class Test {
public static void main(String[] args) throws Exception{
Class<?> aClass = Class.forName("com.shujia.method.User");
User user = (User)aClass.newInstance();
//获取所有的公共方法包括继承自父类的
// Method[] methods = aClass.getMethods();
// for (Method method : methods) {
// System.out.println(method);
// }
//获取当前类所有方法
// Method[] methods = aClass.getDeclaredMethods();
// for (Method method : methods) {
// System.out.println(method);
// }
// Method[] getDeclaredMethod(String name,Class<?>... parameterTypes);
// 返回单个成员方法的对象,(方法的名字,参数类型的Class对象)
Method test1 = aClass.getDeclaredMethod("test2",String.class);
System.out.println(test1);
Method test2 = aClass.getDeclaredMethod("test2",int.class);
System.out.println(test2);
//需求:对test4进行传值
Method test3 = aClass.getDeclaredMethod("test4",Object.class);
System.out.println(test3);
test3.setAccessible(true);//绕过类型安全检测机制
//Method类中
//Object invoke(Object obj, Object... args)
//在具有指定参数的 方法对象上调用此 方法对象表示的底层方法。
test3.invoke(user,"10");
}
}
通过反射获取构造方法
public class User {
public User(){
System.out.println("无参");
}
public User(String name,String age){
System.out.println("有参");
}
private User(String name){
System.out.println("私有");
}
@Override
public String toString() {
return super.toString();
}
}
import java.lang.reflect.Constructor;
/*
获取构造方法
Constructor<T> getConstructor(类<?>... parameterTypes)
返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 类函数。
Constructor<?>[] getConstructors()
返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 类对象。
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定 类函数
Constructor<?>[] getDeclaredConstructors()
返回一个反映 Constructor对象表示的类声明的所有 Constructor对象的数组 类 。
创建对象
T newInstance()
创建由此 类对象表示的类的新实例。
con.newInstance(“zhangsan", 20);
*/
public class Test {
public static void main(String[] args) throws Exception{
Class<?> aClass = Class.forName("com.shujia.cur.User");
//之前创建对象
// Object o = aClass.newInstance();// 默认执行无参构造
// //获取当前类所有构造方法
// Constructor<?>[] constructors = aClass.getDeclaredConstructors();
// for (Constructor<?> constructor : constructors) {
// System.out.println(constructor);
// }
//获取当前类构造方法 --想要有参的构造方法就在()里加上参数类型的Class对象
//不加就是无参的
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class);
System.out.println(declaredConstructor);
declaredConstructor.setAccessible(true);//绕过安全检测机制
Object test = declaredConstructor.newInstance("test");//这里执行有参构造
// Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
// System.out.println(declaredConstructor);
// Object test = declaredConstructor.newInstance();//这里执行无参构造
//这种方式与上面的 Object o = aClass.newInstance() 没有区别
}
}
通过反射绕过泛型的安全检测机制案例
import java.lang.reflect.Method;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) throws Exception{
ArrayList<Integer> strings = new ArrayList<>();//这里的add(String e)方法已经被泛型限定了
strings.add(123);
strings.add(456);
// 通过反射获取strings对象的Class对象
// 获取ArrayList中所有代码其中add(Object e)方法没有被泛型限定
Class<? extends ArrayList> aClass = strings.getClass();
//通过反射获取add方法
Method add = aClass.getDeclaredMethod("add", Object.class);
//添加元素
add.invoke(strings,"789abc");
System.out.println(strings);
}
}