第 10 章 面向对象基础

优质
小牛编辑
127浏览
2023-12-01

面向对象是Java最重要的特性。Java是彻底的、纯粹的面向对象语言,在Java中“一切都是对象”。本章将介绍面向对象基础知识。

10.1 面向对象概述

面向对象的编程思想:按照真实世界客观事物的自然规律进行分析,客观世界中存在什么样的实体,构建的软件系统就存在什么样的实体。

例如:在真实世界的学校里,会有学生和老师等实体,学生有学号、姓名、所在班级等属性(数据),学生还有学习、提问、吃饭和走路等操作。学生只是抽象的描述,这个抽象的描述称为“类”。在学校里活动是学生个体,即:张同学、李同学等,这些具体的个体称为“对象”,“对象”也称为“实例”。

在现实世界有类和对象,面向对象软件世界也会有,只不过它们会以某种计算机语言编写的程序代码形式存在,这就是面向对象编程(Object Oriented Programming,OOP)。作为面向对象的计算机语言——Java,具有定义类和创建对象等面向对象能力。

10.2 面向对象三个基本特性

面向对象思想有三个基本特性:封装性、继承性和多态性。

10.2.1 封装性

在现实世界中封装的例子到处都是。例如:一台计算机内部极其复杂,有主板、CPU、硬盘和内存,而一般用户不需要了解它的内部细节,不需要知道主板的型号、CPU主频、硬盘和内存的大小,于是计算机制造商将用机箱把计算机封装起来,对外提供了一些接口,如鼠标、键盘和显示器等,这样当用户使用计算机就变非常方便。

那么,面向对象的封装与真实世界的目的是一样的。封装能够使外部访问者不能随意存取对象的内部数据,隐藏了对象的内部细节,只保留有限的对外接口。外部访问者不用关心对象的内部细节,使得操作对象变得简单。

10.2.2 继承性

在现实世界中继承也是无处不在。例如:轮船与客轮之间的关系,客轮是一种特殊轮船,拥有轮船的全部特征和行为,即数据和操作。在面向对象中轮船是一般类,客轮是特殊类,特殊类拥有一般类的全部数据和操作,称为特殊类继承一般类。在Java语言中一般类称为“父类”,特殊类称为“子类”。

提示 在有些语言如C++支持多继承,多继承就是一个子类可有多个父类,例如,客轮是轮船也是交通工具,客轮的父类是轮船和交通工具。多继承会引起很多冲突问题,因此现在很多面向对象的语言都不支持多继承。Java语言是单继承的,即只能有一个父类,但Java可以实现多个接口,可以防止多继承所引起的冲突问题。

10.2.3 多态性

多态性是指在父类中成员变量和成员方法被子类继承之后,可以具有不同的状态或表现行为。有关多态性详细解释,请参考12.4节,这里不再赘述。

10.3 类

类是Java中的一种重要的引用数据类型,是组成Java程序的基本要素。它封装了一类对象的数据和操作。

10.3.1 类声明

Java语言中一个类的实现包括:类声明和类体。类声明语法格式如下。

[public][abstract|final] class className [extends superclassName] [implements interfaceNameList] {
    //类体
}

其中,class是声明类的关键字,className是自定义的类名;class前面的修饰符public、abstract、final用来声明类,它们可以省略,它们的具体用法后面章节会详细介绍;superclassName为父类名,可以省略,如果省略则该类继承Object类,Object类所有类的根类,所有类都直接或间接继承Object;interfaceNameList是该类实现的接口列表,可以省略,接口列表中的多个接口之间用逗号分隔。

提示 本书语法表示符号约定,在语法说明中,括号([])部分表示可以省略;竖线(|)表示“或关系”,例如abstract|final,说明可以使用abstract或final关键字,两个关键字不能同时出现。

声明动物(Animal)类代码如下:

     // Animal.java
     public class Animal extends Object {

         //类体
     }

上述代码声明了动物(Animal)类,它继承了Object类。继承Object类extends Object代码可以省略。

类体是类的主体,包括数据和操作,即成员变量和成员方法。下面就来展开介绍一下。

10.3.2 成员变量

声明类体中成员变量语法格式如下:

     class className {
         [public | protected | private ] [static] [final] type variableName;    //成员变量
     }

