Interface(is -a)
- 类似于C语言的.h文件,举个实体的例子,假设狗是interface,那么贵宾犬和哈巴狗就是狗的implents的class,interface有一些类,是狗共有的特性,如public void bark();,在interface里面只有声明方法就够了。如何想在interface里面添加有函数体的方法,需要用default声明。
- 在public class poodle implents dog,相同的方法,如bark,需要@override(虽然不写也许,但是为了防止一些意想不到的错误,如bark写出了bart),
- 对于override和overload区别,override是在子类中存在着与接口相同的方法,而overload是相同的方法,但是参数不一样。
假设同一个类中有两个方法
public static void peek(List61B<String> list) {
System.out.println(list.getLast());
}
public static void peek(SLList<String> list) {
System.out.println(list.getFirst());
}然后你运行这段代码
SLList<String> SP = new SLList<String>();
List61B<String> LP = SP;
SP.addLast("elk");
SP.addLast("are");
SP.addLast("cool");
peek(SP);
peek(LP);
第一次调用 peek() 将使用接受 SLList 的第二个 peek 方法。第二次调用 peek() 将使用第一个 peek 方法,该方法接受 List61B。这是因为两个重载方法之间的唯一区别是参数的类型。当Java检查要调用哪个方法时,它会检查静态类型并使用相同类型的参数调用该方法。
EXTEND
假设我们想要构建一个 RotatingSLList,它具有与 SLList 相同的功能,例如addFirst、size等,但需要执行一个附加rotateRight操作来将最后一项移至列表的前面。
实现此目的的一种方法是复制并粘贴 SLList 中的所有方法并rotateRight在其上写入 - 但这样我们就无法利用继承的力量!请记住,继承允许子类重用已定义的类中的代码。因此,让我们定义 RotatingSLList 类以继承 SLList。
我们可以在类头中使用关键字来设置这种继承关系,extends如下所示:
public class RotatingSLList<Item> extends SLList<Item>
与 AList 与 List61B 共享“is-a”关系一样,RotatingSLList 与 SLList 共享“is-a”关系。该extends关键字使我们能够保留 SLList 的原始功能,同时使我们能够进行修改并添加附加功能
通过使用extends关键字,子类继承父类的所有成员。“成员”包括:
- 所有实例和静态变量
- 所有方法
- 所有嵌套类
注意构造函数不是继承的,私有成员不能被子类直接访问。
public VengefulSLList() {
super();
deletedItems = new SLList<Item>();
}或者不写构造函数,java默认有super();
但是当构造函数有参数的时候,必须用super(x),来call parentClass(x)
Abstract 抽象类
- 所有方法都必须是公共的。
- 所有变量都必须是 public static final。
- 无法实例化
- 所有方法默认都是抽象的,除非指定为
default - 每个类可以实现多个接口
泛型
正如您在视频中看到的,我们可以通过将参数声明为 String 和 Integer 来编写非常有限的方法,如下所示:
public static Integer get(Map61B<String, Integer> map, String key) {
...
}
我们限制此方法仅接受Map61B<String, Integer>,这不是我们想要的!我们希望它采用任何类型的Map61B,无论泛型的实际类型是什么。但是,以下方法头会产生编译错误:
public static V get(Map61B<K, V> map, String key) {
...
}
这是因为,对于在类头中定义的泛型,Java 会等待用户实例化该类的对象,以便了解每个泛型的实际类型。然而,在这里我们想要一个特定于此方法的泛型。此外,我们并不关心我们的参数中实际的类型K和V呈现的类型Map61B——重要的是,无论是什么类型,都会返回V一个类型的对象。V
因此我们看到了对泛型方法的需求。要将方法声明为泛型,必须在返回类型之前指定形式类型参数:
public static <K,V> V get(Map61B<K,V> map, K key) {
if map.containsKey(key) {
return map.get(key);
}
return null;
}
下面是如何调用它的示例:
ArrayMap<Integer, String> isMap = new ArrayMap<Integer, String>();
System.out.println(mapHelper.get(isMap, 5));
您不需要对要插入的类型进行任何显式声明。Java 可以推断 isMap 是ArrayMapfromIntegers到Strings。
如果要进行比较的话:并非所有对象都有compareTo方法,不能直接k.compareTo
public static <K extends Comparable<K>, V> K maxKey(Map61B<K, V> map) {...}ToString
public String toString() {
String returnString = "{";
for (int i = 0; i < size; i += 1) {
returnString += keys[i];
returnString += ", ";
}
returnString += "}";
return returnString;
}
这个解决方案虽然看似简单优雅,但实际上非常幼稚。这是因为当您在 Java 中使用字符串连接时,如下所示:returnString += keys[i];您实际上不仅仅是附加到returnString,您正在创建一个全新的字符串。这是非常低效的,因为创建一个新的字符串对象也需要时间!具体来说,与字符串的长度呈线性关系。
额外问题:假设将一个字符连接到一个字符串需要 1 秒。如果我们有一个大小为 5: 的 ArraySet {1, 2, 3, 4, 5},运行该方法需要多长时间toString()?
答案:我们设置returnString为左括号,这需要一秒钟,因为这涉及{到添加到空字符串""。添加第一个元素将涉及创建一个全新的字符串,添加 } 和 1,这将需要 2 秒。添加第二个元素需要 3 秒,因为我们需要添加{, 1, 2。此过程继续进行,因此对于整个数组集,总时间为1 + 2 + 3 + 4 + 5 + 6 + 7.
为了解决这个问题,Java 有一个特殊的类,称为StringBuilder. 它创建一个可变的字符串对象,因此您可以继续附加到同一个字符串对象,而不是每次都创建一个新的字符串对象。
public String toString() {
StringBuilder returnSB = new StringBuilder("{");
for (int i = 0; i < size - 1; i += 1) {
returnSB.append(items[i].toString());
returnSB.append(", ");
}
returnSB.append(items[size - 1]);
returnSB.append("}");
return returnSB.toString();
}Equal
练习6.4.3:让我们为ArraySet 类编写一个equals 方法。请记住,集合是唯一元素的无序集合。因此,要使两个集合被视为相等,您只需检查它们是否具有相同的元素。
解决方案
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null) {
return false;
}
if (other.getClass() != this.getClass()) {
return false;
}
ArraySet<T> o = (ArraySet<T>) other;
if (o.size() != this.size()) {
return false;
}
for (T item : this) {
if (!o.contains(item)) {
return false;
}
}
return true;
}Exception
被check的Exception是需要处理的,也是易于处理的,举一个例子:
public class Eagle {
public static void gulgate() {
if (today == “Thursday”) {
throw new IOException("hi"); }
}
}$ javac UncheckedExceptionDemo.java
$ java UncheckedExceptionDemo
Exception in thread "main" java.lang.RuntimeException: as a joke.
at UncheckedExceptionDemo.main(UncheckedExceptionDemo.java:3)解决方法:public static void gulgate() {
try {
if (today == “Thursday”) {
throw new IOException("hi");
}
} catch (Exception e) {
System.out.println("psych!");
}
}或者
如果我们不想在gulgate()方法中处理异常,我们可以将责任推迟到其他地方。我们通过修改方法定义来将方法标记或指定为危险方法,如下所示:
public static void gulgate() throws IOException {
... throw new IOException("hi"); ...
}由于gulgate()可能会抛出未捕获的异常,现在main()也可以抛出该异常,并且以下代码将无法编译:
public static void main(String[] args) {
Eagle.gulgate();
}
我们可以通过以下两种方式之一解决这个问题:catch,或者在调用方法中指定。
catch:
public static void main(String[] args) {
try {
gulgate();
} catch(IOException e) {
System.out.println("Averted!");
}
} Specify:
public static void main(String[] args) throws IOException {
gulgate();
}
当您可以处理那里的问题时捕获错误。防止它逃跑!
Packages and JAR files
Using Packages
如果您从同一个包中访问该类,则可以仅使用其简单名称:
Dog d = new Dog(...);
如果您从包外部访问类,请使用其完整规范名称:
ug.joshh.animal.Dog d = new ug.joshh.animal.Dog(...);
为了让事情变得更简单,您可以导入包,并使用简单的名称!
import ug.joshh.animal.Dog;
...
Dog d = new Dog(...);创建包
创建包需要以下两个步骤:
1.) 将包名称放在该包中每个文件的顶部
package ug.joshh.animal;
public class Dog {
private String name;
private String breed;
…
}
2.) 将文件存储在具有适当文件夹名称的文件夹中。该文件夹的名称应与您的包匹配:
ieug.joshh.animal包位于 ug/joshh/animal 文件夹中
Default packages
文件顶部没有显式包名称的任何 Java 类都会自动被视为“默认”包的一部分。但是,在编写实际程序时,您应该避免将文件保留在默认包中(除非它是一个非常小的示例程序)。这是因为无法导入默认包中的代码,并且可能会意外地在默认包下创建同名的类。
例如,如果我要在默认包中创建一个“DogLauncher.java”类,我将无法在默认包之外的任何其他地方访问此 DogLauncher 类。
DogLauncher.launch(); // won’t work
default.DogLauncher.launch(); // doesn’t exist
因此,您的 Java 文件通常应以显式包声明开头。
Access Control
黑色的为不加
Private 只有来自给定类的代码才能访问私有成员。它对于其他一切来说 都是真正私有的,因为子类、包和其他外部类无法访问私有成员。TL;DR:只有类需要这段代码
Package Private 如果没有写入显式修饰符,这是给予 Java 成员的默认访问权限。包私有意味着属于同一包的类可以访问,但不能访问子类!为什么这有用?通常,包由同一(组)人处理和修改。人们扩展最初没有编写的类也很常见。如果人们选择扩展它,正在扩展的类的原始所有者可能不希望某些功能或成员被篡改 - 因此,package-private 允许那些熟悉程序内部工作原理的人访问和修改某些功能或成员。成员,而它阻止子类化的人做同样的事情。 TL;DR:只有位于同一包中的类才能访问
Protected 受保护的成员受到保护,免受“外部”世界的影响,因此同一包和子类中的类可以访问这些成员,但世界的其余部分(例如包外部的类或非子类)不能! TL;DR:子类型可能需要它,但子类型客户端不会
Public 此关键字向所有人开放访问权限!这通常是包的客户端可以依赖使用的,并且一旦部署,公共成员的签名不应更改。这就像对使用此公共代码的人的承诺和合同,他们将始终可以访问它。通常,如果开发人员想要“摆脱”公共的东西,而不是删除它,他们会称之为“已弃用”。


C%5B%25K.png)