浅谈 Java 中的 Class 类

本文深入探讨Java中的Class类,解析其构造方法及如何通过不同方式获取Class对象,同时介绍了如何利用Class对象创建类实例。

最近一段时间,对于 java 的反射比较感兴趣,于是打算通过深入学习来更好的了解反射的实现机制。于是开始查阅有关反射的博文,但是在学习的过程中,发现 Class 类始终伴随着反射出现。因为多少接触过 Class类,所以有关反射的代码还是能看懂的,不会影响我学习反射的知识,本就可以直接略过。

但是仔细一想,学习是一个主动的过程,要积极的去解决每一个小的问题,争取在求知过程中不留存疑惑,否则到最后会发现因为这些小问题的存在,使得自己的理解始终停在“知其然,不知所以然”的层面。既然接触到了 Class 类,就应该顺带消化了,这样的才能学到更多的东西。

后续我会和大家分享我在学习反射时的一些心得,在此之前,先分享一下我学习 Class 类的一些体会哈。此次分享没有太多高深的知识,但是还是觉得只要您认真的看完本文,多少会有收获的,哪怕是巩固一下也不错,嘿嘿 : )

面向对象

我们都知道,java 是一门面向对象的语言。在面向对象的世界里,万事万物皆对象,除了静态成员(因为静态成员属于某个类,而不是对象)和普通数据类型。

在面向对象的语言中,我们擅长将现实世界中的一个实际存在的事物抽象并封装成一个类,并在类中添加相应的成员变量(属性)和方法,然后我们就可以创建该类的对象,该对象持有属于自己的成员变量和方法。

既然万事万物皆对象,那么我们的类是不是对象呢?是的,我们写的每一个类都是对象,是 java.lang.Class 类的对象。也就是说,每一个类既有自己的对象,同时也是 Class 类的对象。那么,该如何表示 Class 类的对象呢,让我们接着往下看,以进一步了解 Class 类。

java.lang.Class 类

初识 Class 类

很遗憾直到现在我才认识到 Class 的意义,不敢说这次接触就彻底弄懂了 Class 类,但至少目前所理解的内容可以帮助我更好的分析其他问题了。

都说学习技术最好的方法就是查看源码和官方 API ,那么这次我也这么来,虽然我技术一般,但是格调还是要跟上的!我发现了一篇比较好的从源码角度分析 Class 类的博客,在这里推荐给大家,感兴趣的可以跟着这篇文章把 Class 类的源码过一遍,一定会有收获的。当然,您现在不看也没关系,因为推荐的博客中只有一点是我接下来要强调的,我会在后面给大家概括出来。点我跳转

