7.1 定义抽象数据类型

7.1 定义抽象数据类型

在C++语言中,我们使用类定义自己的数据类型。通过定义新的类型来反映待解决问题中的各种概念,可以使我们更容易编写、调试和修改程序。本章是第2章关于类的话题的延续,主要关注数据抽象的重要性。数据抽象能帮助我们将对象的具体实现与对象所能执行的操作分离开来。第13章将讨论如何控制对象拷贝、移动、赋值和销毁等行为,在第14章中我们将学习如何自定义运算符。

类的基本思想

类的基本思想是数据抽象(data abstraction)和封装(encapsulation)。数据抽象是一种依赖于接口(interface)和实现(implementation)分离的编程(以及设计)技术。类的接口包括用户所能执行的操作;类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。

封装实现了类的接口和实现的分离。封装后的类隐藏了它的实现细节,也就是说,类的用户只能使用接口而无法访问实现部分。

类要想实现数据抽象和封装,需要首先定义一个抽象数据类型(abstract data type)。在抽象数据类型中,由类的设计者负责考虑类的实现过程;使用该类的程序员则只需要抽象地思考类型做了什么,而无须了解类型的工作细节。

7.1 定义抽象数据类型

在第1章中使用的Sales_item类是一个抽象数据类型,我们通过它的接口(例如1.5.1节描述的操作)来使用一个Sales_item对象。我们不能访问Sales_item对象的数据成员,事实上,我们甚至根本不知道这个类有哪些数据成员。

与之相反,Sales_data类(参见2.6.1节)不是一个抽象数据类型。它允许类的用户直接访问它的数据成员,并且要求由用户来编写操作。要想把Sales_data变成抽象数据类型,我们需要定义一些操作以供类的用户使用。一旦Sales_data定义了它自己的操作,我们就可以封装(隐藏)它的数据成员了。

7.1.1 设计Sales_data类

我们的最终目的是令Sales_data支持与Sales_item类完全一样的操作集合。Sales_item类有一个名为isbn的成员函数(member function),并且支持+、-、+=、<<和>>运算符。

我们将在第14章学习如何自定义运算符。现在,我们先为这些运算定义普通(命名的)函数形式。由于14.1节将要解释的原因,执行加法和减法的函数不作为Sales_data的成员,相反的,我们将其定义成普通函数;执行复合赋值运算的函数是成员函数。Sales_data类无须专门定义赋值运算,其原因将在7.1.5节介绍。

综上所述,Sales_data的接口应该包含以下操作:

  • 一个isbn成员函数,用于返回对象的ISBN编号
  • 一个combine成员函数,用于将一个Sales_data对象加到另一个对象上
  • 一个名为add的函数,执行两个Sales_data对象的加法
  • 一个read函数,将数据从istream读入到Sales_data对象中
  • 一个print函数,将Sales_data对象的值输出到ostream

关键概念:不同的编程角色

程序员们常把运行其程序的人称作用户(user)。类似的,类的设计者也是为其用户设计并实现一个类的人;显然,类的用户是程序员,而非应用程序的最终使用者。

当我们提及“用户”一词时,不同的语境决定了不同的含义。如果我们说用户代码或者Sales_data类的用户,指的是使用类的程序员;如果我们说书店应用程序的用户,则意指运行该应用程序的书店经理。

使用改进的Sales_data类

在考虑如何实现我们的类之前,首先来看看应该如何使用上面这些接口函数。举个例子,我们使用这些函数编写1.6节书店程序的另外一个版本,其中不再使用Sales_item对象,而是使用Sales_data对象:

// 保存当前求和结果的变量
Sales_data total;
if (read(cin, total)) { // 读入第一笔交易
    Sales_data trans;   // 保存下一条交易数据的变量
    while (read(cin, trans)) { // 读入其余的交易
        if (total.isbn() == trans.isbn()) // 检查ISBN
            total.combine(trans); // 更新变量total当前的值
        else {
            print(cout, total) << endl; // 输出结果
            total = trans; // 处理下一本书
        }
    }
    print(cout, total) << endl; // 输出最后一条交易
} else {
    cerr << "No data?!" << endl; // 没有输入任何信息
}

