本课程全部来自于康奈尔大学CS 2110课程,主要介绍以下内容:
- 面向对象编程、推理复杂问题
- 测试,关于正确性的推理
- 程序开发
- 算法复杂度,分析算法
- 数据结构,链表,树,hash表,图等
- 编程范式:递归,并行执行
Is CS2110适合你?
不需要的Java知识
- 大概30%的人知道Java,其他人知道Matlab,Python
- 必须:熟悉一些编程语言,比如CS1110(Python基础)和CS1112(Matlab基础)
- 我们假设你不知道Java!
- 如果你知道Java,前3周的课程可能相对你比较容易,但你仍然得学,可能有一些你不知道的知识点。
这里我想说的一点是国外的大学不轻松,老师会做个人工智能的机器检查你的作业,如果抄袭或者你愚弄这个机器,那你将可能失去这个作业分配或者整个课程。
关于强类型和弱类型
Matlab和Python是弱类型,一个变量可以在不同的时间包含一个数字、字符串和一个数组。它是不确定的类型。
但是Java是强类型,一个变量必须在使用前定义,并仅仅包含一个它定义的类型值。
弱类型VS强类型
- 弱类型,一般更短程序。编程者会更加自由,语言本身对值的操作更自由。
- 强类型,编程者会有更多的纪律(disciplined),声明为变量的注释提供了一个位置。在编译时有更多错误捕获。比如分配一个String类型给int变量的语法错误。
基本变量定义
int [] a;//定义a,能包含一个指针指向int类型的数组,如下图。
char b;//char是数字类型
类型投递
Java OO (面向对象)
python和matlab有对象和类的概念。Java的强类型特性改变了OO的实现方式和用途。先把你之前了解的OO放到一边。
- 描述对象,演示它们的创建和使用。
- 展示你的类定义。包含类中的方法定义。
- 讨论关键字null。
- 介绍异常exceptions。
几个概念
Parameter(参数)和argument(属性)
- 方法可能有参数,方法调用可能有属性。
Function(函数)和Proceducer(程序,步骤)
- 有返回值的方法。
- 没有返回值的方法,或者空方法。
第一个类定义
一个包含了函数定义的类定义
关于null
null表示没有名称。
关于异常
Throwable
Error
AssertionError
…
Exception
IOException
RuntimeException
ArithmeticException
NullPointerException
IllegalArgumentException
…
域(Fields)和它的影响(conse-quences),JUnit 测试
Time类
private访问标识符不能在类之外的地方访问private域信息。
软件编程的原则是:
- 让域私有,除非有实际的理由让它变成public。
- 总是编写清楚的,精确的类注释。
/** An instance maintains a time of day */
public class Time {
/** hour of the day, in 0..23. */
private int hr;
/** minute of the hour, in 0..59. */
private int min;
}
一种新方法-构造方法
目的:初始化一个对象的域。
继承
- Object类
- 继承,is-a
- toString()方法,对象名字,覆盖
- this关键字,投影(shadowing)
- 静态组件
类对象
任何一个类都继承Object类。每个对象的名字如下。W@aa11bb24。格式为@<内存地址>。
继承:“is A”
继承应该反映语义数据模型,意味着与真实事件。A继承B当且仅当A"is a"B。
toString方法
对象中的toString()方法返回对象的名字W@aa11bb24。
this一个对象自己的名字
- this关键字,this为对象自己的名字。
- 让一个对象可以访问自己的名字。
Static组件
用于静态字段(也称为类变量),维护有关已创建对象的信息。
可以用静态域实现单例。
构造函数
-
范围,局部变量,由内向外的规则
-
重载,向上转型。
-
构造函数,this,default,super等关键字。
-
定义在方法头括号内的变量是局部变量。局部变量的范围在声明到其对应的块内,就是它之外第一个“}”之内。
哪个toString()方法将会被调用:向上转型
从子类直到Object对象的toString()方法搜索直到找到一个匹配的toString()方法。
如果有多个析构函数
如果有多个析构函数,应该像下面这么写。
public class Person {
private String firstName; //not null
private String middleName;
private String lastName;
public Person(String f, String l) {
assert f != null;
firstName= f;
lastName= l;
}
public Person(String f, String m, String l) {
this(f, l);
middleName= m;
}
}
使用this(而不是Person)调用class。this必须成为构造函数体中的第一个语句!
父类构造器
同this一样,super必须是构造函数的第一个语句。如果构造函数中第一个语句没有super,则java编译器会给你插入一个super();方法。
在子类对象中,super是指包含super的内存分区之上的分区。
转换规则(Casting)
- 转换,对象转换规则
- 编译时引用规则
- 数组的快速查看
- 实现equals,getClass方法
转换对象
- 基本类型的转换
(int)(5.0/7.5)
(double)6
double d = 5;//隐式转换
- 引用类型的转换
一个类转换并不会改变对象,仅仅改变你查看对象的视角。
Animal pet1 = new Cat(5);//隐式转换
Cat pet2 = (Cat)pet1;
- 强制转换:一元前缀运算符
对象转换规则:在运行时,可以将一个对象强制转换为它内部发生的任何分区的名称,而不是其他分区的名称。a0能被转为Object,Animal,Cat。如果转化为其他,则会抛出转换错误ClassCastException。
4. 隐式向上转型
5. 编译时引用规则
6. instanceof
obj instanceof C 如果为true,则说明obj对象是C对象的子类。
Interfaces and abstract classes
抽象类
我们以Shape及其子类Triangle和Circle为例。如果在Shape类中写一个sumAreas类,下面的代码是不能编译的。怎么解决呢?
- 使调用向下转换。
- 在Shape中实现area方法。
/** Return sum of areas of shapes in s */
public static double sumAreas(Shape[] s) {
double sum= 0;
for (int k= 0; k < s.length; k= k+1)
sum= sum + s[k].area();
return sum;
}
假设是在Shape类中定义area方法。如果像下面这种方式,就会出现子类不一定复写了area()方法。
public double area() {
return 0;
}
如果在Shape类中抛出异常呢?如下所示。
- Shape的子类必须记得复写,不易追踪问题,很容易导致bug。
- 制造了很多运行时错误,如编译时语法错误更容易捕获和修改。
public double area() {
throw new RuntimeException(
"area not overridden");
}
如果让area方法变成抽象方法呢?在抽象类Shape中,一个抽象方法area强制要求子类必须实现。如果一个方法有abstract关键字,在方法名后使用分号替代方法体。
public abstract class Shape {
…
/** Return the area of this shape */
public abstract double area() ;
}
抽象方法总结
- 如果类C不允许常见实例,则让类C变成抽象类。
//语法:如果C是抽象类并且程序包含了一个新的表达式new C(),则程序是不能编译的。
public abstract C {}
- 在抽象类中,如果需要子类复写方法m(),则让子类变成抽象子类。
//语法:如果子类没有复写父类的抽象方法,则不能编译。
public abstract int m() ;
抽象类常常用于定义一个抽象数据类型(ADT)
abstract data type(ADT),如定义整数的Stack类,它有以下操作。
isEmpty() --return true iff the stack is empty
push(k) --push integer k onto the Stack
pop() --pop the top stack element
所以,它的类结构如下。
public abstract class Stack {
public abstract boolean isEmpty();
public abstract void push(int k);
public abstract int pop();
}
下面是Stack的子类ArrayStack。
public class ArrayStack extends Stack {
private int n; // stack elements are in
private int[] b; // b[0..n-1]. b[0] is bottom
/** Constructor: An empty stack of max size s. */
public ArrayStack(int s) {b= new int[s];}
public boolean isEmpty() {return n == 0;}
public void push(int v) { b[n]= v; n= n+1;}
public int pop() {n= n-1; return b[n]; }
}
下面是子类LinkedListStack类。
public class LinkedListStack extends Stack{
private int n; // number of elements in stack
private Node first; // top node on stack
/** Constructor: An empty stack */
public LinkedListStack() {}
public boolean isEmpty() { … }
public void push(int v) { … }
public int pop() { … }
}
这样就会有很好的扩展性。如果想要用linkedList替代Array,仅仅只需改变图中的表达式即可。
接口
接口与抽象方法类似,所有的方法都是抽象方法,仅仅有不同的语法格式。
抽象类和接口
instanceof和getClass
参考:https://www.cs.cornell.edu/courses/cs2110/2019su/
https://www.cs.cornell.edu/courses/cs2110/2019sp/lecturenotes.html
https://github.com/cs2110-staff