小程序中的大道理之三--对称性和耦合问题

再继续扒

继续 前一篇 的话题, 在那里, 提到了抽象, 耦合及 MVC, 现在继续探讨这些, 不过在此之前先说下第一篇里提到的对称性.

注: 以下讨论建立在前面的基础之上, 为控制篇幅起见, 这里将不再重复前面说到的部分, 如果您还没看过前两篇章, 阅读起来可能会有些困难.

这是第一篇的链接小程序中的大道理

对称性(Symmetry)

这里先说下对称性的问题. 问题中的图案是左右对称的, 但到目前为此, 我们的代码却不是对称的, 输出之所以对称原因在于空格与背景之间难以区分.

让我们换个符号, 比如脱字符"^", 再输出一下, 就可以看出不对称来, 因为在右边我们输出星号后就直接换行了:

star and caret not symmetry

只要稍微调整一下程序, 在输出换行之前再输出一下空格(或者现在的脱字符"^"), 就能满足对称的输出了:

private String getLineContent(int lineCount, int lineNumber) {
    StringBuilder content = new StringBuilder();
    
    String firstPart = getFirstPart(lineCount, lineNumber);
    // 1. 空格部分
    content.append(firstPart);
    // 2. 星号部分
    content.append(getSecondPart(lineNumber));
    // 3. 空格部分
    content.append(firstPart);
    // 4. 换行部分
    content.append(System.lineSeparator());
    
    return content.toString();
}

这样之后, 图案是左右对称, 反映在程序上就呈现出上下对称了. 当然也可以把三个语句写在一行里, 这样它也左右对称了:

star symmetry compare to code symmetry

程序是对客观世界的问题的一种映射, 因此程序的结构反映了问题的结构.

当然了, 如果你的程序写得很松散, 结构, 层次不清晰, 就不太容易看出这种对称来.

不知道是否有人意识到了, 在前面我有意无意地忽略了另一个对称问题. 这就是星号的对称, 在上图中只是把它当成对称轴看待.

撇开什么脱字符"^"或者空格, 其实单纯由星号构成的三角形也是对称的, 这是更主要的一个对称:

star symmetry

这种对称又来自哪里呢? 让我们深入到 getSecondPart 里面去:

private String getSecondPart(int lineNumber) {
    int count = getElementCountOfSecondPart(lineNumber);
    StringBuilder part = new StringBuilder();
    for (int i = 0; i < count; i++) {
        part.append(" ");
    }
    return part.toString();
}

很遗憾, 你看不到什么对称. 继续深入到 getElementCountOfSecondPart 去:

public int getElementCountOfSecondPart(int lineNumber) {
    return lineNumber * 2 + 1;
}

好了, 你已经到头了, 可好像还是找不到对称的影子呀? 前面说"程序的结构反映了问题的结构", 难道这个结论并不总是成立的?

