怀旧网,博客详情:Java 反射讲解

1、java枚举类详解

2、java String 类和类方法详解

3、StringBuffer 详解

4、Java Math 类详解

5、java random详解

6、java Date类使用讲解

7、java 集合类详解

8、java算法二分查找

9、SpringBoot 在初始化加载无法使用@Value的时候读取配置文件教程

10、springboot 项目配置本地jar包导入

11、单个java文件运行需要带上jar包的用法

12、spring boot 项目配置https服务

13、Java异常详解

14、Java Collection的使用

15、Java List 集合

16、Java ArrayList 介绍

17、Java LinkedList 讲解

18、Java Set 集合介绍

19、Java HashSet 介绍

20、Java TreeSet 介绍

21、Java Map 介绍以及子类介绍

22、Java 多线程使用介绍

23、Java 注解讲解

24、Java 反射讲解

25、Java 反射讲解

26、HashMap 源码讲解

27、面向对象初级教学

28、Java整合JWT使用

原创

Java 反射讲解

反射机制概述

动态语言

image-20240320102319135

静态语言

image-20240320102329463

Reflection

image-20240320102511650

image-20240320102518861

反射机制提供的功能

image-20240320102644193

反射的优缺点

image-20240320102714480

image-20240320102733346

反射的主要AIP

image-20240320102824453

理解Class类并获取Class实例

定义一个实体类

class Person{
    private String name;
    private 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;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Person{name = " + name + ", age = " + age + "}";
    }
}

获取当前创建类的实力对象

public static void main(String[] args) throws ClassNotFoundException {
    Class c1 = Class.forName("com.huaijiuwang.test.Person");
    System.out.println(c1);
}

image-20240320103232845

创建多个看看是否是同一个对象

Class c1 = Class.forName("com.huaijiuwang.test.Person");
Class c2 = Class.forName("com.huaijiuwang.test.Person");
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());

image-20240320103315776

值相等:说明创建出来的实力都是同一个(一个类在内存中只有一个Class对象)

image-20240320103525359

Class 类

image-20240320103718830

Class 类的常用方法

image-20240320104202894

获取Class实力的方式1

// 方式1
Class c1 = Class.forName("com.huaijiuwang.test.Person");

获取Class实力的方式2

// 方式2
Person person = new Person();
Class c2 = person.getClass();

获取Class实力的方式3

// 方式三
Class c3 = Person.class;

输出看结果

System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());

image-20240320104514236

这几种方式获取的都是同一个类的对象

扩展方式4

Class<Integer> c4 = Integer.TYPE; // 获取系统数据类型(包装类)
System.out.println(c4);

public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int"); // 里面自己写的TYPE类型

image-20240320104754373

获取父类的Class

Class c1 = Person.class;
Class c2 = c1.getSuperclass();
System.out.println(c1);
System.out.println(c2);

image-20240320105011107

拥有Class实例的对象

image-20240320105125464

获取所有可以获取Class对象的Class对象

Class c1 = Object.class; // 类
Class c2 = Comparable.class;  // 接口
Class c3 = String[].class; // 一维数组
Class c4 = String[][].class; // 二维数组
Class c5 = Override.class; // 注解
Class c6 = ElementType.class; // 枚举类型
Class c7 = Integer.class; // 基本数据类型
Class c8 = void.class; // void
Class c9 = Class.class; // Class 本身

System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);

image-20240320111004053

比较两个数组之间的对象区别

一维数组和一维数组间:

String[] s1 = new String[10];
String[] s2 = new String[100];
System.out.println(s1.getClass().hashCode());
System.out.println(s2.getClass().hashCode());

image-20240320111249279

一维数组和二维数组间:

String[] s1 = new String[10];
String[][] s2 = new String[100][];
System.out.println(s1.getClass().hashCode());
System.out.println(s2.getClass().hashCode());

image-20240320111337299

结论:

数据的对象是根据维度来区分是否是同一个Class对象的。(只要元素类型和维度一样就是同一个Class)

类的加载与 ClassLoader

Java 的内存分析

image-20240320111601648

类的加载过程

image-20240320111908408

image-20240320112222808

执行代码看效果:

public class Test2{
    public static void main(String[] args) throws ClassNotFoundException {
        A a = new A();
    }
}

class A{
    static {
        a = 200;
    }

    static int a = 100;

    public A(){
        System.out.println(a);
    }
}

image-20240320113036352

class A{
    static int a = 100;

    static {
        a = 200;
    }

    public A(){
        System.out.println(a);
    }
}

image-20240320113047322

结论:

在执行的时候回执行的代码如下

{

​ a = 100;

​ a = 200;

}

