· 类的加载概述和加载时机
一、类的基本组成
- 成员变量
- 构造方法
- 成员方法
二、类的加载
-
加载
将class文件读入内存,并为之创建一个Class对象。
-
链接
验证 内部结构是否正确并于其他类协调一致。
准备 为类的静态成员分配内存,并设置默认初始化值。
解析 将二进制数据中的符号引用替换为直接引用。
-
初始化
为栈、堆内存开辟空间、默认初始化、显示初始化、构造初始化。
三、类初始化时机
- 创建类的实例。
- 访问类的静态变量,或为静态变量赋值。
- 调用类的静态方法。
- 使用反射的方式强制创建某额类或接口对应的Class对象。
- 初始化某个类的子类。
- 世界使用java.exe命令运行某个主类。
· 类加载器的概述和分类
一、类加载器的职责
负责将.class文件加载到内存中,并为之生产Class对象。
二、类加载器的组成
1.Bootstrap ClassLoader 根类加载器
也称为引导类加载器,负责Java核心类的加载,比如System.String等,在JDK中JRE的lib目录下rt.jar文件中的包。
2.Extension ClassLoader 扩展类加载器。
负责加载JRE的扩展目录中的包,比如JDK中HRE的lib目录下ext目录中的包
3.System ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件以及classpath环境变量所指的的jar包和类路径,也就是我们自己写或引用进来的包。
· Java反射概述
- 反射就是通过class文件,去使用该文件中的成员变量、构造方法、成员方法,而非通过java文件
- 要想这样使用,首先必须获得class文件对象,也就是Class类对象
- 在Class类中包含成员变量、构造方法、成员方法,而对Class类而言他们都被包含在对应的类中
成员变量 Field
构造方法 Constructor
成员方法 Method - 通过类的Class类对象调用方法使用其中类(Field/Constructor/Method)中的对象去调用方法就叫反射
- 也就是不需要使用import就可以使用该类
- 好处是可以通过配置文件变更具体的引用类,不需要修改代码
· 反射中的具体操作
Person类
package cn.xxx;
public class Person {
private String name;
int age;
public String address;
public Person(){}
Person(String name){
this.name = name;
}
Person(String name,int age){
this.name = name;
this.age = age;
}
public Person(String name,int age,String address){
this.name = name;
this.age = age;
this.address = address;
}
public void show(){
System.out.println("show");
}
private void method(String s){
System.out.println(s);
}
@Override
public void toString(){
return "Person[name="+name+",age="+age+",address="+address+"]";
}
}
一、获取class文件对象的三种方法
1. Objec类中的getClass()方法
Person p1 = new Person();
Class c1 = p1.getClass();
2. 数据类型的静态属性class
Class c2 = Person.class;
3. Class类中的静态方法 public static Class forName(String className)
Class c3 = Class.forName("cn.xxx.Person");
并且:
Person p2 = new Person();
System.out.println(p1==p2); // false
System.out.println(c1==c2); // true
System.out.println(c2==c3); // true
二、通过反射获取内部方法并使用
1.获取无参构造方法并使用
A.getConstructors(),但只能获取到公共的构造方法
// 获取字节码文件对象
Class c = Class.forName("cn.xxx.Person");
Constructor[] cons = c.getConstructors();
// 打印所有构造方法
for(Constructor con : cons){
System.out.println(con);
}
// public cn.xxx.Person()
// public cn.xxx.Person(java.lang.String,int)
B.getDeclaredConstructors(),可以获取全部构造方法
Class c
// 获取字节码文件对象 = Class.forName("cn.xxx.Person");
Constructor[] cons = c.getDeclaredConstructors();
// 打印所有构造方法
for(Constructor con : cons){
System.out.println(con);
}
// public cn.xxx.Person()
// private cn.xxx.Person(java.lang.String)
// cn.xxx.Person(java.lang.String,int,java.lang.String)
// public cn.xxx.Person(java.lang.String,int)
C.getConstructor(Class<?>… parameterType),可以获取单个构造方法,但只能获取到公共的构造方法
// 获取字节码文件对象
Class c = Class.forName("cn.xxx.Person");
Constructor con = c.getConstructor();
System.out.println(con);
// public cn.xxx.Person()
// 实例化新的对象
// 1. Person p = new Person();
// 2. 使用该Constructor对象表示的构造方法的声明类的新实例,并指定的出书画参数初始化该实例
// ↑↑↑↑public T newInstance(Object... initargs)↑↑↑
//
// 通过构造方法创建对象
Constructor con1 = c.getConstructor();
Object obj = con.newInstance();
System.out.println(obj);
// Person[name=null, age=0, address=null]
下方<2.获取有参构造方法并使用>有实际运用
D.getDeclaredConstructor(Class<?>… parameterType),可以获取全部类型的构造方法
下方< 3.获取有私有参构造方法并使用>有实际运用
2.获取有参构造方法并使用
使用getConstructor(Class<?>… parameterType)
// 获取字节码文件对象
Class c = Class.forName("cn.xxx.Person");
// 通过构造方法创建对象
Constructor con = c.getConstructor(String.class, int.class, String.class);
Object obj = con.newInstance("姓名",24,"地址");
System.out.println(obj);
// Person[name=姓名, age=24, address=地址]
3.获取有私有参构造方法并使用
使用getDeclaredConstructor(Class<?>… parameterType),可以获取全部类型的构造方法
// 获取字节码文件对象
Class c = Class.forName("cn.xxx.Person");
// 此处会报NoSuchMethodException异常,没有该方法,原因是该方法为private类型
Constructor con1 = c.getConstructor(String.class);
// 所以要改用getDeclaredConstructor(Class<?>... parameterType)
Constructor con2 = c.getDeclaredConstructor(String.class);
// 但直接使用该私有发放创建对象依旧会报错--IllegalAccessException非法访问,
Object obj1 = con.newInstance("姓名");
// 所以要使用setAccessible修改对象的accessible标识
// pubilc void setAccessible(boolean falg) :
// 将此对象的accessible标识设置为指示的布尔值,
// 值为true则指示反射的对象在使用时应取消Java语言访问检查,
// 值为false则指示反射的对象应该实施Java语言访问检查
con.setAccessible(true);
Object obj2 = con.newInstance("姓名");
System.out.println(obj2);
// Person[name=姓名, age=0, address=null]
三、通过反射获取成员变量并使用
1.getFields()
同通过反射获取内部方法
2.getDeclaredFields();
同通过反射获取内部方法
3.getField(String fieldName);
同通过反射获取内部方法
4.getDeclaredField(String fieldName);
同通过反射获取内部方法
5.对成员变量进行具体操作
// 获取字节码文件对象
Class c = Class.forName("cn.xxx.Person");
// 通过无参构造方法创建对象
Constructor con = c.getConstructor();
Object obj = con.newInstance();
// 获取public成员变量
// 获取address并对其赋值
Field addressField = c.getField("address");
// 使用public void set(Object obj,Object value)
// 将指定对象变量上此Field对象表示的字段设置为指定的新值
addressField .set(obj,"地址");
// Person[name=null, age=0, address=地址]
// 获取private成员变量
// 获取age并对其赋值
Field ageField = c.getDeclaredField("age");
// 使用public void set(Object obj,Object value)
// 将指定对象变量上此Field对象表示的字段设置为指定的新值
ageField.setAccessible(true);
ageField.set(obj,24);
// Person[name=null, age=24, address=null]
四、通过反射获取成员方法并使用
1.getMethods()
同通过反射获取内部方法
2.getDeclaredMethods();
同通过反射获取内部方法
3.getMethod(String methodName);
同通过反射获取内部方法
4.getDeclaredMethod(String methodName);
同通过反射获取内部方法
5.对成员变量进行具体操作
// 获取字节码文件对象
Class c = Class.forName("cn.xxx.Person");
// 通过无参构造方法创建对象
Constructor con = c.getConstructor();
Object obj = con.newInstance();
// public Method getMethod(String methodName, Class<?>... parameterTypes)
// methodName方法名,parameterTypes方法的参数class的类型
Method m1 = c.getMethod("show");
// public Object invoke(Object obj,Object... args)
// 第一个参数表示对象是谁,第二参数表示调用该方法的实际参数
m1.invoke(obj);
// show
Method m2 = c.getMethod("method",String.class);
m2.setAccessible(true);
m2.invoke(obj,"hello");
// hello