<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
>
<channel>
<title><![CDATA[51学习网]]></title> 
<atom:link href="http://www.51keeplearning.com/rss.php" rel="self" type="application/rss+xml" />
<description><![CDATA[51学习网]]></description>
<link>http://www.51keeplearning.com/</link>
<language>zh-cn</language>
<generator>www.emlog.net</generator>
<item>
    <title>ClaudeCode 配置手册-实操经验分享（含demo下载）</title>
    <link>http://www.51keeplearning.com/post-76.html</link>
    <description><![CDATA[<p>[该文章已设置加密]</p>]]></description>
    <pubDate>Sun, 24 May 2026 19:17:20 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-76.html</guid>
</item>
<item>
    <title>别再手动重复配置了！Claude Code .claude 目录一劳永逸，团队协作效率翻倍</title>
    <link>http://www.51keeplearning.com/post-71.html</link>
    <description><![CDATA[<div id="cnblogs_post_body" contentScore="3775">
<h1 id="claude-code-官方-claude-配置指南">Claude Code 官方 .claude 配置指南</h1>
<p>Claude Code 的配置项分散在多个文件中——CLAUDE.md 管项目规范，settings.json 管权限，skills 管自定义技能，agents 管子代理……新手往往不知道该改哪个文件，甚至改错了位置导致配置不生效。.claude 目录正是统一管理这些配置的核心入口，本指南帮你理清目录结构、配置优先级和最佳实践。</p>
<div>flowchart TD
    A[".claude/ 目录"] --&gt; B[settings.json]
    A --&gt; C[CLAUDE.md]
    A --&gt; D[rules/]
    A --&gt; E[skills/]
    A --&gt; F[agents/]
    A --&gt; G[commands/]

    B --&gt; H[基础设置与权限]
    C --&gt; I[项目核心指令]
    D --&gt; J[主题化路径规则]
    E --&gt; K[可复用技能]
    F --&gt; L[专项子代理]
    G --&gt; M[自定义命令]
</div><h2 id="一claude-目录核心定位">一、.claude 目录核心定位</h2>
<p>.claude 目录是 Claude Code 的<strong>核心配置载体</strong>，用于统一管理项目指令、工具权限、自定义能力、会话规则与持久记忆，是实现 AI 编码行为标准化、团队协作配置共享、个人偏好隔离的核心入口。Claude Code 启动会话时会自动读取该目录下的配置文件，无需手动重复注入项目规则与指令，大幅提升编码协作效率。</p>
<p>绝大多数日常场景仅需编辑 <strong>CLAUDE.md</strong> 与 <strong>settings.json</strong> 两个核心文件，其余扩展模块（skills、rules、agents 等）按需启用即可，无需全量配置。</p>
<h2 id="二双层作用域项目级与全局级">二、双层作用域：项目级与全局级</h2>
<p>明确了 .claude 目录的核心定位后，第一个关键问题是"配置放在哪里"——Claude Code 采用项目级+全局级双层作用域，兼顾团队共享与个人定制。<br>
Claude Code 采用<strong>项目级+全局级</strong>双层配置作用域，兼顾团队共享与个人定制，两者配置自动合并生效，优先级遵循项目优先、本地覆盖规则。</p>
<ol>
<li><strong>项目级配置</strong><br>
存储于代码仓库根目录的 <code>.claude/</code> 文件夹，可提交至 Git 共享给团队成员，统一项目编码规范、工具命令、架构约束，确保团队内 Claude 行为一致。</li>
<li><strong>全局级配置</strong><br>
存储于用户主目录 <code>~/.claude</code>（Windows 系统对应 <code>%USERPROFILE%.claude</code>），属于个人专属配置，作用于所有项目，用于存放个人偏好、全局技能、插件数据等。</li>
</ol>
<p>若配置环境变量 <code>CLAUDE_CONFIG_DIR</code>，所有 <code>~/.claude</code> 路径将自动指向该自定义目录，满足个性化路径管理需求。</p>
<h2 id="三标准目录与文件结构">三、标准目录与文件结构</h2>
<h2 id="31-项目根目录完整结构">3.1 项目根目录完整结构</h2>
<pre><code>your-project/
├── CLAUDE.md                # 项目核心指令（会话自动加载）
├── .mcp.json                # 项目级 MCP 外部工具配置
├── .worktreeinclude         # 工作树同步忽略文件配置
└── .claude/                 # 项目级配置主目录
    ├── settings.json        # 项目基础设置
    ├── settings.local.json  # 本地个人覆盖配置（Git 忽略）
    ├── rules/               # 主题化路径门控规则
    ├── skills/              # 可复用自定义技能
    ├── commands/            # 快捷自定义命令
    ├── output-styles/       # 响应格式化规则
    ├── agents/              # 专项子代理定义
    └── agent-memory/        # 子代理持久内存
</code></pre>
<h2 id="32-全局-claude-目录核心结构">3.2 全局 ~/.claude 目录核心结构</h2>
<pre><code>~/.claude/
├── settings.json        # 全局默认配置
├── rules/               # 全局通用规则
├── skills/              # 全局复用技能
├── agents/              # 全局子代理
├── plugins/             # 已安装插件数据
├── history.jsonl        # 历史提示记录（向上箭头回忆）
├── stats-cache.json     # 令牌用量统计
└── projects/            # 各项目会话运行数据
</code></pre>
<h2 id="四核心配置文件详解">四、核心配置文件详解</h2>
<p>了解了目录结构，接下来看每个核心文件的具体用途与配置方法。</p>
<h2 id="41-claudemd项目上下文总入口">4.1 CLAUDE.md：项目上下文总入口</h2>
<p>CLAUDE.md 是<strong>每次会话启动必加载</strong>的核心指令文件，相当于 Claude 的项目“入职手册”，决定其对项目的基础认知与行为范式。</p>
<ul>
<li><strong>加载时机</strong>：会话初始化时自动载入上下文</li>
<li><strong>核心用途</strong>：声明技术栈、构建/测试/格式化命令、编码规范、架构约束、团队约定</li>
<li><strong>编写规范</strong>：建议控制在 200 行内，过长文件会降低 Claude 遵循度；专项任务规则建议拆分至 skills 或 rules，避免占用全局上下文</li>
<li><strong>快捷操作</strong>：会话内执行 <code>/memory</code> 可直接编辑；也可存放于 <code>.claude/CLAUDE.md</code>，保持项目根目录整洁</li>
</ul>
<p><strong>示例（TypeScript+React 项目）</strong></p>
<pre><code># Project conventions
## Commands
- Build: `npm run build`
- Test: `npm test`
- Lint: `npm run lint`
## Stack
- TypeScript strict mode
- React 19 纯函数组件
## Rules
- 仅具名导出，禁用默认导出
- 测试文件与源码同级：foo.ts → foo.test.ts
- API 路由统一返回 { data, error } 结构
</code></pre>
<h2 id="42-settingsjson-与-settingslocaljson">4.2 settings.json 与 settings.local.json</h2>
<p><strong>settings.json</strong> 是 Claude Code 的核心控制文件，支持项目级与全局级配置，用于管控工具权限、执行钩子、环境变量、模型默认参数。</p>
<ul>
<li>核心配置项：<code>permissions</code>（工具调用权限）、<code>hooks</code>（工具调用前后脚本）、<code>env</code>（会话环境变量）<br>
<strong>settings.local.json</strong> 为<strong>项目级本地覆盖文件</strong>，自动被 Git 忽略，用于存放个人本地偏好，不影响团队共享配置。</li>
</ul>
<h2 id="43-辅助配置文件">4.3 辅助配置文件</h2>
<ul>
<li><strong>CLAUDE.local.md</strong>：项目私人偏好配置，与 CLAUDE.md 同步加载，需手动创建并加入 <code>.gitignore</code></li>
<li><strong>.mcp.json</strong>：项目级 MCP 服务器配置，用于对接外部工具链，仅支持项目级作用域</li>
<li><strong>managed-settings.json</strong>：企业托管系统级配置，优先级最高，用户无法覆盖</li>
</ul>
<h2 id="五扩展配置模块使用">五、扩展配置模块使用</h2>
<p>核心文件之外，.claude 目录还提供多个扩展模块，按需启用即可。</p>
<h2 id="51-rules主题化路径规则">5.1 rules/：主题化路径规则</h2>
<p>存放主题化规则文件，支持<strong>路径门控</strong>，仅当访问匹配路径时加载，避免全局上下文冗余，适用于代码规范、API 设计、测试规则等专项约束。</p>
<h2 id="52-skills自定义可复用技能">5.2 skills/：自定义可复用技能</h2>
<p>自定义技能模块，通过 <code>/技能名</code> 快捷调用，适合封装高频提示模板、专项操作流程（如安全审查、代码重构），支持项目级与全局级共享。</p>
<h2 id="53-agents专项子代理">5.3 agents/：专项子代理</h2>
<p>定义具备独立提示与工具集的子代理，适用于代码评审、安全审计、文档生成等专项任务，搭配 <code>agent-memory/</code> 实现跨会话持久记忆。</p>
<h2 id="54-output-styles响应格式化">5.4 output-styles/：响应格式化</h2>
<p>自定义 Claude 响应的输出格式与风格，统一代码注释、文档结构、回复排版，满足团队输出规范要求。</p>
<h2 id="六配置优先级与生效规则">六、配置优先级与生效规则</h2>
<p>多个配置文件同时存在时，谁的优先级更高？以下规则确保配置不冲突。<br>
配置生效遵循<strong>从高到低</strong>的优先级顺序，避免多配置冲突：</p>
<ol>
<li>企业托管设置（managed-settings.json）</li>
<li>会话 CLI 标志（如 <code>--permission-mode</code>、<code>--settings</code>）</li>
<li>高优先级环境变量</li>
<li>项目级 settings.local.json</li>
<li>项目级 settings.json</li>
<li>全局级 settings.json</li>
<li>系统默认配置</li>
</ol>
<p>若配置未生效，可通过官方调试文档查看检查命令与症状对照表，定位权限、钩子、文件加载异常问题。</p>
<h2 id="七应用数据管理与安全规范">七、应用数据管理与安全规范</h2>
<h2 id="71-数据存储与自动清理">7.1 数据存储与自动清理</h2>
<p><code>~/.claude</code> 会自动存储会话记录、文件快照、调试日志等运行数据，<strong>默认超过 30 天启动时自动删除</strong>，可通过 <code>cleanupPeriodDays</code> 调整保留时长。</p>
<ul>
<li><strong>自动清理路径</strong>：projects/（会话记录）、file-history/（文件快照）、plans/（计划文件）、debug/（调试日志）等</li>
<li><strong>永久保留路径</strong>：history.jsonl（提示历史）、stats-cache.json（用量统计），需手动删除</li>
</ul>
<h2 id="72-数据安全注意事项">7.2 数据安全注意事项</h2>
<p>所有会话记录与历史文件<strong>以纯文本未加密形式存储</strong>，仅依赖操作系统文件权限保护，敏感信息存在泄露风险：</p>
<ol>
<li>缩短 <code>cleanupPeriodDays</code> 减少敏感数据留存时间</li>
<li>通过权限规则禁止 Claude 读取 <code>.env</code> 等凭证文件</li>
<li>设置 <code>CLAUDE_CODE_SKIP_PROMPT_HISTORY</code> 关闭会话记录写入</li>
</ol>
<h2 id="73-手动清理影响">7.3 手动清理影响</h2>
<p>可随时删除应用数据路径，不影响新会话，但会丢失对应功能：</p>
<ul>
<li>删除 <code>~/.claude/projects/</code>：丢失历史会话恢复、倒回能力</li>
<li>删除 <code>~/.claude/history.jsonl</code>：丢失向上箭头提示回忆功能</li>
<li>删除 <code>~/.claude/file-history/</code>：丢失文件检查点恢复能力</li>
</ul>
<p><strong>禁止删除</strong> <code>~/.claude.json</code>、<code>settings.json</code>、<code>plugins/</code>，会导致认证信息、配置偏好、插件数据丢失。</p>
<h2 id="八配置最佳实践">八、配置最佳实践</h2>
<ol>
<li><strong>最小化核心配置</strong>：优先使用 CLAUDE.md + settings.json 满足基础需求，复杂逻辑拆分至 rules/skills 模块</li>
<li><strong>作用域严格分离</strong>：团队共享规则放项目级 .claude/，个人偏好放全局 ~/.claude</li>
<li><strong>Git 配置规范</strong>：提交项目级配置文件，忽略 *.local.json、本地缓存目录</li>
<li><strong>权限最小化</strong>：仅开放必要工具调用权限，禁止读取/修改敏感文件</li>
<li><strong>轻量化规则设计</strong>：使用路径门控规则按需加载，减少全局上下文占用</li>
</ol>
<p>.claude 目录是 Claude Code 工程化落地的核心，合理配置可彻底消除重复提示、统一团队 AI 编码行为。日常使用中，绝大多数场景仅需关注 CLAUDE.md 和 settings.json 两个核心文件，其余扩展模块按需启用即可。</p>

</div>]]></description>
    <pubDate>Fri, 22 May 2026 23:40:55 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-71.html</guid>
</item>
<item>
    <title>AI人工智能：技术原理、应用场景与未来展望</title>
    <link>http://www.51keeplearning.com/post-42.html</link>
    <description><![CDATA[<p><strong>引言</strong><br />
人工智能（Artificial Intelligence，AI）作为计算机科学的重要分支，旨在通过模拟人类智能行为构建能够感知、学习、推理和决策的智能系统。从1956年达特茅斯会议首次提出AI概念至今，其发展已渗透至社会各领域，成为推动数字化转型的核心驱动力。</p>
<p><strong>一、技术架构与核心术语解析</strong>  </p>
<ol>
<li>
<p><strong>机器学习（Machine Learning）</strong><br />
作为AI的基础实现方式，机器学习通过算法使计算机从数据中自动发现规律。其核心范式包括：  </p>
<ul>
<li><strong>监督学习</strong>：基于标注数据训练模型（如图像分类中的ResNet算法）  </li>
<li><strong>无监督学习</strong>：从无标注数据中发现潜在模式（如客户分群中的K-means聚类）  </li>
<li><strong>强化学习</strong>：通过奖励机制优化决策过程（如AlphaGo的自我博弈训练）  </li>
</ul>
</li>
<li>
<p><strong>深度学习（Deep Learning）</strong><br />
基于多层神经网络的技术，在计算机视觉和自然语言处理领域取得突破。其典型架构包括：  </p>
<ul>
<li><strong>卷积神经网络（CNN）</strong>：专用于处理网格结构数据（如医学影像分析）  </li>
<li><strong>循环神经网络（RNN）</strong>：适用于序列数据处理（如股票价格预测）  </li>
<li><strong>Transformer架构</strong>：通过自注意力机制实现并行计算（如GPT系列模型）  </li>
</ul>
</li>
<li>
<p><strong>关键技术支撑</strong>  </p>
<ul>
<li><strong>自然语言处理（NLP）</strong>：实现机器理解人类语言的技术集合，涵盖词嵌入（Word Embedding）、命名实体识别（NER）等  </li>
<li><strong>知识图谱（Knowledge Graph）</strong>：以图结构呈现的语义网络，支撑智能检索和决策推理  </li>
<li><strong>联邦学习（Federated Learning）</strong>：在数据不出域的前提下实现多方联合建模，满足隐私保护需求  </li>
</ul>
</li>
</ol>
<p><strong>二、行业应用实践</strong>  </p>
<ol>
<li>
<p><strong>医疗健康领域</strong>  </p>
<ul>
<li>辅助诊断系统通过CNN分析CT影像，早期肺癌检测准确率达96%以上  </li>
<li>药物研发平台利用生成式AI设计分子结构，将新药研发周期缩短60%  </li>
</ul>
</li>
<li>
<p><strong>智能制造场景</strong>  </p>
<ul>
<li>工业质检系统采用计算机视觉技术，实现微米级缺陷实时检测  </li>
<li>数字孪生（Digital Twin）技术通过物理实体与虚拟模型交互，优化生产参数  </li>
</ul>
</li>
<li>
<p><strong>金融服务创新</strong>  </p>
<ul>
<li>风险控制模型运用图神经网络识别异常交易网络  </li>
<li>智能投顾平台基于马科维茨投资组合理论，动态优化资产配置  </li>
</ul>
</li>
</ol>
<p><strong>三、技术挑战与发展瓶颈</strong>  </p>
<ol>
<li>
<p><strong>数据依赖性问题</strong><br />
监督学习模型需大量标注数据，而高质量标注成本高昂。弱监督学习与半监督学习正在突破此限制。</p>
</li>
<li>
<p><strong>算法可解释性困境</strong><br />
深度神经网络的黑盒特性阻碍其在关键领域的应用。LIME（局部可解释模型）和SHAP（沙普利加和解释）等解释性AI技术正在发展中。</p>
</li>
<li>
<p><strong>算力资源约束</strong><br />
大模型训练需消耗兆瓦级电力，模型压缩与神经架构搜索（NAS）成为优化方向。</p>
</li>
</ol>
<p><strong>四、未来演进趋势</strong>  </p>
<ol>
<li>
<p><strong>技术融合创新</strong>  </p>
<ul>
<li>脑科学与类脑计算结合，研发脉冲神经网络（SNN）  </li>
<li>量子计算与AI融合，突破组合优化问题计算瓶颈  </li>
</ul>
</li>
<li>
<p><strong>治理框架完善</strong><br />
欧盟《人工智能法案》提出基于风险的监管分级，推动可信AI体系建设  </p>
</li>
<li>
<p><strong>人机协作深化</strong><br />
增强智能（Augmented Intelligence）强调人类与AI的协同进化，在创意设计、科学发现等领域形成新范式  </p>
</li>
</ol>
<p><strong>结语</strong><br />
人工智能正从专用智能向通用智能演进，其发展需兼顾技术创新与伦理约束。随着Transformer架构推动大语言模型突破，以及神经符号系统融合知识推理能力，AI将继续重塑产业格局并拓展人类认知边界。未来十年，以具身智能（Embodied AI）为代表的新兴方向，或将最终实现感知-决策-行动的完整智能闭环。</p>]]></description>
    <pubDate>Sun, 22 Mar 2026 22:52:18 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-42.html</guid>
