Java 基础语法:反射

3194 字
16 分钟
Java 基础语法:反射

学习目标:

  • 熟悉类加载过程与类加载器
  • 熟练掌握类加载时机
  • 掌握如何获取字节码文件对象
  • 掌握使用配置文件
  • 掌握Class,Constructor,Field,Method使用
  • 熟悉了解自定义类加载器与双亲委派模型(有能力的同学掌握)

类加载#

过程#

  • 加载

    • 通过类加载器(ClassLoader)加载.class文件,读取到内存
    • 在这个过程中,生成这个类所对应的字节码文件对象(java.lang.Class)
  • 链接

    • 验证: 对字节码文件格式的验证(aced babe 咖啡宝贝 魔法数字)

    • 准备: 给类的静态成员分配内存并赋予默认初始值

      • static int a =10;
    • 解析: 把符号引(用一组符号来描述被引用的目标)用转化为直接引用(真实的地址)

    class Student{ String name; int age; Subject subject;

    }

    class Subject{ String name; }

  • 初始化

    • 给静态成员赋真实的值, 并且执行静态代码块中的内容

类加载器#

分类#

  • Bootstrap ClassLoader 根/启动类加载器
    • 负责Java运行时核心类的加载,JDK中JRE的lib目录下rt.jar
  • Extension ClassLoader 扩展类加载器(JDK9之后更替为Platform ClassLoader 平台类加载器: 负责加载JDK平台本身的类库,这些类库位于JDK的 lib 目录下非核心模块,但不包括核心的 java.*)
    • 负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录
  • Application ClassLoader 系统类加载器/应用加载器
    • 这是加载用户类路径(Classpath)上的类的类加载器,也是 Java 应用的默认类加载器。 负责加载自己定义的Java类
package _23reflect.com.cskaoyan._01introduction;
/**
* @description:
* @author: 景天
* @date: 2022/10/22 11:42
**/
public class Demo {
public static void main(String[] args) {
// 查看类加载器
// 系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
// 扩展类加载器/平台类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
// 根类加载器 null 不是java写的
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
// 看一下加载路径
System.out.println(System.getProperty("java.class.path")
.replace(";", System.lineSeparator()));
}
}

双亲委派模型#

什么是双亲委派#

双亲委派模型的工作过程是:

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

双亲委派模型对于保证Java程序的稳定运作很重要,但它的实现却非常简单,实现双亲委派的代码都集中在java.lanq.ClassLoader的loadClass()方法之中

  • 先检查类是否已经被加载过
  • 若没有加载则调用父加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。
  • 如果父类加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。

为什么需要双亲委派#

使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。

例如类java.lang.Object,它存放在rt,jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Obiect类在程序的各种类加载器环境中都是同一个类。

相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为java.lang.Object的类,并放在程序的ClassPath中,那系统中将会出现多个不同的Obiect类,Java类型体系中最基础的行为也就无法保证,应用程序也将会变得一片混乱

JDK1.8

image-20221024094028570
image-20221024094028570

类加载时机#

创建类的实例(首次创建该类对象)

访问类的静态变量(首次)

调用类的静态方法(首次)

加载某个类的子类,会先触发父类的加载

直接使用java.exe命令来运行某个主类,也就是执行了某个类的main()方法

使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

java代码的3个阶段#

image-20221024095115934
image-20221024095115934

反射#

什么是反射#

获取运行时类信息的一种手段

反射的起点是字节码文件对象

获取字节码文件对象的几种方式#

  • 对象.getClass()
  • 类名.class
  • Class.forName(String className) 全限定名
  • ClassLoader里的loadClass(String className)

注意:

无论通过什么方式获取的字节码文件对象 都是同一个

Demo

