فهرست منبع

docs: 更新20260812课堂笔记,同步代码变更——新增反射机制(Demo04反射概念/普通vs反射调用5维对比/为什么需要反射三场景/工作机制/反射核心类)与获取Class对象三种方式(Demo05类名.class/对象.getClass/Class.forName三方式对比+类加载时机差异forName触发静态代码块+Student/Person/Animal辅助类)章节,补充Demo02 BootstrapClassLoader由C/C++实现返回null注释说明,同步更新README总览两张表

WanJL 1 هفته پیش
والد
کامیت
ae7df396ff

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
README.md


+ 1 - 1
授课代码/c260812/src/course/Demo02.java

@@ -31,7 +31,7 @@ public class Demo02 {
 
         System.out.println("系统类加载器:"+systemClassLoader);
         System.out.println("平台类加载器:"+platformClassLoader);
-        System.out.println("虚拟机类加载器:"+bootstrapClassLoader);
+        System.out.println("虚拟机类加载器:"+bootstrapClassLoader);    //也是使用C/C++实现的,所以Java调用输出返回的就是null
         //虚拟机类加载器 没有父类加载器,而且不为null,直接就是空指针异常
         System.out.println("虚拟机类加载器还有没有父类加载器?"+bootstrapClassLoader.getParent());
     }

+ 84 - 0
授课代码/c260812/src/course/Demo04.java

@@ -0,0 +1,84 @@
+package course;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Demo04
+ * @description 反射
+ * @create 2026/8/12
+ */
+public class Demo04 {
+    /*
+    反射
+    是Java语言提供的一种【运行时动态机制】,他允许程序在【运行的状态】中:
+    对 任意一个类 都能获取这个类的所有属性和方法(包括私有的成员private)
+    对 任意一个对象 都能够调用它的任意属性和方法。
+    这种 动态获取类的信息 + 动态调用对象方法 的功能 就是Java的反射机制。
+
+
+    普通的调用  反射调用 的区别:
+
+    获取类信息的时机:
+        普通调用(编译期)-  就是在编译器写死,代码写死。
+        反射调用(运行期)-  运行时才会动态获取
+    依赖关系:
+        普通调用(编译期)-  编译期强耦合,耦合度高
+        反射调用(运行期)-  运行时才管理,解耦
+    创建对象:
+        普通调用(编译期)-  new 类名()
+        反射调用(运行期)-  Class.forname() + newInstance()
+    调用方法:
+        普通调用(编译期)-  对象.方法名()
+        反射调用(运行期)-  method对象.invoke(对象,参数)
+    灵活性方面:
+        普通调用(编译期)-  固定不变
+        反射调用(运行期)-  高度灵活,可以动态切换
+
+    一句话:普通方式是代码写死的,反射方式是动态的。反射是Java中各种框架的基础——Spring、MyBatis、Hibernate、JDBC驱动加载等等都离不开反射。
+
+
+    为什么需要反射?
+        如果没有反射,我们的程序需要使用某个类,就必须在编译期就要知道它的名字,然后使用new创建对象,使用. 调用方法。
+        这些在很多真实的场景中无法满足需求:
+            1、配置驱动:比如某个框架需要读取配置文件,文件里面写的时候字符串类型的类名,框架在运行的时候才会拿到这个字符串,
+            并且需要通过反射才能够把它翻译成真实的类并且进行实例化。
+            2、通用工具:写一个能够打印任意对象的全部属性的通用工具,调用方法后传入什么参数不确定,那么就只能通过反射来实现。
+            3、第三方类库的解耦:比如我们使用某个类库,Java只需要定义这个类库的规则(接口),类库自己实现具体的操作,
+            然后通过反射,在运行时动态加载,源码里面根本就不需要依赖某个具体的类。
+
+    注意:我们需要学习反射,目的是1、学习框架的时候帮助我们理解框架的底层源码。2、当我们进行架构设计、开发中间件程序的时候需要使用反射做解耦和通用适配。
+        至于日常的业务开发中,一定一定一定尽量不要使用反射。开发使用反射,就相当于玩游戏使用金手指(开挂)
+            1、使用反射可能破坏封装性
+            2、使用反射的效率要远比正常创建对象效率低的多,最低3~100倍
+
+    反射的工作机制:
+        Java程序运行的时候,.class字节码文件会被类加载器(ClassLoader)加载进JVM(Java虚拟机),JVM会为每一个类创建
+        唯一的一个 java.lang.Class 对象。这个对象就是 “类的类模板”,里面记录类这个类的全部信息(类名、父类、接口、字段属性、方法、构造方法等等)
+        反射的一切操作,都是从这个Class对象出发的。拿到了Class之后,可以依次获取它的构造方法对象(Constructor)、方法对象(Method)、属性对象(Field)
+        再通过这些对象去创建这个类的实例对象,读写字段属性,调用方法...
+
+
+
+        Person.java源码  --javac编译--> Person.class字节码 --类加载器(ClassLoader)加载-->Person的Class对象(类的类模板)
+        通过Person的Class对象
+            |-获取Person类的 构造方法对象Constructor --> newInstance()创建Person对象
+            |-获取Person类的 字段属性对象Field -->get/set 读写字段属性变量
+            |-获取Person类的 方法对象Method -->invoke()执行方法
+
+        反射涉及到的核心类:
+            Class       类             java.lang.Class     表示一个类的模板,是反射的入口
+            Constructor 构造方法        java.lang.reflect.Constructor 表示一个构造方法,可以创建对象
+            Field       字段属性        java.lang.reflect.Field 表示一个成员变量,可以读写字段属性
+            Method      方法           java.lang.reflect.Method 表示一个成员方法,可以调用方法
+            Modifier    修饰符         java.lang.reflect.Modifier 解析修饰符(public/private/static等等)
+            Parameter   参数          java.lang.reflect.Modifier  代表的是方法参数
+
+
+     */
+    public void printUtil(Object obj){
+
+        //输出这个类的所有属性名---就必须要使用反射来实现
+    }
+}