</item>
<item>
    <title>Java继承</title>
    <link>http://www.51keeplearning.com/post-37.html</link>
    <description><![CDATA[<p><meta name="referrer" content="no-referrer" /><div id="cnblogs_post_body" contentScore="4224"></p>
<h1>继承与合成基本概念</h1>
<p><strong>继承：</strong>可以基于已经存在的类构造一个新类。继承已经存在的类就可以复用这些类的方法和域。在此基础上，可以添加新的方法和域，从而扩充了类的功能。</p>
<p><strong>合成：</strong>在新类里创建原有的对象称为合成。这种方式可以重复利用现有的代码而不更改它的形式。</p>
<h3>1.继承的语法</h3>
<p>关键字<code>extends</code>表明新类派生于一个已经存在的类。已存在的类称为父类或基类，新类称为子类或派生类。例如:</p>
<div>
<pre><span>class</span> Student <span>extends</span><span> Person {

}</span></pre>
</div>
<p>类Student继承了Person，Person类称为父类或基类，Student类称为子类或派生类。</p>
<h3>2.合成的语法</h3>
<p>合成比较简单，就是在一个类中创建一个已经存在的类。</p>
<div>
<pre><span>class</span><span> Student {
    Dog dog;
}</span></pre>
</div>
<h1>&nbsp;</h1>
<h1>上溯造型</h1>
<h3>1.基本概念</h3>
<p>继承的作用在于代码的复用。由于继承意味着父类的所有方法亦可在子类中使用，所以发给父类的消息亦可发给衍生类。如果Person类中有一个eat方法，那么Student类中也会有这个方法，这意味着Student对象也是Person的一种类型。</p>
<div>
<pre><span>class</span><span> Person {
    </span><span>public</span> <span>void</span><span> eat() {
        System.out.println(</span>"eat"<span>);
    }

    </span><span>static</span> <span>void</span><span> show(Person p) {
        p.eat();
    }
}
</span><span>public</span> <span>class</span> Student <span>extends</span><span> Person{
    </span><span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
        Student s </span>= <span>new</span><span> Student();
        Person.show(s);     </span><span>//</span><span> ①</span>
<span>    }
}</span></pre>
</div>
<p>&nbsp;</p>
<p>【运行结果】：<br>eat</p>
<p>在Person中定义的show方法是用来接收Person句柄的，但是在①处接收的却是Student对象的引用。这是因为Student对象也是Person对象。在show方法中，传入的句柄(对象的引用)可以是Person对象以及Person的衍生类对象。这种将Student句柄转换成Person句柄的行为成为<strong>上溯造型</strong>。</p>
<h3>2.为什么要上溯造型</h3>
<p>为什么在调用eat是要有意忽略调用它的对象类型呢？如果让show方法简单地获取Student句柄似乎更加直观易懂，但是那样会使衍生自Person类的每一个新类都要实现专属自己的show方法：</p>
<div>
<pre><span>class</span><span> Value {
    </span><span>private</span> <span>int</span> count = 1<span>;

    </span><span>private</span> Value(<span>int</span><span> count) {
        </span><span>this</span>.count =<span> count;
    }

    </span><span>public</span> <span>static</span> <span>final</span><span> Value
            v1 </span>= <span>new</span> Value(1<span>),
            v2 </span>= <span>new</span> Value(2<span>),
            v3 </span>= <span>new</span> Value(3<span>);
}

</span><span>class</span><span> Person {

    </span><span>public</span> <span>void</span><span> eat(Value v) {
        System.out.println(</span>"Person.eat()"<span>);
    }
}

</span><span>class</span> Teacher <span>extends</span><span> Person {
    </span><span>public</span> <span>void</span><span> eat(Value v) {
        System.out.println(</span>"Teacher.eat()"<span>);
    }
}

</span><span>class</span> Student <span>extends</span><span> Person {
    </span><span>public</span> <span>void</span><span> eat(Value v) {
        System.out.println(</span>"Student.eat()"<span>);
    }
}

</span><span>public</span> <span>class</span><span> UpcastingDemo {
    </span><span>public</span> <span>static</span> <span>void</span><span> show(Student s) {
        s.eat(Value.v1);
    }

    </span><span>public</span> <span>static</span> <span>void</span><span> show(Teacher t) {
        t.eat(Value.v1);
    }

    </span><span>public</span> <span>static</span> <span>void</span><span> show(Person p) {
        p.eat(Value.v1);
    }

    </span><span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
        Student s </span>= <span>new</span><span> Student();
        Teacher t </span>= <span>new</span><span> Teacher();
        Person p </span>= <span>new</span><span> Person();
        show(s);
        show(t);
        show(p);
    }
}</span></pre>
</div>
<p>这种做法一个很明显的缺陷就是必须为每一个Person类的衍生类定义与之紧密相关的方法，产生了很多重复的代码。另一方面，对于如果忘记了方法的重载也不会报错。上例中的三个show方法完全可以合并为一个：</p>
<div>
<pre><span>public</span> <span>static</span> <span>void</span><span> show(Person p) {
     p.eat(Value.v1);
}</span></pre>
</div>
<p>&nbsp;</p>
<h1>动态绑定</h1>
<p>当执行show(s)时,输出结果是Student.eat()，这确实是希望得到的结果，但是似乎没有按照我们希望的形式来执行，再来看一下show方法：</p>
<div>
<pre><span>public</span> <span>static</span> <span>void</span><span> show(Person p) {
     p.eat(Value.v1);
}</span></pre>
</div>
<p>它接收的是Person句柄，当执行show(s)时，它是如何知道Person句柄指向的是一个Student对象而不是Teacher对象呢？编译器是无从得知的，这涉及到接下来要说明的绑定问题。</p>
<h3>1.方法调用的绑定</h3>
<p>将一个方法同一个方法主体连接在一起就称为<strong>绑定(Binding)</strong>。若在运行运行前执行绑定，就称为“早期绑定”。上面的例子中，在只有一个Person句柄的情况下，编译器不知道具体调用哪个方法。Java实现了一种方法调用机制，可在运行期间判断对象的类型，然后调用相应的方法，这种在运行期间进行，以对象的类型为基础的绑定称为<strong>动态绑定</strong>。除非一个方法被声明为final，Java中的所有方法都是动态绑定的。</p>
<p>用一张图表示上溯造型的继承关系：</p>
<p><img src="https://images2015.cnblogs.com/blog/525435/201512/525435-20151203233242393-347102988.jpg" alt=""></p>
<pre><code>&nbsp;</code></pre>
<p data-anchor-id="44e8">用代码概括为：</p>
<div>
<pre>Shape s = <span>new</span> Shape();</pre>
</div>
<p>按照继承关系，将创建的Circle对象句柄赋给一个Shape是合法的，因为Circle属于Shape的一种。</p>
<p>当调用其中一个基础类方法时：</p>
<div>
<pre>Shape s = <span>new</span> Shape();</pre>
</div>
<p data-anchor-id="h0zj">此时，调用的是Circle.draw(),这是由于动态绑定的原因。</p>
<div>
<pre><span>class</span><span> Person {
    </span><span>void</span><span> eat() {}
    </span><span>void</span><span> speak() {}
}
</span><span>class</span> Boy <span>extends</span><span> Person {
    </span><span>void</span><span> eat() {
        System.out.println(</span>"Boy.eat()"<span>);
    }
    </span><span>void</span><span> speak() {
        System.out.println(</span>"Boy.speak()"<span>);
    }
}
</span><span>class</span> Girl <span>extends</span><span> Person {
    </span><span>void</span><span> eat() {
        System.out.println(</span>"Girl.eat()"<span>);
    }
    </span><span>void</span><span> speak() {
        System.out.println(</span>"Girl.speak()"<span>);
    }
}
</span><span>public</span> <span>class</span><span> Persons {
    </span><span>public</span> <span>static</span><span> Person randPerson() {
        </span><span>switch</span> ((<span>int</span>)(Math.random() * 2<span>)) {
        </span><span>default</span><span>:
        </span><span>case</span> 0<span>:
            </span><span>return</span> <span>new</span><span> Boy();
        </span><span>case</span> 1<span>:
            </span><span>return</span> <span>new</span><span> Girl();
        }
    }
    </span><span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
        Person[] p </span>= <span>new</span> Person[4<span>];
        </span><span>for</span> (<span>int</span> i = 0; i &lt; p.length; i++<span>) {
            p[i] </span>= randPerson();    <span>//</span><span> 随机生成Boy或Girl</span>
<span>        }
        </span><span>for</span> (<span>int</span> i = 0; i &lt; p.length; i++<span>) {
            p[i].eat();
        }
    }
}</span></pre>
</div>
<p data-anchor-id="3ct7">对所有从Person衍生出来的类，Person建立了一个通用接口，所有衍生的类都有eat和speak两种行为。衍生类覆盖了这些定义，重新定义了这两种行为。在主类中，randPerson随机选择Person对象的句柄。**上诉造型是在return语句里发生的。**return语句取得一个Boy或Girl的句柄并将其作为Person类型返回，此时并不知道具体是什么类型，只知道是Person对象句柄。在main方法中调用randPerson方法为数组填入Person对象，但不知具体情况。当调用数组每个元素的eat方法时，动态绑定的作用就是执行对象的重新定义了的方法。</p>
<p data-anchor-id="g641">然而，动态绑定是有前提的，<strong>绑定的方法必须存在于基类中</strong>，否则无法编译通过。</p>
<div>
<pre><span>class</span><span> Person {
    </span><span>void</span><span> eat() {
        System.out.println(</span>"Person.eat()"<span>);
    }
}
</span><span>class</span> Boy <span>extends</span><span> Person {
    </span><span>void</span><span> eat() {
        System.out.println(</span>"Boy.eat()"<span>);
    }
    </span><span>void</span><span> speak() {
        System.out.println(</span>"Boy.speak()"<span>);
    }
}
</span><span>public</span> <span>class</span><span> Persons {
    </span><span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
        Person p </span>= <span>new</span><span> Boy();
        p.eat();
        p.speak();  </span><span>//</span><span> The method speak() is undefined for the type Person</span>
<span>    }
}</span></pre>
</div>
<p data-anchor-id="kz0j">如果子类中没有定义覆盖方法，则会调用父类中的方法：</p>
<div>
<pre><span>class</span><span> Person {
    </span><span>void</span><span> eat() {
        System.out.println(</span>"Person.eat()"<span>);
    }
}
</span><span>class</span> Boy <span>extends</span><span> Person {
}
</span><span>public</span> <span>class</span><span> Persons {
    </span><span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
        Person p </span>= <span>new</span><span> Boy();
        p.eat();
    }
}</span></pre>
</div>
<p data-anchor-id="725w">【运行结果】:<span>&nbsp;<br>Person.eat()</span></p>
<h3 id="2静态方法的绑定" data-anchor-id="hvfk">2.静态方法的绑定</h3>
<p data-anchor-id="wiag">将上面的方法都加上static关键字，变成静态方法：</p>
<div>
<pre><span>class</span><span> Person {
    </span><span>static</span> <span>void</span><span> eat() {
        System.out.println(</span>"Person.eat()"<span>);
    }
    </span><span>static</span> <span>void</span><span> speak() {
        System.out.println(</span>"Person.speak()"<span>);
    }
}
</span><span>class</span> Boy <span>extends</span><span> Person {
    </span><span>static</span> <span>void</span><span> eat() {
        System.out.println(</span>"Boy.eat()"<span>);
    }
    </span><span>static</span> <span>void</span><span> speak() {
        System.out.println(</span>"Boy.speak()"<span>);
    }
}
</span><span>class</span> Girl <span>extends</span><span> Person {
    </span><span>static</span> <span>void</span><span> eat() {
        System.out.println(</span>"Girl.eat()"<span>);
    }
    </span><span>static</span> <span>void</span><span> speak() {
        System.out.println(</span>"Girl.speak()"<span>);
    }
}
</span><span>public</span> <span>class</span><span> Persons {
    </span><span>public</span> <span>static</span><span> Person randPerson() {
        </span><span>switch</span> ((<span>int</span>)(Math.random() * 2<span>)) {
        </span><span>default</span><span>:
        </span><span>case</span> 0<span>:
            </span><span>return</span> <span>new</span><span> Boy();
        </span><span>case</span> 1<span>:
            </span><span>return</span> <span>new</span><span> Girl();
        }
    }
    </span><span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
        Person[] p </span>= <span>new</span> Person[4<span>];
        </span><span>for</span> (<span>int</span> i = 0; i &lt; p.length; i++<span>) {
            p[i] </span>= randPerson();    <span>//</span><span> 随机生成Boy或Girl</span>
<span>        }
        </span><span>for</span> (<span>int</span> i = 0; i &lt; p.length; i++<span>) {
            p[i].eat();
        }
    }
}</span></pre>
</div>
<p data-anchor-id="35g1">【运行结果】：<span>&nbsp;<br>Person.eat()<span>&nbsp;<br>Person.eat()<span>&nbsp;<br>Person.eat()<span>&nbsp;<br>Person.eat()<span>&nbsp;<br>观察结果，<strong>对于静态方法而言，不管父类引用指向的什么子类对象，调用的都是父类的方法。</strong></span></span></span></span></span></p>
<blockquote data-anchor-id="3axo" contentScore="104">
<p><strong>助记口诀</strong><span>&nbsp;<br>- 静态方法:静态方法看父类<span>&nbsp;<br>- 非静态方法：非静态方法看子类</span></span></p>

</blockquote>
<p></div></p>
<footer>
    <p>本站部分内容来源于网络，如有侵犯您的权益，请通过以下方式联系我们：</p>
    <p>邮箱：zytlsd@163.com</p>
    <p>我们将在收到通知后第一时间处理。</p>
</footer>]]></description>
    <pubDate>Sun, 19 Oct 2025 21:30:49 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-37.html</guid>
