美文网首页
Java包、接口和其他类特性, since 2021-11-23

Java包、接口和其他类特性, since 2021-11-23

作者: Mc杰夫 | 来源:发表于2021-11-22 22:26 被阅读0次

(2021.11.23 Tues)

限定符

  • public, protected, private: 也叫access modifier,用于控制对类、方法和变量的访问
  • static: 用于创建类方法和类变量
  • final: 用于固定(finalise)类、方法和变量的实现,i.e., 常量之类
  • abstract: 用于创建抽象类和方法(抽象基类)
  • synchronizedvolatile: 用于线程

限定符用于语句的最前面,如

public class RedButton extends javax.swing.Jbutton {
    // ...
}
private boolean offline;
static final double WEEKS = 9.5;
public static void main(String[] arguments) {
    // ...
}

控制对方法和变量的访问

public, protected, private这些限定符决定了类中的哪些方法和变量对其他类是可见的。

通过访问控制,可以控制其他类如何使用您的类。类中的有些方法和变量只能在该类中使用。应对与该类进行交互的其他类隐藏他们。这被称为封装:对象控制外部世界对它的了解程度以及如何与它进行交互。

封装防止类中的变量被其他类读取或修改。只能通过调用该类的方法类使用这些变量。

Java提供四种级别的访问控制:公有、私有、受保护和默认(不适用访问控制限定符)。

1. 默认
声明变量和方法时不使用任何限定符

String version = "0.7a";
boolean processOrder() {
    // ...
    return true;
}

变量:同一个包中的其他任何类都可读取或修改它。
方法:同一个包中的其他任何类都可以调用它。
除此之外,其他任何类都不能以任何方式访问这些元素。

2. 私有
private限定符:将方法或变量完全隐藏起来,不被其他任何类使用。这种变量或方法只在其所在的类中是可见的,被所属类中的方法使用或调用。任何私有变量或方法不能被子类继承。

发生下面两种情况,私有方法有用:

  • 其他类没有理由使用该变量
  • 其他类以不合适的方式修改该变量将带来严重后果
class Logger {
    private String format;

    // 通过一个getFormat存储器方法可使外部访问私有变量
    public String getFormat() {
        return this.format;
    }

    public void setFormat(String fmt) {
        if ((fmt.equals('common')) || (fmt.equals('combined')) ) {
            this.format = fmt;
        }
    }
}

3. 公有
(2021.11.23 Tues)
类中的方法或变量可供任何类使用,可声明其为公有public。如果变量为公有,其他类可使用它。调用方法为

class_name.var_name

if (yard < 0 ) {
    System.out.println('Touchdown!');
    score = score + Football.TOUCHDOWN; // Football类中的公有变量TOUCHDOWN
}

应用程序的main()方法必须是公有的,否则Java虚拟机(JVM)将不能调用它,以运行该程序。

由于类的继承性,所有的公有方法和变量都将被子类继承。
4. 保护
有的方法和变量仅供下面两种情况访问:

  • 子类
  • 同一个包中的其他类
    这两种情况下可使用protected限定符,如下
protected boolean outOfData = true;

对比保护访问控制和默认访问控制:被保护的变量可被子类使用,即使子类和超类位于不同的包中。

各类访问控制对比

可见性 public protected default private
同一个类中 Y Y Y Y
同一个包中的任何类中 Y Y Y N
包外的任何类 Y N N N
同一包中的子类 Y Y Y N
包外的子类 Y Y N N

访问控制的继承
一个通用的规则,覆盖方法时,新方法的访问控制不能比原来的方法更严格,但可以更松。或者说,子类中,方法的可见性不能低于它覆盖的方法的可见性。一些继承的规则:

  • 超类中被声明为公有的方法,在子类中必须也是公有的
  • 超类中被声明为保护的方法,在子类中可以是公有或者保护,但不能为私有
  • 对于没有访问控制的方法,即默认方法,子类中其访问控制可以更严格
  • 超类中被声明为私有的方法不能被继承,因此上述规则不适用

存储器方法
对于声明为私有的变量,外部无法访问。可让其他类通过存储器方法来访问该私有变量。即在类中定义一个方法,该方法的内容仅仅是返回这个私有变量。一般读取方法的名称以get开头,设置方法的名称以set开头。如果访问的变量是boolean变量,存储器方法以is开头。如

private int zipCode;
public int getZipCode() {
    return zipCode;
}
private boolean empty;
public boolean isEmpty() {
    return empty;
}

静态变量和方法

限定符static用于创建类方法和类变量。
访问类变量和类方法,可使用类名和变量(方法)名,并用句点将他们连接,如Colour.blackCircle.pi,也可以使用类的对象名,但对于类变量和类方法而言,使用类名更好,这样变量和方法的类型将更清晰。对于实例变量和实例方法,根本不能通过类名来引用。

下面这个例子,在构建函数中有自加操作,使得每次创建类时类变量都加1,创建n个实例,则最后一次创建之后类变量值为n。

public class InstanceCounter {
    private static int numInstances = 0;

    protected static int getCount() {
        return numInstances;
    }

