第六章 包、接口和其他类特性
6.1 限定符
Java语言有很多限定符:
- 限定符public、protected和private:用于控制对类、方法和变量的访问
- 限定符static:用于创造类方法和类变量
- 限定符final:用于固定(finalize)类、方法和变量的实现。
- 限定符abstract:用于创建抽象的类和方法。
- 限定符synchronized和volatile:用于线程
在一条语句中使用多个限定符时,它们的顺序无关紧要,只要位于要限定的元素之前即可。不要将方法的返回类型(如void)看作限定符。返回类型必须位于方法名前面,且在返回类型和方法名之间没有限定符。
控制对方法和变量的访问
可见性 | 公有 | 保护 | 默认 | 私有 |
---|---|---|---|---|
同一个类中 | 是 | 是 | 是 | 是 |
同一个包的任何类中 | 是 | 是 | 是 | 否 |
包外的任何类 | 是 | 否 | 否 | 否 |
同一个包中的子类 | 是 | 是 | 是 | 否 |
包外的子类 | 是 | 是 | 否 | 否 |
访问控制和继承
作为通用的规则,覆盖方法时,新方法的访问控制不能比原来的方法更严格,但可以更送。换而言之,在子类中,方法的可见性不能低于它覆盖的方法的可见性。
存取器方法
用存取器方法来提供对私有变量的访问,可以控制该变量将如何被使用。
6.2 静态变量和方法
限定符static用于创建类方法和类变量
6.3 final类、方法和变量
限定符final用于类、方法和变量,指出它们将不会被修改
6.3.1 变量
final变量常常被称为常量(或常量变量),因为它们的值不会改变。对于变量,限定符final通常与static一起使用,这样,该常量将是类变量。
6.3.2 方法
1.将方法声明为final的最常见的原因是,提高类的运行效率。通常,当JVM运行方法时,它首先在当前类中查找该方法,接下来在其超类中查找,并一直沿类层次结构向上查找,直到找到该方法为止。这提供了灵活性,简化了开发工作,但代价是速度较低。
2.如果方法是final的,java编译器便可将其可执行字节码直接放到调用它的程序中,因为该方法不会被子类覆盖而发生变化。
6.3.3 类
在类的声明中使用限定符final可将其指定为不能继承的,
public final class ChatServer{
// body of method
}
在创建子类的声明中,final类不能出现在关键字extends的后面。
- 在final类中,所有方法都将是final的,因此声明它们时,无须使用限定符
6.4 抽象类和方法
- 定义类层次结构的过程中,当您确定通用的行为和属性时,有时可能遇到一些永远不会被实例化的类。这样的类用于定义其子类都有的行为和方法。这些类被称为抽象类,是使用限定符abstract来声明。
- 抽象类可以包含常规类能够包含的任何东西,包括构造函数,因为子类可能需要继承这种方法。抽象类也可以包含抽象方法,这时只有方法特征标,而没有实现的方法。这些方法将在抽象类的子类中被实现。
不能在非抽象类中声明抽象方法。如果一个抽象类除抽象方法外什么都没有,则使用接口更合适
6.5 包
要引用其他包中的类,可以使用全名:包名和类名。
java.awt.Font text = new java.awt.Font();
6.5.1 import 声明
- 导入单个类:
import java.util.ArrayList;
- 也可以导入包中的所有类,方法是使用星号(*)代替类名
import java.awt.*;
- 通过使用import static语句,可以通过更简短的方法引用指定类中的变量。
import static java.lang.Math.*;
6.5.2 类名冲突
在不同包中有多个名称相同的类。在导入含相同名称类的包后,Java编译器无法判断语句中引用的是哪个Date类,因此必须像下面那样指定包名:
java.util.Data = new java.util.Date();
6.6 创建自己的包
6.6.1 选择包名
有一个约定是包名不使用大写字母,以便将其与类名区别开来。
6.6.2 创建文件夹结构
6.6.3 将类加入到包中
6.6.4 包和类访问控制
如果没有指定限定符,则类的访问控制为默认级别,即可被同一个包中的其他任何类使用,但在包外不可见,也不可用。通过包保护的类被隐藏在其所在的包中,不能通过名称来导入或引用它。
6.7 接口
6.7.1 单继承存在的问题
对类层次的简化是受到限制的,尤其需要在同一棵继承树的不同分支上同时使用某些行为时。
- Java接口是一组抽象行为,可以被混合到任何类中,从而给它添加超类不支持的行为。
- Java接口只包含抽象方法定义和常量——既没有实例变量,也没有方法实现。
6.7.2 接口和类
在大多数情况下,在可以使用类的地方,也可以使用接口。接口补充并扩展了类的功能,它们几乎被同等对待,但接口不能被实例化:new只能创造非抽象类的实例。
6.7.3 实现和使用接口
要使用接口,可以在类定义中包含关键字implements:
public class AnimatedSign extends Sign implements Runnable{
//...
}
实现接口时,必须实现该接口中所有的方法,而不能有选择地实现其中的某些。通过实现接口,您在告诉用户,这个类支持整个接口。
6.7.4 实现多个接口
要在类中包含多个接口,只需将它们的名称用逗号分开即可:
public class AnimatedSign extends Sign implements Runnable,Observer{
// ...
}
6.7.5 接口的其他用途
几乎在任何可以使用类的地方,都可以使用接口来代替。
Iterator loop;
当变量的类型被声明为接口时,就只能存储实现了该接口的对象。在这个例子中,可将实现了接口Iterator的任何类的对象存储到变量loop中;由于loop是一个Iterator对象,因此可通过它调用该接口的全部3个方法:hasNext()、next()和remove()。
- 可以将对象强制转换为接口,就像可以将对象强制转换为其他的类那样。
6.8 创建和扩展接口
6.8.1 新接口
要创建新的接口,可以这样声明它:
interface Expandable{
// ...
}
上述声明几乎与类定义相同,只是使用的是关键字interface而不是class。接口定义内是方法和变量。
- 接口内的方法定义是公有和抽象的,可以显式地声明这一点,如果没有包括这些限定符,它们将被自动转换为公有和抽象的。不能在接口内将方法声明为私有或保护的
- 除方法外,接口还可以包含变量,但这些变量必须声明为公有、静态和final的(使之成为常量)。
6.8.2 接口中的方法
可以在任何能够使用类名的地方使用接口名。通过将方法参数定义为接口类型,可以创建通用参数,适用于可能使用该接口的任何类。
6.8.3 拓展接口
与类一样,也可以将接口组织成层次接口。当接口继承另一个接口时,子接口将获得父接口中声明的所有方法定义和常量。
- 要扩展接口,可使用关键字extends,就像扩展类一样:
interface PreciselyTrackable extends Trackable{
// ...
}
- 另外,不同于类层次,接口层次可以多重继承。(一个接口可以继承任意数量的接口)
6.8.4 创建网上商店
package com.java21days.day6;
public class Item implements Comparable {
private String id;
private String name;
private double retail;
private int quantity;
private double price;
Item(String idIn,String nameIn,String retailIn,String qIn){
id=idIn;
name=nameIn;
retail=Double.parseDouble(retailIn);
quantity= Integer.parseInt(qIn);
if(quantity>400)
price=retail*.5D;
else if(quantity>200)
price = retail*.6D;
else
price =retail*.7D;
price =Math.floor(price*100 + .5)/100;
}
@Override
public int compareTo(Object obj) {
Item temp=(Item) obj;
if(this.price<temp.price){
return 1;
}
else if(this.price>temp.price){
return 1;
}
return 0;
}
public String getId(){
return id;
}
public String getName(){
return name;
}
public double getRetail(){
return retail;
}
public int getQuantity(){
return quantity;
}
public double getPrice(){
return price;
}
}
package com.java21days.day6;
import java.util.*;
public class Storefront {
private LinkedList catalog = new LinkedList();
public void addItem(String id, String name, String price, String quant) {
Item it = new Item(id, name, price, quant);
catalog.add(it);
}
public Item getItem(int i) {
return (Item) catalog.get(i);
}
public int getSize() {
return catalog.size();
}
public void sort() {
Collections.sort(catalog);
}
}
package com.java21days.day6;
public class GiftShop {
public static void main(String[] args) {
Storefront store = new Storefront();
store.addItem("CO1", "MUG", "9.99", "150");
store.addItem("CO2", "LG MUG", "12.99", "82");
store.addItem("CO3", "MOUSEPAD", "10.49", "800");
store.addItem("DO1", "T SHIRT", "16.99", "90");
store.sort();
for (int i = 0; i < store.getSize(); i++) {
Item show = (Item) store.getItem(i);
System.out.println("\nItem ID: " + show.getId() + "\nName: " + show.getName() + "\nRetail Price: $" +
show.getRetail() + "\nPrice: $" + show.getPrice() + "\nQuantity: " + show.getQuantity());
}
}
}
输出:
Item ID: CO1
Name: MUG
Retail Price: $9.99
Price: $6.99
Quantity: 150
Item ID: CO2
Name: LG MUG
Retail Price: $12.99
Price: $9.09
Quantity: 82
Item ID: CO3
Name: MOUSEPAD
Retail Price: $10.49
Price: $5.25
Quantity: 800
Item ID: DO1
Name: T SHIRT
Retail Price: $16.99
Price: $11.89
Quantity: 90