以下为个人学习笔记和习题整理
课程:面向对象程序设计 ——Java 语言
- 浙江大学 - 翁恺 @ 中国大学 MOOC
https://www.icourse163.org/course/ZJU-1001542001
# 课堂笔记
# 对象交互
当一个对象里有多个对象的时候,那些对象之间是如何交互的,对象和对象之间的联系是如何建立的,对象如何和其他对象交流。
对象和对象之间的联系紧密程度叫做耦合。
对象和对象的耦合程度越紧,表现在源代码上,就是它们的代码是互相依赖、互相牵制的。
我们理想的模型,是对象和对象之间的耦合要尽可能的松,平行的对象要尽量减少直接联系,让更高层次的对象来提供通信服务。
# 访问属性
# 封装
封装,就是把数据和对这些数据的操作放在一起,并且用这些操作把数据掩盖起来,是面向对象的基本概念之一,也是最核心的概念。
在设计类时做到封装的方法:
- 所有的成员变量必须是
private
的,这样就避免别人任意使用你的内部数据; - 所有
public
的函数,只是用来实现这个类的对象,或类自己要提供的服务的,而不是用来直接访问数据的。除非对数据的访问就是这个类及对象的服务。
简单地说,给每个成员变量提供一对用于读写的get
/set
函数也是不合适的设计。
# 访问权限
private
:只有这个类内部可以访问,类内部指类的成员函数和定义初始化,这个限制是对类的而不是对对象的。
即同一个类的不同对象之间,可以互相访问各自的私有成员。
friendly
:如果一个成员函数前没有 public
或 private
等关键字来限定访问权限,那么称为 friendly
,则与其位于同一个包的其他类可以访问。
类前加 public
指任何人都可以使用它来定义变量,但是类名需与编译单元文件名相同。
编译单元:一个 .java
源代码文件是一个编译单元,当一个编译单元不止一个类时,只有一个类前可以加 public
,其他类都只在该包内起作用。
# package
包
当你的程序越来越大的时候,你就会需要有一个机制帮助你管理一个工程中众多的类了。
包就是 Java 的类库管理机制,它借助文件系统的目录来管理类库。
一个包就是一个目录,一个包内的所有的类必须放在一个目录下,那个目录的名字必须是包的名字。
例如本项目
clock
文件夹目录下有src/clock
和src/display
两个文件夹,即clock
和display
两个包。
如果要引用别的包里的类,则需要使用import 包.类;
,或者使用包.类
的全名。
包的名字可以带有.
,如果包名命名为display.led
,则会在src/display/led
目录下。
# static
类变量
类是描述,对象是实体。
在类里所描述的成员变量,是位于这个类的每一个对象中的。
而如果某个成员有 static
关键字做修饰,它就不再属于每一个对象,而是属于整个类。
通过每个对象都可以访问到这些类变量和类函数,但是也可以通过类的名字来访问它们。
类函数由于不属于任何对象,因此也没有办法建立与调用它们的对象的关系,就不能访问任何非 static
的成员变量和成员函数了。
# static
类函数
main
函数前有 static
,即表示其属于类,而不是某个对象。static
函数可以直接访问别的 static
函数,只能访问 static
函数或成员变量。
# 课内项目
# 数字钟
Error: Failed to launch the browser process! spawn /Applications/Google Chrome.app/Contents/MacOS/Google Chrome ENOENT TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md
package display.led; | |
public class Display {// 当前时间,时间递增,时间上限 | |
// 私有成员变量 | |
private int value = 0;// 当前时间(小时还是分钟还是秒) | |
private int limit = 0;// 上限 | |
private static int step = 1; | |
// 构造函数 | |
public Display(int limit) { | |
this.limit = limit; | |
} | |
// 时间递增 | |
public void increase() { | |
value++; | |
// 如果下一个时间段到了上限 | |
if(value == limit) { | |
value = 0; | |
} | |
} | |
public int getValue() { | |
return value; | |
} | |
public static void f() { | |
//value++;// 报错 | |
} | |
public static void main(String[] args) { | |
/*Display d = new Display(24); | |
for (;;) { | |
d.increase(); | |
Display d = new Display(24); | |
}*/ | |
Display d1 = new Display(10); | |
d1.step = 2; | |
System.out.println(d1.step); // 2 | |
Display d2 = new Display(20); | |
System.out.println(d2.step); // 2 | |
d1.increase(); | |
f(); // 可以直接访问 | |
d1.f();// 也可以通过对象访问 | |
//increase (); 报错 | |
System.out.println(d1.getValue()); // 1 | |
System.out.println(d2.getValue()); // 0 | |
System.out.println(d1.step); // 1 | |
System.out.println(d2.step); // 1 | |
d1.step = 2; | |
System.out.println(d1.step); // 2 | |
System.out.println(d2.step); // 2 | |
Display.step = 3; | |
// Display.value = 3; // 错误 | |
System.out.println(d1.step); // 3 | |
System.out.println(d2.step); // 3 | |
} | |
} |
package clock; | |
import display.led.Display; | |
public class Clock { | |
private Display hour = new Display(24); | |
private Display minute = new Display(60); | |
public void start() { | |
minute.increase(); | |
if(minute.getValue() == 0) { | |
hour.increase(); | |
} | |
// 带格式输出 | |
// %02d 表示占据两个字符位置的整数,如果只有个位数,用 0 填充 | |
System.out.printf("%02d:%02d\n", hour.getValue(), minute.getValue()); | |
} | |
public static void main(String[] args) { | |
Clock clock = new Clock(); | |
clock.start(); | |
} | |
} |
# 课堂讨论
increase
返回boolean
来表示翻转好不好?
如果Display
的increase
函数,在发现翻转之后,就返回true
表示这次increase
翻转了,是不是一个好的设计?
不是一个好的设计。
首先,违反了函数 “单一出口原则”;
其次,造成了函数的多义。
如果想减少如0
这样的 MagicNumber 出现,可以在类中添加公共变量,赋予0
意义。
public class display { | |
private final int limit; | |
private int value = 0; | |
public boolean isCarry = false; | |
display(int limit){ | |
this.limit = limit; | |
} | |
public void increase(){ | |
value++; | |
if(value == limit){ | |
value = 0; | |
isCarry = true; | |
} | |
else{ | |
isCarry = false; | |
} | |
} | |
public int getValue(){ | |
return value; | |
} | |
} |
- 视频中的代码,表示分钟的对象和表示小时的对象没有直接交互。如果想要做直接交互,让表示分钟的对象在翻转的时候直接调用表示小时的对象的那个
increase
函数,代码需要怎样修改?
public class Display { | |
private int value=0; | |
private int limit=0; | |
public Display(int limit){ | |
this.limit=limit; | |
} | |
public void increase(){ | |
value++; | |
if(value==limit){ | |
value=0; | |
} | |
} | |
public void increase(Display d){ | |
value++; | |
if(value==limit){ | |
// 如果当前时间等于了上限,我们就去调用小时对象的递增 | |
value=0; | |
d.increase(); | |
} | |
} | |
public int getvalue(){// 输出 | |
return value; | |
} | |
public static void main(String[] args) { | |
Display h=new Display(24); | |
for(;;){ | |
h.increase(); | |
System.out.println(h.getvalue()); | |
} | |
} | |
} | |
class Clock { | |
//hour 与 minute | |
private Display hour=new Display(24); | |
private Display minute=new Display(60); | |
public void start() { | |
while (true) { | |
minute.increase(hour); | |
System.out.printf("%02d:%02d ",hour.getvalue(),minute.getvalue()); | |
} | |
} | |
public static void main(String[] args) { | |
Clock a=new Clock(); | |
a.start(); | |
} | |
} |
- 为什么说
private
是对类的不是对对象的?private
的访问属性到底限制的是什么?
在同一类中,哪怕生成了两个不同的对象,也可以相互访问
private
变量。
但在类外,无论如何无法访问private
变量。private
是否可以访问,是取决于它在类内还是类外,而不是是否是该类的对象。
- 在类函数中有
this
吗?为什么?
没有,类变量和类函数属于类,不与对象发生关系。
this
是指对象实例,类函数不直接属于任何对象,因此试图访问this
是没有意义的。