package _23reflect.com.cskaoyan._02cls;
/**
* @description:
* @author: 景天
* @date: 2022/10/24 9:58
**/
/*
获取字节码文件对象的几种方式
*/
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
// 对象.getClass
A a = new A();
Class<? extends A> c1 = a.getClass();
// 类名.class
Class<A> c2 = A.class;
System.out.println(c1 == c2);
// Class.forName(全限定名)
Class<?> c3 = Class.forName("_23reflect.com.cskaoyan._02cls.A");
System.out.println(c1 == c3);
// ClassLoader.loadClass(String className)
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
Class<?> c4 = systemClassLoader.loadClass("_23reflect.com.cskaoyan._02cls.A");
System.out.println(c1 == c4);
}
}
class A{
}

注意

package _23reflect.com.cskaoyan._02cls;
/**
* @description:
* @author: 景天
* @date: 2022/10/24 10:08
**/
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException {
// 类名.class
// 没有执行静态代码块
//Class<B> c1 = B.class;
// Class.forName()
// 执行静态代码块
Class<?> c2 = Class.forName("_23reflect.com.cskaoyan._02cls.B");
}
}
class B{
static {
System.out.println("静态代码块执行了!");
}
}

关于Class#

Class 类的实例表示正在运行的 Java 应用程序中的类和接口

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

image-20221024101228387
image-20221024101228387

配置文件(.properties)#

配置文件的几种格式.properties .xml .yml

配置文件的作用: 放配置信息的 (数据库的, 第三方服务的配置信息)

.properties的格式 键值对(key-value) key=value key是不能重复的

注释是# 文件里面全是String

image-20221024101640808
image-20221024101640808

获取配置信息

Properties类

Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。

构造方法

Properties() 创建一个无默认值的空属性列表。

成员方法

voidload(InputStream inStream) 从输入流中读取属性列表(键和元素对)。
voidload(Reader reader) 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
StringgetProperty(String key) 用指定的键在此属性列表中搜索属性。
package _23reflect.com.cskaoyan._03config;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
/**
* @description:
* @author: 景天
* @date: 2022/10/24 10:27
**/
public class Demo {
public static void main(String[] args) throws IOException {
// 创建Properties对象
Properties properties = new Properties();
// load
properties.load(new FileInputStream("config.properties"));
// 获取属性值
// getProperty(String key)
String port = properties.getProperty("port");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String host = properties.getProperty("host");
System.out.println(port);
System.out.println(password);
System.out.println(user);
System.out.println(host);
}
}
package _23reflect.com.cskaoyan._03config;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;
/**
* @description:
* @author: 景天
* @date: 2022/10/24 10:27
**/
public class Demo2 {
public static void main(String[] args) throws IOException {
// 创建Properties对象
Properties properties = new Properties();
// 通过类加载器
URL systemResource = ClassLoader.getSystemResource("");
System.out.println(systemResource);
InputStream in = ClassLoader.getSystemResourceAsStream("config.properties");
// load
properties.load(in);
// 获取属性值
// getProperty(String key)
String port = properties.getProperty("port");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String host = properties.getProperty("host");
System.out.println(port);
System.out.println(password);
System.out.println(user);
System.out.println(host);
}
}

有中文的情况

package _23reflect.com.cskaoyan._03config;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;
/**
* @description:
* @author: 景天
* @date: 2022/10/24 11:26
**/
public class Demo3 {
public static void main(String[] args) throws IOException {
// 创建Properties对象
Properties properties = new Properties();
// load
properties.load(
new InputStreamReader(
new FileInputStream("config.properties"),"GBK"));
// 获取属性
String user = properties.getProperty("user");
System.out.println(user);
}
}

通过反射获取构造方法(Constructor)#

通过反射获取所有构造方法

Constructor[] getConstructors()
Constructor[] getDeclaredConstructors()

获取指定构造方法

Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

使用Constructor创建对象

Person p = new Person("zs",20,true)
newInstance(参数列表)

暴力破解