执行逻辑就是被static修饰的代码会在加载类初始化阶段进行执行,执行逻辑是从上到下,谁的代码在后,最后就是运行的结果,

这边是先给a赋值为100,然后在覆盖赋值为200,所有执行的结果就是200.

什么时候发生类的初始化

image-20240320113721933

测试:

创建类

class A{
    static int a = 100;
    static {
        System.out.println("父类初始化");
    }
}

class B extends A{
    static int b = 100;
    static final int M = 200;
    static {
        System.out.println("子类初始化");
    }
}
public class Test2{
    static {
        System.out.println("Main 加载初始化");
    }

    public static void main(String[] args) throws ClassNotFoundException {
        // 会触发类的加载
    }
}

image-20240320114516634

public static void main(String[] args) throws ClassNotFoundException {
    // 会触发类的加载
     new B(); // 主动引用
}

image-20240320144205756

Class.forName("com.huaijiuwang.test.B"); // 通过反射

image-20240320144306933

System.out.println(B.b); // 引用子类常量

image-20240320144410862

// 不会输出类的加载的方式
        System.out.println(B.a); // 掉用父类的静态属性
B[] bs = new B[10]; // 创建对象数组

image-20240320150450253

System.out.println(B.M); // 访问常量

image-20240320150552450

类加载器

类加载器的作用

image-20240320151722797

image-20240320151738780

image-20240320151952982

获取加载器

测试:

public static void main(String[] args){
    // 获取系统类加载器
    ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();

    // 获取系统类加载器的父加载器--->扩展类加载器
    ClassLoader parent = systemClassLoader.getParent();

    // 获取扩展类加载器的父加载器--->根加载器
    ClassLoader parent1 = parent.getParent();

    // 输出
    System.out.println(systemClassLoader); // sun.misc.Launcher$AppClassLoader@14dad5dc
    System.out.println(parent); // sun.misc.Launcher$ExtClassLoader@2503dbd3
    System.out.println(parent1); // null
}

测试当前运行的类是那个加载器加载的

Class c1 = Class.forName("com.huaijiuwang.test.Test2");
ClassLoader classLoader = c1.getClassLoader();
System.out.println(classLoader); // sun.misc.Launcher$AppClassLoader@14dad5dc

默认是系统类加载器(应用类加载器)

测试系统类的加载器是谁:

c1 = Class.forName("java.lang.Object");
ClassLoader classLoader1 = c1.getClassLoader();
System.out.println(classLoader1); // null
// 获取系统类加载器的路径
System.out.println(System.getProperty("java.class.path"));
/*
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\charsets.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\deploy.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\access-bridge-64.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\cldrdata.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\dnsns.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\jaccess.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\jfxrt.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\localedata.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\nashorn.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunec.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunjce_provider.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunmscapi.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\sunpkcs11.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\ext\zipfs.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\javaws.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\jce.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\jfr.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\jfxswt.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\jsse.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\management-agent.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\plugin.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\resources.jar;
            C:\Program Files\Java\jdk1.8.0_40\jre\lib\rt.jar;
            D:\Idea\Test11\target\classes;
            C:\Users\Administrator\.m2\repository\org\apache\httpcomponents\httpclient\4.5.13\httpclient-4.5.13.jar;
            C:\Users\Administrator\.m2\repository\org\apache\httpcomponents\httpcore\4.4.13\httpcore-4.4.13.jar;
            C:\Users\Administrator\.m2\repository\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;
            C:\Users\Administrator\.m2\repository\commons-codec\commons-codec\1.11\commons-codec-1.11.jar;
            C:\Users\Administrator\.m2\repository\org\apache\httpcomponents\httpmime\4.5.13\httpmime-4.5.13.jar;
            C:\Users\Administrator\.m2\repository\org\jsoup\jsoup\1.12.1\jsoup-1.12.1.jar;D:\办公软件\IntelliJ IDEA 2021.2\lib\idea_rt.jar
*/

获取运行时类的完整结构

image-20240320154008699

image-20240320154015776

代码测试-通过反射获取对象和属性方法

获取类名:

Class c1 = Class.forName("com.huaijiuwang.test.Person");

// 获取类的名字
System.out.println(c1.getName()); // com.huaijiuwang.test.Person 获取类名: 包名 + 类名
System.out.println(c1.getSimpleName()); // Person 获取类名

获取类的属性

Class c1 = Class.forName("com.huaijiuwang.test.Person");

// 获取所有属性
Field[] fields = c1.getFields(); // 只能回去公开属性
for (Field field : fields) {
    System.out.println(field);
}
System.out.println("---------------");
fields = c1.getDeclaredFields();
for (Field field : fields) {
    System.out.println(field);
}