    private static void addInstance() {
        numInstances++;
    }
    
    InstanceCounter() {
        InstanceCounter.addInstance();
    }

    public static void main(String[] arguments) {
        System.out.println("starting with " + InstanceCounter.getCount() + " objects");
        for (int i = 0; i < 500; ++i) {
            new InstanceCounter();
        }
        System.out.println('Created " + InstanceCounter.getCount() + " objects");
    }
}

final类、方法和变量

(2021.11.24 Wed)
变量
关键字final标注的变量被称为常量,它们的值不会被修改。变量的finalstatic限定符往往一起使用,这样变量就可以被声明为类变量。对于保持不变的值,没有理由让每个对象都存储这个值的一个拷贝,应将其声明为类变量。

publish static final int TOUCHDOWN = 6;
static final String TITLE = 'caption';

方法
被标记为final的方法不能被子类覆盖。

public final void getSignature() {
    //
}

将方法声明为final最常见的原因是提高类的运行效率。通常,当JVM运行方法时,首先在当前类中查找该方法,接下来在其超类中查找,并一直沿着层次结构向上查找,直到找到该方法。提供了灵活性,简化了开发工作,但代价是速度降低(?)。如果方法是声明为final时,编译器便可将其可执行字节码直接放到调用它的程序中,因为该方法不能被子类覆盖而发生变化。

注意,私有方法是final的,无须显式的声明,因为不可能在子类中覆盖他们。


类限定符声明为final的,该类将不能被继承。

public final class ChatServer {
//
}

创建final类时,final不能出现在关键字extends后面。与final方法一样,以降低灵活性为代价,提高了语言的速度。

final类中,所有方法都是final的,无须声明,直接使用。

抽象类和方法

类的层次越高,抽象程度越高。位于类层次顶部的类只能定义所有类都有的行为和属性。

定义类层次的过程中,当您确定通用的行为和属性时,有时可能遇到一些永远都不会被实例化的类,这样的类用于定义其子类都有的行为和方法。这些类被称为抽象类,使用限定符abstract来声明。

public abstract class xxx {
    //
}

包是组织类的方式。包中可以包含任意多的类,这些类可能有关或者有继承关系。

程序小,使用的类少,就不需要用包。随着程序量增大,组织成包的好处就凸显。

包的出现应对了下面几种情况

  • 包能将类组织成单元。在硬盘中,您通过文件夹来组织文件和应用程序,而包让用户能够将类编组,让每个程序只是用所需的类
  • 包减少名称冲突带来的问题,随着Java类数量增长,类重名的可能性增加。当同一个程序中使用多组类时,可能发生名称冲突,导致错误。包能够引用所需的类,即使这个类与另一个包中的类同名
  • 包可以大面积的保护类、变量和方法,而不是分别对每个类进行保护
  • 包可以用于唯一的标识类

当使用import指令或者通过全名来引用类时比如java.util.StringTokenizer,都使用了包。

三种机制使用包中的类

  1. 仅类名:如果要使用的类位于包java.lang中,比如SystemDate,可以通过类名来引用,java.lang包中的类将自动导入到所有的程序中,无须事先使用import声明。
  2. 全名:如果要使用的类位于其他包中,可以通过全名(包括包名)来引用,如java.awt.Font
java.awt.Font text = new java.awt.Font();
  1. 导入:对于频繁使用的类,可以导入单个类或整个包。类或包被导入后,只需通过名称便可引用响应的类。

对于只使用一两次的类,使用全名可能更合适。如果需要使用某个类多次,应将其导入减少输入量,可使用import关键字导入。

import声明

(2021.11.25 Thur)
import声明,可用于导入