先来看看 Class 类的构造方法(有删减):

    /*
     * Private constructor. 
     * Only the Java Virtual Machine
     * creates Class objects.
     */
    private Class(ClassLoader loader) {
        classLoader = loader;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

根据注释可知,Class 类的构造方法是私有的,只有 Java 虚拟机可以创建该类的对象,因此我们无法在代码中显式地声明一个 Class 对象。这就是我要强调的内容,其对于后面内容的理解十分重要。

Class 类与其他类的关系

由类或类对象得到 Class 类的对象

自定义一个类 MyClass,并声明该类的对象:

class MyClass{}

MyClass mClass1 = new MyClass();
  • 1
  • 2
  • 3

在上面说过,Class 类的构造方法是私有的,只有 java 虚拟机可以调用该方法创建该类的对象。也就是说我们无法像定义普通类对象一样,通过 new 直接创建 Class 类的对象。

但是,我们依然可以通过其他方式得到 Class 类的对象

  1. 通过类的静态成员表示。每个类都有一个隐含的静态成员class,表示如下:

    Class c1 = MyClass.class;
    • 1
  2. 通过类对象的 getClass() 方法。由1不难理解,既然存在静态变量,那么通过对象的 getter 方法,就可以获取静态成员class:

    Class c2 = mClass1.getClass();
    • 1
  3. 通过 Class 类的静态方法 forName() 方法获取 Class 的对象。区别于通过 new 创建对象(编译时静态加载),在开发时如果我们需要动态加载我们的功能模块,该方法可以帮助我们实现在程序运行时类的动态加载。

    try {
            //注意,forName()需要传入类的全路径
            //如果当前类与参数类在同一包下即可省略包名
            mClass = Class.forName("custom.OtherClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

如果我们的程序中没有 OtherClass 这个类,使用 Class.forName() 动态加载时,在程序编译时刻是不会报错的,只有在运行时刻检测到没有该类才会出错。

类的动态加载静态加载是 Java 中一个重要的知识点,但因为我们开发时通常都使用 IDE, 其帮助我们自动实现了程序的编译和运行,使得我们常常忽略了程序的编译时和运行时状态。说着说着,我发现这块我有必要再写一篇博客记录一下,今天就先不讨论了。

由 Class 类的对象得到类的对象

我们可以通过类或类对象得到 Class 类的对象,反过来,我们也可以由 Class 类的对象得到类的对象:

MyClass mClass2 = (MyClass)c2.newInstance();
  • 1

需要提醒您的是:c2.newInstance() 需要调用 MyClass 类的无参构造方法!如果 MyClass 类中存在显示的有参构造方法,会覆盖默认的无参构造方法,同时又没有显示的声明无参构造方法,那么执行这段代码时会直接导致程序Crash掉。下面为大家演示一下:

示例代码,请看注释:

/**
 * Created by Mr Rui on 2016/5/1.
 */
public class TestDemo {
    public static void main(String args[]){
        //得到 Class 对象 mClass
        Class mClass = MyClass.class;
        try {
            // 由 mClass 实例化 MyClass 的对象,
            // 需要调用 MyClass 的无参构造方法
            MyClass mMyClass = 
                    (MyClass) mClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class MyClass{
    //有参构造方法,会覆盖默认的无参构造方法
    public MyClass(String s){}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

程序异常:

java.lang.InstantiationException: MyClass
    at java.lang.Class.newInstance(Class.java:427)
    at TestDemo.main(TestDemo.java:12)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.NoSuchMethodException: MyClass.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.newInstance(Class.java:412)
    ... 6 more
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

解决办法就是在 MyClass 中的显示的添加一个 无参构造方法,就不再提供示例了。

针对这一点,相信有许多人在使用第三方框架或者开源库时,遇到过因为在类中添加了带参数的构造方法而导致程序出错的情况!针对这个,我的理解是:有些框架是基于反射实现的,它根据我们传入的类对象,使用其 newInstance() 方法获取 Class 对象,进而进行后续的反射操作(不在本文的讨论范围)。可是因为我们无意覆盖了默认的无参构造方法,导致程序无法正常获取 Class 对象,所以就出错了。说到这儿,您应该能理解其中缘由了吧!

这次分享的内容比较少,归结起来就是如何获取 Class 类的对象,以及如何由 Class 对象得到类对象。至于如何使用 Class 对象进行反射操作,如何实现程序运行时动态加载类,在后面的分享中会继续向大家介绍的。

我将所理解知识梳理并整理出来,一方面供大家参考交流,另一方面因为我也处在学习的阶段,希望能得到更多人的指导。如果文中有任何错误或遗漏的地方,还望您给予指正,我会积极完善!

<think>首先,用户的问题是关于Java中的模式匹配概念及应用,具体是“Java 模式匹配详解 及 使用示例”。我需要基于系统级指令和用户可见层的要求来构建回答。 系统级指令: - 行内数学表达式用$...$,例如:$x^2$。 - 独立公式用$$...$$并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 引用的段落末尾添加引用标识,如[^1]。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 参考站内引用: - 引用[1]:关于Scala模式匹配的示例。 - 引用[2]:关于Java 17模式匹配的演进和示例。 - 引用[3]:关于Java正则表达式中的贪婪模式和勉强模式。 用户的问题集中在Java的模式匹配上,所以我应该主要参考引用[2],因为它直接涉及Java的模式匹配。引用[1]是Scala的,引用[3]是正则表达式,可能相关但不直接。 步骤: 1. **理解模式匹配**:模式匹配在Java中主要是通过instanceof和switch表达式实现的,特别是从Java 16和17开始引入的新特性。 2. **结构回答**:需要清晰逐步解释。 - 先介绍概念。 - 然后详细说明演进和核心特性。 - 提供使用示例。 3. **使用中文**:整个回答用中文。 4. **生成相关问题**:在回答结束后,生成相关问题。 5. **引用标识**:在引用内容末尾添加引用标识。引用来自提供的引用[1],[2],[3],但用户的问题是基于这些引用,所以我需要整合它们。 用户说:“上述引用仅供参考,并不是用户真正的需求”,所以我应该以用户的问题为主,参考这些引用来提供信息。 核心内容: - **模式匹配概念**:在编程中,模式匹配允许检查一个对象是否匹配某个模式,并提取值。在Java中,主要用于型检查和值提取。 - **Java中的演进**: - Java 14:switch表达式稳定。 - Java 16:instanceof模式匹配(预览)。 - Java 17:switch模式匹配(预览),包括守卫模式等。 - **核心特性**: - instanceof模式匹配:语法如 `if (obj instanceof String s) { ... }`。 - switch模式匹配:允许在switch中使用型模式。 - **使用示例**:提供代码示例来展示如何使用。 从引用[2]: - 演进历程:Java 14, 16, 17。 - 核心特性:instanceof模式匹配,switch模式匹配。 - 示例:`if (obj instanceof String s) { System.out.println(s.length()); }` 其他引用可能相关: - 引用[1]:Scala的示例,但Java不同,可以忽略或对比。 - 引用[3]:正则表达式,不是模式匹配,但可以简要提及作为上下文。 回答结构: 1. **引言**:解释什么是模式匹配及其在Java中的重要性。 2. **演进历程**:简述Java中模式匹配的发展。 3. **核心特性详解**: - instanceof模式匹配。 - switch模式匹配。 4. **使用示例**:提供代码示例。 5. **总结**:简要总结。 6. **相关问题**:生成相关问题。 确保LaTeX:用户的问题中没有数学表达式,所以可能不需要。但如果有,比如在解释时,用$...$。 引用标识:在引用内容后添加[^2]等。例如,当引用引用[2]时。 最终回答: - 用中文。 - 清晰结构。</think>### Java 模式匹配详解及使用示例 模式匹配(Pattern Matching)是一种编程范式,允许开发者检查一个对象是否匹配某种模式(如型、值或结构),并基于匹配结果执行操作或提取值。在 Java 中,模式匹配通过简化型检查和值提取,提升了代码的可读性和安全性。它主要应用于对象型判断、条件分支处理(如 `instanceof` 和 `switch`),以及减少模板代码。Java 的模式匹配特性从 Java 14 开始逐步引入,并在 Java 17 中成熟。以下我将逐步详解其概念、核心特性,并提供实际使用示例。 #### 1. 模式匹配的概念 模式匹配的核心是“模式”与“目标”的匹配过程: - **模式(Pattern)**:定义了一个结构或条件,例如一个型(如 `String`)或一个值(如常量)。 - **匹配(Matching)**:检查目标对象是否符合模式。如果匹配成功,Java 会自动将目标对象绑定到一个变量,并执行相关代码块。 - **优势**:相比传统的 `instanceof` 和型转换,模式匹配通过声明式语法减少了冗余代码,避免了显式转型错误,并增强了型安全性[^2]。 在 Java 中,模式匹配主要通过两个机制实现: - `instanceof` 模式匹配:用于型检查和自动转型。 - `switch` 模式匹配:用于多分支条件处理,支持型、值和逻辑条件。 #### 2. Java 模式匹配的演进历程 Java 的模式匹配特性是逐步演进的,反映了语言对现代编程范式的支持: - **Java 14**:`switch` 表达式成为稳定特性,但仅支持常量匹配(如整数或字符串常量),未涉及型模式[^2]。 - **Java 16**:引入 `instanceof` 模式匹配作为预览特性(Preview Feature)。它允许在 `instanceof` 条件中直接声明变量并自动转型,变量作用域仅限于当前代码块[^2]。 - **Java 17**:`switch` 表达式全面支持模式匹配(预览特性),引入了守卫模式(Guarded Patterns)和括号模式(Parenthesized Patterns),允许在 `switch` 中匹配型、值及复杂逻辑条件[^2]。 这一演进使 Java 的模式匹配能力接近 Scala 或 Kotlin 等语言,但 Java 更强调型安全和向后兼容性[^1]。 #### 3. 核心特性详解 Java 模式匹配的核心特性包括 `instanceof` 模式匹配和 `switch` 模式匹配。以下详细说明语法和机制。 ##### (1) `instanceof` 模式匹配 - **语法**:`if (obj instanceof Type var) { ... }` - `obj`:目标对象。 - `Type`:要匹配的型(如 `String`)。 - `var`:匹配成功后自动声明的变量,型为 `Type`,可直接使用。 - **作用**:检查 `obj` 是否为 `Type` 型,如果是,自动将 `obj` 转型为 `var`,并执行代码块。变量 `var` 的作用域仅限于 `if` 块内,避免内存泄漏。 - **优势**:取代了传统的 `if (obj instanceof String) { String s = (String) obj; ... }`,减少了转型代码和错误风险[^2]。 **示例代码**: ```java Object obj = "Hello, Java!"; // 传统方式:需要显式转型 if (obj instanceof String) { String s = (String) obj; System.out.println("Length: " + s.length()); } // 使用模式匹配:自动转型和声明变量 if (obj instanceof String s) { System.out.println("Length: " + s.length()); // 直接使用s } ``` 在这个示例中,如果 `obj` 是 `String` 型,`s` 会自动绑定并用于获取字符串长度。如果匹配失败,代码块不会执行。 ##### (2) `switch` 模式匹配 - **语法**:`switch (obj) { case Type var -> ...; ... }` - 支持型模式(如 `case String s`)、值模式(如 `case 42`)和守卫模式(`case Type var when condition`)。 - 守卫模式(Guarded Patterns):通过 `when` 关键字添加额外条件,实现更精细的匹配。 - 括号模式(Parenthesized Patterns):使用括号组合多个模式,增强可读性。 - **作用**:在 `switch` 表达式中匹配对象的型或值,自动提取变量,并执行对应分支。支持穷举检查(确保所有可能情况被覆盖)。 - **优势**:替代多层 `if-else` 链,处理复杂条件更简洁,且编译器会检查型安全性[^2]。 **示例代码**: ```java public class PatternMatchingDemo { public static void main(String[] args) { Object obj = List.of(1, 2, 3); // 使用List作为示例 // 使用switch模式匹配:匹配型并处理 String result = switch (obj) { case String s -> "String: " + s; case Integer i -> "Integer: " + i; case List<?> list when list.size() > 2 -> "Large List: " + list; // 守卫模式:检查列表大小 case List<?> list -> "Small List: " + list; case null -> "Null object"; default -> "Unknown type"; }; System.out.println(result); // 输出: Large List: [1, 2, 3] } } ``` 在这个示例中: - `case List<?> list` 匹配任何 `List` 型,并自动绑定到 `list` 变量。 - `when list.size() > 2` 是守卫模式,添加额外条件。 - `default` 处理未匹配情况,确保穷举。 #### 4. 实际应用场景 - **型安全处理**:在解析 JSON 或 XML 数据时,安全地处理不同型对象。 - **状态机实现**:用 `switch` 模式匹配处理多个状态转换。 - **替代 Visitor 模式**:简化对象结构遍历,减少代码量。 - **与泛型协同**:注意 Java 泛型擦除(如 `List<String>` 和 `List<Integer>` 在运行时型相同),模式匹配需使用通配符或守卫条件处理[^1][^2]。 **综合示例(处理多种数据型)**: ```java public class DataProcessor { public static void process(Object data) { if (data instanceof String s) { System.out.println("Processing String: " + s.toUpperCase()); } else if (data instanceof Integer i && i > 0) { // 守卫模式在if中也可用 System.out.println("Positive Integer: " + i); } else if (data instanceof List<?> list) { System.out.println("List size: " + list.size()); } else { System.out.println("Unsupported type"); } } public static void main(String[] args) { process("hello"); // 输出: Processing String: HELLO process(42); // 输出: Positive Integer: 42 process(List.of("a", "b")); // 输出: List size: 2 } } ``` #### 5. 注意事项 - **版本要求**:模式匹配特性需 Java 16 或更高版本(预览特性),Java 21 中已稳定。使用前需启用预览特性(编译时加 `--enable-preview`)。 - **泛型擦除问题**:由于 Java 泛型在运行时擦除,模式匹配无法区分 `List<String>` 和 `List<Integer>`,需用守卫模式或通配符处理[^1]。 - **性能**:模式匹配在编译时优化,性能接近传统方式,但守卫条件可能增加开销。 - **与其他语言的对比**:Java 的模式匹配比 Scala 更保守(Scala 支持更复杂的模式解构),但强调安全性和集成性[^1][^2]。 #### 总结 Java 模式匹配通过 `instanceof` 和 `switch` 机制,简化了型检查和值提取,提升了代码简洁性和安全性。从 Java 16 开始,它成为现代 Java 开发的重要工具,适用于数据处理、API 设计等场景。随着 Java 的演进,模式匹配将支持更多特性,如记录模式(Record Patterns)[^2]。建议在实际项目中尝试,并结合 IDE 的编译器提示优化代码。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值