一开始我们定义了一个Sales_data对象用于保存实时的汇总信息。在if条件内部,调用read函数将第一条交易读入到total中,这里的条件部分与之前我们使用>>运算符的效果是一样的。read函数返回它的流参数,而条件部分负责检查这个返回值,如果read函数失败,程序将直接跳转到else语句并输出一条错误信息。

如果检测到读入了数据,我们定义变量trans用于存放每一条交易。while语句的条件部分同样是检查read函数的返回值,只要输入操作成功,条件就被满足,意味着我们可以处理一条新的交易。

在while循环内部,我们分别调用total和trans的isbn成员以比较它们的ISBN编号。如果total和trans指示的是同一本书,我们调用combine函数将trans的内容添加到total表示的实时汇总结果中去。如果trans指示的是一本新书,我们调用print函数将之前一本书的汇总信息输出出来。因为print返回的是它的流参数的引用,所以我们可以把print的返回值作为<<运算符的左侧运算对象。通过这种方式,我们输出print函数的处理结果,然后转到下一行。接下来,把trans赋给total,从而为接着处理文件中下一本书的记录做好了准备。

处理完所有输入数据后,使用while循环之后的print语句将最后一条交易的信息输出出来。

希望这篇博客能帮助您更好地理解如何定义抽象数据类型以及如何设计一个封装良好的类。如果有任何问题或需要进一步的解释,请随时留言!

7.1.2 定义改进的Sales_data类

在前一节中,我们讨论了如何定义抽象数据类型。这一节,我们将探讨如何改进Sales_data类,使其更加符合数据抽象和封装的原则。

改进后的Sales_data类

改进之后的Sales_data类的数据成员将与2.6.1节中定义的版本保持一致,它们包括:

  • bookNostring类型,表示ISBN编号
  • units_soldunsigned类型,表示某本书的销量
  • revenuedouble类型,表示这本书的总销售收入

此外,我们的类将包含两个成员函数:combineisbn。我们还将添加一个名为avg_price的成员函数,用于返回售出书籍的平均价格。因为avg_price的用途并非通用,所以它应该属于类的实现部分,而非接口部分。

类的声明和定义

定义和声明成员函数的方式与普通函数差不多。成员函数的声明必须在类的内部,而定义则既可以在类的内部也可以在类的外部。作为接口组成部分的非成员函数,例如addreadprint等,它们的定义和声明都在类的外部。

改进后的Sales_data类示例

struct Sales_data {
    // 成员函数的声明
    std::string isbn() const { return bookNo; }
    Sales_data& combine(const Sales_data&);
    double avg_price() const;