+ 78 - 0
授课代码/c260812/src/course/Demo05.java

@@ -0,0 +1,78 @@
+package course;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Demo05
+ * @description 反射--Class
+ * @create 2026/8/12
+ */
+public class Demo05 {
+    /*
+        获取Class对象的方式有三种,但是殊途同归,同一个类无论通过哪种方式,得到的都是同一个对象,JVM对每个类只加载一份字节码。
+            方式一:    类名.class
+            直接调用类名.class变量,这种方式最直接最简单,编译期就会进行类型检查,不允许运行时才失败。前提就是需要在编译期就知道类名。
+            Class<Object> objectClass = Object.class;   //JVM需要在编译期就知道这个类是存在的,才行。
+            Class<String> stringClass = String.class;
+            方式二:    对象.getClass()
+            getClass()是Object类中定义的方法,所有的对象都能调用,这种方式需要先有一个对象实例,适合在已经拿到对象想要知道它是什么类型的时候使用。
+            Animal animal=new Teacher();    //父类的引用指向子类的对象--多态
+            Class<? extends Animal> aClass = animal.getClass();
+            方式三: Class.forName("全类名")
+            Class.forName("全类名")是Class类提供的静态方法,参数是字符串形式的全类名(包名+类名),它是三种方式中最灵活的一种,
+            类名以字符串的形式传入,可以是变量、配置文件的内容、网络传过来的数据都可以。
+            所以它也是框架最常用的方式。注意:全类名写错了或类不存在的时候或抛出ClassNotFoundException异常 受检异常必须要处理的。
+            Class.forName("com.wanjl.Student");
+
+        注意:
+            forName()传错全类名会抛异常ClassNotFoundException异常
+            基本类型(int\double\byte...)也有对应的Class对象 int.class
+                int.class == Integer.TYPE
+            数组也有Class对象 int[].class
+            类加载的时机:
+                Class.forName()在返回Class之前会执行类的静态初始化块(static块);
+                类名.class和对象.getClass不会触发静态代码块
+
+
+     */
+    public static void main(String[] args) throws ClassNotFoundException {
+        //方式一:类名.class 编译期确定,最安全
+        Class<Student> clazz1 = Student.class;
+        Class<Integer> integerClass = int.class;
+        Class<Integer> integerClass1 = Integer.TYPE;
+        System.out.println(integerClass1==integerClass);    //true
+
+        int[] arr=new int[10];
+        Class<? extends int[]> aClass = arr.getClass();
+        Class<int[]> aClass1 = int[].class;
+
+        //方式二:对象.getClass() 需要有对象
+        Object student=new Student();
+        Class<?> clazz2 = student.getClass();
+
+        //方式三:Class.forName("全类名")
+        Class<?> clazz3 = Class.forName("course.Student");
+
+        //三种方式获取的到的对象是同一个对象,也就是 clazz1==clazz2==clazz3
+
+        System.out.println("clazz1==clazz2 : "+ (clazz1==clazz2));
+        System.out.println("clazz2==clazz3 : "+ (clazz2==clazz3));
+        System.out.println("clazz3==clazz1 : "+ (clazz3==clazz1));
+        System.out.println(clazz1);
+        System.out.println(clazz2);
+        System.out.println(clazz3);
+
+        //通过Class对象直接查看完整的类型信息
+        System.out.println("类名:"+clazz1.getName());
+        System.out.println("简单类名:"+clazz1.getSimpleName());
+        System.out.println("父类:"+clazz1.getSuperclass().getName());
+        System.out.println("包名:"+clazz1.getPackage().getName());
+        // Person.class不会加载静态代码块
+        //Class<Person> personClass = Person.class;
+        //当前案例看不出来,但是反序列化就可以看出对象.getClass()不加载静态代码块
+        // Class<? extends Person> personClass = new Person().getClass();
+        //forName("course.Person") 一定会触发静态代码块
+        Class.forName("course.Person");
+    }
+
+}

+ 27 - 0
授课代码/c260812/src/course/Student.java

@@ -0,0 +1,27 @@
+package course;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Student
+ * @description
+ * @create 2026/8/12
+ */
+public class Student{
+    private Integer id;
+    private String name;
+    private Integer age;
+    private String gender;
+
+    public void study(){
+        System.out.println(name+"在学习.....");
+    }
+}
+class Person extends Animal{
+    static{
+        System.out.println("人类的静态代码块.............");
+    }
+}
+class Animal{
+
+}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
课堂笔记/01-JavaSE基础-笔记/20260812-笔记.md


برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است