</item>
<item>
    <title>Java RMI</title>
    <link>http://www.51keeplearning.com/post-36.html</link>
    <description><![CDATA[<p><meta name="referrer" content="no-referrer" /><div contentScore="2380">/*<em> <br></em> Created by IntelliJ IDEA. <br><em> User: leizhimin <br></em> Date: 2008-8-7 22:03:35 <br><em> 创建RMI注册表，启动RMI服务，并将远程对象注册到RMI注册表中。 <br></em>/ <br>public class HelloServer { <br>&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String args[]) { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//创建一个远程对象 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IHello rhello = new HelloImpl(); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//本地主机上的远程对象注册表Registry的实例，并指定端口为8888，这一步必不可少（Java默认端口是1099），必不可缺的一步，缺少注册表创建，则无法绑定对象到远程注册表上 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LocateRegistry.createRegistry(8888); </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//把远程对象注册到RMI注册服务器上，并命名为RHello <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//绑定的URL标准格式为：rmi://host:port/name(其中协议名可以省略，下面两种写法都是正确的） <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Naming.bind(&quot;rmi://localhost:8888/RHello&quot;,rhello); <br>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Naming.bind(&quot;//localhost:8888/RHello&quot;,rhello); </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&quot;&gt;&gt;&gt;&gt;&gt;INFO:远程IHello对象绑定成功！&quot;); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (RemoteException e) { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&quot;创建远程对象发生异常！&quot;); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace(); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (AlreadyBoundException e) { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&quot;发生重复绑定对象异常！&quot;); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace(); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (MalformedURLException e) { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&quot;发生URL畸形异常！&quot;); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace(); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <br>&nbsp;&nbsp;&nbsp;&nbsp;} <br>}</p></div></p>
<footer>
    <p>本站部分内容来源于网络，如有侵犯您的权益，请通过以下方式联系我们：</p>
    <p>邮箱：zytlsd@163.com</p>
    <p>我们将在收到通知后第一时间处理。</p>
</footer>]]></description>
    <pubDate>Sun, 19 Oct 2025 21:30:48 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-36.html</guid>
</item>
<item>
    <title>Java学习之二-Java反射机制</title>
    <link>http://www.51keeplearning.com/post-35.html</link>
    <description><![CDATA[<p><meta name="referrer" content="no-referrer" /><div id="cnblogs_post_body" contentScore="6074"></p>
<p><strong>问题：</strong>  </p><p>在运行时，对一个JAVA类，能否知道属性和方法；能否调用它的任意方法？  </p><p>答案是可以的，JAVA提供一种反射机制可以实现。  </p><p>&nbsp; </p><p><strong>目录</strong>  </p><ol> <li>什么是JAVA的反射机制  </li><li>JDK中提供的Reflection API  </li><li>JAVA反射机制提供了什么功能  <ul> <li>获取类的Class对象  </li><li>获取类的Fields  </li><li>获取类的Method  </li><li>获取类的Constructor  </li><li>新建类的实例<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class&lt;T&gt;的函数newInstance<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通过Constructor对象的方法newInstance</li></ul> </li><li>调用类的函数<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 调用private函数  </li><li>设置/获取类的属性值<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private属性  </li><li>动态创建代理类<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 动态代理源码分析  </li><li>JAVA反射Class&lt;T&gt;类型源代码分析  </li><li>JAVA反射原理分析<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class文件结构<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JVM加载类对象，对反射的支持  </li><li>JAVA反射的应用</li></ol> <p>&nbsp;</p> <p><strong>一、什么是JAVA的反射机制</strong>  </p><p>Java反射是Java被视为动态（或准动态）语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息，包括其modifiers（诸如public, static 等）、superclass（例如Object）、实现之interfaces（例如Cloneable），也包括fields和methods的所有信息，并可于运行时改变fields内容或唤起methods。  </p><p>Java反射机制容许程序在运行时加载、探知、使用编译期间完全未知的classes。  </p><p>换言之，Java可以加载一个运行时才得知名称的class，获得其完整结构。  </p><p>&nbsp; </p><p><strong>二、JDK中提供的Reflection API</strong>  </p><p>Java反射相关的API在包<a>java.lang.reflect</a>中，JDK 1.6.0的reflect包如下图：  </p><p><a href="http://images.cnblogs.com/cnblogs_com/Quincy/201106/201106191021251045.png"><img title="clip_image001" alt="clip_image001" src="https://images.cnblogs.com/cnblogs_com/Quincy/201106/201106191021272672.png" width="232" height="377"></a>  </p><p> </p><table cellspacing="0" cellpadding="2" width="984"> <tbody> <tr> <td valign="top" width="122">Member接口</td> <td valign="top" width="860">该接口可以获取有关类成员（域或者方法）后者构造函数的信息。</td></tr> <tr> <td valign="top" width="128">AccessibleObject类</td> <td valign="top" width="855">该类是域(field)对象、方法(method)对象、构造函数(constructor)对象的基础类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。</td></tr> <tr> <td valign="top" width="133">Array类</td> <td valign="top" width="850">该类提供动态地生成和访问JAVA数组的方法。</td></tr> <tr> <td valign="top" width="138">Constructor类</td> <td valign="top" width="845">提供一个类的构造函数的信息以及访问类的构造函数的接口。</td></tr> <tr> <td valign="top" width="143">Field类</td> <td valign="top" width="841">提供一个类的域的信息以及访问类的域的接口。</td></tr> <tr> <td valign="top" width="147">Method类</td> <td valign="top" width="837">提供一个类的方法的信息以及访问类的方法的接口。</td></tr> <tr> <td valign="top" width="151">Modifier类</td> <td valign="top" width="834">提供了 static 方法和常量，对类和成员访问修饰符进行解码。</td></tr> <tr> <td valign="top" width="154">Proxy类</td> <td valign="top" width="831" contentScore="60"> <p>提供动态地生成代理类和类实例的静态方法。</p></td></tr></tbody></table> <p>&nbsp; </p><p><strong>三、JAVA反射机制提供了什么功能</strong>  </p><p>Java反射机制提供如下功能：  </p><p>在运行时判断任意一个对象所属的类  </p><p>在运行时构造任意一个类的对象  </p><p>在运行时判段任意一个类所具有的成员变量和方法  </p><p>在运行时调用任一个对象的方法  </p><p>在运行时创建新类对象  </p><p>在使用Java的反射功能时，基本首先都要获取类的Class对象，再通过Class对象获取其他的对象。  </p><p>这里首先定义用于测试的类:</p><pre>class Type{
    public int pubIntField;
    public String pubStringField;
    private int prvIntField;

    public Type(){
        Log("Default Constructor");
    }

    Type(int arg1, String arg2){
        pubIntField = arg1;
        pubStringField = arg2;

        Log("Constructor with parameters");
    }

    public void setIntField(int val) {
        this.prvIntField = val;
    }
    public int getIntField() {
        return prvIntField;
    }

    private void Log(String msg){
        System.out.println("Type:" + msg);
    }
}

class ExtendType extends Type{
    public int pubIntExtendField;
    public String pubStringExtendField;
    private int prvIntExtendField;

    public ExtendType(){
        Log("Default Constructor");
    }   

    ExtendType(int arg1, String arg2){      
        pubIntExtendField = arg1;
        pubStringExtendField = arg2;

        Log("Constructor with parameters");
    }

    public void setIntExtendField(int field7) {
        this.prvIntExtendField = field7;
    }
    public int getIntExtendField() {
        return prvIntExtendField;
    }

    private void Log(String msg){
        System.out.println("ExtendType:" + msg);
    }
}
</pre>
<p>&nbsp;</p>
<p><strong>1、获取类的Class对象</strong></p>
<p>Class 类的实例表示正在运行的 Java 应用程序中的类和接口。获取类的Class对象有多种方式：</p>
<table cellspacing="0" cellpadding="2" width="986">
<tbody>
<tr>
<td valign="top" width="167">调用getClass</td>
<td valign="top" width="817" contentScore="133">
<p>Boolean var1 = true; 
</p><p>Class&lt;?&gt; classType2 = var1.getClass(); 
</p><p>System.out.println(classType2); 
</p><p>输出：class java.lang.Boolean</p></td></tr>
<tr>
<td valign="top" width="171">运用.class 语法</td>
<td valign="top" width="813" contentScore="109">
<p>Class&lt;?&gt; classType4 = Boolean.class; 
</p><p>System.out.println(classType4); 
</p><p>输出：class java.lang.Boolean</p></td></tr>
<tr>
<td valign="top" width="175">运用static method Class.forName()</td>
<td valign="top" width="810" contentScore="130">
<p>Class&lt;?&gt; classType5 = Class.forName("java.lang.Boolean"); 
</p><p>System.out.println(classType5); 
</p><p>输出：class java.lang.Boolean</p></td></tr>
<tr>
<td valign="top" width="178" contentScore="111">
<p>运用primitive wrapper classes的TYPE 语法 
</p><p>这里返回的是原生类型，和Boolean.class返回的不同</p></td>
<td valign="top" width="807" contentScore="139">
<p>Class&lt;?&gt; classType3 = Boolean.TYPE; 
</p><p>System.out.println(classType3);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p><p>输出：boolean</p></td></tr></tbody></table>
<p>&nbsp; </p><p><strong>2、获取类的Fields</strong> 
</p><p>可以通过反射机制得到某个类的某个属性，然后改变对应于这个类的某个实例的该属性值。JAVA 的Class&lt;T&gt;类提供了几个方法获取类的属性。</p>
<table cellspacing="0" cellpadding="2" width="985">
<tbody>
<tr>
<td valign="top" width="168">public <a>Field</a> getField(<a>String</a> name)</td>
<td valign="top" width="815">返回一个 Field 对象，它反映此 Class 对象所表示的类或接口的指定公共成员字段</td></tr>
<tr>
<td valign="top" width="173">public <a>Field</a>[] getFields()</td>
<td valign="top" width="811">返回一个包含某些 Field 对象的数组，这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段</td></tr>
<tr>
<td valign="top" width="177">public <a>Field</a> getDeclaredField(<a>String</a> name)</td>
<td valign="top" width="807">返回一个 Field 对象，该对象反映此 Class 对象所表示的类或接口的指定已声明字段</td></tr>
<tr>
<td valign="top" width="181">public <a>Field</a>[] getDeclaredFields()</td>
<td valign="top" width="803" contentScore="119">
<p>返回 Field 对象的一个数组，这些对象反映此 Class 对象所表示的类或接口所声明的所有字段</p></td></tr></tbody></table>
<p>&nbsp;</p><pre>  Class&lt;?&gt; classType = ExtendType.class;

    // 使用getFields获取属性
    Field[] fields = classType.getFields();
    for (Field f : fields)
    {
        System.out.println(f);
    }

    System.out.println();

    // 使用getDeclaredFields获取属性
    fields = classType.getDeclaredFields();
    for (Field f : fields)
    {
        System.out.println(f);
    }
</pre>
<p>&nbsp;</p>
<p>输出： 
</p><p>public int com.quincy.ExtendType.pubIntExtendField 
</p><p>public java.lang.String com.quincy.ExtendType.pubStringExtendField 
</p><p>public int com.quincy.Type.pubIntField 
</p><p>public java.lang.String com.quincy.Type.pubStringField 
</p><p>public int com.quincy.ExtendType.pubIntExtendField 
</p><p>public java.lang.String com.quincy.ExtendType.pubStringExtendField 
</p><p>private int com.quincy.ExtendType.prvIntExtendField 
</p><p>可见getFields和getDeclaredFields区别： 
</p><p>getFields返回的是申明为public的属性，包括父类中定义， 
</p><p>getDeclaredFields返回的是指定类定义的所有定义的属性，不包括父类的。 
</p><p>&nbsp; </p><p><strong>3、获取类的Method</strong> 
</p><p>通过反射机制得到某个类的某个方法，然后调用对应于这个类的某个实例的该方法 
</p><p>Class&lt;T&gt;类提供了几个方法获取类的方法。</p>
<table cellspacing="0" cellpadding="2" width="985">
<tbody>
<tr>
<td valign="top" width="171">public <a>Method</a> getMethod(<a>String</a> name, <a>Class</a>&lt;?&gt;... parameterTypes)</td>
<td valign="top" width="812" contentScore="105">
<p>返回一个 Method 对象，它反映此 Class 对象所表示的类或接口的指定公共成员方法</p></td></tr>
<tr>
<td valign="top" width="176">public <a>Method</a>[] getMethods()</td>
<td valign="top" width="807" contentScore="233">
<p>返回一个包含某些 Method 对象的数组，这些对象反映此 Class 对象所表示的类或接口（包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口）的公共 member 方法</p></td></tr>
<tr>
<td valign="top" width="181" contentScore="77">
<p>public <a>Method</a> getDeclaredMethod(<a>String</a> name,<a>Class</a>&lt;?&gt;... parameterTypes)</p></td>
<td valign="top" width="803" contentScore="108">
<p>返回一个 Method 对象，该对象反映此 Class 对象所表示的类或接口的指定已声明方法</p></td></tr>
<tr>
<td valign="top" width="185">public <a>Method</a>[] getDeclaredMethods()</td>
<td valign="top" width="799" contentScore="207">
<p>返回 Method 对象的一个数组，这些对象反映此 Class 对象表示的类或接口声明的所有方法，包括公共、保护、默认（包）访问和私有方法，但不包括继承的方法</p></td></tr></tbody></table>
<p>&nbsp;</p><pre>  // 使用getMethods获取函数 
    Class&lt;?&gt; classType = ExtendType.class;
    Method[] methods = classType.getMethods();
    for (Method m : methods)
    {
        System.out.println(m);
    }

    System.out.println();

    // 使用getDeclaredMethods获取函数 
    methods = classType.getDeclaredMethods();
    for (Method m : methods)
    {
        System.out.println(m);
    }
</pre>
<p>输出： 
</p><p>public void com.quincy.ExtendType.setIntExtendField(int) 
</p><p>public int com.quincy.ExtendType.getIntExtendField() 
</p><p>public void com.quincy.Type.setIntField(int) 
</p><p>public int com.quincy.Type.getIntField() 
</p><p>public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException 
</p><p>public final void java.lang.Object.wait() throws java.lang.InterruptedException 
</p><p>public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException 
</p><p>public boolean java.lang.Object.equals(java.lang.Object) 
</p><p>public java.lang.String java.lang.Object.toString() 
</p><p>public native int java.lang.Object.hashCode() 
</p><p>public final native java.lang.Class java.lang.Object.getClass() 
</p><p>public final native void java.lang.Object.notify() 
</p><p>public final native void java.lang.Object.notifyAll() 
</p><p>private void com.quincy.ExtendType.Log(java.lang.String) 
</p><p>public void com.quincy.ExtendType.setIntExtendField(int) 
</p><p>public int com.quincy.ExtendType.getIntExtendField() 
</p><p>&nbsp; </p><p><strong>4、获取类的Constructor</strong> 
</p><p>通过反射机制得到某个类的构造器，然后调用该构造器创建该类的一个实例&nbsp; </p><p>Class&lt;T&gt;类提供了几个方法获取类的构造器。</p>
<table cellspacing="0" cellpadding="2" width="985">
<tbody>
<tr>
<td valign="top" width="179">public <a>Constructor</a>&lt;<a>T</a>&gt; getConstructor(<a>Class</a>&lt;?&gt;... parameterTypes)</td>
<td valign="top" width="804" contentScore="101">
<p>返回一个 Constructor 对象，它反映此 Class 对象所表示的类的指定公共构造方法</p></td></tr>
<tr>
<td valign="top" width="185">public <a>Constructor</a>&lt;?&gt;[] getConstructors()</td>
<td valign="top" width="798" contentScore="131">
<p>返回一个包含某些 Constructor 对象的数组，这些对象反映此 Class 对象所表示的类的所有公共构造方法</p></td></tr>
<tr>
<td valign="top" width="190" contentScore="84">
<p>public <a>Constructor</a>&lt;<a>T</a>&gt; getDeclaredConstructor(<a>Class</a>&lt;?&gt;... parameterTypes)</p></td>
<td valign="top" width="793" contentScore="110">
<p>返回一个 Constructor 对象，该对象反映此 Class 对象所表示的类或接口的指定构造方法</p></td></tr>
<tr>
<td valign="top" width="195" contentScore="55">
<p>public <a>Constructor</a>&lt;?&gt;[] getDeclaredConstructors()</p></td>
<td valign="top" width="789" contentScore="188">
<p>返回 Constructor 对象的一个数组，这些对象反映此 Class 对象表示的类声明的所有构造方法。它们是公共、保护、默认（包）访问和私有构造方法</p></td></tr></tbody></table>
<p>&nbsp;</p><pre>  // 使用getConstructors获取构造器  
    Constructor&lt;?&gt;[] constructors = classType.getConstructors();
    for (Constructor&lt;?&gt; m : constructors)
    {
        System.out.println(m);
    }

    System.out.println();

    // 使用getDeclaredConstructors获取构造器   
    constructors = classType.getDeclaredConstructors();
    for (Constructor&lt;?&gt; m : constructors)
    {
        System.out.println(m);
    }

    输出：
    public com.quincy.ExtendType()

    public com.quincy.ExtendType()
    com.quincy.ExtendType(int,java.lang.String)
</pre>
<p>&nbsp;</p>
<p><strong>5、新建类的实例</strong> 
</p><p>通过反射机制创建新类的实例，有几种方法可以创建</p>
<table cellspacing="0" cellpadding="2" width="987">
<tbody>
<tr>
<td valign="top" width="183">调用无自变量ctor</td>
<td valign="top" width="802" contentScore="685">
<p><strong>1、调用类的Class对象的newInstance方法，该方法会调用对象的默认构造器，如果没有默认构造器，会调用失败.</strong></p>
<p>Class&lt;?&gt; classType = ExtendType.class; 
</p><p>Object inst = classType.newInstance(); 
</p><p>System.out.println(inst); 
</p><p>输出： 
</p><p>Type:Default Constructor 
</p><p>ExtendType:Default Constructor 
</p><p><a href="mailto:com.quincy.ExtendType@d80be3" rel="noopener nofollow">com.quincy.ExtendType@d80be3</a></p>
<p>&nbsp;</p>
<p><strong>2、调用默认Constructor对象的newInstance方法</strong> 
</p><p>Class&lt;?&gt; classType = ExtendType.class; 
</p><p>Constructor&lt;?&gt; constructor1 = classType.getConstructor(); 
</p><p>Object inst = constructor1.newInstance(); 
</p><p>System.out.println(inst); 
</p><p>输出： 
</p><p>Type:Default Constructor 
</p><p>ExtendType:Default Constructor 
</p><p>com.quincy.ExtendType@1006d75</p></td></tr>
<tr>
<td valign="top" width="186">调用带参数ctor</td>
<td valign="top" width="800" contentScore="339">
<p><strong>3、调用带参数Constructor对象的newInstance方法</strong> 
</p><p>Constructor&lt;?&gt; constructor2 = 
</p><p>classType.getDeclaredConstructor(int.class, String.class); 
</p><p>Object inst = constructor2.newInstance(1, "123"); 
</p><p>System.out.println(inst); 
</p><p>输出： 
</p><p>Type:Default Constructor 
</p><p>ExtendType:Constructor with parameters 
</p><p>com.quincy.ExtendType@15e83f9</p></td></tr></tbody></table>
<p>&nbsp; </p><p><strong>6、调用类的函数</strong> 
</p><p>通过反射获取类Method对象，调用Field的Invoke方法调用函数。</p><pre>   Class&lt;?&gt; classType = ExtendType.class;
    Object inst = classType.newInstance();
    Method logMethod = classType.<strong>getDeclaredMethod</strong>("Log", String.class);
    logMethod.invoke(inst, "test");

    输出：
    Type:Default Constructor
    ExtendType:Default Constructor
    Class com.quincy.ClassT can not access a member of class com.quincy.ExtendType with modifiers "private"

    上面失败是由于没有权限调用private函数，这里需要设置Accessible为true;
    Class&lt;?&gt; classType = ExtendType.class;
    Object inst = classType.newInstance();
    Method logMethod = classType.getDeclaredMethod("Log", String.class);
    logMethod.setAccessible(true);
    logMethod.invoke(inst, "test");
</pre>
<p><strong>7、设置/获取类的属性值</strong> 
</p><p>通过反射获取类的Field对象，调用Field方法设置或获取值</p><pre> Class&lt;?&gt; classType = ExtendType.class;
    Object inst = classType.newInstance();
    Field intField = classType.getField("pubIntExtendField");
    intField.<strong>setInt</strong>(inst, 100);
        int value = intField.<strong>getInt</strong>(inst);</pre><pre>&nbsp;</pre>
<p><strong>四、动态创建代理类</strong> 
</p><p>代理模式：代理模式的作用=为其他对象提供一种代理以控制对这个对象的访问。 
</p><p>代理模式的角色： 
</p><blockquote contentScore="63">
<p>抽象角色：声明真实对象和代理对象的共同接口</p></blockquote>
<blockquote contentScore="99">
<p>代理角色：代理角色内部包含有真实对象的引用，从而可以操作真实对象。</p></blockquote>
<blockquote contentScore="90">
<p>真实角色：代理角色所代表的真实对象，是我们最终要引用的对象。</p></blockquote>
<p>动态代理：</p>
<table cellspacing="0" cellpadding="2" width="985">
<tbody>
<tr>
<td valign="top" width="180">java.lang.reflect.Proxy</td>
<td valign="top" width="803" contentScore="129">
<p>Proxy 提供用于创建动态代理类和实例的静态方法，它还是由这些方法创建的所有动态代理类的超类</p></td></tr>
<tr>
<td valign="top" width="185">InvocationHandler</td>
<td valign="top" width="798" contentScore="243">
<p>是代理实例的调用处理程序 实现的接口，每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时，将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。</p></td></tr></tbody></table>
<p>&nbsp; </p><p>动态Proxy是这样的一种类: 
</p><p>它是在运行生成的类，在生成时你必须提供一组Interface给它，然后该class就宣称它实现了这些interface。你可以把该class的实例当作这些interface中的任何一个来用。当然，这个Dynamic Proxy其实就是一个Proxy，它不会替你作实质性的工作，在生成它的实例时你必须提供一个handler，由它接管实际的工作。 
</p><p>在使用动态代理类时，我们必须实现InvocationHandler接口 
</p><p>步骤： 
</p><p><strong>1、定义抽象角色</strong></p>
<p>public interface Subject { 
</p><p>public void Request(); 
</p><p>} 
</p><p>&nbsp; </p><p><strong>2、定义真实角色</strong></p>
<p>public class RealSubject implements Subject { 
</p><p>@Override 
</p><p>public void Request() { 
</p><p>// TODO Auto-generated method stub 
</p><p>System.out.println("RealSubject"); 
</p><p>} 
</p><p>} 
</p><p>&nbsp; </p><p><strong>3、定义代理角色</strong></p>
<p>public class DynamicSubject implements InvocationHandler { 
</p><p>private Object sub; 
</p><p>public DynamicSubject(Object obj){ 
</p><p>this.sub = obj; 
</p><p>} 
</p><p>@Override 
</p><p>public Object invoke(Object proxy, Method method, Object[] args) 
</p><p>throws Throwable { 
</p><p>// TODO Auto-generated method stub 
</p><p>System.out.println("Method:"+ method + ",Args:" + args); 
</p><p>method.invoke(sub, args); 
</p><p>return null; 
</p><p>} 
</p><p>} 
</p><p>&nbsp; </p><p><strong>4、通过Proxy.newProxyInstance构建代理对象</strong></p>
<p>RealSubject realSub = new RealSubject(); 
</p><p>InvocationHandler handler = new DynamicSubject(realSub); 
</p><p>Class&lt;?&gt; classType = handler.getClass(); 
</p><p>Subject sub = (Subject)Proxy.newProxyInstance(classType.getClassLoader(), 
</p><p>realSub.getClass().getInterfaces(), handler); 
</p><p>System.out.println(sub.getClass());&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p><p>&nbsp; </p><p><strong>5、通过调用代理对象的方法去调用真实角色的方法。</strong></p>
<p>sub.Request(); 
</p><p>输出： 
</p><p>class $Proxy0 新建的代理对象，它实现指定的接口 
</p><p>Method:public abstract void DynamicProxy.Subject.Request(),Args:null 
</p><p>RealSubject 调用的真实对象的方法</p>
<p>待续...</p>
</div>
<footer>
    <p>本站部分内容来源于网络，如有侵犯您的权益，请通过以下方式联系我们：</p>
    <p>邮箱：zytlsd@163.com</p>
    <p>我们将在收到通知后第一时间处理。</p>
</footer>]]></description>
    <pubDate>Sun, 19 Oct 2025 21:30:48 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-35.html</guid>
</item>
<item>
    <title>【JAVA】浅谈java枚举类</title>
    <link>http://www.51keeplearning.com/post-34.html</link>
    <description><![CDATA[<p><meta name="referrer" content="no-referrer" /><div id="cnblogs_post_body" contentScore="2224"></p>
<p><span>一、什么情况下使用枚举类？</span></p>
<p>　　有的时候一个类的对象是有限且固定的，这种情况下我们使用枚举类就比较方便？</p>
<p><span>二、为什么不用静态常量来替代枚举类呢？</span></p>
<div>
<pre>    <span>public</span> <span>static</span> <span>final</span> <span>int</span> SEASON_SPRING = 1<span>;
    </span><span>public</span> <span>static</span> <span>final</span> <span>int</span> SEASON_SUMMER = 2<span>;
    </span><span>public</span> <span>static</span> <span>final</span> <span>int</span> SEASON_FALL = 3<span>;
    </span><span>public</span> <span>static</span> <span>final</span> <span>int</span> SEASON_WINTER = 4;</pre>
</div>
<p>　　枚举类更加直观，类型安全。使用常量会有以下几个缺陷：</p>
<p>　　1. 类型不安全。若一个方法中要求传入季节这个参数，用常量的话，形参就是int类型，开发者传入任意类型的int类型值就行，但是如果是枚举类型的话，就只能传入枚举类中包含的对象。</p>
<p>　　2. 没有命名空间。开发者要在命名的时候以SEASON_开头，这样另外一个开发者再看这段代码的时候，才知道这四个常量分别代表季节。</p>
<p><span>三、枚举类入门</span></p>
<p>　　先看一个简单的枚举类。</p>
<div>
<pre><span>package</span><span> enumcase;

</span><span>public</span> <span>enum</span><span> SeasonEnum {
    SPRING,SUMMER,FALL,WINTER;
}</span></pre>
</div>
<ol>
<li>enum和class、interface的地位一样</li>
<li>使用enum定义的枚举类默认继承了<span>java.lang.</span>Enum，而不是继承Object类。枚举类可以实现一个或多个接口。</li>
<li>枚举类的所有实例都必须放在第一行展示，不需使用new 关键字，不需显式调用构造器。自动添加public static final修饰。</li>
<li>使用enum定义、非抽象的枚举类默认使用final修饰，不可以被继承。</li>
<li>枚举类的构造器只能是私有的。</li>
</ol>
<p><span>四、枚举类介绍</span></p>
<p>　　枚举类内也可以定义属性和方法，可是是静态的和非静态的。</p>
<div>
<pre><span>package</span><span> enumcase;

</span><span>public</span> <span>enum</span><span> SeasonEnum {
    SPRING(</span>"春天"),SUMMER("夏天"),FALL("秋天"),WINTER("冬天"<span>);

    </span><span>private</span><span> final String name;

    </span><span>private</span><span> SeasonEnum(String name)
    {
        </span><span>this</span>.name =<span> name;
    }

    </span><span>public</span><span> String getName() {
        </span><span>return</span><span> name;
    }
}</span></pre>
</div>
<p>&nbsp;　&nbsp; 实际上在第一行写枚举类实例的时候，默认是调用了构造器的，所以此处需要传入参数，因为没有显式申明无参构造器，只能调用有参数的构造器。</p>
<p>　　构造器需定义成私有的，这样就不能在别处申明此类的对象了。枚举类通常应该设计成不可变类，它的Field不应该被改变，这样会更安全，而且代码更加简洁。所以我们将Field用private final修饰。</p>
<p><span>五、枚举类实现接口</span></p>
<p>　　枚举类可以实现一个或多个接口。与普通类一样，实现接口的时候需要实现接口中定义的所有方法，若没有完全实现，那这个枚举类就是抽象的，只是不需显式加上abstract修饰，系统化会默认加上。</p>
<p>　　</p>
<div>
<pre><span>package</span><span> enumcase;

</span><span>public</span> <span>enum</span><span> Operation {
    PLUS{

        @Override
        </span><span>public</span> <span>double</span> eval(<span>double</span> x, <span>double</span><span> y) {
            </span><span>return</span> x +<span> y;
        }

    },
    MINUS{

        @Override
        </span><span>public</span> <span>double</span> eval(<span>double</span> x, <span>double</span><span> y) {
            </span><span>return</span> x -<span> y;
        }

    },
    TIMES{

        @Override
        </span><span>public</span> <span>double</span> eval(<span>double</span> x, <span>double</span><span> y) {
            </span><span>return</span> x *<span> y;
        }

    },
    DIVIDE{

        @Override
        </span><span>public</span> <span>double</span> eval(<span>double</span> x, <span>double</span><span> y) {
            </span><span>return</span> x /<span> y;
        }

    };

    </span><span>/**</span><span>
     * 抽象方法，由不同的枚举值提供不同的实现。
     * </span><span>@param</span><span> x
     * </span><span>@param</span><span> y
     * </span><span>@return</span>
     <span>*/</span>
    <span>public</span> <span>abstract</span> <span>double</span> eval(<span>double</span> x, <span>double</span><span> y);

    </span><span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
        System.out.println(Operation.PLUS.eval(</span>10, 2<span>));
        System.out.println(Operation.MINUS.eval(</span>10, 2<span>));
        System.out.println(Operation.TIMES.eval(</span>10, 2<span>));
        System.out.println(Operation.DIVIDE.eval(</span>10, 2<span>));
    }
}</span></pre>
</div>
<p>　　<span>Operatio类实际上是抽象的，不可以创建枚举值，所以此处在申明枚举值的时候，都实现了抽象方法，这其实是匿名内部类的实现，花括号部分是一个类体。我们可以看下编译以后的文件。</span></p>
<p><span>　　<img alt="" src="data:image/png;base64,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"> </span></p>
<p><span>　　共生成了五个class文件，这样就证明了PLUS,MINUS,TIMES,DIVIDE是Operation的匿名内部类的实例。</span></p>
<p><span>六、switch语句里的表达式可以是枚举值</span></p>
<p>　　Java5新增了enum关键字，同时扩展了switch。</p>
<div>
<pre><span>package</span><span> enumcase;

