Java反射
# Java反射
# 类加载
# 类的生命周期
类一共有7生命周期的阶段分别为:
加载
- 通过类的 全限定名 找到 .class字节码文件,加载至JVM中
- JVM 在内存中生成该类的Class对象,作为入口(JVM不管多少次使用该类,而 JVM 只会生成一个该对象的Class对象)
验证
- 文件格式:检验是否符合 .class文件、头文件等
- 元数据:(以下说明)
- 字节码:检验 代码 语句/逻辑 是否符合
- 字符引用:检验 类路径/修饰符的使用 是否符合
准备 如果该类有类对象(类被加载生成的Class对象),则开始为该类进行分配内存空间以及赋予初始化的值。例如:
// 该阶段是 赋予的值是初始值0,而不是赋值18 public static int age = 18; // 因为常量,赋予的值不是初始值,而是赋值18 public static final int age = 18;
解析 将常量池的 符号引用 替换为 直接引用
初始化
- 执行类构造器
- 如果有 静态变量,静态代码块,则在上一阶段被执行
- 如果有 父类,则先执行其父类的构造器
使用 调用实例的 属性/方法
卸载 JVM垃圾回收
元数据:
- 是否有父类
- 是否继承了不允许被继承的类 (final修饰)
- 如果该类不是抽象类,是否实现其 父类/接口 中的实现方法
- 类中的 字段/方法 是否无误
# 加载器
负责把 .class字节码文件 加载到 JVM内存 中,并且生成对象
JVM中默认有三种类加载器:
- 启动类加载器 BootstrapClassLoader
- 扩展类加载器 ExtensionClassLoader
- 应用类加载器 App ClassLoader
# 启动类加载器
启动类加载器 BootstrapClassLoader,Java类加载器是根据类全限定名(包含有包路径)来读取类的 二进制字节流 到 JVM 中,然后加载到JVM的内存中
- 主要加载 jre/lib 核心库
- 通过 C++ 进行创建(本地系统语言代码)
- 开发者不能直接 调用该库进行操作
# 扩展类加载器
扩展类加载器 ExtensionClassLoader 是Java编写,且它的父类加载器是启动类加载器
由 sun.misc.Launcher$ExtClassLoader类
实现,主要加载 JAVA_HOME/lib/ext
目录中的类库(也可以更改系统里的环境变量,指定路径目录)
- 主要加载 lib/ext 下的库文件
- 通过 Java执行
- 开发者 可以使用标准扩展类加载器
# 应用类加载器
应用类加载器 App ClassLoader,且它的父类加载器是 扩展类加载器
由 sun.misc.Launcher$AppClassLoader类
实现,主要加载应用程序 java -classpath
/-Djava.class.path
目录下所有 jar和class文件
- 主要加载 开发者 自己编写的类
- 通过 Java执行
类加载器间的关系
- 启动类加载器,由C++实现,没有父类
- 扩展类加载器,由Java实现,父类加载器 启动类加载器
- 应用类加载器,由Java实现,父类加载器 扩展类加载器
类加载器的唯一性
JVM中有两个类,判断两类是否相同,前提两类是同一个加载器加载的!否则它们不是同一个类
# 双亲委派
它们并非是通常的类继承关系,而是采用组合关系来复用父类加载器的方式加载!
工作原理
类加载器收到类加载请求,它并不会自己去加载,而是把请求委托给父类的加载器去执行加载,如果父类加载器还存在有父类加载器,则进一步向上委托,以此传递请求委托,直至 顶层的启动类加载器。如果顶层加载器加载失败,则将请求返回给 子加载器 尝试自己去加载,依旧向下传递委托。如果加载器完成类加载,则直接成功返回,无需传递请求!委派的好处就是避免有些类被重复加载
故事描述: (坑爹)
他爸生有两个儿子,儿子特别懒,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子只好自己想办法去完成。。。
顶层类加载器是ClassLoader类
, 是一个抽象类,loadClass()方法是ClassLoader类自己实现的,该方法中的逻辑就是双亲委派模式的实现,代码示例:
protected synchronized Class<?> loadClass(String name , boolean resolve) throws ClassNotFoundException{
//检查是否被加载
Class c = findLoadedClass(name);
//如果没加载,则调用父类加载器
if(c == null){
try{
//父类加载器不为空
if(this.parent != null){
// 请求委托给父类
c = this.parent.c(name , false);
}else{
//父类加载器为空,则使用启动类加载器(执行到这里代表加载到顶层加载器)
c = findBootstrapClassOrNull(name);
}
}catch(ClassNotFoundException e){
//如果父类加载器加载失败,则抛回给子类运行
c = findClass(name);
}
}
if(resolve){
resolveClass(c);
}
return c;
}
# 反射
Java反射机制,可以在程序中访问 已经写好类和方法 的Java对象的描述,实现访问、检测、修改Java本身的信息 等功能
- 运行中获取
- 解析类
- 操作类
# 类对象
java.lang.Class<T>
类对象 是描述类的类
获取对象类的类的前提,必须该类在运行时已经加载至内存(JVM在加载器会自动加载)
获取类对象的方式
- 静态方法 ==Class.forName(全限定名)==
- 对象方法 ==实例对象.getClass()==
- 对象属性 ==实例对象.class==
注意:
- 在调用时, 如果类在内存中不存在, 则会加载到内存! 如果类已经在内存中存在, 不会重复加载, 而是重复利用 !
- Class类对象 不管获取多少个相同的对象,它们的地址始终都是一样
package com.sans;
// 类的获取
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
Class class1 = Class.forName("com.sans.People");
Class class2 = com.sans.People.class;
People people = new People();
Class class3 = people.getClass();
System.out.println(class1);
System.out.println(class2);
System.out.println(class3);
// 比较地址
System.out.println(class1 == class2);
System.out.println(class1 == class3);
System.out.println(class2 == class3);
}
}
class People{}
/** 控制台结果
class com.sans.People
class com.sans.People
class com.sans.People
true
true
true
*/
# 构造方法
java.lang.reflect.Constructor<T>
反射应用的 构造方法 对象
获取构造方法 的前提 需要获取指定类的对象。通过类对象的方法获取构造方法
获取构造方法:
返回 | 方法 | 说明 |
---|---|---|
Constructor | ==getConstructor(Class<?>···parameterTypes)== 示例:==getConstructor(int.class,String.class)== | 获取 权限为public的指定构造方法 |
Constructor[] | ==getConstructors()== | 获取 所有权限为public的构造方法 |
Constructor | ==getDeclaredConstructor(Class<?>···parameterTypes)== | 获取 所有权限的单个构造方法 |
Constructor[] | ==getDeclaredConstructors()== | 获取 所有权限的全部构造方法,按声明顺序排列(所有权限) |
Constructor类 常用方法: (获取更多方法自行API)
返回 | 方法 | 说明 |
---|---|---|
String | ==getName()== | 获取 构造方法的名字 |
Class[] | ==getParameterTypes()== | 获取 构造方法参数类型的数组 |
boolean | ==isVarArgs()== | 是否带有可变数量的参数 |
Class[] | ==getExceptionTypes()== | 获取 构造方法可能抛出的异常类型 |
Object T | ==newInstance(Object··· initargs)== | 指定参数创建 该类对象的构造方法。如方法无参,则创建无参的构造方法 |
void | ==setAccessible(boolean bool)== | 是否无视访问权限检查 |
通过 java.lang.reflect.Modifier类 来解析部分无法识别 构造方法 的信息,比如==getModifiers()== 返回的值是需要解析的
package com.sans.constructor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test {
public static void main(String[] args) throws Exception {
Class<Student> sClass = (Class<Student>) Class.forName("com.sans.constructor.Student");
//No.1 获取无参
Constructor<Student> c1 = sClass.getConstructor();
Student s = c1.newInstance();
System.out.println(s);
System.out.println("==============");
//No.2 获取指定参数类型的
Constructor<Student> c2 = sClass.getConstructor(String.class , int.class);
Student s2 = c2.newInstance("张三" , 22);
System.out.println(s2);
System.out.println("==============");
//No.3 获取所有权限及构造方法
Constructor<Student>[] c3 = (Constructor<Student>[]) sClass.getDeclaredConstructors();
Student s3;
for (Constructor<Student> container : c3) {
System.out.println(container);
}
}
}
class Student {
String name;
int age;
public Student(){}
private Student(String...strs){
for (String s : strs) {
System.out.println(s);
}
}
public Student(String name , int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
/* 控制台结果
Student{name='null', age=0}
==============
Student{name='张三', age=22}
==============
public com.sans.constructor.Student(java.lang.String,int)
private com.sans.constructor.Student(java.lang.String[])
public com.sans.constructor.Student()
*/
# 属性
java.lang.reflect.Field 反射应用的 属性 对象 通过方法访问成员变量,将返回Field类型对象,每个Field对象代表一个成员变量 **注意: ** 属性必须要有修饰符修饰才可以获取类属性
获取属性
返回 | 方法 | 说明 |
---|---|---|
Field | ==getField(String name)== | 获取 指定属性 |
Field | ==getDeclaredField(String name)== | 获取 所有权限的 指定属性 |
Field[] | ==getFields()== | 获取 所有属性 |
Field[] | ==getDeclaredFields()== | 获取 所有权限的全部属性,按声明顺序排列 |
Field类 常用方法 (获取更多方法自行API)
返回 | 方法 | 说明 |
---|---|---|
String | ==getName()== | 获取 属性名 |
class | ==getType()== | 获取 该是属性类型的class对象 |
Object | ==get(Object obj)== | 指定对象obj中成员变量的值进行返回 |
void | ==set(Object obj , Object value)== | 指定对象obj中成员变量的值置为value |
void | ==setAccessible(boolean flag)== | 设置是否忽略权限限制直接访问私有成员 |
int | ==getModifiers()== | 获取该成员变量修饰符的整型 |
package com.sans.field;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Exception {
// 反射实例对象
Class<Person> aClass = (Class<Person>) Class.forName("com.sans.field.Person");
Constructor<Person> ct = aClass.getConstructor(String.class, int.class, String.class);
Object o = ct.newInstance("柏竹" , 20 , "18122335634");
//No.1 直接获取
Field name = aClass.getField("name");
System.out.println(name);
System.out.println("=============");
//No.2 获取所有权限 获取手机号码 private获取
Field phoneNumber = aClass.getDeclaredField("phoneNumber");
// 无视权限访问
phoneNumber.setAccessible(true);
System.out.println(phoneNumber);
System.out.println("=============");
//No.3 获取 所有属性
//无法访问私有属性
for (Field tmp : aClass.getFields()) {
System.out.println(tmp);
}
System.out.println("=============");
/*
* 方法使用
* */
System.out.println("------获取属性名");
System.out.println(" ["+phoneNumber.getName()+"] ");
System.out.println("------获取属性类型");
System.out.println(" ["+phoneNumber.getType()+"] ");
System.out.println("------获取属性值");
System.out.println(" ["+phoneNumber.get(o)+"] ");
System.out.println("------设置属性值");
phoneNumber.set(o , "18122334455");
System.out.println(" ["+o+"] ");
}
}
class Person{
public String name;
public int age;
private String phoneNumber;
public Person() {
}
public Person(String name , int age , String phoneNumber) {
this.name = name;
this.age = age;
this.phoneNumber = phoneNumber;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", phoneNumber=" + phoneNumber +
'}';
}
}
/* 控制台结果
public java.lang.String com.sans.field.Person.name
=============
private java.lang.String com.sans.field.Person.phoneNumber
=============
public java.lang.String com.sans.field.Person.name
public int com.sans.field.Person.age
=============
------获取属性名
[phoneNumber]
------获取属性类型
[class java.lang.String]
------获取属性值
[18122335634]
------设置属性值
[Person{name='柏竹', age=20, phoneNumber=18122334455}]
*/
# 方法
java.lang.reflect.Method 反射应用的 方法 对象
通过类的方法访问方法,将返回Method类型对象,每个Method对象代表一个方法
获取类的方法
返回 | 方法 | 说明 |
---|---|---|
Method | ==getMethod(String name , Class<?>····parameterTypes)== | 获取指定方法 |
Method | ==getDeclaredMethod(String name , Class<?>····parameterTypes)== | 获取所有权限的 指定方法 |
Method[] | ==ethods()== | 获取所有权限的指定方法 |
Method[] | ==getDeclaredMethods()== | 获取 所有权限的全部方法,按声明顺序排列 |
Method类 常用方法 (获取更多方法自行API)
返回 | 方法 | 说明 |
---|---|---|
String | ==getName()== | 获取方法名称 |
Class[] | ==getParameterTypes()== | 获取参数的类型 |
Class | ==getReturnType()== | 获取方法返回的类型 |
Class[] | ==getExceptionTypes()== | 返回方法抛出的异常类型 |
Object | ==invoke(Object obj , Object ... args)== | 指定参数args指定obj方法(obj 类/方法)? |
Boolean | ==isVarArgs()== | 带有可变数量的参数,则true |
int | ==getModifiers()== | 获取方法的修饰符(呈现形式整数) |
void | ==setAccessible(boolean bool)== | 是否无视访问权限检查 |
package com.sans.method;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
Class<Person> aClass = (Class<Person>) Class.forName("com.sans.method.Person");
//获取 类构造方法
Constructor<Person> constructor = aClass.getConstructor(String.class , int.class);
//获取 类实例的对象
Object o = constructor.newInstance("柏竹" , 20);
/*No.1
* 直接获取
* */
Method setName = aClass.getMethod("setName" , String.class);
Method getName = aClass.getMethod("getName");
System.out.println(setName);
System.out.println(getName);
System.out.println("===================");
/*No.2
* 获取所有权限 获取setAge()方法
* */
Method setAge = aClass.getDeclaredMethod("setAge", int.class);
System.out.println(setAge);
System.out.println("===================");
/*No.3
* 获取所有方法
* */
Method[] methods = aClass.getDeclaredMethods();
//无视权限进行测试使用
for (Method tmp : methods) {
System.out.println(tmp);
}
System.out.println("===================");
/*
* 方法使用
* */
System.out.println("\n-----模拟使用方法");
//获取 该方法所有权限
setAge.setAccessible(true);
System.out.println("使用前 : "+o);
setAge.invoke(o , 24);
System.out.println("使用后 : "+o);
System.out.println("\n-----获取方法名");
System.out.println(" ["+setAge.getName()+"] ");
System.out.println("\n-----获取方法参数类型");
Method test = aClass.getMethod("test", String.class, int[].class);
for (Class tmp : test.getParameterTypes()) {
System.out.println(" ["+tmp+"] ");
}
System.out.println("\n-----获取方法返回类型");
System.out.println(" ["+getName.getReturnType()+"] ");
}
}
class Person{
String name;
int age;
public Person() {
}
public Person(String name , int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
/**
* 私有修饰
* @param age
*/
private void setAge(int age) {
this.age = age;
}
public void test(String str , int...numAll){
System.out.println("参数方法测试");
}
@Override
public String toString() {
return "person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
/*
public void com.sans.method.Person.setName(java.lang.String)
public java.lang.String com.sans.method.Person.getName()
===================
private void com.sans.method.Person.setAge(int)
===================
private void com.sans.method.Person.setAge(int)
public int com.sans.method.Person.getAge()
public java.lang.String com.sans.method.Person.toString()
public java.lang.String com.sans.method.Person.getName()
public void com.sans.method.Person.setName(java.lang.String)
public void com.sans.method.Person.test(java.lang.String,int[])
===================
-----模拟使用方法
使用前 : person{name='柏竹', age=20}
使用后 : person{name='柏竹', age=24}
-----获取方法名
[setAge]
-----获取方法参数类型
[class java.lang.String]
[class [I]
-----获取方法返回类型
[class java.lang.String]
*/
# 注解
java.text.Annotation 类型未了解,可前去网址了解:点击了解 (opens new window)
访问的前提注解要有该注解@Retention(RetentionPolicy.RUNTIME)
获取注解
返回 | 方法 | 说明 |
---|---|---|
Annotation | ==getAnnotation(Class annotationClass)== | 获取指定的Annotation,不存在则返回null |
Annotation[] | ==getAnnotations()== | 获取所有的Annotation |
package kkb;
import java.lang.reflect.Field;
public class Demo {
public static void main(String[] args) throws Exception {
Class<Book> aClass = (Class<Book>) Class.forName("kkb.Book");
//通过反射 表详细
MyAnnotationTable at = aClass.getAnnotation(MyAnnotationTable.class);
String value = at.tableName();
System.out.println("表名 : " + value);
//属性
for (Field tmp : aClass.getDeclaredFields()) {
MyAnnotationField af = tmp.getAnnotation(MyAnnotationField.class);
System.out.println(tmp.getName() +
"属性 , 对应数据库中的字段为 : "+ af.name()+
" , 数据类型 : "+af.type()+
" , 数据长度 : "+af.length()
);
}
}
}
/*
表名 : test_Book
id属性 , 对应数据库中的字段为 : id , 数据类型 : int , 数据长度 : 20
name属性 , 对应数据库中的字段为 : name , 数据类型 : varchar , 数据长度 : 50
info属性 , 对应数据库中的字段为 : info , 数据类型 : varchar , 数据长度 : 200
*/
# Book类
package kkb;
import java.util.Objects;
/**
* @Author: 柏竹
* @Description: 一个简洁主义...
* @Date_Created_in: 2021-03-06 16:44
* @Modified_By:
* @Project: 数据
*/
@MyAnnotationTable(tableName = "test_Book")
public class Book {
@MyAnnotationField(name = "id" , type = "int" , length = 20)
private int id;
@MyAnnotationField(name = "name" , type = "varchar" , length = 50)
private String name;
@MyAnnotationField(name = "info" , type = "varchar" , length = 200)
private String info;
public Book() {
}
public Book(int id , String name , String info) {
this.id = id;
this.name = name;
this.info = info;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return id == book.id &&
Objects.equals(name , book.name) &&
Objects.equals(info , book.info);
}
@Override
public int hashCode() {
return Objects.hash(id , name , info);
}
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;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", info='" + info + '\'' +
'}';
}
}
# 注解MyAnnotationField
package kkb;
import java.lang.annotation.*;
/**
* @Author: 柏竹
* @Description: 一个简洁主义...
* @Date_Created_in: 2021-03-06 16:34
* @Modified_By:
* @Project: 字段
*/
@Inherited
@Documented
@Target (ElementType.FIELD)
@Retention (RetentionPolicy.RUNTIME)
public @interface MyAnnotationField {
/**
* 列名
* @return
*/
String name();
/**
* 类型
* @return
*/
String type();
/**
* 数据长度
* @return
*/
int length();
}
# 注解MyAnnotationTable
package kkb;
import java.lang.annotation.*;
/**
* @Author: 柏竹
* @Description: 一个简洁主义...
* @Date_Created_in: 2021-03-06 16:32
* @Modified_By:
* @Project: 表名
*/
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotationTable {
/**
* 表名称
* @return
*/
String tableName();
}
# 加载配置文件
给项目添加根路径:
项目右键 -> 新建文件夹 -> 设置名 source
-> 右键创建的文件夹 -> 标记项目为 -> 资源 根 即可
一般类加载器 加载资源文件默认是src路径下的文件,但是当项目存在 资源根 加载文件这就是该文件夹设置的根!
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
public class Demo {
public static void main(String[] args) throws IOException {
InputStream is = Demo.class.getClassLoader().getResourceAsStream("test.txt");
BufferedReader br = null;
if (is != null) {
br = new BufferedReader(new InputStreamReader(is));
}
if (br != null){
System.out.println(br.readLine());
br.close();
}
}
}