其中type是成员变量数据类型,variableName是成员变量名。type前的关键字都是成员变量修饰符,它们说明如下:

  1. public、protected和private修饰符用于封装成员变量。

  2. static修饰符用于声明静态变量,所以静态变量也称为“类变量”。

  3. final修饰符用于声明变量,该变量不能被修改。

下面看一个声明成员变量示例:

     // Animal.java
     public class Animal extends Object {

         //动物年龄
         int age = 1;
         //动物性别
         public boolean sex = false;
         //动物体重
         private double weight = 0.0;

     }

上述代码中没有展示静态变量声明,有关静态变量稍后会详细介绍。

10.3.3 成员方法

声明类体中成员方法语法格式如下:

     class className {

             [public | protected | private ] [static] [final | abstract] [native] [synchronized]
                     type methodName([paramList]) [throws exceptionList] {
                         //方法体
            }
     }

其中type是方法返回值数据类型,methodName是方法名。type前的关键字都是方法修饰符,它们说明如下:

  1. public、protected和private修饰符用于封装方法。

  2. static修饰符用于声明静态方法,所以静态方法也称为“类方法”。

  3. final | abstract不能同时修饰方法,final修饰的方法不能在子类中被覆盖;abstract用来修饰抽象方法,抽象方法必须在子类中被实现。

  4. native修饰的方法,称为“本地方法”,本地方法调用平台本地代码(如:C或C++编写的代码),不能实现跨平台。

  5. synchronized修饰的方法是同步的,当多线程方式同步方法时,只能串行地执行,保证是线程安全的。

方法声明中还有([paramList])部分,它是方法的参数列表。throws exceptionList是声明抛出异常列表。

下面看一个声明方法示例:

     public class Animal {// extends Object {

         //动物年龄
         int age = 1;
         //动物性别
         public boolean sex = false;
         //动物体重
         private double weight = 0.0;

         public void eat() {                ①
             // 方法体
             return;                        ②
         }

         int run() {                        ③
             // 方法体
             return 10;                     ④
         }

         protected int getMaxNumber(int number1, int number2) {        ⑤
             // 方法体
             if (number1 > number2) {
                 return number1;            ⑥
             }
             return number2;
         }
     }

上述代码第①、③、⑤行声明了三个方法。方法在执行完毕后把结果返还给它的调用者,方法体包含“return 返回结果值;”语句,见代码第④行的“return 10;”,“返回结果值”数据类型与方法的返回值类型要匹配。如果方法返回值类型为void时,方法体包含“return;”语句,见代码第②行,如果“return;”语句是最后一行则可以省略。

提示 通常return语句通常用在一个方法体的最后,否则会产生编译错误,除非用在if-else语句中,见代码第⑥行。

10.4 包

在程序代码中给类起一个名字是非常重要的,但是有时候会出现非常尴尬的事情,名字会发生冲突,例如:项目中自定义了一个日期类,我为它取名为Date,但是会发现Java SE核心库中还有两个Date,它们分别位于java.util包和java.sql包中。

10.4.1 包作用

在Java中为了防止类、接口、枚举和注释等命名冲突引用了包(package)概念,包本质上命名空间(namespace)1。在包中可以定义一组相关的类型(类、接口、枚举和注释),并为它们提供访问保护和命名空间管理。

1命名空间,也称名字空间、名称空间等,它表示着一个标识符(identifier)的可见范围。一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。这样,在一个新的命名空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其他命名空间中。 ——引自于 维基百科 https://zh.wikipedia.org/wiki/命名空间

在前面提到的Date类名称冲突问题,很好解决,将不同Date类放到不同的包中,我们自定义Date,可以放到自己定义的包com.a51work6中,这样就不会与java.util包和java.sql包中Date发生冲突问题了。

10.4.2 包定义

Java中使用package语句定义包,package语句应该放在源文件的第一行,在每个源文件中只能有一个包定义语句,并且package语句适用于所有类型(类、接口、枚举和注释)的文件。定义包语法格式如下:

     package pkg1[.pkg2[.pkg3…]];

pkg1~ pkg3都是组成包名一部分,之间用点(.)连接,它们命名应该是合法的标识符,其次应该遵守Java包命名规范,即全部小写字母。

定义包示例代码如下:

     // Date.java文件
     package com.a51work6;

     public class Date {

     }

com.a51work6是自定义的包名,包名一般是公司域名的倒置。