setAccessible(true)
package _23reflect.com.cskaoyan._04api;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @description:
* @author: 景天
* @date: 2022/10/24 11:34
**/
public class ConstructorTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 反射技术的起点 获取字节码文件对象
Class<?> stuCls = Class.forName("_23reflect.com.cskaoyan.bean.Person");
System.out.println("获取所有的public的构造方法----");
// Constructor[] getConstructors()
Constructor<?>[] constructors = stuCls.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println("获取所有的构造方法----");
//Constructor[] getDeclaredConstructors()
Constructor<?>[] declaredConstructors = stuCls.getDeclaredConstructors();
for (Constructor<?> constructor : declaredConstructors) {
System.out.println(constructor);
}
System.out.println("获取指定的public的构造方法----");
// Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor<?> constructor = stuCls.getConstructor(String.class, int.class, boolean.class);
//Constructor<?> constructor = stuCls.getConstructor(String.class, int.class);
// java.lang.NoSuchMethodException
System.out.println(constructor);
System.out.println("获取指定的构造方法----");
//Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
Constructor<?> declaredConstructor = stuCls.getDeclaredConstructor(String.class, int.class);
System.out.println(declaredConstructor);
// 使用构造方法对象创建对象
// newInstance
Object o = constructor.newInstance("zs", 20, true);
System.out.println(o);
// java.lang.IllegalAccessException
// setAccessible(true) 忽略java语法检查
declaredConstructor.setAccessible(true);
Object o1 = declaredConstructor.newInstance("ls", 21);
System.out.println(o1);
}
}

通过反射获取成员变量(Field)#

通过反射获取所有成员变量

Field[] getFields()
Field[] getDeclaredFields()

获取指定成员变量

Field getField(String name)
Field getDeclaredField(String name)

通过Field读写对象的成员变量(可暴力破解)

Object get(Object obj):获取值,传入对象
void set(Object obj, Object value):赋值,传入对象
package _23reflect.com.cskaoyan._04api;
import _23reflect.com.cskaoyan.bean.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
* @description:
* @author: 景天
* @date: 2022/10/24 14:25
**/
/*
反射获取成员变量
*/
public class FieldTest {
public static void main(String[] args) throws Exception{
// 获取字节码文件对象
Class<?> personCls = Class.forName("_23reflect.com.cskaoyan.bean.Person");
System.out.println("获取所有的public的成员变量------");
// Field[] getFields()
Field[] fields = personCls.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("获取所有的成员变量------");
//Field[] getDeclaredFields()
Field[] declaredFields = personCls.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println(field);
}
System.out.println("获取指定的public的成员变量------");
// Field getField(String name)
Field nameField = personCls.getField("name");
System.out.println(nameField);
System.out.println("获取指定的成员变量------");
//Field getDeclaredField(String name)
Field ageField = personCls.getDeclaredField("age");
System.out.println(ageField);
// 给成员变量赋值 获取成员变量的值
//void set(Object obj, Object value):赋值,传入对象
Constructor<?> declaredConstructor = personCls.getDeclaredConstructor();
// 实例化对象
Object o = declaredConstructor.newInstance();
nameField.set(o, "zs");
System.out.println(o);
ageField.setAccessible(true);
ageField.set(o, 22);
System.out.println(o);
// Object get(Object obj):获取值,传入对象
Object o1 = nameField.get(o);
System.out.println(o1);
}
}

通过反射获取方法(Method)#

获取所有成员方法

Method[] getMethods()// 父类的也能获取到
Method[] getDeclaredMethods()

获取指定的成员方法

Method getMethod(String name, Class<?>... parameterTypes)
Method getDeclaredMethod(String name, Class<?>... parameterTypes)

利用Method调用对象的方法

