以下为个人学习笔记和习题整理
课程:面向对象程序设计 ——Java 语言
- 浙江大学 - 翁恺 @ 中国大学 MOOC
https://www.icourse163.org/course/ZJU-1001542001

# 课堂笔记

# 对象交互

当一个对象里有多个对象的时候,那些对象之间是如何交互的,对象和对象之间的联系是如何建立的,对象如何和其他对象交流。
对象和对象之间的联系紧密程度叫做耦合。
对象和对象的耦合程度越紧,表现在源代码上,就是它们的代码是互相依赖、互相牵制的。
我们理想的模型,是对象和对象之间的耦合要尽可能的松,平行的对象要尽量减少直接联系,让更高层次的对象来提供通信服务。

# 访问属性

# 封装

封装,就是把数据和对这些数据的操作放在一起,并且用这些操作把数据掩盖起来,是面向对象的基本概念之一,也是最核心的概念。

在设计类时做到封装的方法:

  1. 所有的成员变量必须是 private 的,这样就避免别人任意使用你的内部数据;
  2. 所有 public 的函数,只是用来实现这个类的对象,或类自己要提供的服务的,而不是用来直接访问数据的。除非对数据的访问就是这个类及对象的服务。
    简单地说,给每个成员变量提供一对用于读写的 get / set 函数也是不合适的设计。

# 访问权限

private :只有这个类内部可以访问,类内部指类的成员函数和定义初始化,这个限制是对类的而不是对对象的。
即同一个类的不同对象之间,可以互相访问各自的私有成员。

friendly :如果一个成员函数前没有 publicprivate 等关键字来限定访问权限,那么称为 friendly ,则与其位于同一个包的其他类可以访问。

类前加 public 指任何人都可以使用它来定义变量,但是类名需与编译单元文件名相同。
编译单元:一个 .java 源代码文件是一个编译单元,当一个编译单元不止一个类时,只有一个类前可以加 public ,其他类都只在该包内起作用。

# package

当你的程序越来越大的时候,你就会需要有一个机制帮助你管理一个工程中众多的类了。
包就是 Java 的类库管理机制,它借助文件系统的目录来管理类库。
一个包就是一个目录,一个包内的所有的类必须放在一个目录下,那个目录的名字必须是包的名字。

例如本项目 clock 文件夹目录下有 src/clocksrc/display 两个文件夹,即 clockdisplay 两个包。
如果要引用别的包里的类,则需要使用 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
Display.java
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
	
	}
}
Clock.java
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();
	}
}

# 课堂讨论

  1. increase 返回 boolean 来表示翻转好不好?
    如果 Displayincrease 函数,在发现翻转之后,就返回 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;
    }
 
}
  1. 视频中的代码,表示分钟的对象和表示小时的对象没有直接交互。如果想要做直接交互,让表示分钟的对象在翻转的时候直接调用表示小时的对象的那个 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();
    }
}
  1. 为什么说 private 是对类的不是对对象的? private 的访问属性到底限制的是什么?

在同一类中,哪怕生成了两个不同的对象,也可以相互访问 private 变量。
但在类外,无论如何无法访问 private 变量。
private 是否可以访问,是取决于它在类内还是类外,而不是是否是该类的对象。

  1. 在类函数中有 this 吗?为什么?

没有,类变量和类函数属于类,不与对象发生关系。 this 是指对象实例,类函数不直接属于任何对象,因此试图访问 this 是没有意义的。

阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Ruri Shimotsuki 微信支付

微信支付

Ruri Shimotsuki 支付宝

支付宝

Ruri Shimotsuki 贝宝

贝宝