  • 单个类
import java.util.ArrayList;
  • 包中的所有类,用星号(*)代替类名
import java.awt.*;
  • 导入类中的常量,常与static联合使用。import static后面跟一个接口(类)的名称和星号。
import static java.lang.Math.*;

public class ShortConstants {
    public static void main(String[] arguments) {
        System.out.println('PI: ' + PI);
        System.out.println("" + (PI * 3));
    }
}

除了这种引用,还可以直接用<类名.变量名>的格式,如Math.PIColor.black等。

创建自己的包

命名

约定1:Oracle推荐用户使用受自己控制的Internet域名来给包命名,并将域名的各部分颠倒过来。如个人域名是xxxx.org,则所有包应以org.xxxx开头,如org.xxxx.newsorg.xxxx.fin等。这个规则可以确保其他Java人员不会提供同名的包,如果他们也这么做。

约定2:包名不使用大写字母,以便将其与类名区别开来。如内置类String的全名是java.lang.String,从中容易区分包名和类名。

创建文件夹结构

如包org.xxxx.news,先创建文件夹org,在其中再创建文件夹xxxx,在其中创建文件夹news。之后便可以在news文件夹中创建所需要的类。

将类加入到包中

创建包的最后一个步骤是将类加入到包中。在类文件的import语句和类声明之前添加一条package语句

package org.xxxx.news;

package声明必须在源代码文件的第一行,注释和空行除外。

包和类访问控制

前面提到的用于方法和变量的访问控制限定符,也可以控制对类的访问。

如没有指定限定符,则类的访问控制为默认级别,即可被同一个包中的其他任何类使用,但在包外不可见,也不可用。

通过包保护的类被隐藏其所在的包中,不能通过名称来导入或引用。

要让类在包外可见可导入,可在其定义中加入public,使之称为公有。

注意,在import语句中使用星号(*)时,导入的只有包中的公有类,私有类仍被隐藏,只能被包中的其他类使用。

接口

接口和抽象类和抽象方法一样,提供了其他类要实现的行为模板。他们对Java的单继承面向对象编程方法提供了有益的补充。

单继承存在的问题

多重继承,使得类可以继承多个超类,从而获得全部超类的行为和属性。使用多重继承后,方法调用以及如何组织类层次的问题将复杂的多,让人迷惑,带来了多义性。而Java没有多重继承,采用的是更简单的单继承。

Java接口是一种抽象行为,可以被混合到任何类中,从而给它添加超类不支持的行为。接口只包含抽象方法定义和常量,既没有实例变量,也没有方法实现。

在Java类库中,期望很多完全不同的类都实现某种行为时,都将实现和使用接口。后面将使用一个接口,java.lang.Comparable

接口和类

(*注:接口需要重新学习, 2021.11.27 Sat)
接口与类概念不同,但有很多相同的地方。接口也是在源文件中声明的,并被编译为.class文件;多数情况下,可以使用类的地方,也可以使用接口。

这里的示例中,计划都可以将类名替换为接口名。我们常说的Java类,常常指的是“类或接口”。但是接口不能被实例化:new只能创建非抽象类的实例

实现和使用接口

在类中使用接口,定义关键字implements

public class AnimatedSign extends Sign
    implements Runnable {
    //
}

接口Runnable扩展了Sign的子类AnimatedSign的行为。

实现多个接口

用逗号分开

public class AnimatedSign extends Sign 
    implements Runnable, Observer {
        //
}

如果两个接口定义了相同的方法,可以采用下面三种方式来解决这个问题

  • 如果两个方法的特征标相同,可以在类中实现一个方法。其定义能够满足两个接口
  • 如果方法的参数列表不同,是一种简单的方法重载:实现两种方法特征标,分别满足各自的接口定义
  • 如果方法的参数列表相同,但返回值不同,则无法创建一个能够满足两个接口的方法。

接口的其他用途

可以使用类的任何地方,都可使用接口代替。可以将变量的类型声明为接口

Iterator loop;

当变量的类型被声明为接口时,只能存储实现了该接口的对象。

创建和扩展接口

新接口

创建新接口,这样声明

interface Expandable {
    //
}

声明和类定义相同, 只是使用的关键是是interface而非class,接口定义内是方法和变量。

接口内的方法定义是(默认)公有和抽象的,可以显式的声明这一点,如果没有包括这些限定符,将自动转换为公有和抽象。不能再接口内将方法声明为私有或保护的。

下面的Expandable接口有两个方法,其中的一个expand()被显式的声明为公有和抽象的,另一个contract()被隐式的声明为公有和抽象的。

public interface Expandable {
    public abstract void expand (); // explicitly public and abstract
    void contract(); // effectively public and abstract
}  

与类中的抽象方法一样,接口中的方法也没有方法体。接口只包含方法的特征标,不涉及任何实现。

接口中的方法

(2021.11.27 Sat)
接口中的方法默认是公有和抽象。考虑到可以在任何能够使用雷鸣的地方使用接口名,通过将方法参数定义为接口类型,可以创建通用参数,适用于可能使用接口的任何类。

来看接口Trackable,定义了方法track()quitTracking(),都不带任何参数。另一个方法beginTracking(),接收一个参数,即Trackable对象。在接口中将参数声明为Trackable

public interface Trackable {
    public abstract Trackable beginTracking(Trackable self);
}

在类中实现该方法时,接受通用的Trackable参数,并将它强制转换为相应的对象

public class Monitor implements Trackable {

    public Trackable beginTracking(Trackable self) {
        Monitor mon = (Monitor) self;
        //
         return mon;
    }
} 

扩展接口

和类相同,接口可以继承另一个接口,子接口将获得父接口中生命的所有方法定义和常量。扩展的关键字为extend,像扩展类一样

interface PreciselyTrackable extends Trackable {
    //
}

与类不同,接口层次中没有像Object类那样的“根”接口。接口可独立存在,也可继承其他接口。

不同于类层次,接口层次可以多重继承。在定义的extends部分,任意数量的接口用逗号隔开,新接口将包含其所有父类接口中的方法和常量。
接口管理中,管理方法名冲突的规则与使用多个接口的类相同。多个只有返回值不同的方法将导致编译错误。

接口与抽象类的对比

placeholder

Reference

1 R. Cadenhead著,袁国忠译,21天学通Java(第7版),中国工信出版集团,人民邮电出版社

相关文章

网友评论

      本文标题:Java包、接口和其他类特性, since 2021-11-23

      本文链接:https://www.haomeiwen.com/subject/onaitrtx.html