// 获取单个属性
System.out.println(c1.getField("name"));
System.out.println(c1.getField("age"));

image-20240320160729315

报错原因:getField方法自能获取public修饰属性

修改代码

System.out.println(c1.getField("name"));
System.out.println(c1.getDeclaredField("age"));

image-20240320160808350

获得类的方法

在Person中添加private方法用于测试

private void test(){}
Class c1 = Class.forName("com.huaijiuwang.test.Person");

// 获取类的方法(可以同时可以直接拿到父类的方法)
Method[] methods = c1.getMethods(); // 获得本类以及父类的全部public方法
for (Method method : methods) {
    System.out.println(method);
}
System.out.println("---------------------------------");
methods = c1.getDeclaredMethods(); // 获得本类的所有方法包括private修饰的
for (Method method : methods) {
    System.out.println(method);
}

输出结果:

public java.lang.String com.huaijiuwang.test.Person.toString()
public java.lang.String com.huaijiuwang.test.Person.getName()
public void com.huaijiuwang.test.Person.setName(java.lang.String)
public int com.huaijiuwang.test.Person.getAge()
public void com.huaijiuwang.test.Person.setAge(int)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
---------------------------------
public java.lang.String com.huaijiuwang.test.Person.toString()
public java.lang.String com.huaijiuwang.test.Person.getName()
public void com.huaijiuwang.test.Person.setName(java.lang.String)
private void com.huaijiuwang.test.Person.test()
public int com.huaijiuwang.test.Person.getAge()
public void com.huaijiuwang.test.Person.setAge(int)
// 获得指定方法
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class); // 这里需要参数类型,因为需要通过参数来判断是否为重载方法 
System.out.println(getName);
System.out.println(setName);

image-20240320161612645

获取构造器

添加代码(加一个私有构造器用作区分)

protected Person(int age){
    this.age = age;
}
// 获取指定的构造器
Constructor[] constructors = c1.getConstructors(); // 获取public构造器
for (Constructor constructor : constructors) {
    System.out.println(constructor);
}
System.out.println("---------------------------");
constructors = c1.getDeclaredConstructors();  // 获取所有构造器
for (Constructor constructor : constructors) {
    System.out.println(constructor);
}

image-20240320162016889

// 获取指定构造器
Constructor constructor = c1.getConstructor(String.class, int.class); // 还是一样只能获取公开的构造器
System.out.println(constructor);
// 输出内容
public com.huaijiuwang.test.Person(java.lang.String,int)

调用运行时类的指定结构

image-20240320162609595

// 创建实例对象
Person person = (Person)c1.newInstance();
System.out.println(person);

image-20240320162657881

成功创建对象--删除无参构造方法后测试

image-20240320162733426

直接报错了

当没有空参构造方法的时候我们可以通过获得构造器的方式来创建

// 创建实例对象
// 1.获取构造器
Constructor constructor = c1.getConstructor(String.class, int.class);
// 创建实例对象
Person person = (Person)constructor.newInstance("小红", 20);
System.out.println(person);

image-20240320163211774

创建成功

image-20240320170225823

image-20240320170436854

通过反射调用普通方法

// 创建实例对象
// 1.获取构造器
Constructor constructor = c1.getConstructor(String.class, int.class);
// 创建实例对象
Person person = (Person)constructor.newInstance("小红", 20);

// 通过反射先获取方法
Method setName = c1.getMethod("setName", String.class);
Method getName = c1.getMethod("getName");

// 通过 invoke 方法执行方法
setName.invoke(person, "怀旧");
String name = (String)getName.invoke(person);

System.out.println(person);
System.out.println(name);

image-20240320163832306

通过反射掉用私有方法

// 通过反射调用私有方法
Method test = c1.getDeclaredMethod("test");
test.invoke(person);
// 运行报错
Exception in thread "main" java.lang.IllegalAccessException: Class com.huaijiuwang.test.Test2 can not access a member of class com.huaijiuwang.test.Person with modifiers "private"
	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
	at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
	at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
	at java.lang.reflect.Method.invoke(Method.java:490)
	at com.huaijiuwang.test.Test2.main(Test2.java:21)

原因是有一个开关没打开,打开开关后测试

image-20240320170503859

// 通过反射调用私有方法
Method test = c1.getDeclaredMethod("test");
test.setAccessible(true); // 关闭检查
test.invoke(person);

image-20240320164424989

调用成功,同样属性的操作和方法一样