</span><span>public</span> <span>class</span><span> SeasonTest {
    </span><span>public</span> <span>void</span><span> judge(SeasonEnum s)
    {
        </span><span>switch</span><span>(s)
        {
        </span><span>case</span><span> SPRING:
            System.out.println(</span>"春天适合踏青。"<span>);
            </span><span>break</span><span>;
        </span><span>case</span><span> SUMMER:
            System.out.println(</span>"夏天要去游泳啦。"<span>);
            </span><span>break</span><span>;
        </span><span>case</span><span> FALL:
            System.out.println(</span>"秋天一定要去旅游哦。"<span>);
            </span><span>break</span><span>;
        </span><span>case</span><span> WINTER:
            System.out.println(</span>"冬天要是下雪就好啦。"<span>);
            </span><span>break</span><span>;
        }
    }

    </span><span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
        SeasonEnum s </span>=<span> SeasonEnum.SPRING;
        SeasonTest test </span>= <span>new</span><span> SeasonTest();
        test.judge(s);
    }
}</span></pre>
</div>
<p>　　case表达式中直接写入枚举值，不需加入枚举类作为限定。</p>
<p></div></p>
<footer>
    <p>本站部分内容来源于网络，如有侵犯您的权益，请通过以下方式联系我们：</p>
    <p>邮箱：zytlsd@163.com</p>
    <p>我们将在收到通知后第一时间处理。</p>
</footer>]]></description>
    <pubDate>Sun, 19 Oct 2025 21:30:47 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-34.html</guid>