    // 数据成员
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

// 非成员接口函数的声明
Sales_data add(const Sales_data&, const Sales_data&);
std::ostream& print(std::ostream&, const Sales_data&);
std::istream& read(std::istream&, Sales_data&);

详细解释

成员函数isbn

isbn函数的参数列表为空,返回值是一个string对象。它直接返回对象的bookNo数据成员。

std::string isbn() const { return bookNo; }
成员函数combine

combine函数用于将一个Sales_data对象的内容合并到调用该函数的对象中。它的参数是一个常量引用,因为不需要修改传入的对象。

Sales_data& combine(const Sales_data& rhs) {
    units_sold += rhs.units_sold;
    revenue += rhs.revenue;
    return *this;
}

在这个函数中,我们将rhs对象的units_soldrevenue累加到调用该函数的对象中,并返回当前对象本身。

成员函数avg_price

avg_price函数用于计算并返回售出书籍的平均价格。如果units_sold为0,则返回0。

double avg_price() const {
    if (units_sold)
        return revenue / units_sold;
    else
        return 0;
}

非成员接口函数

非成员接口函数通常包括addreadprint等,它们用于执行基本的I/O操作。

Sales_data add(const Sales_data& lhs, const Sales_data& rhs) {
    Sales_data sum = lhs;
    sum.combine(rhs);
    return sum;
}

std::ostream& print(std::ostream& os, const Sales_data& item) {
    os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price();
    return os;
}

std::istream& read(std::istream& is, Sales_data& item) {
    double price = 0;
    is >> item.bookNo >> item.units_sold >> price;
    item.revenue = price * item.units_sold;
    return is;
}

这些函数分别实现了两个Sales_data对象的加法操作,输出一个Sales_data对象的内容,以及从输入流中读取Sales_data对象的数据。

引入this指针

成员函数通过一个名为this的隐式参数来访问调用它的对象。当我们调用一个成员函数时,用请求该函数的对象地址初始化this。例如:

total.isbn()

在这里,我们使用点运算符来访问total对象的isbn成员然后调用它。当isbn返回bookNo时,实际上它隐式地返回total.bookNo

引入const成员函数

为了保证成员函数不会修改对象的内容,我们在成员函数后面加上const关键字。例如:

double avg_price() const { ... }

这样,编译器会确保在这个函数内部不会修改对象的任何成员。

总结

通过这一节,我们学习了如何定义一个改进的Sales_data类,如何通过成员函数和非成员函数来实现数据抽象和封装。希望这些内容能帮助您更好地理解C++中类的设计和使用。如果有任何问题或需要进一步的解释,请随时留言!

 

7.1.3 定义类相关的非成员函数

在C++编程中,类的作者常常需要定义一些辅助函数,例如addreadprint。虽然这些函数在概念上属于类的接口部分,但它们实际上并不属于类本身。定义非成员函数的方式与定义其他函数一样,通常将函数的声明和定义分开。本文将详细介绍如何定义这些辅助函数,以及它们在类设计中的重要性。

定义非成员函数

如果一个非成员函数在概念上属于类,但不定义在类中,则它通常应与类的声明在同一个头文件内。在这种方式下,用户使用接口的任何部分都只需要引入一个文件。

定义readprint函数

下面的readprint函数与2.6.2节中的代码作用一样,而且代码本身也非常相似:

// 输入的交易信息包括ISBN、售出总数和售出价格
std::istream& read(std::istream& is, Sales_data& item) {
    double price = 0;
    is >> item.bookNo >> item.units_sold >> price;
    item.revenue = price * item.units_sold;
    return is;
}

std::ostream& print(std::ostream& os, const Sales_data& item) {
    os << item.isbn() << " " << item.units_sold << " "
       << item.revenue << " " << item.avg_price();
    return os;
}
  • read函数从给定的输入流中读取数据到Sales_data对象中。
  • print函数将Sales_data对象的内容打印到输出流中。
关键点
  1. 引用传递I/O流readprint函数分别接受一个输入流和输出流的引用作为其参数。这是因为I/O类属于不能被拷贝的类型,因此只能通过引用来传递它们。此外,因为读取和写入的操作会改变流的内容,所以两个函数接受的都是普通引用,而非对常量的引用。
  2. 格式控制print函数不负责换行。执行输出任务的函数应该尽量减少对格式的控制,这样可以确保由用户代码来决定是否换行。

定义add函数

add函数接受两个Sales_data对象作为其参数,返回值是一个新的Sales_data对象,用于表示前两个对象的和:

Sales_data add(const Sales_data& lhs, const Sales_data& rhs) {
    Sales_data sum = lhs; // 拷贝lhs的数据成员到sum
    sum.combine(rhs); // 将rhs的数据成员加到sum中
    return sum;
}
关键点
  1. 拷贝和合并:在函数体中,我们定义了一个新的Sales_data对象并将其命名为sumsum用于存放两笔交易的和,我们用lhs的副本来初始化sum。接下来我们调用combine函数,将rhsunits_soldrevenue添加到sum中。
  2. 返回值:函数返回sum的副本。通过返回Sales_data对象,我们可以在不修改原始对象的情况下合并数据。

非成员函数的声明与定义

头文件声明

通常,非成员函数的声明应该与类在同一个头文件内:

// Sales_data.h
struct Sales_data {
    std::string isbn() const { return bookNo; }
    Sales_data& combine(const Sales_data&);
    double avg_price() const;

    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

Sales_data add(const Sales_data&, const Sales_data&);
std::ostream& print(std::ostream&, const Sales_data&);
std::istream& read(std::istream&, Sales_data&);

源文件定义

在源文件中定义非成员函数的实现:

// Sales_data.cpp
#include "Sales_data.h"

std::istream& read(std::istream& is, Sales_data& item) {
    double price = 0;
    is >> item.bookNo >> item.units_sold >> price;
    item.revenue = price * item.units_sold;
    return is;
}

std::ostream& print(std::ostream& os, const Sales_data& item) {
    os << item.isbn() << " " << item.units_sold << " "
       << item.revenue << " " << item.avg_price();
    return os;
}

Sales_data add(const Sales_data& lhs, const Sales_data& rhs) {
    Sales_data sum = lhs;
    sum.combine(rhs);
    return sum;
}

结论

通过定义类相关的非成员函数,我们可以扩展类的功能,同时保持类的接口简洁明了。这种设计方式有助于分离接口和实现,使代码更加模块化和易于维护。

希望这些内容能帮助您更好地理解C++中类相关非成员函数的设计和使用。如果有任何问题或需要进一步的解释,请随时留言!

7.1.4 构造函数

每个类都分别定义了它的对象被初始化的方式,类通过一个或几个特殊的成员函数来控制其对象的初始化过程,这些函数叫做构造函数(constructor)。构造函数的任务是初始化类对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数。

在这一节中,我们将介绍定义构造函数的基础知识。构造函数是一个非常复杂的问题,我们还会在后续章节中介绍更多关于构造函数的知识。

构造函数的基本概念

构造函数的名字和类名相同。与其他函数不同的是,构造函数没有返回类型。除此之外,构造函数也有一个(可能为空的)参数列表和一个(可能为空的)函数体。类可以包含多个构造函数,和其他重载函数一样,不同的构造函数之间必须在参数数量或参数类型上有所区别。

合成的默认构造函数

当我们没有显式定义任何构造函数时,编译器会为我们合成一个默认构造函数(synthesized default constructor)。这个合成的默认构造函数将按照如下规则初始化类的数据成员:

  • 如果存在类内的初始值,用它来初始化成员。
  • 否则,默认初始化该成员。

定义Sales_data的构造函数

对于我们的Sales_data类,我们将定义四个不同的构造函数:

  • 一个istream&,从中读取一条交易信息。
  • 一个const string&,表示ISBN编号;一个unsigned,表示售出的图书数量;以及一个double,表示图书的售出价格。
  • 一个const string&,表示ISBN编号,其他成员使用默认值。
  • 一个空参数列表(即默认构造函数)。

以下是改进后的Sales_data类的定义:

struct Sales_data {
    // 新增的构造函数
    Sales_data() = default;
    Sales_data(const std::string &s) : bookNo(s) {}
    Sales_data(const std::string &s, unsigned n, double p) : bookNo(s), units_sold(n), revenue(p * n) {}
    Sales_data(std::istream &);

    // 之前已有的其他成员
    std::string isbn() const { return bookNo; }
    Sales_data& combine(const Sales_data&);
    double avg_price() const;

    // 数据成员
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

=default 的含义

我们从解释默认构造函数的含义开始:

Sales_data() = default;

因为该构造函数不接受任何实参,所以它是一个默认构造函数。我们定义这个构造函数的目的仅仅是因为我们既需要其他形式的构造函数,也需要默认的构造函数。C++11标准允许我们通过在参数列表后面写上=default来要求编译器生成构造函数。

构造函数初始值列表

构造函数初始值列表(constructor initializer list)负责为新创建的对象的一个或几个数据成员赋初值。构造函数初始值是成员名字的一个列表,每个名字后面紧跟括号括起来的(或者在花括号内的)成员初始值。不同成员的初始化通过逗号分隔开来。

例如:

Sales_data(const std::string &s) : bookNo(s) {}
Sales_data(const std::string &s, unsigned n, double p) : bookNo(s), units_sold(n), revenue(p * n) {}

在类的外部定义构造函数

与其他几个构造函数不同,以istream为参数的构造函数需要执行一些实际的操作:

Sales_data::Sales_data(std::istream &is) {
    read(is, *this); // read函数的作用是从is中读取一条交易信息然后存入this对象中
}

构造函数没有返回类型,所以上述定义从我们指定的函数名字开始。和其他成员函数一样,当我们在类的外部定义构造函数时,必须指明该构造函数是哪个类的成员。

总结

构造函数是类的关键组成部分,用于初始化类对象的数据成员。通过定义不同的构造函数,我们可以灵活地控制对象的初始化过程。希望这些内容能帮助您更好地理解C++中构造函数的设计和使用。如果有任何问题或需要进一步的解释,请随时留言!

7.1.5 拷贝、赋值和析构

除了定义类的对象如何初始化之外,类还需要控制拷贝、赋值和销毁对象时的行为。对象在几种情况下会被拷贝,如我们初始化变量以及以值的方式传递或返回一个对象等。当我们使用赋值运算符时会发生对象的赋值操作。当对象不再存在时,例如一个局部对象在创建它的块结束时被销毁,或者vector对象销毁时,存储在其中的对象也会被销毁。

合成的拷贝、赋值和析构操作

如果我们不主动定义这些操作,则编译器将替我们合成它们。一般来说,编译器生成的版本将对对象的每个成员执行拷贝、赋值和销毁操作。例如在前面的书店程序中,当编译器执行如下赋值语句时:

total = trans; // 处理下一本书的信息

它的行为与下面的代码相同:

// Sales_data的默认赋值操作等价于:
total.bookNo = trans.bookNo;
total.units_sold = trans.units_sold;
total.revenue = trans.revenue;

在第13章中,我们将详细讨论如何自定义上述操作。

某些类不能依赖于合成的版本

尽管编译器能替我们合成拷贝、赋值和销毁的操作,但是对于某些类来说,合成的版本无法正常工作。特别是,当类需要分配类对象之外的资源时,合成的版本常常会失效。例如,管理动态内存的类通常不能依赖于上述操作的合成版本。不过,很多需要动态内存的类能(而且应该)使用vector对象或者string对象管理必要的存储空间。使用vector或者string的类能避免分配和释放内存带来的复杂性。

进一步讲,如果类包含vector或者string成员,则其拷贝、赋值和销毁的合成版本能够正常工作。当我们对含有vector成员的对象执行拷贝或者赋值操作时,vector类会拷贝或者赋值成员中的元素。当这样的对象被销毁时,将销毁vector对象,也就是依次销毁vector中的每一个元素。这一点与string是非常类似的。

定义Sales_data的拷贝、赋值和析构函数

对于我们的Sales_data类,我们可以定义拷贝构造函数、赋值运算符和析构函数,确保对象在这些操作中能正确处理其成员。

struct Sales_data {
    // 新增的构造函数
    Sales_data() = default;
    Sales_data(const std::string &s) : bookNo(s) {}
    Sales_data(const std::string &s, unsigned n, double p) : bookNo(s), units_sold(n), revenue(p * n) {}
    Sales_data(std::istream &);