// 通过反射操作属性
Field age = c1.getDeclaredField("age");
age.setAccessible(true); // 因为age是私有属性 同样关闭检测
// 设置值
age.set(person, 15);
// 获取值
int age1 = (int)age.get(person);
// 打印结果
System.out.println(person);
System.out.println(age1);

image-20240320164748220

分享反射性能问题

普通方式创建对象性能测试

Person person = new Person("怀旧", 18);

long beginTime = System.currentTimeMillis();

for (int i = 0; i < 1000000000; i++) {
    person.getName();
}

long endTime = System.currentTimeMillis();

System.out.println("花费时间: " + (endTime - beginTime) + " ms.");

image-20240320171231198

反射的方式创建测试

Class c1 = Class.forName("com.huaijiuwang.test.Person");

Constructor constructor = c1.getDeclaredConstructor(String.class, int.class);
Person person = (Person)constructor.newInstance("怀旧", 18);

Method getName = c1.getDeclaredMethod("getName");

long beginTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
    getName.invoke(person);
}
long endTime = System.currentTimeMillis();

System.out.println("花费时间: " + (endTime - beginTime) + " ms.");

image-20240320171655893

反射的方式并关闭检测创建测试

Class c1 = Class.forName("com.huaijiuwang.test.Person");

Constructor constructor = c1.getDeclaredConstructor(String.class, int.class);
Person person = (Person)constructor.newInstance("怀旧", 18);

Method getName = c1.getDeclaredMethod("getName");
getName.setAccessible(true);

long beginTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
    getName.invoke(person);
}
long endTime = System.currentTimeMillis();

System.out.println("花费时间: " + (endTime - beginTime) + " ms.");

image-20240320171720281

总结

通过上面的测试可以看出来,直接通过对象调用的方式效率会高很多,而反射的方式,相对于关闭检测后,效率也会有所提升。

在代码中要是用反射的情况较多的情况下,就是建议关闭检测,提高效率

通过反射操作泛型

创建测试返回方法

public Map<String, Integer> test(Map<String, Integer> map, List<String>  list){
    return null;
}
Method method = Test2.class.getMethod("test", Map.class, List.class);
// 获取参数的泛型数据
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
    System.out.println(genericParameterType);
    if(genericParameterType instanceof ParameterizedType){
        Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
        for (Type actualTypeArgument : actualTypeArguments) {
            System.out.println(actualTypeArgument);
        }
    }
}
System.out.println("--------------------------");
// 获取返回值的泛型参数数据
Type genericReturnType = method.getGenericReturnType();
System.out.println(genericReturnType);
if(genericReturnType instanceof ParameterizedType){
    Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
    for (Type actualTypeArgument : actualTypeArguments) {
        System.out.println(actualTypeArgument);
    }
}

image-20240320173747681

反射操作注解

创建一个User类,模拟通过实体类,创建数据库表

  1. 创建User类
class User{
    private int id;
    private String name;
}
  1. 创建注解,用来标记表明和字段
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
    String tableName(); // 记录表名
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Filed{
    String columnName(); // 字段名
    String type(); // 字段类型
    int len(); // 字段长度
}
  1. 给实体类添加注解
@Table(tableName = "db_user")
class User{
    @Filed(columnName = "user_id", type = "int", len = 10)
    private int id;
    @Filed(columnName = "user_name", type = "varchar", len = 3)
    private String name;
}
  1. 使用反射读取注解数据
public static void main(String[] args) throws NoSuchMethodException {
    // 创建实例
    Class c1 = User.class;

    // 定义sql
    String sql = "create table ";

    // 获取注解
    Annotation[] annotations = c1.getAnnotations();
    for (Annotation annotation : annotations) {
        System.out.println(annotation);
    }

    // 获取注解的值
    Table table = (Table)c1.getAnnotation(Table.class);
    System.out.println(table.tableName());

    // 拼接表名
    sql += table.tableName() + "( ";

    // 获取类所有的字段
    for (Field field : c1.getDeclaredFields()) {
        Filed filed = field.getAnnotation(Filed.class);
        System.out.println(filed.columnName());
        System.out.println(filed.type());
        System.out.println(filed.len());

        // 拼接每个字段
        sql += filed.columnName() + " " + filed.type() + "( " + filed.len() + " ), ";
    }

    // 拼接最后的括号
    sql = sql.substring(0, sql.length()-2) + ");";

    // 执行sql语句
    System.out.println("拼接的sql语句:" + sql);
}

image-20240320195056603

  • 平台作者:怀旧(联系作者)
  • QQ:444915368
  • 邮箱:444915368@qq.com
  • 电话:17623747368
  • 评论

    登录后才可以进行评论哦!

    回到顶部 留言