</item>
<item>
    <title>Java提高篇——Java 异常处理</title>
    <link>http://www.51keeplearning.com/post-33.html</link>
    <description><![CDATA[<p><meta name="referrer" content="no-referrer" /><div contentScore="14658"></p>
<div id="cnblogs_post_body" contentScore="14592">
<h2 id="0">异常的概念</h2>
<p>异常是程序中的一些错误，但并不是所有的错误都是异常，并且错误有时候是可以避免的。</p>
<p>比如说，你的代码少了一个分号，那么运行出来结果是提示是错误<code>java.lang.Error</code>；如果你用<code>System.out.println(11/0)</code>，那么你是因为你用0做了除数，会抛出<code>java.lang.ArithmeticException</code>的异常。</p>
<p>异常发生的原因有很多，通常包含以下几大类：</p>
<ul>
<li>用户输入了非法数据。</li>
<li>要打开的文件不存在。</li>
<li>网络通信时连接中断，或者JVM内存溢出。</li>
</ul>
<p>这些异常有的是因为用户错误引起，有的是程序错误引起的，还有其它一些是因为物理错误引起的。-</p>
<p>要理解Java异常处理是如何工作的，你需要掌握以下三种类型的异常：</p>
<ul>
<li><strong>检查性异常：</strong>最具代表的检查性异常是用户错误或问题引起的异常，这是程序员无法预见的。例如要打开一个不存在文件时，一个异常就发生了，这些异常在编译时不能被简单地忽略。</li>
<li><strong>运行时异常：</strong>&nbsp;运行时异常是可能被程序员避免的异常。与检查性异常相反，运行时异常可以在编译时被忽略。</li>
<li><strong>错误：</strong>&nbsp;错误不是异常，而是脱离程序员控制的问题。错误在代码中通常被忽略。例如，当栈溢出时，一个错误就发生了，它们在编译也检查不到的。</li>
</ul>
<p>&nbsp;</p>
<p>异常指不期而至的各种状况，如：文件找不到、网络连接失败、除0操作、非法参数等。异常是一个事件，它发生在程序运行期间，干扰了正常的指令流程。</p>
<p>Java语言在设计的当初就考虑到这些问题，提出异常处理的框架的方案，所有的异常都可以用一个异常类来表示，不同类型的异常对应不同的子类异常（目前我们所说的异常包括错误概念），定义异常处理的规范，在<code>JDK1.4</code>版本以后增加了异常链机制，从而便于跟踪异常。</p>
<p>Java异常是一个描述在代码段中发生异常的对象，当发生异常情况时，一个代表该异常的对象被创建并且在导致该异常的方法中被抛出，而该方法可以选择自己处理异常或者传递该异常。</p>
<h2 id="1">异常的体系结构</h2>
<p>Java把异常当作对象来处理，并定义一个基类<code>java.lang.Throwable</code>作为所有异常的超类。</p>
<p>在Java API中已经定义了许多异常类，这些异常类分为两大类，<span><strong>错误<code>Error</code>和异常<code>Exception</code></strong></span>。</p>
<p>Java异常层次结构图如下图所示：</p>
<p><img src="https://images2015.cnblogs.com/blog/690102/201607/690102-20160728164909622-1770558953.png" alt=""></p>
<p>从图中可以看出所有异常类型都是内置类<code>Throwable</code>的子类，因而<code>Throwable</code>在异常类的层次结构的顶层。</p>
<p>接下来<code>Throwable</code>分成了两个不同的分支，<span><strong>一个分支是<code>Error</code>，它表示不希望被程序捕获或者是程序无法处理的错误</strong>。<strong>另一个分支是<code>Exception</code>，它表示用户程序可能捕捉的异常情况或者说是程序可以处理的异常</strong></span>。其中异常类<code>Exception</code>又分为运行时异常(<code>RuntimeException</code>)和非运行时异常。</p>
<p>Java异常又可以分为不受检查异常（<code>Unchecked Exception</code>）和检查异常（<code>Checked Exception</code>）。</p>
<p>&nbsp;</p>
<p>下面将详细讲述这些异常之间的区别与联系：</p>
<ul>
<li><span><strong><code>Error</code></strong>：<span><code>Error</code>类对象由 Java 虚拟机生成并抛出，大多数错误与代码编写者所执行的操作无关</span></span><span>。</span>例如，Java虚拟机运行错误（<code>Virtual MachineError</code>），当JVM不再有继续执行操作所需的内存资源时，将出现&nbsp;<code>OutOfMemoryError</code>。这些异常发生时，Java虚拟机（JVM）一般会选择线程终止；还有发生在虚拟机试图执行应用时，如类定义错误（<code>NoClassDefFoundError</code>）、链接错误（<code>LinkageError</code>）。这些错误是不可查的，因为它们在应用程序的控制和处理能力之 外，而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说，即使确实发生了错误，本质上也不应该试图去处理它所引起的异常状况。在Java中，错误通常是使用<code>Error</code>的子类描述。</li>
<li><strong><code>Exception</code></strong>：在<code>Exception</code>分支中有一个重要的<span>子类<code>RuntimeException</code>（运行时异常）</span>，该类型的异常自动为你所编写的程序定义<code>ArrayIndexOutOfBoundsException</code>（数组下标越界）、<code>NullPointerException</code>（空指针异常）、<code>ArithmeticException</code>（算术异常）、<code>MissingResourceException</code>（丢失资源）、<code>ClassNotFoundException</code>（找不到类）等异常，<span>这些异常是不检查异常，程序中可以选择捕获处理，也可以不处理。这些异常一般是由程序逻辑错误引起的，程序应该从逻辑角度尽可能避免这类异常的发生</span>；而<code>RuntimeException</code>之外的异常我们统称为非运行时异常，类型上属于<code>Exception</code>类及其子类，从程序语法角度讲是必须进行处理的异常，如果不处理，程序就不能编译通过。如<code>IOException</code>、<code>SQLException</code>等以及用户自定义的<code>Exception</code>异常，一般情况下不自定义检查异常。</li>
</ul>
<blockquote contentScore="314">
<p>&nbsp;<img src="https://images2015.cnblogs.com/blog/690102/201607/690102-20160728170617356-848311160.png" alt="">注意</p>
<p><code>Error</code>和<code>Exception</code>的区别：<code>Error</code>通常是灾难性的致命的错误，是程序无法控制和处理的，当出现这些异常时，Java虚拟机（JVM）一般会选择终止线程；<code>Exception</code>通常情况下是可以被程序处理的，并且在程序中应该尽可能的去处理这些异常。</p>
</blockquote>
<ul>
<li><span><strong><code>检查异常</code></strong>：在正确的程序运行过程中，很容易出现的、情理可容的异常状况，在一定程度上这种异常的发生是可以预测的，并且一旦发生该种异常，就必须采取某种方式进行处理。</span></li>
</ul>
<blockquote contentScore="238">
<p>♠提示</p>
<p><strong>除了<code>RuntimeException</code>及其子类以外，其他的<code>Exception</code>类及其子类都属于检查异常</strong>，当程序中可能出现这类异常，<strong>要么使用<code>try-catch</code>语句进行捕获，要么用<code>throws</code>子句抛出</strong>，否则编译无法通过。</p>
</blockquote>
<ul>
<li><span><strong><code>不受检查异常</code></strong>：<strong>包括<code>RuntimeException</code>及其子类和<code>Error</code></strong>。</span></li>
</ul>
<blockquote contentScore="120">
<p>♠提示</p>
<p><code>不受检查异常</code>为编译器不要求强制处理的异常，<code>检查异常</code>则是编译器要求必须处置的异常。</p>
</blockquote>
<h2 id="2">Java 异常的处理机制</h2>
<p><span>Java的异常处理本质上是<strong>抛出异常</strong>和<strong>捕获异常</strong>。</span></p>
<ul>
<li><strong><code>抛出异常</code></strong>：要理解抛出异常，首先要明白什么是异常情形（exception condition），它是指阻止当前方法或作用域继续执行的问题。其次把异常情形和普通问题相区分，普通问题是指在当前环境下能得到足够的信息，总能处理这个错误。对于异常情形，已经无法继续下去了，因为在当前环境下无法获得必要的信息来解决问题，你所能做的就是从当前环境中跳出，并把问题提交给上一级环境，这就是抛出异常时所发生的事情。抛出异常后，会有几件事随之发生。首先，是像创建普通的java对象一样将使用<code>new</code>在堆上创建一个异常对象；然后，当前的执行路径（已经无法继续下去了）被终止，并且从当前环境中弹出对异常对象的引用。此时，异常处理机制接管程序，并开始寻找一个恰当的地方继续执行程序，这个恰当的地方就是异常处理程序或者异常处理器，它的任务是将程序从错误状态中恢复，以使程序要么换一种方式运行，要么继续运行下去。</li>
</ul>
<p>举个简单的例子，假使我们创建了一个学生对象Student的一个引用stu,在调用的时候可能还没有初始化。所以在使用这个对象引用调用其他方法之前，要先对它进行检查，可以创建一个代表错误信息的对象，并且将它从当前环境中抛出，这样就把错误信息传播到更大的环境中。</p>
<div>
<pre><span>if</span>(stu == <span>null</span><span>){
    </span><span>throw</span> <span>new</span><span> NullPointerException();
}</span></pre>
</div>
<p>这就抛出了异常，它将在其他的地方得到执行或者处理，具体是哪个地方后面将很快介绍，代码中出现的&nbsp;<code>throw</code>&nbsp;是一个关键字，暂时先不做过多讲解，后面会详细讲解。</p>
<ul>
<li><strong><code>捕获异常</code></strong>：在方法抛出异常之后，运行时系统将转为寻找合适的异常处理器（exception handler）。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时，即为合适的异常处理器。运行时系统从发生异常的方法开始，依次回查调用栈中的方法，直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适的异常处理器，则运行时系统终止。同时，意味着Java程序的终止。</li>
</ul>
<blockquote contentScore="777">
<p>&nbsp;提示</p>
<p>对于<code>运行时异常</code>、<code>错误</code>和<code>检查异常</code>，Java技术所要求的异常处理方式有所不同。</p>
<p>由于运行时异常及其子类的不可查性，为了更合理、更容易地实现应用程序，Java规定，<strong>运行时异常将由Java运行时系统自动抛出，允许应用程序忽略运行时异常</strong>。</p>
<p>对于方法运行中可能出现的<code>Error</code>，当运行方法不欲捕捉时，Java允许该方法不做任何抛出声明。因为，大多数<code>Error</code>异常属于永远不能被允许发生的状况，也属于合理的应用程序不该捕捉的异常。</p>
<p>对于所有的检查异常，Java规定：一个方法必须捕捉，或者声明抛出方法之外。也就是说，当一个方法选择不捕捉检查异常时，它必须声明将抛出异常。</p>
</blockquote>
<p>Java异常处理涉及到五个关键字，分别是：<code>try</code>、<code>catch</code>、<code>finally</code>、<code>throw</code>、<code>throws</code>。下面将骤一介绍，通过认识这五个关键字，掌握基本异常处理知识。</p>
<p>　　•&nbsp;<strong>try</strong>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;-- 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内，当try语句块内发生异常时，异常就被抛出。<br>　　•&nbsp;<strong>catch</strong>&nbsp;&nbsp; -- 用于捕获异常。catch用来捕获try语句块中发生的异常。<br>　　•&nbsp;<strong>finally</strong>&nbsp;&nbsp;-- finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块，执行完成之后，才会回来执行try或者catch块中的return或者throw语句，如果finally中使用了return或者throw等终止方法的语句，则就不会跳回执行，直接停止。<br>　　•&nbsp;<strong>throw</strong>&nbsp;&nbsp; -- 用于抛出异常。<br>　　•&nbsp;<strong>throws</strong>&nbsp;-- 用在方法签名中，用于声明该方法可能抛出的异常。</p>
<h2 id="3">异常处理的基本语法</h2>
<h3 id="4">1. try-catch</h3>
<div>
<pre><span>try</span><span>{
    </span><span>//</span><span>code that might generate exceptions    </span>
}<span>catch</span><span>(Exception e){
    </span><span>//</span><span>the code of handling exception1</span>
}<span>catch</span><span>(Exception e){
    </span><span>//</span><span>the code of handling exception2</span>
}</pre>
</div>
<p>要明白异常捕获，还要理解<code>监控区域</code>（guarded region）的概念。它是一段可能产生异常的代码，并且后面跟着处理这些异常的代码。</p>
<p>因而可知，上述<code>try-catch</code>所描述的即是监控区域，关键词<code>try</code>后的一对大括号将一块可能发生异常的代码包起来，即为监控区域。Java方法在运行过程中发生了异常，则创建异常对象。将异常抛出监控区域之外，由Java运行时系统负责寻找匹配的<code>catch</code>子句来捕获异常。若有一个<code>catch</code>语句匹配到了，则执行该<code>catch</code>块中的异常处理代码，就不再尝试匹配别的<code>catch</code>块了。</p>
<blockquote contentScore="190">
<p>匹配的原则是：如果抛出的异常对象属于<code>catch</code>子句的异常类，或者属于该异常类的子类，则认为生成的异常对象与<code>catch</code>块捕获的异常类型相匹配。</p>
</blockquote>
<p>举个例子算术异常：</p>
<div>
<pre><span>public</span> <span>class</span><span> TestException {  
    </span><span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {  
        </span><span>int</span> a = 1<span>;  
        </span><span>int</span> b = 0<span>;  
        </span><span>try</span> { <span>//</span><span> try监控区域               </span>
            <span>if</span> (b == 0) <span>throw</span> <span>new</span> ArithmeticException(); <span>//</span><span> 通过throw语句抛出异常  </span>
            System.out.println("a/b的值是：" + a /<span> b);  
            System.out.println(</span>"this will not be printed!"<span>);
        }  
        </span><span>catch</span> (ArithmeticException e) { <span>//</span><span> catch捕捉异常  </span>
            System.out.println("程序出现异常，变量b不能为0！"<span>);  
        }  
        System.out.println(</span>"程序正常结束。"<span>);  
    }  
}  </span></pre>
</div>
<p>运行结果：</p>
<div>
<pre>D:\java&gt;<span>java TestException

程序出现异常，变量b不能为0！

程序正常结束。</span></pre>
</div>
<blockquote contentScore="186">
<p>显示一个异常的描述，<code>Throwable</code>重载了<code>toString()</code>方法（由<code>Object</code>定义），所以它将返回一个包含异常描述的字符串。例如，将前面的<code>catch</code>块重写成：</p>
<div>
<pre><span>catch</span> (ArithmeticException e) { <span>//</span><span> catch捕捉异常  </span>
    System.out.println("程序出现异常"+<span>e);  
} </span></pre>
</div>
<p>结果：</p>
<div>
<pre>D:\java&gt;<span>java TestException

程序出现异常java.lang.ArithmeticException

程序正常结束。</span></pre>
</div>
</blockquote>
<p>根据前面讲述的，算术异常属于运行时异常，因而实际上该异常不需要程序抛出，运行时系统自动抛出，将例子改为如下：</p>
<div>
<pre><span>public</span> <span>class</span><span> TestException {  
    </span><span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {  
        </span><span>int</span> a = 1<span>;  
        </span><span>int</span> b = 0<span>;    
        System.out.println(</span>"a/b的值是：" + a /<span> b);
        System.out.println(</span>"this will not be printed!"<span>);
    }  
}  </span></pre>
</div>
<p>结果：</p>
<div>
<pre>D:\java&gt;<span>java TestException

Exception in thread </span>"main" java.lang.ArithmeticException: /<span> by zero
    at TestException.main(TestException.java:</span>7)</pre>
</div>
<p><strong><code>使用多重的catch语句</code></strong>：很多情况下，由单个的代码段可能引起多个异常。处理这种情况，我们需要定义两个或者更多的<code>catch</code>子句，每个子句捕获一种类型的异常，当异常被引发时，每个<code>catch</code>子句被依次检查，第一个匹配异常类型的子句执行，当一个<code>catch</code>子句执行以后，其他的子句将被旁路。</p>
<p>编写多重catch语句块注意事项：</p>
<p>　　顺序问题：先小后大，即先子类后父类</p>
<p>&nbsp;<img src="https://images2015.cnblogs.com/blog/690102/201607/690102-20160729100020388-1471612369.jpg" alt=""></p>
<blockquote contentScore="584">
<p>Java通过异常类描述异常类型。对于有多个<code>catch</code>子句的异常程序而言，应该尽量将捕获底层异常类的<code>catch</code>子句放在前面，同时尽量将捕获相对高层的异常类的<code>catch</code>子句放在后面。否则，捕获底层异常类的<code>catch</code>子句将可能会被屏蔽。</p>
<p><code>RuntimeException</code>异常类包括运行时各种常见的异常，<code>ArithmeticException</code>类和<code>ArrayIndexOutOfBoundsException</code>类都是它的子类。因此，<code>RuntimeException</code>异常类的<code>catch</code>子句应该放在最后面，否则可能会屏蔽其后的特定异常处理或引起编译错误。</p>
</blockquote>
<p><strong><code>嵌套try语句</code></strong>：<code>try</code>语句可以被嵌套。也就是说，一个<code>try</code>语句可以在另一个<code>try</code>块的内部。每次进入<code>try</code>语句，异常的前后关系都会被推入堆栈。如果一个内部的<code>try</code>语句不含特殊异常的<code>catch</code>处理程序，堆栈将弹出，下一个<code>try</code>语句的<code>catch</code>处理程序将检查是否与之匹配。这个过程将继续直到一个<code>catch</code>语句被匹配成功，或者是直到所有的嵌套<code>try</code>语句被检查完毕。如果没有<code>catch</code>语句匹配，Java运行时系统将处理这个异常。</p>
<p>例如：</p>
<div>
<pre><span>class</span><span> NestTry{
    </span><span>public</span> <span>static</span> <span>void</span><span> main(String[] args){
        </span><span>try</span><span>{
            </span><span>int</span> a =<span> args.length;
            </span><span>int</span> b = 42 /<span> a;
            System.out.println(</span>"a = "+<span> a);
            </span><span>try</span><span>{
                </span><span>if</span>(a == 1<span>){
                a </span>= a/(a-<span>a);
                }
                </span><span>if</span>(a == 2<span>){
                    </span><span>int</span> c[] = {1<span>};
                    c[</span>42] =99<span>;
                }
            }</span><span>catch</span><span>(ArrayIndexOutOfBoundsException e){
                System.out.println(</span>"ArrayIndexOutOfBounds :"+<span>e);
            }    
        }</span><span>catch</span><span>(ArithmeticException e){
            System.out.println(</span>"Divide by 0"+<span> e);
        }
    }
}</span></pre>
</div>
<p>正如程序中所显示的，该程序在一个try块中嵌套了另一个<code>try</code>块。程序工作如下：当你在没有命令行参数的情况下执行该程序，外面的<code>try</code>块将产生一个被0除的异常。程序在有一个命令行参数条件下执行，由嵌套的<code>try</code>块产生一个被0除的异常，由于内部的<code>catch</code>块不匹配这个异常，它将把异常传给外部的<code>try</code>块，在外部异常被处理。如果你在具有两个命令行参数的条件下执行该程序，将由内部<code>try</code>块产生一个数组边界异常。</p>
<p>结果：</p>
<div>
<pre>D:\java&gt;<span>javac estTry.java

D:\java</span>&gt;&gt;<span>java NestTry

Divide by </span>0 java.lang.ArithmeticExceptio: /<span> by zero

D:\java</span>&gt;<span>java NestTry one

a </span>= 1<span>

Divide by 0java.lang.ArithmeticException: </span>/<span> by zero

D:\java</span>&gt;<span>java NestTry one two

a </span>= 2<span>

ArrayIndexOutOfBounds :java.lang.ArrayIndexOutOfBoundsException: </span>42</pre>
</div>
<p>注意：当有方法调用时，<code>try</code>语句的嵌套可以很隐蔽的发生。例如，我们可以将对方法的调用放在一个<code>try</code>块中。在该方法的内部，有另一个<code>try</code>语句。在这种情况下，方法内部的<code>try</code>仍然是嵌套在外部调用该方法的<code>try</code>块中的。下面我们将对上述例子进行修改，嵌套的<code>try</code>块移到方法nesttry()的内部：</p>
<div>
<pre><span>class</span><span> NestTry{
    </span><span>static</span> <span>void</span> nesttry(<span>int</span><span> a){
        </span><span>try</span><span>{
            </span><span>if</span>(a == 1<span>){
                a </span>= a/(a-<span>a);
            }
            </span><span>if</span>(a == 2<span>){
                </span><span>int</span> c[] = {1<span>};
                c[</span>42] =99<span>;
            }
        }</span><span>catch</span><span>(ArrayIndexOutOfBoundsException e){
            System.out.println(</span>"ArrayIndexOutOfBounds :"+<span>e);
        }    
    }
    </span><span>public</span> <span>static</span> <span>void</span><span> main(String[] args){
        </span><span>try</span><span>{
            </span><span>int</span> a =<span> args.length;
            </span><span>int</span> b = 42 /<span> a;
            System.out.println(</span>"a = "+<span> a);
            nesttry(a);
        }</span><span>catch</span><span>(ArithmeticException e){
            System.out.println(</span>"Divide by 0"+<span> e);
        }
    }
}</span></pre>
</div>
<p>结果输出与前面例子一致：<code></code></p>
<div>
<pre>D:\java&gt;<span>javac NestTry.java

D:\java</span>&gt;<span>java NestTry

Divide by 0java.lang.ArithmeticException: </span>/<span> by zero

D:\java</span>&gt;<span>java NestTry one

a </span>= 1<span>

Divide by 0java.lang.ArithmeticException: </span>/<span> by zero

D:\java</span>&gt;<span>java NestTry one two

a </span>= 2<span>

ArrayIndexOutOfBounds :java.lang.ArrayIndexOutOfBoundsException: </span>42</pre>
</div>
<h3 id="5">2. throw</h3>
<p>到目前为止，我们只是获取了被Java运行时系统引发的异常。然而，我们还可以用<code>throw</code>语句抛出明确的异常。<code>Throw</code>的语法形式如下：</p>
<div>
<pre><span>throw</span> ThrowableInstance;</pre>
</div>
<p>这里的ThrowableInstance一定是<code>Throwable</code>类类型或者<code>Throwable</code>子类类型的一个对象。简单的数据类型，例如<code>int</code>，<code>char</code>,以及非<code>Throwable</code>类，例如<code>String</code>或<code>Object</code>，不能用作异常。有两种方法可以获取<code>Throwable</code>对象：在<code>catch</code>子句中使用参数或者使用<code>new</code>操作符创建。</p>
<p>程序执行完<code>throw</code>语句之后立即停止；<code>throw</code>后面的任何语句不被执行，最邻近的<code>try</code>块用来检查它是否含有一个与异常类型匹配的<code>catch</code>语句。如果发现了匹配的块，控制转向该语句；如果没有发现，次包围的<code>try</code>块来检查，以此类推。如果没有发现匹配的<code>catch</code>块，默认异常处理程序中断程序的执行并且打印堆栈轨迹。</p>
<p>例如：</p>
<div>
<pre><span>class</span><span> TestThrow{
    </span><span>static</span> <span>void</span><span> proc(){
        </span><span>try</span><span>{
            </span><span>throw</span> <span>new</span> NullPointerException("demo"<span>);
        }</span><span>catch</span><span>(NullPointerException e){
            System.out.println(</span>"Caught inside proc"<span>);
            </span><span>throw</span><span> e;
        }
    }

    </span><span>public</span> <span>static</span> <span>void</span><span> main(String [] args){
        </span><span>try</span><span>{
            proc();
        }</span><span>catch</span><span>(NullPointerException e){
            System.out.println(</span>"Recaught: "+<span>e);
        }
    }
}</span></pre>
</div>
<p>结果：</p>
<div>
<pre>D:\java&gt;<span>java TestThrow

Caught inside proc

Recaught: java.lang.NullPointerException: demo</span></pre>
</div>
<p>该程序两次处理相同的错误，首先，<code>main()</code>方法设立了一个异常关系然后调用proc()。proc()方法设立了另一个异常处理关系并且立即抛出一个<code>NullPointerException</code>实例，<code>NullPointerException</code>在<code>main()</code>中被再次捕获。</p>
<p>该程序阐述了怎样创建Java的标准异常对象，特别注意这一行：</p>
<div>
<pre>throw new NullPointerException("demo");
</pre>
</div>
<blockquote contentScore="409">
<p>此处<code>new</code>用来构造一个<code>NullPointerException</code>实例，所有的Java内置的运行时异常有两个构造方法：一个没有参数，一个带有一个字符串参数。当用第二种形式时，参数指定描述异常的字符串。如果对象用作<code>print()</code>或者<code>println()</code>的参数时，该字符串被显示。这同样可以通过调用getMessage()来实现，getMessage()是由<code>Throwable</code>定义的。</p>
</blockquote>
<h3 id="6">3. throws</h3>
<p>如果一个方法可以导致一个异常但不处理它，它必须指定这种行为以使方法的调用者可以保护它们自己而不发生异常。要做到这点，我们可以在方法声明中包含一个<code>throws</code>子句。一个<code>throws</code>子句列举了一个方法可能引发的所有异常类型。这对于除了<code>Error</code>或<code>RuntimeException</code>及它们子类以外类型的所有异常是必要的。一个方法可以引发的所有其他类型的异常必须在<code>throws</code>子句中声明，否则会导致编译错误。</p>
<p>下面是<code>throws</code>子句的方法声明的通用形式：</p>
<div>
<pre><span>public</span> <span>void</span> info() <span>throws</span><span> Exception
{
   </span><span>//</span><span>body of method</span>
}</pre>
</div>
<p>Exception 是该方法可能引发的所有的异常,也可以是异常列表，中间以逗号隔开。</p>
<p>例如：</p>
<div>
<pre><span>class</span><span> TestThrows{
    </span><span>static</span> <span>void</span><span> throw1(){
        System.out.println(</span>"Inside throw1 . "<span>);
        </span><span>throw</span> <span>new</span> IllegalAccessException("demo"<span>);
    }
    </span><span>public</span> <span>static</span> <span>void</span><span> main(String[] args){
        throw1();
    }
}</span></pre>
</div>
<p>上述例子中有两个地方存在错误，你能看出来吗？</p>
<p>该例子中存在两个错误，首先，throw1()方法不想处理所导致的异常，因而它必须声明<code>throws</code>子句来列举可能引发的异常即<code>IllegalAccessException</code>；其次，<code>main()</code>方法必须定义<code>try/catch</code>语句来捕获该异常。</p>
<p>正确例子如下：</p>
<div>
<pre><span>class</span><span> TestThrows{
    </span><span>static</span> <span>void</span> throw1() <span>throws</span><span> IllegalAccessException {
        System.out.println(</span>"Inside throw1 . "<span>);
        </span><span>throw</span> <span>new</span> IllegalAccessException("demo"<span>);
    }
    </span><span>public</span> <span>static</span> <span>void</span><span> main(String[] args){
        </span><span>try</span><span> {
            throw1();
        }</span><span>catch</span><span>(IllegalAccessException e ){
            System.out.println(</span>"Caught " +<span> e);
        }
    }
}</span></pre>
</div>
<blockquote contentScore="30">
<p><code>Throws</code>抛出异常的规则：</p>
<ul>
<li>如果是不受检查异常（<code>unchecked exception</code>），即<code>Error</code>、<code>RuntimeException</code>或它们的子类，那么可以不使用<code>throws</code>关键字来声明要抛出的异常，编译仍能顺利通过，但在运行时会被系统抛出。</li>
<li>必须声明方法可抛出的任何检查异常（<code>checked exception</code>）。即如果一个方法可能出现受可查异常，要么用<code>try-catch</code>语句捕获，要么用<code>throws</code>子句声明将它抛出，否则会导致编译错误</li>
<li>仅当抛出了异常，该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候，应该继续抛出，而不是囫囵吞枣。</li>
<li>调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法，则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。</li>
</ul>
</blockquote>
<h3 id="7">4. finally</h3>
<p>当异常发生时，通常方法的执行将做一个陡峭的非线性的转向，它甚至会过早的导致方法返回。例如，如果一个方法打开了一个文件并关闭，然后退出，你不希望关闭文件的代码被异常处理机制旁路。<code>finally</code>关键字为处理这种意外而设计。</p>
<p><code>finally</code>创建的代码块在<code>try/catch</code>块完成之后另一个<code>try/catch</code>出现之前执行。<code>finally</code>块无论有没有异常抛出都会执行。如果抛出异常，即使没有<code>catch</code>子句匹配，<code>finally</code>也会执行。一个方法将从一个<code>try/catch</code>块返回到调用程序的任何时候，经过一个未捕获的异常或者是一个明确的返回语句，<code>finally</code>子句在方法返回之前仍将执行。这在关闭文件句柄和释放任何在方法开始时被分配的其他资源是很有用。</p>
<blockquote contentScore="118">
<p><code>finally</code>子句是可选项，可以有也可以无，但是每个<code>try</code>语句至少需要一个<code>catch</code>或者<code>finally</code>子句。</p>
</blockquote>
<div>
<pre><span>class</span><span> TestFinally{
    </span><span>static</span> <span>void</span><span> proc1(){
        </span><span>try</span><span>{
            System.out.println(</span>"inside proc1"<span>);
            </span><span>throw</span> <span>new</span> RuntimeException("demo"<span>);
        }</span><span>finally</span><span>{
            System.out.println(</span>"proc1's finally"<span>);
        }
    }
    </span><span>static</span> <span>void</span><span> proc2(){
        </span><span>try</span><span>{
            System.out.println(</span>"inside proc2"<span>);
            </span><span>return</span><span> ;
        } </span><span>finally</span><span>{
            System.out.println(</span>"proc2's finally"<span>);
        }
    } 
    </span><span>static</span> <span>void</span><span> proc3(){
        </span><span>try</span><span>{
            System.out.println(</span>"inside proc3"<span>);
        }</span><span>finally</span><span>{
            System.out.println(</span>"proc3's finally"<span>);
        }
    }
    </span><span>public</span> <span>static</span> <span>void</span><span> main(String [] args){
        </span><span>try</span><span>{
            proc1();
        }</span><span>catch</span><span>(Exception e){
            System.out.println(</span>"Exception caught"<span>);
        }
        proc2();
        proc3();
    }
}</span></pre>
</div>
<p>该例子中，proc1()抛出了异常中断了<code>try</code>，它的<code>finally</code>子句在退出时执行。proc2的<code>try</code>语句通过<code>return</code>语句返回，但在返回之前<code>finally</code>语句执行。在proc3()中<code>try</code>语句正常执行，没有错误，<code>finally</code>语句也被执行。</p>
<p>输出结果：</p>
<div>
<pre>D:\java&gt;<span>java TestFinally

inside proc1

proc1</span>'s finally
<span>
Exception caught

inside proc2

proc2</span>'s finally
<span> 
inside proc3

proc3</span>'s finally</pre>
</div>
<blockquote contentScore="89">
<p>注：如果<code>finally</code>块与一个<code>try</code>联合使用，<code>finally</code>块将在<code>try</code>结束之前执行。</p>
</blockquote>
<h3><strong>问题扩展（面试题）</strong>：</h3>
<p id="a90cca7caf301432632ebdf031c45c4c"><strong>1、try{} 里有一个 return 语句，那么紧跟在这个 try 后的 finally{} 里的 code 会不会被执行，什么时候被执行，在 return 前还是后?</strong></p>
<p>答案：会执行，在方法返回调用者前执行。</p>
<blockquote contentScore="666">
<p><strong>注意：</strong>在finally中改变返回值的做法是不好的，因为如果存在finally代码块，try中的return语句不会立马返回调用者，而是记录下返回值待finally代码块执行完毕之后再向调用者返回其值，然后如果在finally中修改了返回值，就会返回修改后的值。显然，在finally中返回或者修改返回值会对程序造成很大的困扰，C#中直接用编译错误的方式来阻止程序员干这种龌龊的事情，Java中也可以通过提升编译器的语法检查级别来产生警告或错误，Eclipse中可以在如图所示的地方进行设置，强烈建议将此项设置为编译错误。</p>
</blockquote>
<p>&nbsp;<img src="https://images2015.cnblogs.com/blog/690102/201607/690102-20160729110647216-246845823.jpg" alt=""></p>
<p><strong>2、Java语言如何进行异常处理，关键字：throws、throw、try、catch、finally分别如何使用？</strong></p>
<p>答：Java通过面向对象的方法进行异常处理，把各种不同的异常进行分类，并提供了良好的接口。在Java中，每个异常都是一个对象，它是Throwable类或其子类的实例。当一个方法出现异常后便抛出一个异常对象，该对象中包含有异常信息，调用这个对象的方法可以捕获到这个异常并可以对其进行处理。Java的异常处理是通过5个关键词来实现的：try、catch、throw、throws和finally。一般情况下是用try来执行一段程序，如果系统会抛出（throw）一个异常对象，可以通过它的类型来捕获（catch）它，或通过总是执行代码块（finally）来处理；try用来指定一块预防所有异常的程序；catch子句紧跟在try块后面，用来指定你想要捕获的异常的类型；throw语句用来明确地抛出一个异常；throws用来声明一个方法可能抛出的各种异常（当然声明异常时允许无病呻吟）；finally为确保一段代码不管发生什么异常状况都要被执行；try语句可以嵌套，每当遇到一个try语句，异常的结构就会被放入异常栈中，直到所有的try语句都完成。如果下一级的try语句没有对某种异常进行处理，异常栈就会执行出栈操作，直到遇到有处理这种异常的try语句或者最终将异常抛给JVM。</p>
<p><strong>3、运行时异常与受检异常有何异同？</strong>&nbsp;</p>
<p>答：异常表示程序运行过程中可能出现的非正常状态，运行时异常表示虚拟机的通常操作中可能遇到的异常，是一种常见运行错误，只要程序设计得没有问题通常就不会发生。受检异常跟程序运行的上下文环境有关，即使程序设计无误，仍然可能因使用的问题而引发。Java编译器要求方法必须声明抛出可能发生的受检异常，但是并不要求必须声明抛出未被捕获的运行时异常。异常和继承一样，是面向对象程序设计中经常被滥用的东西，在<em>Effective Java</em>中对异常的使用给出了以下指导原则：&nbsp;<br>- 不要将异常处理用于正常的控制流（设计良好的API不应该强迫它的调用者为了正常的控制流而使用异常）&nbsp;<br>- 对可以恢复的情况使用受检异常，对编程错误使用运行时异常&nbsp;<br>- 避免不必要的使用受检异常（可以通过一些状态检测手段来避免异常的发生）&nbsp;<br>- 优先使用标准的异常&nbsp;<br>- 每个方法抛出的异常都要有文档&nbsp;<br>- 保持异常的原子性&nbsp;<br>- 不要在catch中忽略掉捕获到的异常</p>
<p><strong>4、列出一些你常见的运行时异常？</strong>&nbsp;</p>
<p>答：&nbsp;<br>- ArithmeticException（算术异常）&nbsp;<br>- ClassCastException （类转换异常）&nbsp;<br>- IllegalArgumentException （非法参数异常）&nbsp;<br>- IndexOutOfBoundsException （下标越界异常）&nbsp;<br>- NullPointerException （空指针异常）&nbsp;<br>- SecurityException （安全异常）</p>
<p>&nbsp;<img src="https://images2015.cnblogs.com/blog/690102/201607/690102-20160729131101138-1250422700.jpg" alt="" width="1542" height="351"></p>
<h2 id="8">异常链</h2>
<p>异常链顾名思义就是将异常发生的原因一个传一个串起来，即把底层的异常信息传给上层，这样逐层抛出。 Java API文档中给出了一个简单的模型：</p>
<div>
<pre><span>try</span><span> {   
    lowLevelOp();   
} </span><span>catch</span><span> (LowLevelException le) {   
    </span><span>throw</span> (HighLevelException) <span>new</span><span> HighLevelException().initCause(le);   
}</span></pre>
</div>
<p>当程序捕获到了一个底层异常，在处理部分选择了继续抛出一个更高级别的新异常给此方法的调用者。 这样异常的原因就会逐层传递。这样，位于高层的异常递归调用getCause()方法，就可以遍历各层的异常原因。 这就是<code>Java异常链</code>的原理。异常链的实际应用很少，发生异常时候逐层上抛不是个好注意， 上层拿到这些异常又能奈之何？而且异常逐层上抛会消耗大量资源， 因为要保存一个完整的异常链信息.</p>
<h2 id="9">自定义异常</h2>
<p>使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外，用户还可以自定义异常。用户自定义异常类，只需继承<code>Exception</code>类即可。</p>
<p>在程序中使用自定义异常类，大体可分为以下几个步骤:</p>
<ul>
<li>创建自定义异常类。</li>
<li>在方法中通过<code>throw</code>关键字抛出异常对象。</li>
<li>如果在当前抛出异常的方法中处理异常，可以使用<code>try-catch</code>语句捕获并处理；否则在方法的声明处通过<code>throws</code>关键字指明要抛出给方法调用者的异常，继续进行下一步操作。</li>
<li>在出现异常方法的调用者中捕获并处理异常。</li>
</ul>
<p>举例自定义异常：</p>
<div>
<pre><span>class</span> MyException <span>extends</span><span> Exception {
    </span><span>private</span> <span>int</span><span> detail;
    MyException(</span><span>int</span><span> a){
        detail </span>=<span> a;
    }
    </span><span>public</span><span> String toString(){
        </span><span>return</span> "MyException ["+ detail + "]"<span>;
    }
}
</span><span>public</span> <span>class</span><span> TestMyException{
    </span><span>static</span> <span>void</span> compute(<span>int</span> a) <span>throws</span><span> MyException{
        System.out.println(</span>"Called compute(" + a + ")"<span>);
        </span><span>if</span>(a &gt; 10<span>){
            </span><span>throw</span> <span>new</span><span> MyException(a);
        }
        System.out.println(</span>"Normal exit!"<span>);
    }
    </span><span>public</span> <span>static</span> <span>void</span><span> main(String [] args){
        </span><span>try</span><span>{
            compute(</span>1<span>);
            compute(</span>20<span>);
        }</span><span>catch</span><span>(MyException me){
            System.out.println(</span>"Caught " +<span> me);
        }
    }
}</span></pre>
</div>
<p>该例子完全按照上述步骤。</p>
<div>
<pre><span>运行结果如下：
D:\java</span>&gt;<span>java TestMyException

Called compute(</span>1<span>)

Normal exit</span>!<span>

Called compute(</span>20<span>)

Caught MyException [</span>20]</pre>
</div>
<h2>总结</h2>
<p>&nbsp;<img src="https://images2015.cnblogs.com/blog/690102/201607/690102-20160729105042231-937633681.jpg" alt=""></p>
<p><img src="https://images2015.cnblogs.com/blog/690102/201607/690102-20160729100445981-600538221.jpg" alt=""></p>
<hr>
<p>参考文档：</p>
<p id="tutorial-title"><span><a href="http://www.tianmaying.com/tutorial/Java-Exception" target="_blank" rel="noopener nofollow"><span>Java入门之异常处理</span></a></span></p>
<p>&nbsp;</p>
</div>
<div id="MySignature" role="contentinfo" contentScore="66">
    -------------我是低调的分割线--------------------------<br>
<p>如果对你有帮助，可以点击“推荐”哦`(*∩_∩*)′</p>
<img src="http://files.cnblogs.com/files/Qian123/rabbit.gif">
<img src="http://files.cnblogs.com/files/Qian123/rabbit.gif">
<img src="http://files.cnblogs.com/files/Qian123/rabbit.gif">
<img src="http://files.cnblogs.com/files/Qian123/rabbit.gif">
</div>
<div></div>
<div id="blog_post_info_block" role="contentinfo">
    <div id="blog_post_info"></div>
    <div></div>
    <div id="post_next_prev"></div>
</div>
<pre><code>        &lt;/div&gt;</code></pre>
<footer>
    <p>本站部分内容来源于网络，如有侵犯您的权益，请通过以下方式联系我们：</p>
    <p>邮箱：zytlsd@163.com</p>
    <p>我们将在收到通知后第一时间处理。</p>
</footer>]]></description>
    <pubDate>Sun, 19 Oct 2025 21:30:46 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-33.html</guid>
</item>
<item>
    <title>Java：Java快速入门</title>
    <link>http://www.51keeplearning.com/post-32.html</link>
    <description><![CDATA[<p><meta name="referrer" content="no-referrer" /><div id="cnblogs_post_body" contentScore="4323"></p>
<h1>你好，世界！</h1>
<h2>源代码组织方式</h2>
<p>Java程序由package+class组成，package对应目录的相对路径，class对应文件，如</p>
<p><strong>E:\Workspaces\MyEclipse 10\JavaStudy\src\com\happyframework\javastudy\hello\Hello.java</strong></p>
<div>
<pre><span>1</span> <span>package</span><span> com.happyframework.javastudy.hello;
</span><span>2</span> 
<span>3</span> <span>public</span> <span>final</span> <span>class</span><span> Hello {
</span><span>4</span>     <span>public</span> <span>static</span> <span>void</span><span> hello(){
</span><span>5</span>         System.out.println("hello!"<span>);
</span><span>6</span> <span>    }
</span><span>7</span> }</pre>
</div>
<p><strong>关于class有如下几点规则：</strong></p>
<ol>
<li>文件的名字必须和class的名字一致（public级别的class名字）。</li>
<li>文件必须只包含一个public访问基本的class（可以包含多个非public级别的class）。</li>
<li>package名字必须和目录一致。</li>
</ol>
<h2>入口方法</h2>
<p><strong>App.java</strong></p>
<div>
<pre><span>1</span> <span>public</span> <span>class</span><span> App {
</span><span>2</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span>3</span> <span>        com.happyframework.javastudy.hello.Hello.hello();
</span><span>4</span> <span>    }
</span><span>5</span> }</pre>
</div>
<h2>最终的项目结构</h2>
<p><img src="https://images0.cnblogs.com/blog/492619/201309/21221554-3db17718f28f4415a611c492a573dadf.png" alt=""></p>
<h1>数据类型</h1>
<p><strong>8种原子类型</strong></p>
<ol>
<li>整数类型：byte、short、int和long。</li>
<li>小数类型：float和double。</li>
<li>字符类型：char。</li>
<li>布尔类型：bool。</li>
</ol>
<p>除此之外的是interface、class和array。</p>
<p>小数类型的常量默认是double类型，声明float类型的常量需要使用F作为后缀。</p>
<div>
<pre><span> 1</span> <span>public</span> <span>class</span><span> Program {
</span><span> 2</span> 
<span> 3</span>     <span>/**</span>
<span> 4</span> <span>     * </span><span>@param</span><span> args
</span><span> 5</span>      <span>*/</span>
<span> 6</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 7</span>             <span>float</span> age = 28.0F<span>;
</span><span> 8</span> <span>            System.out.println(age);
</span><span> 9</span> <span>    }
</span><span>10</span> 
<span>11</span> }</pre>
</div>
<h1>运算符</h1>
<ol>
<li>算术运算符：+、-、*、/ 和 %，两个整数相除，结果还是整数。</li>
<li>赋值运算符：=、+=、-=、*=、/=、%=、&amp;=、|=、~=、^=、&lt;&lt;=、&gt;&gt;= 、 &gt;&gt;&gt;=、++ 和 --。</li>
<li>比较运算符：==、!=、&lt;、&lt;=、&gt; 和 &gt;=。</li>
<li>逻辑运算符：&amp;&amp;、|| 和 !。</li>
<li>位运算符：&amp;、|、~、^、&lt;&lt;、&gt;&gt; 和 &gt;&gt;&gt;。</li>
</ol>
<h1>字符串</h1>
<p>String是拥有“值语义”的引用类型，字符串常量实现了“享元模式”，equals会按照内容进行比较，==按照地址比较。</p>
<div>
<pre><span> 1</span> <span>public</span> <span>class</span><span> Program {
</span><span> 2</span> 
<span> 3</span>     <span>/**</span>
<span> 4</span> <span>     * </span><span>@param</span><span> args
</span><span> 5</span>      <span>*/</span>
<span> 6</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 7</span>         String x = "段光伟"<span>;
</span><span> 8</span>         String y = <span>new</span> String("段光伟"<span>);
</span><span> 9</span>         
<span>10</span>         System.out.println(x.equals(y)); <span>//</span><span> true</span>
<span>11</span>         System.out.println(x == y); <span>//</span><span> false</span>
<span>12</span> <span>    }
</span><span>13</span> 
<span>14</span> }</pre>
</div>
<p>为了高效的修改字符串Java引入了StringBuffer。</p>
<div>
<pre><span>1</span> <span>        {
</span><span>2</span>             StringBuffer sb = 
<span>3</span>                     <span>new</span><span> StringBuffer()
</span><span>4</span>                     .append("段"<span>)
</span><span>5</span>                     .append("光"<span>)
</span><span>6</span>                     .append("伟"<span>);
</span><span>7</span>             
<span>8</span> <span>            System.out.println(sb.toString());
</span><span>9</span>         }</pre>
</div>
<h1>数组</h1>
<p><strong>声明语法</strong></p>
<p>DataType[]&nbsp;name 或 DataType name[]。</p>
<p><strong>初始化语法</strong></p>
<p>DataType[]&nbsp;name = new DataType[length]。</p>
<p>DataType[]&nbsp;name = new DataType[] { element1, element2, ...elementn&nbsp;}。</p>
<p>DataType[]&nbsp;name = { element1,&nbsp;element2, ...elementn&nbsp;}。</p>
<div>
<pre><span> 1</span> <span>public</span> <span>class</span><span> Program {
</span><span> 2</span> 
<span> 3</span>     <span>/**</span>
<span> 4</span> <span>     * </span><span>@param</span><span> args
</span><span> 5</span>      <span>*/</span>
<span> 6</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 7</span> <span>        {
</span><span> 8</span>             String[] strs = { "段", "光", "伟"<span> };
</span><span> 9</span> 
<span>10</span>             <span>for</span><span> (String item : strs) {
</span><span>11</span> <span>                System.out.print(item);
</span><span>12</span> <span>            }
</span><span>13</span> <span>        }
</span><span>14</span> <span>    }
</span><span>15</span> 
<span>16</span> }</pre>
</div>
<p><strong>多维数组</strong></p>
<p>只有不等长多维数组DataType[][]，没有DataType[xxx, xxx]。</p>
<h1>控制结构</h1>
<ol>
<li>条件：if-else if-else、switch-case-default和三元运算符（?:）。</li>
<li>循环：while、do-while、for和foreach。</li>
<li>Labeled block。</li>
</ol>
<div>
<pre><span> 1</span> <span>public</span> <span>class</span><span> Program {
</span><span> 2</span> 
<span> 3</span>     <span>/**</span>
<span> 4</span> <span>     * </span><span>@param</span><span> args
</span><span> 5</span>      <span>*/</span>
<span> 6</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 7</span> <span>        task: {
</span><span> 8</span>             <span>int</span> age = 25<span>;
</span><span> 9</span> 
<span>10</span>             System.out.println("start"<span>);
</span><span>11</span> 
<span>12</span>             <span>if</span> (age &lt; 30<span>) {
</span><span>13</span>                 <span>break</span><span> task;
</span><span>14</span> <span>            }
</span><span>15</span> 
<span>16</span>             System.out.println("end"<span>);
</span><span>17</span> <span>        }
</span><span>18</span> <span>    }
</span><span>19</span> }</pre>
</div>
<p>最近觉得label是个不错的东西，最起码多了一种选择。</p>
<h1>方法</h1>
<p>Java中所有的赋值和方法调用都是“按值“处理的，引用类型的值是对象的地址，原始类型的值是其自身。</p>
<p>Java支持变长方法参数。</p>
<div>
<pre><span> 1</span> <span>public</span> <span>class</span><span> Program {
</span><span> 2</span> 
<span> 3</span>     <span>/**</span>
<span> 4</span> <span>     * </span><span>@param</span><span> args
</span><span> 5</span>      <span>*/</span>
<span> 6</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 7</span>         print("段光伟", "段光宇"<span>);
</span><span> 8</span>         print(<span>new</span> String[] { "段光伟", "段光宇"<span> });
</span><span> 9</span> <span>    }
</span><span>10</span> 
<span>11</span>     <span>private</span> <span>static</span> <span>void</span><span> print(String... args) {
</span><span>12</span>         <span>for</span><span> (String item : args) {
</span><span>13</span> <span>            System.out.println(item);
</span><span>14</span> <span>        }
</span><span>15</span> <span>    }
</span><span>16</span> }</pre>
</div>
<h1>类</h1>
<div>
<pre><span> 1</span> <span>public</span> <span>class</span><span> Program {
</span><span> 2</span> 
<span> 3</span>     <span>/*</span><span>*
</span><span> 4</span> <span>     * @param args
</span><span> 5</span>      <span>*/</span>
<span> 6</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 7</span>         Point point = <span>new</span> Point(<span>100</span><span>);
</span><span> 8</span> 
<span> 9</span>         System.<span>out</span><span>.print(point);
</span><span>10</span> <span>    }
</span><span>11</span> <span>}
</span><span>12</span> 
<span>13</span> <span>class</span><span> Point {
</span><span>14</span>     <span>private</span> <span>int</span> x = <span>0</span><span>;
</span><span>15</span>     <span>private</span> <span>int</span> y = <span>0</span><span>;
</span><span>16</span> 
<span>17</span>     <span>public</span> Point(<span>int</span> x, <span>int</span><span> y) {
</span><span>18</span>         <span>this</span>.x =<span> x;
</span><span>19</span>         <span>this</span>.y =<span> y;
</span><span>20</span> <span>    }
</span><span>21</span> 
<span>22</span>     <span>public</span> Point(<span>int</span><span> x) {
</span><span>23</span>         <span>this</span><span>(x, x);
</span><span>24</span> <span>    }
</span><span>25</span> 
<span>26</span>     <span>public</span><span> String toString() {
</span><span>27</span>         <span>return</span> <span>"</span><span>(x:</span><span>"</span> + <span>this</span>.x + <span>"</span><span>,y:</span><span>"</span> + <span>this</span>.y + <span>"</span><span>)</span><span>"</span><span>;
</span><span>28</span> <span>    }
</span><span>29</span> }</pre>
</div>
<p>注意：调用自身的构造方法是用this(xxx,xxx,...)来完成，且必须位于第一行。</p>
<h1>静态成员</h1>
<p>Java中类似静态构造方法的结构，称之为：静态初始化代码块，与之对应的是实例初始化代码块，见下例：</p>
<div>
<pre><span> 1</span> <span>public</span> <span>class</span><span> Program {
</span><span> 2</span> 
<span> 3</span>     <span>/**</span>
<span> 4</span> <span>     * </span><span>@param</span><span> args
</span><span> 5</span>      <span>*/</span>
<span> 6</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 7</span> <span>        System.out.println(Point.getValue());
</span><span> 8</span>         System.out.println(<span>new</span><span> Point());
</span><span> 9</span> <span>    }
</span><span>10</span> <span>}
</span><span>11</span> 
<span>12</span> <span>class</span><span> Point {
</span><span>13</span>     <span>private</span> <span>static</span> <span>int</span> value = 0<span>;
</span><span>14</span> 
<span>15</span>     <span>public</span> <span>static</span> <span>int</span><span> getValue() {
</span><span>16</span>         <span>return</span><span> value;
</span><span>17</span> <span>    }
</span><span>18</span> 
<span>19</span>     <span>static</span><span> {
</span><span>20</span>         value++<span>;
</span><span>21</span> <span>    }
</span><span>22</span> 
<span>23</span>     <span>static</span><span> {
</span><span>24</span>         value++<span>;
</span><span>25</span> <span>    }
</span><span>26</span> 
<span>27</span>     <span>private</span> <span>int</span> x = 0<span>;
</span><span>28</span>     <span>private</span> <span>int</span> y = 0<span>;
</span><span>29</span> 
<span>30</span> <span>    {
</span><span>31</span>         <span>this</span>.x = 10<span>;
</span><span>32</span> <span>    }
</span><span>33</span> 
<span>34</span> <span>    {
</span><span>35</span>         <span>this</span>.y = 10<span>;
</span><span>36</span> <span>    }
</span><span>37</span> 
<span>38</span>     <span>public</span><span> String toString() {
</span><span>39</span>         <span>return</span> "(x:" + <span>this</span>.x + ",y:" + <span>this</span>.y + ")"<span>;
</span><span>40</span> <span>    }
</span><span>41</span> }</pre>
</div>
<h1>继承</h1>
<p>继承使用 extends，抽象类和抽象方法使用abstract声明，向下转型使用 (ChildType)instance，判断是否是某个类型使用 instanceof，见下例：</p>
<div>
<pre><span> 1</span> <span>public</span> <span>class</span><span> Program {
</span><span> 2</span> 
<span> 3</span>     <span>/**</span>
<span> 4</span> <span>     * </span><span>@param</span><span> args
</span><span> 5</span>      <span>*/</span>
<span> 6</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 7</span>         printAnimal(<span>new</span><span> Animal());
</span><span> 8</span>         printAnimal(<span>new</span><span> Dog());
</span><span> 9</span> <span>    }
</span><span>10</span> 
<span>11</span>     <span>private</span> <span>static</span> <span>void</span><span> printAnimal(Animal animal) {
</span><span>12</span>         <span>if</span>(animal <span>instanceof</span><span> Dog){
</span><span>13</span>             System.out.println("I am a " +<span> (Dog) animal);
</span><span>14</span> <span>        }
</span><span>15</span>         <span>else</span>
<span>16</span> <span>        {
</span><span>17</span>             System.out.println("I am an " +<span> animal);
</span><span>18</span> <span>        }
</span><span>19</span> <span>    }
</span><span>20</span> <span>}
</span><span>21</span> 
<span>22</span> <span>class</span><span> Animal {
</span><span>23</span>     <span>public</span><span> String toString() {
</span><span>24</span>         <span>return</span> "Animal"<span>;
</span><span>25</span> <span>    }
</span><span>26</span> <span>}
</span><span>27</span> 
<span>28</span> <span>class</span> Dog <span>extends</span><span> Animal {
</span><span>29</span>     <span>public</span><span> String toString() {
</span><span>30</span>         <span>return</span> "Dog"<span>;
</span><span>31</span> <span>    }
</span><span>32</span> }</pre>
</div>
<h1>重写</h1>
<p>Java中的重写规则比较灵活，具体如下：</p>
<ol>
<li>除了 private 修饰之外的所有实例方法都可以重写，不需要显式的声明。</li>
<li>重写的方法为了显式的表达重写这一概念，使用&nbsp;@Override进行注解。</li>
<li>重写的方法可以修改访问修饰符和返回类型，只要和父类的方法兼容（访问级别更高，返回类型更具体）。</li>
<li>可以使用final将某个方法标记为不可重写。</li>
<li>在构造方法中使用 super(xxx, xxx)调用父类构造方法，在常规实例方法中使用 super.method(xxx, xxx)调用父类方法。</li>
<li>Java不支持覆盖（new）。</li>
</ol>
<div>
<pre><span> 1</span> <span>public</span> <span>class</span><span> Program {
</span><span> 2</span> 
<span> 3</span>     <span>/**</span>
<span> 4</span> <span>     * </span><span>@param</span><span> args
</span><span> 5</span>      <span>*/</span>
<span> 6</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 7</span>         Animal animal = <span>new</span><span> Animal();
</span><span> 8</span>         Animal dog = <span>new</span><span> Dog();
</span><span> 9</span> 
<span>10</span> <span>        animal.say();
</span><span>11</span> <span>        dog.say();
</span><span>12</span> 
<span>13</span> <span>        animal.eat(animal);
</span><span>14</span> <span>        dog.eat(dog);
</span><span>15</span>         
<span>16</span> <span>        System.out.println(animal.info());
</span><span>17</span> <span>        System.out.println(dog.info());
</span><span>18</span> <span>    }
</span><span>19</span> <span>}
</span><span>20</span> 
<span>21</span> <span>class</span><span> Animal {
</span><span>22</span>     <span>private</span> String name = "Animal"<span>;
</span><span>23</span> 
<span>24</span>     <span>protected</span> <span>void</span><span> say() {
</span><span>25</span>         System.out.println("Animal" + " " + <span>this</span><span>.name);
</span><span>26</span> <span>    }
</span><span>27</span> 
<span>28</span>     <span>public</span> <span>void</span><span> eat(Animal food) {
</span><span>29</span>         System.out.println("Animal eat " +<span> food);
</span><span>30</span> <span>    }
</span><span>31</span> 
<span>32</span>     <span>public</span><span> Object info() {
</span><span>33</span>         <span>return</span> "Animal"<span>;
</span><span>34</span> <span>    }
</span><span>35</span>     
<span>36</span> <span>    @Override
</span><span>37</span>     <span>public</span><span> String toString() {
</span><span>38</span>         <span>return</span> "Animal"<span>;
</span><span>39</span> <span>    }
</span><span>40</span> <span>}
</span><span>41</span> 
<span>42</span> <span>class</span> Dog <span>extends</span><span> Animal {
</span><span>43</span>     <span>private</span> String name = "Dog"<span>;
</span><span>44</span> 
<span>45</span> <span>    @Override
</span><span>46</span>     <span>public</span> <span>final</span> <span>void</span><span> say() {
</span><span>47</span>         System.out.println("Dog" + " " + <span>this</span><span>.name);
</span><span>48</span> <span>    }
</span><span>49</span> 
<span>50</span> <span>    @Override
</span><span>51</span>     <span>public</span> <span>final</span> <span>void</span><span> eat(Animal food) {
</span><span>52</span>         <span>super</span><span>.eat(food);
</span><span>53</span>         
<span>54</span>         System.out.println("Dog eated"<span>);
</span><span>55</span> <span>    }
</span><span>56</span> 
<span>57</span> <span>    @Override
</span><span>58</span>     <span>public</span> <span>final</span><span> String info() {
</span><span>59</span>         <span>return</span> "Dog"<span>;
</span><span>60</span> <span>    }
</span><span>61</span> 
<span>62</span> <span>    @Override
</span><span>63</span>     <span>public</span> <span>final</span><span> String toString() {
</span><span>64</span>         <span>return</span> "Dog"<span>;
</span><span>65</span> <span>    }
</span><span>66</span> }</pre>
</div>
<h1>包</h1>
<p>包的名字和项目路径下的目录路径相对应，比如：项目路径为：C:\Study，有一个Java源文件位于：C：\Study\com\happyframework\study\App.java，那么App.java的包名字必须为：com.happyframework.study，且 App.java 的第一行语句必须为：package&nbsp;com.happyframework.study。</p>
<p>Java支持三种导入语法：</p>
<ol>
<li>导入类型：import xxx.xxx.xxxClass。</li>
<li>导入包：import xxx.xxx.xxx.*。</li>
<li>导入静态成员：import static xxx.xxx.*。</li>
</ol>
<div>
<pre><span> 1</span> <span>import</span> <span>static</span> util.Helper.*<span>;
</span><span> 2</span> 
<span> 3</span> <span>public</span> <span>class</span><span> Program {
</span><span> 4</span> 
<span> 5</span>     <span>/**</span>
<span> 6</span> <span>     * </span><span>@param</span><span> args
</span><span> 7</span>      <span>*/</span>
<span> 8</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 9</span>         puts("段光伟"<span>);
</span><span>10</span> <span>    }
</span><span>11</span> }</pre>
</div>
<h1>访问级别</h1>
<p>Java支持四种访问级别：public、private、protected 和 default（默认），类型和接口只能使用public 和 default，成员和嵌套类型可以使用所有，下面简单的解释一下 protected 和 default。</p>
<ul>
<li>protected 修饰过的成员只能被自己、子类和同一个包里的（不包括子包）其他类型访问。</li>
<li>default 修改过的类型或成员只能被自己和同一个包里的（不包括子包）其他类型访问。</li>
</ul>
<h1>嵌套类</h1>
<p>Java支持如下几种嵌套类：</p>
<ol>
<li>nested class，定义在类型内部的类型。<ol>
<li>static nested class，使用 static 声明的 nested class，static nested class 可以访问所有外部类的静态成员。</li>
<li>inner class，没有使用 static 声明的 nested class，inner class 可以访问所有外部类的实例成员，inner class 不能定义静态成员。</li>
</ol></li>
</ol>
<p><strong>代码示例</strong></p>
<div>
<pre><span> 1</span> <span>public</span> <span>class</span><span> Program {
</span><span> 2</span> 
<span> 3</span>     <span>/**</span>
<span> 4</span> <span>     * </span><span>@param</span><span> args
</span><span> 5</span>      <span>*/</span>
<span> 6</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 7</span>         OuterClass outer = <span>new</span><span> OuterClass();
</span><span> 8</span>         OuterClass.InnerClass inner = outer.<span>new</span><span> InnerClass();
</span><span> 9</span>         OuterClass.InnerClass.InnerInnerClass innerInner = inner.<span>new</span><span> InnerInnerClass();
</span><span>10</span> <span>        outer.show();
</span><span>11</span> <span>        inner.show();
</span><span>12</span> <span>        innerInner.show();
</span><span>13</span>         
<span>14</span>         OuterClass.StaticNestedClass staticNested=<span>new</span><span> OuterClass.StaticNestedClass();
</span><span>15</span>         OuterClass.StaticNestedClass.StaticNestedNestedClass staticNestedNested=<span>new</span><span> OuterClass.StaticNestedClass.StaticNestedNestedClass();
</span><span>16</span>         
<span>17</span> <span>        staticNested.show();
</span><span>18</span> <span>        staticNestedNested.show();
</span><span>19</span> <span>    }
</span><span>20</span> <span>}
</span><span>21</span> 
<span>22</span> <span>class</span><span> OuterClass {
</span><span>23</span>     <span>int</span> x = 1<span>;
</span><span>24</span>     <span>static</span> <span>int</span> i = 1<span>;
</span><span>25</span> 
<span>26</span>     <span>void</span><span> show() {
</span><span>27</span> <span>        System.out.println(x);
</span><span>28</span> <span>        System.out.println(i);
</span><span>29</span> <span>    }
</span><span>30</span> 
<span>31</span>     <span>class</span><span> InnerClass {
</span><span>32</span>         <span>int</span> y = 2<span>;
</span><span>33</span> 
<span>34</span>         <span>void</span><span> show() {
</span><span>35</span> <span>            System.out.println(x);
</span><span>36</span> <span>            System.out.println(y);
</span><span>37</span> <span>        }
</span><span>38</span> 
<span>39</span>         <span>class</span><span> InnerInnerClass {
</span><span>40</span>             <span>int</span> z = 3<span>;
</span><span>41</span> 
<span>42</span>             <span>void</span><span> show() {
</span><span>43</span>                 System.out.println(OuterClass.<span>this</span><span>.x);
</span><span>44</span> <span>                System.out.println(y);
</span><span>45</span> <span>                System.out.println(z);
</span><span>46</span> <span>            }
</span><span>47</span> <span>        }
</span><span>48</span> <span>    }
</span><span>49</span> 
<span>50</span>     <span>static</span> <span>class</span><span> StaticNestedClass {
</span><span>51</span>         <span>static</span> <span>int</span> j = 2<span>;
</span><span>52</span> 
<span>53</span>         <span>void</span><span> show() {
</span><span>54</span> <span>            System.out.println(i);
</span><span>55</span> <span>            System.out.println(j);
</span><span>56</span> <span>        }
</span><span>57</span> 
<span>58</span>         <span>static</span> <span>class</span><span> StaticNestedNestedClass {
</span><span>59</span>             <span>static</span> <span>int</span> k = 3<span>;
</span><span>60</span> 
<span>61</span>             <span>void</span><span> show() {
</span><span>62</span> <span>                System.out.println(i);
</span><span>63</span> <span>                System.out.println(j);
</span><span>64</span> <span>                System.out.println(k);
</span><span>65</span> <span>            }
</span><span>66</span> <span>        }
</span><span>67</span> <span>    }
</span><span>68</span> }</pre>
</div>
<p><strong>特殊的inner class：local class</strong></p>
<div>
<pre><span> 1</span> <span>public</span> <span>class</span><span> LocalClassExample {
</span><span> 2</span> 
<span> 3</span>     <span>static</span> String staticValue = <span>"</span><span>static value</span><span>"</span><span>;
</span><span> 4</span>     String instanceValue = <span>"</span><span>instance value</span><span>"</span><span>;
</span><span> 5</span> 
<span> 6</span>     <span>public</span> <span>void</span><span> test() {
</span><span> 7</span> 
<span> 8</span>         final String finalLocalValue = <span>"</span><span>final local value</span><span>"</span><span>;
</span><span> 9</span> 
<span>10</span>         <span>class</span><span> LocalClass {
</span><span>11</span>             <span>void</span><span> test() {
</span><span>12</span>                 System.<span>out</span><span>.println(staticValue);
</span><span>13</span>                 System.<span>out</span><span>.println(instanceValue);
</span><span>14</span>                 System.<span>out</span><span>.println(finalLocalValue);
</span><span>15</span> <span>            }
</span><span>16</span> <span>        }
</span><span>17</span> 
<span>18</span>         LocalClass local = <span>new</span><span> LocalClass();
</span><span>19</span> <span>        local.test();
</span><span>20</span> <span>    }
</span><span>21</span> }</pre>
</div>
<p>除了inner class的规则之外，local class可以访问局部final变量，在Java8中有更多的改进。</p>
<p><strong>特殊的local class：anonymous class</strong></p>
<div>
<pre><span> 1</span> <span>public</span> <span>class</span><span> Program {
</span><span> 2</span> 
<span> 3</span>     <span>/**</span>
<span> 4</span> <span>     * </span><span>@param</span><span> args
</span><span> 5</span>      <span>*/</span>
<span> 6</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 7</span>         execute(<span>new</span><span> Action() {
</span><span> 8</span> <span>            @Override
</span><span> 9</span>             <span>public</span> <span>void</span><span> execute() {
</span><span>10</span>                 System.out.println("执行业务逻辑"<span>);
</span><span>11</span> <span>            }
</span><span>12</span> <span>        });
</span><span>13</span> <span>    }
</span><span>14</span> 
<span>15</span>     <span>static</span> <span>void</span><span> execute(Action action) {
</span><span>16</span>         System.out.println("事物开始"<span>);
</span><span>17</span> <span>        action.execute();
</span><span>18</span>         System.out.println("事物结束"<span>);
</span><span>19</span> <span>    }
</span><span>20</span> <span>}
</span><span>21</span> 
<span>22</span> <span>interface</span><span> Action {
</span><span>23</span>     <span>void</span><span> execute();
</span><span>24</span> }</pre>
</div>
<h1>常量</h1>
<p>不废话了，直接看代码：</p>
<div>
<pre><span> 1</span> <span>public</span> <span>final</span> <span>class</span><span> Program {
</span><span> 2</span>     <span>static</span> <span>final</span> String STATIC_CONSTANTS = "STATIC_CONSTANTS"<span>;
</span><span> 3</span>     <span>final</span> String INSTANCE_CONSTANTS = "INSTANCE_CONSTANTS"<span>;
</span><span> 4</span> 
<span> 5</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 6</span>         <span>final</span> String LOCAL_CONSTANTS = "LOCAL_CONSTANTS"<span>;
</span><span> 7</span> 
<span> 8</span> <span>        System.out.println(STATIC_CONSTANTS);
</span><span> 9</span>         System.out.println(<span>new</span><span> Program().INSTANCE_CONSTANTS);
</span><span>10</span> <span>        System.out.println(LOCAL_CONSTANTS);
</span><span>11</span>         <span>new</span> Program().test("PARAMETER_CONSTANTS"<span>);
</span><span>12</span> <span>    }
</span><span>13</span> 
<span>14</span>     <span>public</span> <span>final</span> <span>void</span> test(<span>final</span><span> String msg) {
</span><span>15</span> <span>        System.out.println(msg);
</span><span>16</span> <span>    }
</span><span>17</span> }</pre>
</div>
<p>有一点需要注意的是：只有一种情况Java的常量是编译时常量（编译器会帮你替换），其它情况都是运行时常量，这种情况是：静态类型常量且常量的值可以编译时确定。</p>
<h1>接口</h1>
<p>Java的接口可以包含方法签名、常量和嵌套类，见下例：</p>
<div>
<pre><span> 1</span> <span>public</span> <span>final</span> <span>class</span><span> Program {
</span><span> 2</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 3</span> <span>        Playable.EMPTY.play();
</span><span> 4</span> 
<span> 5</span>         <span>new</span><span> Dog().play();
</span><span> 6</span> <span>    }
</span><span> 7</span> <span>}
</span><span> 8</span> 
<span> 9</span> <span>interface</span><span> Playable {
</span><span>10</span>     Playable EMPTY = <span>new</span><span> EmptyPlayable();
</span><span>11</span> 
<span>12</span>     <span>void</span><span> play();
</span><span>13</span> 
<span>14</span>     <span>class</span> EmptyPlayable <span>implements</span><span> Playable {
</span><span>15</span> 
<span>16</span> <span>        @Override
</span><span>17</span>         <span>public</span> <span>void</span><span> play() {
</span><span>18</span>             System.out.println("无所事事"<span>);
</span><span>19</span> <span>        }
</span><span>20</span> 
<span>21</span> <span>    }
</span><span>22</span> <span>}
</span><span>23</span> 
<span>24</span> <span>class</span> Dog <span>implements</span><span> Playable {
</span><span>25</span> 
<span>26</span> <span>    @Override
</span><span>27</span>     <span>public</span> <span>void</span><span> play() {
</span><span>28</span>         System.out.println("啃骨头"<span>);
</span><span>29</span> <span>    }
</span><span>30</span> 
<span>31</span> }</pre>
</div>
<h1>枚举</h1>
<p>Java枚举是class，继承自java.lang.Enum，枚举中可以定义任何类型可以定义的内容，构造方法只能是private或package private，枚举成员会被编译器动态翻译为枚举实例常量，见下例：</p>
<div>
<pre><span> 1</span> <span>public</span> <span>final</span> <span>class</span><span> Program {
</span><span> 2</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 3</span> <span>        System.out.println(State.ON);
</span><span> 4</span> <span>        System.out.println(State.OFF);
</span><span> 5</span> 
<span> 6</span>         <span>for</span><span> (State item : State.values()) {
</span><span> 7</span> <span>            System.out.println(item);
</span><span> 8</span> <span>            System.out.println(State.valueOf(item.name()));
</span><span> 9</span> <span>        }
</span><span>10</span> <span>    }
</span><span>11</span> <span>}
</span><span>12</span> 
<span>13</span> <span>enum</span><span> State {
</span><span>14</span>     ON(1), OFF(0<span>);
</span><span>15</span> 
<span>16</span>     <span>int</span> value = 1<span>;
</span><span>17</span> 
<span>18</span>     State(<span>int</span><span> value) {
</span><span>19</span>         <span>this</span>.value =<span> value;
</span><span>20</span> <span>    }
</span><span>21</span> }</pre>
</div>
<p>调用枚举的构造方法格式是：常量名字(xxx, xxx)，如果构造方法没有参数只需要：常量名子，如：</p>
<div>
<pre><span>1</span> <span>enum</span><span> State {
</span><span>2</span> <span>    ON, OFF
</span><span>3</span> }</pre>
</div>
<h1>异常</h1>
<p>Java中的异常分为checked和unchecked，checked异常必须声明在方法中或被捕获，这点我觉得比较好，必定：异常也是API的一部分，见下例：</p>
<div>
<pre><span> 1</span> <span>public</span> <span>final</span> <span>class</span><span> Program {
</span><span> 2</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 3</span>         <span>try</span><span> {
</span><span> 4</span> <span>            test();
</span><span> 5</span>         } <span>catch</span><span> (Exception e) {
</span><span> 6</span> <span>            System.out.println(e.getMessage());
</span><span> 7</span> <span>        }
</span><span> 8</span> <span>    }
</span><span> 9</span> 
<span>10</span>     <span>public</span> <span>static</span> <span>void</span> test() <span>throws</span><span> Exception {
</span><span>11</span>         <span>throw</span> <span>new</span> Exception("I am wrong!"<span>);
</span><span>12</span> <span>    }
</span><span>13</span> }</pre>
</div>
<p>所有继承Exception的异常（除了RuntimeException和它的后代之外）都是checked异常。</p>
<h1>装箱和拆箱</h1>
<p>Java提供了原始类型对应的引用类型，在1.5之后的版本还提供了自动装箱和自动拆箱，结合最新版本的泛型，几乎可以忽略这块。</p>
<div>
<pre><span> 1</span> <span>import</span> java.util.*<span>;
</span><span> 2</span> 
<span> 3</span> <span>public</span> <span>final</span> <span>class</span><span> Program {
</span><span> 4</span>     <span>public</span> <span>static</span> <span>void</span><span> main(String[] args) {
</span><span> 5</span>         ArrayList list = <span>new</span><span> ArrayList();
</span><span> 6</span>         
<span> 7</span>         list.add(1<span>);
</span><span> 8</span>         <span>int</span> item1 = (Integer) list.get(0<span>);
</span><span> 9</span>         
<span>10</span> <span>        System.out.println(item1);
</span><span>11</span> <span>    }
</span><span>12</span> }</pre>
</div>
<p>注意：自动装箱和自动拆箱是Java提供的语法糖。</p>
<h1>泛型</h1>
<p>Java的泛型是编译器提供的语法糖，官方称之为：类型参数搽除，先看一下语法，然后总结一点规律：</p>
<h2>泛型方法</h2>
<p><strong>测试代码</strong></p>
<div>
<pre><span> 1</span>     <span>static</span> &lt;T&gt; <span>void</span><span> puts(T msg) {
</span><span> 2</span> <span>        println(msg);
</span><span> 3</span> <span>    }
</span><span> 4</span> 
<span> 5</span>     <span>static</span> <span>void</span><span> println(Object msg) {
</span><span> 6</span>         System.out.println("Object:" +<span> msg);
</span><span> 7</span> <span>    }
</span><span> 8</span> 
<span> 9</span>     <span>static</span> <span>void</span><span> println(String msg) {
</span><span>10</span>         System.out.println("String:" +<span> msg);
</span><span>11</span>     }</pre>
</div>
<p><strong>调用泛型方法</strong></p>
<div>
<pre><span>1</span>         System.out.println("generic method test"<span>);
</span><span>2</span>         puts("hello"<span>);
</span><span>3</span>         Program.&lt;String&gt; puts("hello");</pre>
</div>
<p><strong>输出的结果是</strong></p>
<div>
<pre><span>1</span> <span>generic method test
</span><span>2</span> <span>Object:hello
</span><span>3</span> Object:hello</pre>
</div>
<h2>泛型类</h2>
<p><strong>测试代码</strong></p>
<div>
<pre><span>1</span> <span>class</span> TestGenericClass&lt;T&gt;<span> {
</span><span>2</span> <span>    T value;
</span><span>3</span> 
<span>4</span>     <span>void</span><span> setValue(T value) {
</span><span>5</span>         <span>this</span>.value =<span> value;
</span><span>6</span> <span>    }
</span><span>7</span> }</pre>
</div>
<p><strong>调用代码</strong></p>
<div>
<pre><span>1</span>         System.out.println("generic class test"<span>);
</span><span>2</span>         System.out.println(t.value);</pre>
</div>
<p><strong>输出结果</strong></p>
<div>
<pre><span>1</span> generic <span>class</span><span> test
</span><span>2</span> 1</pre>
</div>
<h2>泛型接口</h2>
<p><strong>测试代码</strong></p>
<div>
<pre><span> 1</span> <span>interface</span> TestInterface&lt;T&gt;<span> {
</span><span> 2</span>     <span>void</span><span> test(T item);
</span><span> 3</span> <span>}
</span><span> 4</span> 
<span> 5</span> <span>class</span> TestInterfaceImp1 <span>implements</span> TestInterface&lt;String&gt;<span> {
</span><span> 6</span> 
<span> 7</span> <span>    @Override
</span><span> 8</span>     <span>public</span> <span>void</span><span> test(String item) {
</span><span> 9</span> <span>        System.out.println(item);
</span><span>10</span> <span>    }
</span><span>11</span> <span>}
</span><span>12</span> 
<span>13</span> <span>class</span> TestInterfaceImp2&lt;T&gt; <span>implements</span> TestInterface&lt;T&gt;<span> {
</span><span>14</span> 
<span>15</span> <span>    @Override
</span><span>16</span>     <span>public</span> <span>void</span><span> test(T item) {
</span><span>17</span> <span>        System.out.println(item);
</span><span>18</span> <span>    }
</span><span>19</span> }</pre>
</div>
<p><strong>调用代码</strong></p>
<div>
<pre><span> 1</span>         System.out.println("generic interface test"<span>);
</span><span> 2</span>         TestInterface&lt;String&gt; testInterface1 = <span>new</span><span> TestInterfaceImp1();
</span><span> 3</span>         testInterface1.test("hi"<span>);
</span><span> 4</span>         <span>for</span><span> (Method item : testInterface1.getClass().getMethods()) {
</span><span> 5</span>             <span>if</span> (item.getName() == "test"<span>) {
</span><span> 6</span>                 System.out.println(item.getParameterTypes()[0<span>].getName());
</span><span> 7</span> <span>            }
</span><span> 8</span> <span>        }
</span><span> 9</span> 
<span>10</span>         TestInterface&lt;String&gt; testInterface2 = <span>new</span> TestInterfaceImp2&lt;&gt;<span>();
</span><span>11</span>         testInterface2.test("hi"<span>);
</span><span>12</span>         <span>for</span><span> (Method item : testInterface2.getClass().getMethods()) {
</span><span>13</span>             <span>if</span> (item.getName() == "test"<span>) {
</span><span>14</span>                 System.out.println(item.getParameterTypes()[0<span>].getName());
</span><span>15</span> <span>            }
</span><span>16</span>         }</pre>
</div>
<p><strong>输出结果</strong></p>
<div>
<pre><span>1</span> generic <span>interface</span><span> test
</span><span>2</span> <span>hi
</span><span>3</span> <span>java.lang.String
</span><span>4</span> <span>java.lang.Object
</span><span>5</span> <span>hi
</span><span>6</span> java.lang.Object</pre>
</div>
<h2>类型参数约束</h2>
<p><strong>测试代码</strong></p>
<div>
<pre><span> 1</span> <span>class</span><span> Animal {
</span><span> 2</span> <span>}
</span><span> 3</span> 
<span> 4</span> <span>class</span> Dog <span>extends</span><span> Animal {
</span><span> 5</span> <span>}
</span><span> 6</span> 
<span> 7</span> <span>class</span> Base&lt;T <span>extends</span> Animal&gt;<span> {
</span><span> 8</span>     <span>public</span> <span>void</span><span> test(T item) {
</span><span> 9</span>         System.out.println("Base:" +<span> item);
</span><span>10</span> <span>    }
</span><span>11</span> <span>}
</span><span>12</span> 
<span>13</span> <span>class</span> Child <span>extends</span> Base&lt;Dog&gt;<span> {
</span><span>14</span> 
<span>15</span> <span>    @Override
</span><span>16</span>     <span>public</span> <span>void</span><span> test(Dog item) {
</span><span>17</span>         System.out.println("Child:" +<span> item);
</span><span>18</span> <span>    }
</span><span>19</span> }</pre>
</div>
<p><strong>调用代码</strong></p>
<div>
<pre><span>1</span>         System.out.println("bounded type parameters test"<span>);
</span><span>2</span>         Base&lt;Dog&gt; base = <span>new</span><span> Child();
</span><span>3</span>         base.test(<span>new</span><span> Dog());
</span><span>4</span>         <span>for</span><span> (Method item : base.getClass().getMethods()) {
</span><span>5</span>             <span>if</span> (item.getName() == "test"<span>) {
</span><span>6</span>                 System.out.println(item.getParameterTypes()[0<span>].getName());
</span><span>7</span> <span>            }
</span><span>8</span>         }</pre>
</div>
<p><strong>输出结果</strong></p>
<div>
<pre><span>1</span> <span>bounded type parameters test
</span><span>2</span> <span>Child:Dog@533c2ac3
</span><span>3</span> <span>Dog
</span><span>4</span> Animal</pre>
</div>
<h2>类型搽除过程</h2>
<ol>
<li>将泛型定义中的类型参数去掉。<br>
<div>
<pre><span>class</span><span> Base {
    </span><span>public</span> <span>void</span><span> test(T item) {
        System.out.println(</span>"Base:" +<span> item);
    }
}</span></pre>
</div>
</li>
<li>将T换成extends指定的约束类型，默认是Object。<br>
<div>
<pre><span>1</span> <span>class</span><span> Base {
</span><span>2</span>     <span>public</span> <span>void</span><span> test(Animal item) {
</span><span>3</span>         System.out.println("Base:" +<span> item);
</span><span>4</span> <span>    }
</span><span>5</span> }</pre>
</div>
</li>
<li>如果有非泛型类型继承或实现了泛型基类或接口，而且进行了重写，根据情况，编译器会自动生成一些方法。<br>
<div>
<pre><span> 1</span> <span>class</span> Child <span>extends</span><span> Base {
</span><span> 2</span> <span>    @Override
</span><span> 3</span>     <span>public</span> <span>void</span><span> test(Animal item) {
</span><span> 4</span>         <span>this</span><span>.test((Dog)item);
</span><span> 5</span> <span>    }
</span><span> 6</span>     
<span> 7</span>     <span>public</span> <span>void</span><span> test(Dog item) {
</span><span> 8</span>         System.out.println("Child:" +<span> item);
</span><span> 9</span> <span>    }
</span><span>10</span> }</pre>
</div>
</li>
<li>根据泛型参数的实际参数搽除调用代码。<br>
<div>
<pre><span>1</span>         System.out.println("bounded type parameters test"<span>);
</span><span>2</span>         Base base = <span>new</span><span> Child();
</span><span>3</span>         base.test(<span>new</span><span> Dog());
</span><span>4</span>         <span>for</span><span> (Method item : base.getClass().getMethods()) {
</span><span>5</span>             <span>if</span> (item.getName() == "test"<span>) {
</span><span>6</span>                 System.out.println(item.getParameterTypes()[0<span>].getName());
</span><span>7</span> <span>            }
</span><span>8</span>         }</pre>
</div>
</li>
</ol>
<p>&nbsp;这里说的不一定正确，特别是Java泛型的约束支持&amp;（如：可以约束实行多个接口），不过过程估计差别不大，我没有看Java语言规范，这里只是大概的猜测。</p>
<h1>备注</h1>
<p>这几天完成了Java基本语法的学习，关于一些高级特性在后面再慢慢总结，如：运行时进程模型、类型加载机制、反射、注解、动态代理等。</p>
<p>&nbsp;</p>
<p></div></p>
<footer>
    <p>本站部分内容来源于网络，如有侵犯您的权益，请通过以下方式联系我们：</p>
    <p>邮箱：zytlsd@163.com</p>
    <p>我们将在收到通知后第一时间处理。</p>
</footer>]]></description>
    <pubDate>Sun, 19 Oct 2025 21:30:45 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-32.html</guid>
</item>
<item>
    <title>Java 枚举</title>
    <link>http://www.51keeplearning.com/post-31.html</link>
    <description><![CDATA[<p><meta name="referrer" content="no-referrer" /><div id="cnblogs_post_body" contentScore="4850"></p>
<h1 id="java-枚举">Java 枚举</h1>
<h2 id="知识点">知识点</h2>
<p><img src="https://upload-images.jianshu.io/upload_images/3101171-9c7b947d48b0de29.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="枚举.png" loading="lazy"></p>
<h2 id="概念">概念</h2>
<p><code>enum</code> 的全称为 enumeration， 是 JDK 1.5  中引入的新特性。</p>
<p>在Java中，被 <code>enum</code> 关键字修饰的类型就是枚举类型。形式如下：</p>
<pre><code>enum Color { RED, GREEN, BLUE }
</code></pre>
<p>如果枚举不添加任何方法，<strong>枚举值默认为从0开始的有序数值</strong>。以 Color 枚举类型举例，它的枚举常量依次为 <code>RED：0，GREEN：1，BLUE：2</code>。</p>
<p><strong>枚举的好处</strong>：可以将常量组织起来，统一进行管理。</p>
<p><strong>枚举的典型应用场景</strong>：错误码、状态机等。</p>
<h3 id="枚举类型的本质">枚举类型的本质</h3>
<p>尽管 <code>enum</code> 看起来像是一种新的数据类型，事实上，<strong>enum是一种受限制的类，并且具有自己的方法</strong>。</p>
<p>创建enum时，编译器会为你生成一个相关的类，这个类继承自 <code>java.lang.Enum</code>。</p>
<p><code>java.lang.Enum</code>类声明</p>
<pre><code>public abstract class Enum&lt;E extends Enum&lt;E&gt;&gt;
        implements Comparable&lt;E&gt;, Serializable { ... }
</code></pre>
<h2 id="枚举的方法">枚举的方法</h2>
<p>在enum中，提供了一些基本方法：</p>
<p><code>values()</code>：返回 enum 实例的数组，而且该数组中的元素严格保持在 enum 中声明时的顺序。</p>
<p><code>name()</code>：返回实例名。</p>
<p><code>ordinal()</code>：返回实例声明时的次序，从0开始。</p>
<p><code>getDeclaringClass()</code>：返回实例所属的 enum 类型。</p>
<p><code>equals()</code> ：判断是否为同一个对象。</p>
<p>可以使用 <code>==</code> 来比较<code>enum</code>实例。</p>
<p>此外，<code>java.lang.Enum</code>实现了<code>Comparable</code>和 <code>Serializable</code> 接口，所以也提供 <code>compareTo()</code> 方法。</p>
<p><strong>例：展示enum的基本方法</strong></p>
<pre><code>public class EnumMethodDemo {
    enum Color {RED, GREEN, BLUE;}
    enum Size {BIG, MIDDLE, SMALL;}
    public static void main(String args[]) {
        System.out.println("=========== Print all Color ===========");
        for (Color c : Color.values()) {
            System.out.println(c + " ordinal: " + c.ordinal());
        }
        System.out.println("=========== Print all Size ===========");
        for (Size s : Size.values()) {
            System.out.println(s + " ordinal: " + s.ordinal());
        }

        Color green = Color.GREEN;
        System.out.println("green name(): " + green.name());
        System.out.println("green getDeclaringClass(): " + green.getDeclaringClass());
        System.out.println("green hashCode(): " + green.hashCode());
        System.out.println("green compareTo Color.GREEN: " + green.compareTo(Color.GREEN));
        System.out.println("green equals Color.GREEN: " + green.equals(Color.GREEN));
        System.out.println("green equals Size.MIDDLE: " + green.equals(Size.MIDDLE));
        System.out.println("green equals 1: " + green.equals(1));
        System.out.format("green == Color.BLUE: %b\n", green == Color.BLUE);
    }
}
</code></pre>
<p><strong>输出</strong></p>
<pre><code>=========== Print all Color ===========
RED ordinal: 0
GREEN ordinal: 1
BLUE ordinal: 2
=========== Print all Size ===========
BIG ordinal: 0
MIDDLE ordinal: 1
SMALL ordinal: 2
green name(): GREEN
green getDeclaringClass(): class org.zp.javase.enumeration.EnumDemo$Color
green hashCode(): 460141958
green compareTo Color.GREEN: 0
green equals Color.GREEN: true
green equals Size.MIDDLE: false
green equals 1: false
green == Color.BLUE: false
</code></pre>
<h2 id="枚举的特性">枚举的特性</h2>
<p>枚举的特性，归结起来就是一句话：</p>
<blockquote contentScore="72">
<p><strong>除了不能继承，基本上可以将 <code>enum</code> 看做一个常规的类</strong>。</p>
</blockquote>
<p>但是这句话需要拆分去理解，让我们细细道来。</p>
<h3 id="枚举可以添加方法">枚举可以添加方法</h3>
<p>在概念章节提到了，<strong>枚举值默认为从0开始的有序数值</strong> 。那么问题来了：如何为枚举显示的赋值。</p>
<h4 id="java-不允许使用--为枚举常量赋值">Java 不允许使用 = 为枚举常量赋值</h4>
<p>如果你接触过C/C++，你肯定会很自然的想到赋值符号 <code>=</code> 。在C/C++语言中的enum，可以用赋值符号<code>=</code>显示的为枚举常量赋值；但是 ，很遗憾，<strong>Java 语法中却不允许使用赋值符号 <code>=</code> 为枚举常量赋值</strong>。</p>
<p><strong>例：C/C++ 语言中的枚举声明</strong></p>
<pre><code>typedef&nbsp;enum{
&nbsp;&nbsp;&nbsp; ONE = 1,
&nbsp;&nbsp;&nbsp; TWO,
&nbsp;&nbsp;&nbsp; THREE = 3,
&nbsp;&nbsp;&nbsp; TEN = 10
} Number;
</code></pre>
<h4 id="枚举可以添加普通方法静态方法抽象方法构造方法">枚举可以添加普通方法、静态方法、抽象方法、构造方法</h4>
<p>Java 虽然不能直接为实例赋值，但是它有更优秀的解决方案：<strong>为 enum 添加方法来间接实现显示赋值</strong>。</p>
<p>创建 <code>enum</code> 时，可以为其添加多种方法，甚至可以为其添加构造方法。</p>
<p><strong>注意一个细节：如果要为enum定义方法，那么必须在enum的最后一个实例尾部添加一个分号。此外，在enum中，必须先定义实例，不能将字段或方法定义在实例前面。否则，编译器会报错。</strong></p>
<p><strong>例：全面展示如何在枚举中定义普通方法、静态方法、抽象方法、构造方法</strong></p>
<pre><code>public enum ErrorCode {
    OK(0) {
        public String getDescription() {
            return "成功";
        }
    },
    ERROR_A(100) {
        public String getDescription() {
            return "错误A";
        }
    },
    ERROR_B(200) {
        public String getDescription() {
            return "错误B";
        }
    };

    private int code;

    // 构造方法：enum的构造方法只能被声明为private权限或不声明权限
    private ErrorCode(int number) { // 构造方法
        this.code = number;
    }
    public int getCode() { // 普通方法
        return code;
    } // 普通方法
    public abstract String getDescription(); // 抽象方法
    public static void main(String args[]) { // 静态方法
        for (ErrorCode s : ErrorCode.values()) {
            System.out.println("code: " + s.getCode() + ", description: " + s.getDescription());
        }
    }
}
</code></pre>
<p>注：上面的例子并不可取，仅仅是为了展示枚举支持定义各种方法。下面是一个简化的例子</p>
<p><strong>例：一个错误码枚举类型的定义</strong></p>
<p>本例和上例的执行结果完全相同。</p>
<pre><code>public enum ErrorCodeEn {
    OK(0, "成功"),
    ERROR_A(100, "错误A"),
    ERROR_B(200, "错误B");

    ErrorCodeEn(int number, String description) {
        this.code = number;
        this.description = description;
    }
    private int code;
    private String description;
    public int getCode() {
        return code;
    }
    public String getDescription() {
        return description;
    }
    public static void main(String args[]) { // 静态方法
        for (ErrorCodeEn s : ErrorCodeEn.values()) {
            System.out.println("code: " + s.getCode() + ", description: " + s.getDescription());
        }
    }
}
</code></pre>
<h3 id="枚举可以实现接口">枚举可以实现接口</h3>
<p><strong><code>enum</code> 可以像一般类一样实现接口。</strong></p>
<p>同样是实现上一节中的错误码枚举类，通过实现接口，可以约束它的方法。</p>
<pre><code>public interface INumberEnum {
    int getCode();
    String getDescription();
}

public enum ErrorCodeEn2 implements INumberEnum {
    OK(0, "成功"),
    ERROR_A(100, "错误A"),
    ERROR_B(200, "错误B");

    ErrorCodeEn2(int number, String description) {
        this.code = number;
        this.description = description;
    }

    private int code;
    private String description;

    @Override
    public int getCode() {
        return code;
    }

    @Override
    public String getDescription() {
        return description;
    }
}
</code></pre>
<h3 id="枚举不可以继承">枚举不可以继承</h3>
<p><strong>enum 不可以继承另外一个类，当然，也不能继承另一个 enum 。</strong></p>
<p>因为 <code>enum</code> 实际上都继承自 <code>java.lang.Enum</code> 类，而 Java 不支持多重继承，所以 <code>enum</code> 不能再继承其他类，当然也不能继承另一个 <code>enum</code>。</p>
<h2 id="枚举的应用场景">枚举的应用场景</h2>
<h3 id="组织常量">组织常量</h3>
<p>在JDK1.5 之前，在Java中定义常量都是<code>public static final TYPE a;</code> 这样的形式。有了枚举，你可以将有关联关系的常量组织起来，使代码更加易读、安全，并且还可以使用枚举提供的方法。</p>
<h4 id="枚举声明的格式">枚举声明的格式</h4>
<p><strong>注：如果枚举中没有定义方法，也可以在最后一个实例后面加逗号、分号或什么都不加。</strong></p>
<p>下面三种声明方式是等价的：</p>
<pre><code>enum Color { RED, GREEN, BLUE }
enum Color { RED, GREEN, BLUE, }
enum Color { RED, GREEN, BLUE; }
</code></pre>
<h3 id="switch-状态机">switch 状态机</h3>
<p>我们经常使用switch语句来写状态机。JDK7以后，switch已经支持 <code>int</code>、<code>char</code>、<code>String</code>、<code>enum</code> 类型的参数。这几种类型的参数比较起来，使用枚举的switch代码更具有可读性。</p>
<pre><code>enum Signal {RED, YELLOW, GREEN}

public static String getTrafficInstruct(Signal signal) {
    String instruct = "信号灯故障";
    switch (signal) {
        case RED:
            instruct = "红灯停";
            break;
        case YELLOW:
            instruct = "黄灯请注意";
            break;
        case GREEN:
            instruct = "绿灯行";
            break;
        default:
            break;
    }
    return instruct;
}
</code></pre>
<h3 id="组织枚举">组织枚举</h3>
<p>可以将类型相近的枚举通过接口或类组织起来。</p>
<p>但是一般用接口方式进行组织。</p>
<p>原因是：Java接口在编译时会自动为enum类型加上<code>public static</code>修饰符；Java类在编译时会自动为 <code>enum</code> 类型加上static修饰符。看出差异了吗？没错，就是说，在类中组织 <code>enum</code>，如果你不给它修饰为 <code>public</code>，那么只能在本包中进行访问。</p>
<p><strong>例：在接口中组织 enum</strong></p>
<pre><code>public interface Plant {
    enum Vegetable implements INumberEnum {
        POTATO(0, "土豆"),
        TOMATO(0, "西红柿");

        Vegetable(int number, String description) {
            this.code = number;
            this.description = description;
        }

        private int code;
        private String description;

        @Override
        public int getCode() {
            return 0;
        }

        @Override
        public String getDescription() {
            return null;
        }
    }

    enum Fruit implements INumberEnum {
        APPLE(0, "苹果"),
        ORANGE(0, "桔子"),
        BANANA(0, "香蕉");

        Fruit(int number, String description) {
            this.code = number;
            this.description = description;
        }

        private int code;
        private String description;

        @Override
        public int getCode() {
            return 0;
        }

        @Override
        public String getDescription() {
            return null;
        }
    }
}
</code></pre>
<p><strong>例：在类中组织 enum</strong></p>
<p>本例和上例效果相同。</p>
<pre><code>public class Plant2 {
    public enum Vegetable implements INumberEnum {...}  // 省略代码
    public enum Fruit implements INumberEnum {...} // 省略代码
}
</code></pre>
<h3 id="策略枚举">策略枚举</h3>
<p>EffectiveJava中展示了一种策略枚举。这种枚举通过枚举嵌套枚举的方式，将枚举常量分类处理。</p>
<p>这种做法虽然没有switch语句简洁，但是更加安全、灵活。</p>
<p><strong>例：EffectvieJava中的策略枚举范例</strong></p>
<pre><code>enum PayrollDay {
    MONDAY(PayType.WEEKDAY), TUESDAY(PayType.WEEKDAY), WEDNESDAY(
            PayType.WEEKDAY), THURSDAY(PayType.WEEKDAY), FRIDAY(PayType.WEEKDAY), SATURDAY(
            PayType.WEEKEND), SUNDAY(PayType.WEEKEND);

    private final PayType payType;

    PayrollDay(PayType payType) {
        this.payType = payType;
    }

    double pay(double hoursWorked, double payRate) {
        return payType.pay(hoursWorked, payRate);
    }

    // 策略枚举
    private enum PayType {
        WEEKDAY {
            double overtimePay(double hours, double payRate) {
                return hours &lt;= HOURS_PER_SHIFT ? 0 : (hours - HOURS_PER_SHIFT)
                        * payRate / 2;
            }
        },
        WEEKEND {
            double overtimePay(double hours, double payRate) {
                return hours * payRate / 2;
            }
        };
        private static final int HOURS_PER_SHIFT = 8;

        abstract double overtimePay(double hrs, double payRate);

        double pay(double hoursWorked, double payRate) {
            double basePay = hoursWorked * payRate;
            return basePay + overtimePay(hoursWorked, payRate);
        }
    }
}
</code></pre>
<p><strong>测试</strong></p>
<pre><code>System.out.println("时薪100的人在周五工作8小时的收入：" + PayrollDay.FRIDAY.pay(8.0, 100));
System.out.println("时薪100的人在周六工作8小时的收入：" + PayrollDay.SATURDAY.pay(8.0, 100));
</code></pre>
<h2 id="enumset和enummap">EnumSet和EnumMap</h2>
<p>Java 中提供了两个方便操作enum的工具类——EnumSet 和 EnumMap。</p>
<p><code>EnumSet</code> 是枚举类型的高性能 <code>Set</code> 实现。它要求放入它的枚举常量必须属于同一枚举类型。<br>
<code>EnumMap</code> 是专门为枚举类型量身定做的 <code>Map</code> 实现。虽然使用其它的 Map 实现（如HashMap）也能完成枚举类型实例到值得映射，但是使用 EnumMap 会更加高效：它只能接收同一枚举类型的实例作为键值，并且由于枚举类型实例的数量相对固定并且有限，所以 EnumMap 使用数组来存放与枚举类型对应的值。这使得 EnumMap 的效率非常高。</p>
<pre><code>// EnumSet的使用
System.out.println("EnumSet展示");
EnumSet&lt;ErrorCodeEn&gt; errSet = EnumSet.allOf(ErrorCodeEn.class);
for (ErrorCodeEn e : errSet) {
    System.out.println(e.name() + " : " + e.ordinal());
}

// EnumMap的使用
System.out.println("EnumMap展示");
EnumMap&lt;StateMachine.Signal, String&gt; errMap = new EnumMap(StateMachine.Signal.class);
errMap.put(StateMachine.Signal.RED, "红灯");
errMap.put(StateMachine.Signal.YELLOW, "黄灯");
errMap.put(StateMachine.Signal.GREEN, "绿灯");
for (Iterator&lt;Map.Entry&lt;StateMachine.Signal, String&gt;&gt; iter = errMap.entrySet().iterator(); iter.hasNext();) {
    Map.Entry&lt;StateMachine.Signal, String&gt; entry = iter.next();
    System.out.println(entry.getKey().name() + " : " + entry.getValue());
}
</code></pre>
<p></div></p>
<footer>
    <p>本站部分内容来源于网络，如有侵犯您的权益，请通过以下方式联系我们：</p>
    <p>邮箱：zytlsd@163.com</p>
    <p>我们将在收到通知后第一时间处理。</p>
</footer>]]></description>
    <pubDate>Sun, 19 Oct 2025 21:30:43 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-31.html</guid>
</item></channel>
</rss>