提示 我们公司的域名是51work6.com,倒置后是com.51work6,其中51work6是非法标识符(不能用数字开头),所以com.51work6包名是非法的,于是将包名改为com.a51work6。

如果在源文件中没有定义包,那么类、接口、枚举和注释类型文件将会被放进一个无名的包中,也称为默认包。

定义好包后,包采用层次结构管理这些类型(类、接口、枚举和注释),如图10-1所示是在Eclipse包资源视图中查看包,可见有默认包和com.a51work6包。如果文件系统中查看这些包,会发现如图10-2所示的层次结构,源文件目录是根目录,也是默认包目录,可见其中有一个HelloWorld.java文件。com是文件夹,a51work6子文件夹,在a51work6中包含:Animal.java和Date.java两个文件。Java编译器把包对应于文件系统的目录管理,不仅是源文件,编译之后的字节码文件也采用文件系统的目录管理的。

{%}

图10-1 Eclipse包资源视图中查看包

{%}

图10-2 文件系统目录与包

10.4.3 包引入

为了能够使用一个包中类型(类、接口、枚举和注释),需要在Java程序中明确引入该包。使用import语句实现引入包,import语句应位于package语句之后,所有类的定义之前,可以有0~n条import语句,其语法格式为:

     import package1[.package2…].(类型名|*);

“包名.类型名”形式只引入具体类型,“包名.*”采用通配符,表示引入这个包下所有的类型。但从编程规范的角度提倡明确引入类型名,即“包名.类型名”形式可以提高程序的可读性。

如果需要在程序代码中使用com.a51work6包中Date类。示例代码如下:

     // HelloWorld.java文件
     import com.a51work6.Date;                  ①

     public class HelloWorld {

         public static void main(String[] args) {

             Date date = new Date();            ②
             System.out.println(date);
         }
     }

上述代码第②行使用了Date类,需要引入Date所在的包,见代码第①行,import是关键字,代码第①行的import语句采用“包名.类型名”形式。

提示 如果在一个源文件中引入两个相同包名+类型名,见如下代码,代码第②行会发生编译错误。为避免这个编译错误,可以在没有引入包的类型名前加上包名,详见如下代码第②行中的java.util.Date。

     // HelloWorld.java文件
     import com.a51work6.Date;
     //import java.util.Date;                                  ①

     public class HelloWorld {

         public static void main(String[] args) {

             Date date = new Date();
             System.out.println(date);
             java.util.Date now = new java.util.Date();        ②
             System.out.println(now);

         }
     }

注意 当前源文件与要使用的类型(类、接口、枚举和注释)在同一个包中,可以不用引入包。

10.4.4 常用包

Java SE提供一些常用包,其中包含了Java开发中常用的基础类。这些包有:java.lang、java.io、java.net、java.util、java.text、java.awt和javax.swing。

  1. java.lang包

    java.lang包含中包含了Java语言的核心类,如Object、Class、String、包装类和Math等,还有包装类Boolean、Character、Integer、Long、Float和Double。使用java.lang包中的类型,不需要显示使用import语句引入,它是由解释器自动引入。

  2. java.io包

    java.io包含中提供多种输入/输出流类,如InputStream、OutputStream、Reader和Writer。还有文件管理相关类和接口,如File和FileDescriptor类以及FileFilter接口。

  3. java.net包

    java.net包含进行网络相关的操作的类,如URL、Socket和ServerSocket等。

  4. java.util包

    java.util包含一些实用工具类和接口,如集合、日期和日历相关类和接口。

  5. java.text包

    java.text包中提供文本处理、日期式化和数字格式化等相关类和接口。

  6. java.awt和javax.swing包

    java.awt和javax.swing包提供了Java图形用户界面开发所需要的各种类和接口。java.awt提供是一些基础类和接口,javax.swing提供了一些高级组件。

10.5 方法重载(Overload)

在第10章介绍字符串时就已经用到过方法重载,这一节详细介绍一下重载。出于使用方便等原因,在设计一个类时将具有相似功能的方法起相同的名字。例如String字符串查找方法indexOf有很多不同版本,如图10-3所示:

{%}

图10-3 indexOf方法重载

这些相同名字的方法之所以能够在一个类中同时存在,是因为它们的方法参数列表,调用时根据参数列表调用相应重载方法。