Object invoke(Object obj, Object... args)
package _23reflect.com.cskaoyan._04api;
import _23reflect.com.cskaoyan.bean.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* @description:
* @author: 景天
* @date: 2022/10/24 14:39
**/
/*
通过反射获取方法
*/
public class MethodTest {
public static void main(String[] args) throws Exception{
// 获取字节码文件对象
Class<?> personCls = Class.forName("_23reflect.com.cskaoyan.bean.Person");
System.out.println("获取所有的public的方法----");
// Method[] getMethods()
Method[] methods = personCls.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("获取所有的方法----");
//Method[] getDeclaredMethods()
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method method : declaredMethods) {
System.out.println(method);
}
System.out.println("获取指定的public的方法----");
// Method getMethod(String name, Class<?>... parameterTypes)
Method eatMethod1 = personCls.getMethod("eat");
System.out.println(eatMethod1);
System.out.println("获取指定的方法----");
//Method getDeclaredMethod(String name, Class<?>... parameterTypes)
Method eatMethod2 = personCls.getDeclaredMethod("eat", String.class);
System.out.println(eatMethod2);
//Person p = new Person();
//p.eat();
// 反射调用方法
// Object invoke(Object obj, Object... args)
Constructor<?> declaredConstructor = personCls.getDeclaredConstructor();
Object o = declaredConstructor.newInstance();
Object invoke = eatMethod1.invoke(o);
System.out.println(invoke);
eatMethod2.setAccessible(true);
eatMethod2.invoke(o, "apple");
}
}

补充#

其他API#

可以通过Class直接实例化 , 但是要有一个无参构造方法

package _23reflect.com.cskaoyan._04api;
import java.lang.reflect.Constructor;
/**
* @description:
* @author: 景天
* @date: 2022/10/24 11:48
**/
public class Demo {
public static void main(String[] args) throws Exception{
Class<?> c = Class.forName("_23reflect.com.cskaoyan._04api.A");
// 通过class对象直接实例化对象
Object o = c.newInstance();
System.out.println(o);
}
}
class A {
int a;
public A(int a) {
this.a = a;
}
public A() {
}
}

其他API

package _23reflect.com.cskaoyan.bean;
/**
* @description:
* @author: 景天
* @date: 2022/10/24 11:31
**/
public class Person {
// 定义成员变量
public String name;
private int age;
boolean gender;
// 构造方法
public Person(String name, int age, boolean gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
private Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
// 定义成员方法
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", gender=" + gender +
'}';
}
public String eat(){
System.out.println("eat food!");
return "吃了";
}
private void eat(String food){
System.out.println("eat " + food);
}
}
package _23reflect.com.cskaoyan._05add;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* @description:
* @author: 景天
* @date: 2022/10/24 14:57
**/
public class OtherApi {
public static void main(String[] args) throws Exception{
// 获取字节码文件对象
Class<?> c = Class.forName("_23reflect.com.cskaoyan.bean.Person");
//Class<?> c = Class.forName("java.io.OutputStream");
// 获取全限定类名
System.out.println(c.getName());
// 获取简单名称
System.out.println("c.getSimpleName() = " + c.getSimpleName());
// 获取父类
Class<?> superclass = c.getSuperclass();
System.out.println("superclass = " + superclass.getSimpleName());
// 获取实现的接口
Class<?>[] interfaces = c.getInterfaces();
for (Class<?> i : interfaces) {
System.out.println(i);
}
// 获取类加载器
ClassLoader classLoader = c.getClassLoader();
System.out.println("classLoader = " + classLoader);
// 获取name这个成员变量对象
Field nameField = c.getDeclaredField("name");
// 获取权限修饰符
int modifiers = nameField.getModifiers();
System.out.println(modifiers);
// static String toString(int mod)
// 返回描述指定修饰符中的访问修饰符标志的字符串。
String s = Modifier.toString(modifiers);
System.out.println("s = " + s);
// 获取eat(String s)方法对象
Method eatMethod = c.getDeclaredMethod("eat", String.class);
Class<?> returnType = eatMethod.getReturnType();
System.out.println("returnType = " + returnType);
Class<?>[] parameterTypes = eatMethod.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println(parameterType.getSimpleName());
}
}
}

反射应用场景#

通过反射获取注解信息

动态代理

ORM(Object Relational Mapping)框架, 数据库框架

image-20221024163122140
image-20221024163122140

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Java 基础语法:反射
https://firefly-mu-weld.vercel.app/posts/19-反射/
作者
Daisy
发布于
2026-06-10
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Daisy
Hello, I'm Daisy.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签

文章目录