    // 拷贝构造函数
    Sales_data(const Sales_data& other) : bookNo(other.bookNo), units_sold(other.units_sold), revenue(other.revenue) {}

    // 赋值运算符
    Sales_data& operator=(const Sales_data& other) {
        if (this != &other) {
            bookNo = other.bookNo;
            units_sold = other.units_sold;
            revenue = other.revenue;
        }
        return *this;
    }

    // 析构函数
    ~Sales_data() = default;

    // 其他成员函数
    std::string isbn() const { return bookNo; }
    Sales_data& combine(const Sales_data&);
    double avg_price() const;

    // 数据成员
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

拷贝构造函数

拷贝构造函数用于创建对象的副本:

Sales_data(const Sales_data& other) : bookNo(other.bookNo), units_sold(other.units_sold), revenue(other.revenue) {}

赋值运算符

赋值运算符用于将一个对象的值赋给另一个对象:

Sales_data& operator=(const Sales_data& other) {
    if (this != &other) {
        bookNo = other.bookNo;
        units_sold = other.units_sold;
        revenue = other.revenue;
    }
    return *this;
}

析构函数

析构函数用于在对象生命周期结束时释放资源:

~Sales_data() = default;

总结

通过定义拷贝构造函数、赋值运算符和析构函数,我们可以控制对象在拷贝、赋值和销毁时的行为。这些操作的正确实现对于管理资源和确保类的正确性至关重要。希望这些内容能帮助您更好地理解C++中对象生命周期管理的基本原理。如果有任何问题或需要进一步的解释,请随时留言!

 

 

  • 25
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚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 。。。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值