提示 方法重载中参数列表不同的含义是:参数的个数不同或者是参数类型不同。另外,返回类型不能用来区分方法重载。

方法重载示例MethodOverloading.java代码如下:

// MethodOverloading.java文件
package com.a51work6;

class MethodOverloading {

    void receive(int i) {                            ①
        System.out.println("接收一个int参数");
        System.out.println("i = " + i);
    }

    void receive(int x, int y) {                     ②
        System.out.println("接收两个int参数");
        System.out.printf("x = %d, y = %d r", x, y);
    }

    int receive(double x, double y) {                ③
        System.out.println("接收两个double参数");
        System.out.printf("x = %f, y = %f r", x, y);
        return 0;
    }
}

// HelloWorld.java文件调用MethodOverloading
package com.a51work6;

public class HelloWorld {
    public static void main(String[] args) {

        MethodOverloading mo = new MethodOverloading();

        //调用void receive(int i)
        mo.receive(1);                        ④

        //调用void receive(int x, int y)
        mo.receive(2, 3);                     ⑤

        //调用void receive(double x, double y)
        mo.receive(2.0, 3.3);                 ⑥
    }
}

MethodOverloading类中有三个相同名字的receive方法,在HelloWorld的main方法中调用MethodOverloading的receive方法。运行结果如下:

接收一个int参数
i = 1
接收两个int参数
x = 2, y = 3
接收两个double参数
x = 2.000000, y = 3.300000

调用哪一个receive方法是根据参数列表决定的。如果参数类型不一致,编译器会进行自动类型转换寻找适合版本的方法,如果没有适合方法,则会发生编译错误。假设删除代码第②行的void receive(int x, int y)方法,代码第⑤行的mo.receive(2, 3)语句调用的是void receive(double x, double y)方法,其中int类型参数(2和3)自动会转换为double类型(2.0和3.0)再调用。

10.6 封装性与访问控制

Java面向对象的封装性是通过对成员变量和方法进行访问控制实现的,访问控制分为4个等级:私有、默认、保护和公有,具体规则如表10-1所示。

表 10-1 Java类成员的访问控制

{%}

下面详细解释一下这4种访问级别。

10.6.1 私有级别

私有级别的关键字是private,私有级别的成员变量和方法只能在其所在类的内部自由使用,在其他的类中则不允许直接访问。私有级别限制性最高。私有级别示例代码如下:

// PrivateClass.java文件
package com.a51work6;

public class PrivateClass {            ①

    private int x;                     ②

    public PrivateClass() {            ③
        x = 100;
    }

    private void printX() {            ④
        System.out.println("Value Of x is" + x);
    }

}

// HelloWorld.java文件调用PrivateClass
package com.a51work6;

public class HelloWorld {
    public static void main(String[] args) {

        PrivateClass p;
        p = new PrivateClass();

        //编译错误,PrivateClass中的方法 printX()不可见
        p.printX();                    ⑤
    }
}

上述代码第①行声明PrivateClass类,其中的代码第②行是声明私有实例变量x,代码第③行是声明公有的构造方法,构造方法将在第12章详细介绍。代码第④行声明私有实例方法。

HelloWorld类中代码第⑤行会有编译错误,因为PrivateClass中printX()的方法是私有方法。

10.6.2 默认级别

默认级别没有关键字,也就是没有访问修饰符,默认级别的成员变量和方法,可以在其所在类内部和同一个包的其他类中被直接访问,但在不同包的类中则不允许直接访问。

默认级别示例代码如下:

// DefaultClass.java文件
package com.a51work6;

public class DefaultClass {

    int x;                            ①

    public DefaultClass() {
        x = 100;
    }

    void printX() {                   ②
        System.out.println("Value Of x is" + x);
    }

}

上述代码第①行的x变量前没有访问限制修饰符,代码第②行的方法也是没有访问限制修饰符。它们的访问级别都有默认访问级别。

在相同包(com.a51work6)中调用DefaultClass类代码如下:

// com.a51work6包中HelloWorld.java文件
package com.a51work6;

public class HelloWorld {

    public static void main(String[] args) {

        DefaultClass p;
        p = new DefaultClass();
        p.printX();
    }
}

默认访问级别可以在同一包中访问,上述代码可以编译通过。

在不同的包中调用DefaultClass类代码如下:

// 默认包中HelloWorld.java文件
import com.a51work6.DefaultClass;

public class HelloWorld {

    public static void main(String[] args) {

        DefaultClass p;
        p = new DefaultClass();
        // 编译错误,DefaultClass中的方法 printX()不可见
        p.printX();
    }
}

该HelloWorld.java文件与DefaultClass类不在同一个包中,printX()是默认访问级别,所以p.printX()方法无法编译通过。

10.6.3 公有级别

公有级别的关键字是public,公有级别的成员变量和方法可以在任何场合被直接访问,是最宽松的一种访问控制等级。

公有级别示例代码如下:

// PublicClass.java文件
package com.a51work6;

public class PublicClass {

    public int x;                 ①

    public PublicClass() {
        x = 100;
    }

    public void printX() {        ②
        System.out.println("Value Of x is" + x);
    }

}

上述代码第①行的x变量是公有级别,代码第②行的方法也是公有级别。调用PublicClass类代码如下:

// 默认包中HelloWorld.java文件
import com.a51work6.PublicClass;

public class HelloWorld {

    public static void main(String[] args) {

        PublicClass p;
        p = new PublicClass();
        p.printX();
    }
}

该HelloWorld.java文件与PublicClass类不在同一个包中,可以直接访问公有的printX()方法。

10.6.4 保护级别

保护级别的关键字是protected,保护级别在同一包中完全与默认访问级别一样,但是不同包中子类能够继承父类中的protected变量和方法,这就是所谓的保护级别,“保护”就是保护某个类的子类都能继承该类的变量和方法。

保护级别示例代码如下:

// ProtectedClass.java文件
package com.a51work6;

public class ProtectedClass {

    protected int x;                     ①

    public ProtectedClass() {
        x = 100;
    }

    protected void printX() {            ②
        System.out.println("Value Of x is " + x);
    }

}

上述代码第①行的x变量是保护级别,代码第②行的方法也是保护级别。

在相同包(com.a51work6)中调用ProtectedClass类代码如下:

// 默认包中HelloWorld.java文件
package com.a51work6;

import com.a51work6.ProtectedClass;

public class HelloWorld {

    public static void main(String[] args) {

        ProtectedClass p;
        p = new ProtectedClass();
        // 同一包中可以直接访问ProtectedClass中的方法 printX()
        p.printX();

    }
}

同一包中保护访问级别与默认访问级别一样,可以直接访问ProtectedClass的printX()方法,上述代码可以编译通过。

在不同的包中调用ProtectedClass类代码如下:

// 默认包中HelloWorld.java文件
import com.a51work6.ProtectedClass;

public class HelloWorld {

    public static void main(String[] args) {

        ProtectedClass p;
        p = new ProtectedClass();
        // 编译错误,不同包中不能直接访问保护方法printX()
        p.printX();

    }
}

该HelloWorld.java文件与ProtectedClass类不在同一个包中,不同包中不能直接访问保护方法printX(),所以p.printX()方法无法编译通过。

在不同的包中继承ProtectedClass类代码如下:

// 默认包中SubClass.java文件
import com.a51work6.ProtectedClass;

public class SubClass extends ProtectedClass {

    void display() {
        //printX()方法是从父类继承过来
        printX();                            ①
        //x实例变量是从父类继承过来
        System.out.println(x);               ②
    }
}

不同包中SubClass从ProtectedClass类继承了printX()方法和x实例变量。代码第①行是调用从父类继承下来的方法,代码第②行是调用从父类继承下来的实例变量。

提示 访问成员有两种方式:一种是调用,即通过类或对象调用它的成员,如p.printX()语句;另一种是继承,即子类继承父类的成员变量和方法。

  • 公有访问级别任何情况下两种方式都可以;

  • 默认访问级别在同一包中两种访问方式都可以,不能在包之外访问;

  • 保护访问级别在同一包中与默认访问级别一样,两种访问方式都可以。但是在不同包之外只能继承访问;

  • 私有访问级别只能在本类中通过调用方法访问,不能继承访问。

 

提示 访问类成员时,在能满足使用的前提下,应尽量限制类中成员的可见性,访问级别顺序是:私有级别→默认级别→保护级别→公有级别。

10.7 静态变量和静态方法