有这么一个故事, 我估计很多人都听说过(文字摘自以下网址 http://www.niwota.com/submsg/5682754/):

有一位牧师在某个星期六的早晨正在为明天的讲道稿大伤脑筋. 他的太太外出买东西了, 外面正下着雨, 小儿子失去了户外活动的机会, 在屋里折腾. 牧师的思路一再被儿子打断.

牧师手边正好有本旧杂志, 为了把儿子从身边支开, 他撕下一张彩色世界地图, 再撕成碎片, 丢到客厅地板上对儿子说: "Johnny, 你把它拼成原样, 我就给你一个 Nickle(25 美分镍币). "

儿子有事可做, 又有报酬可得, 积极性很高, 立刻拼了起来. 牧师想, 这下子我可以安静一个上午, 构思自己的讲道稿了. 谁知道只隔了十分钟, 儿子就来敲书房的门了, 说已经拼好. 牧师不相信, 跑到客厅一看, 果然整幅地图完整无缺. 牧师又懊恼又惊奇地问儿子: "你怎么那么快就拼好了呢? "

儿子得意地说, 这再简单不过了, 这张地图的背后印着一幅人物肖像, 我想, 如果这个人拼对了, 世界地图也就拼对了.

牧师忍不住笑了起来, 很高兴地给了儿子一个镍币, 说, 你替我把明天讲道的题目也准备好了: 如果一个人是对的, 那么这个人的世界也是对的.

现在我们的程序能够输出一个对称的图案来, 肯定不是巧合, 而且我也可以肯定地告诉你, 这里面是有对称的. 你可以再仔细找找看, 如果你已经找到或者实在找不出来, 那么可以往下看了:

line num symmetry

现在看来是不是很明显呢? 也许你早也看出来了. 我们能得到有什么启示呢?

运用**直觉(Intuition)**去思考!

如果你的代码中怎么变换也找不出对称来, 当你的人都是错的时候, 你的世界还可能正确吗? 所以你甚至不用费心去上机验证了.

发明了**差分机(difference engine)的计算机先驱查尔斯·巴贝奇(Charles Babbage)**说:

我曾两次被(议员)问到, "巴贝奇先生, 请问假如您往机器里输入了错误的数据, 还会出来正确的答案吗? "我实在无法恰当地理解是怎样的逻辑混乱才会(使他们)提出这样的一个问题.

On two occasions I have been asked [by members of Parliament], ‘Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?’ I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question.

看得出来巴贝奇的评论还是很客气的, 他当时似乎在寻求议员拨款来支持他的差分机研究, 我估计他当时内心其实想说: “你丫脑子进水了, 问这种问题!”

巴贝奇谈的是往正确的机器里输入错误的数据的情况, 在这里, 如果连机器里面(即代码)都已经是错的, 那么即便给出了正确输入, 也不可能有正确的输出了.

在某些特殊情况下, 你也许会碰到"负负得正"的情况, 但这不过是巧合而已.

你可能遇到过这样的 bug:

你加了一个新特性, 系统出错了, 你找到一处 bug, 然后你很奇怪, 为何之前居然没问题? 而在你改正这个 bug 后, 之前的一些功能反而不正常了!你可能打死也想不到系统中还有另一个你没发现的 bug 在默默地抵消了它!

诚然, 这样的"负负得正"情况即便能工作也是非常脆弱的.

当事实与直觉不符时, 那么一定有什么地方出错了.

回到我们的问题, 再单独地拿输出空格部分来看:

public int getElementCountOfFirstPart(int lineCount, int lineNumber) {
    return lineCount - lineNumber - 1;
}

有对称吗? 它有两个不同变量, 单独的一份随便你怎么去变换, 你都找不到对称. 这也是为何你需要二份一左一右围绕在星号旁边才能形成对称.

关于直觉, 如果你还有兴趣, 可以见我之前写的另一篇文章(有些读者可能已经看过), 深入图解字符集与字符集编码–定长与变长, 也有谈到用天平之类的模型来直观地思考问题.

本质, 证明以及直觉

按前面那图:

line num symmetry part

如果有人试图从输出的 1, 3, 5, 7 的展开式的对称性来向你证明表达式"lineNumber*2+1"的对称性, 那么呢? 这不过是本末倒置. 正如前面所说, 你把表达式做个变换就可以看出表达式是对称的, 对称是该表达式的本质属性, 所以输出的1, 3, 5, 7 的对称性恰恰是表达式所决定的.

相信很多人都跟作者类似, 在大学的数学课上, 被那些形形色色的恐怖的证明弄得苦不堪言:

这里是在对""弹琴, 数学大""们请走开或请无视(要鄙视, 俺也认了), 这里不是在说你们!你们不会懂的!

如果你的老师只能通过证明来告诉你一件事情为什么是这样, 通常你还是很难明白它为什么是这样, 你甚至可以怀疑老师是否真的深刻理解了这件事情, 要么他就是不打算告诉你真正的原因:

好好反思一下你是否在什么事情上得罪了他? 你写作业是不是全靠"Ctrl+C, Ctrl+V"?

又或者他想让你自己去领悟:

真是用心良苦!你体会到了没有? 你领悟了吗? 要是没有请继续, 学费是不会退滴, 你别领悟到其它地方去了~

而如果你的老师可以通过直觉告诉你事情为什么是这样, 你也许就会说: "啊哈, 原来如此(Aha moment)!"从此你就记住了事情为什么是这样了, 那些冗长的证明你都可以丢到一边去了.

独立的数据模型(Isolated Data Model)

前面说到 getElementCountOfFirstPartgetElementCountOfSecondPart 两个方法达到了抽象的极致, 从而与具体的表现形式解耦. 在有了对称性之后, 我们甚至可以反转两个表现形式, 依然可以呈现出所谓的"三角形"出来:

star triangle compare to space triangle

对比两种情况, 不变的是三个部分中的那些数字:

star triangle number compare to space triangle number

所以这才是图案的"魂", 或者说是它的"意", 抽象出意, 我们就能"得意而忘形".

前面说过也许有一天, 客户的需求可能扩展到 web service 上, 对于一个较大的行数, 需要传输较大的数据, 但有了这种解耦, 我们可以把一个纯粹的数组传递过去:

[3][1][3]
[2][3][2]
[1][5][1]
[0][7][0]

另一方可以在收到这个数据模型后, 再把图形还原.

因为存在对称性, 第三列甚至也可以不传.

灵活性(Flexibility)与松耦合(Loose Couple)

而这个还原过程则可以很灵活地处理, 下图演示了在本地直接利用上述两方法获取图案模式并用 icon 图片展示的效果:

红薯三角形

代码如下(我对 swing 之类的编程也不是很熟, 随便在网上搜来的代码改了下):

public class PatternPic extends JPanel {

    private static final long serialVersionUID = 1L;
    
    private Image image;
    
    public PatternPic(Image image) {
        this.image = image;
    };
    
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        int width = image.getWidth(this);
        int height = image.getHeight(this);
    
        int lineCount = 4;
        Pattern pattern = new Pattern();
    
        for (int lineNumber = 0, y = 0; lineNumber < lineCount; lineNumber++, y += height) {
            // 直接获取各部分的数目
            int elementCountOfFirst = pattern.getElementCountOfFirstPart(lineCount, lineNumber);
            int elementCountOfSecond = pattern.getElementCountOfSecondPart(lineNumber);
    
            for (int i = 0, x = elementCountOfFirst * width; i < elementCountOfSecond; i++, x += width) {
                g.drawImage(image, x, y, width, height, this);
            }
        }
    }
    
    public static void main(String[] args) throws IOException {
        Image image = ImageIO.read(PatternPic.class.getResource("/logo-git-oschina.png"));
        JFrame frame = new JFrame();
        frame.add(new PatternPic(image));
        frame.setSize(800, 600);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

所以这样的抽象是有很大好处的, 我们说高层的一些抽象如果觉得啰嗦可以去掉一些(如上述就没作过多的抽象), 但这一层的抽象反而要保留. 我们把它作为公共的 API 提供出去, 调用者就不再受限于那些写死的"空格与星号", 这种灵活性与前面说到的可重用性及可扩展性都是紧密相关的.

MVC

MVC 中重要的一点即是强调模型(M, Model)与视图(V, View)的分离. 所谓的模型即是一种独立于视图的数据, 这与我们抽象出来的数据模型是很相似的.

真正的 MVC 与我们这里的例子间或许还有很大差异, 但在解耦合这一点, 两者是一致.

可以对比下最初的玩具式的代码, 那种情况就是一种 紧耦合(tight couple), 而这里则达到了 松耦合(loose couple) 的目的.

在当下, 常常一方面要处理传统的 web 界面, 另一方面又要处理移动端的界面, 抽取出纯粹的数据模型无疑将带给我们很大方便.

在下一篇将谈谈单元测试的问题.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与国已互相成为双方最大的交易伙伴。国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占国外贸总值的15.4%。在过去20余年,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值