有一个Account(银行账户)类,假设它有三个成员变量:amount(账户金额)、interestRate(利率)和owner(账户名)。在这三个成员变量中,amount和owner会因人而异,对于不同的账户这些内容是不同的,而所有账户的interestRate都是相同的。

amount和owner成员变量与账户个体有关,称为“实例变量”,interestRate成员变量与个体无关,或者说是所有账户个体共享的,这种变量称为“静态变量”或“类变量”。

静态变量和静态方法示例代码如下:

// Account.java文件
package com.a51work6;

public class Account {

    // 实例变量账户金额
    double amount = 0.0;                    ①
    // 实例变量账户名
    String owner;                           ②

    // 静态变量利率
    static double interestRate = 0.0668;    ③

    // 静态方法
    public static double interestBy(double amt) {        ④
        //静态方法可以访问静态变量和其他静态方法
        return interestRate * amt;                       ⑤
    }

    // 实例方法
    public String messageWith(double amt) {        ⑥
        //实例方法可以访问实例变量、实例方法、静态变量和静态方法
        double interest = Account.interestBy(amt);       ⑦
        StringBuilder sb = new StringBuilder();
        // 拼接字符串
        sb.append(owner).append("的利息是").append(interest);
        // 返回字符串
        return sb.toString();
    }
}

static修饰的成员变量是静态变量,见代码第③行。staitc修饰的方法是静态方法,见代码第④行。相反,没有static修饰的成员变量是实例变量,见代码第①行和第②行;没有staitc修饰的方法是实例方法,见代码第⑥行。

注意 静态方法可以访问静态变量和其他静态方法,例如访问代码第⑤行中的interestRate静态变量。实例方法可以访问实例变量、其他实例方法、静态变量和静态方法,例如访问代码第⑦行interestBy静态方法。

调用Account代码如下:

// HelloWorld.java文件
package com.a51work6;

public class HelloWorld {

    public static void main(String[] args) {
        // 访问静态变量
        System.out.println(Account.interestRate);            ①
        // 访问静态方法
        System.out.println(Account.interestBy(1000));        ②

        Account myAccount = new Account();
        // 访问实例变量
        myAccount.amount = 1000000;                          ③
        myAccount.owner = "Tony";                            ④
        // 访问实例方法
        System.out.println(myAccount.messageWith(1000));     ⑤

        // 通过实例访问静态变量
        System.out.println(myAccount.interestRate);          ⑥
    }
}

调用静态变量或静态方法时,可以通过类名或实例名调用,代码第①行Account.interestRate通过类名调用静态变量,代码第②行Account.interestBy(1000)是通过类名调用静态方法。代码第⑥行是通过实例调用静态变量。

10.8 静态代码块

前面介绍的静态变量interestRate,可以在声明同时初始化,如下代码所示。

public class Account {

    // 静态变量利率
    static double interestRate = 0.0668;
    ...
}

如果初始化静态变量不是简单常量,需要进行计算才能初始化,可以使用静态(static)代码块,静态代码块在类第一次加载时执行,并只执行一次。示例代码如下:

// Account.java文件
package com.a51work6;

public class Account {

    // 实例变量账户金额
    double amount = 0.0;
    // 实例变量账户名
    String owner;

    // 静态变量利率
    static double interestRate;

    // 静态方法
    public static double interestBy(double amt) {
        // 静态方法可以访问静态变量和其他静态方法
        return interestRate * amt;
    }

    // 静态代码块
    static {                            ①
        System.out.println("静态代码块被调用...");
        // 初始化静态变量
        interestRate = 0.0668;          ②
    }
}

上述代码第①行是静态代码块,在静态代码块中可以初始化静态变量,见代码第②行,也可以调用静态方法。

调用Account代码如下:

// HelloWorld.java文件
package com.a51work6;

public class HelloWorld {

    public static void main(String[] args) {

        Account myAccount = new Account();               ①
        // 访问静态变量
        System.out.println(Account.interestRate);        ②
        // 访问静态方法
        System.out.println(Account.interestBy(1000));

    }
}

Account静态代码块是在第一次加载Account类时调用。上述代码第①行是第一次使用Account类,此时会调用静态代码块。

本章小结

本章主要介绍了面向对象基础知识。首先介绍了面向对象一些基本概念,面向对象三个基本特性。然后介绍了类、包、方法重载和访问控制。最后介绍了静态变量、静态方法和静态代码块。