0%

JAVA基础 | 2、类与对象

第二部分 类与对象

众所周知,“xxx是一门面向对象的编程语言”这句话折磨了许多初学面向对象的同学们(总之包括而我),因此,虽然这个说法非常不负责并且非常有问题,但是我需要直接了当的说:面向对象不就是结构体+函数吗(当然不是)

I 面向对象中java与C++的区别

事实上,Java作为一个摒弃了指针的语言来讲,面向对象这部分与C++有着显著的不同,比如说在对象实例化的部分:

1
2
3
Date birthday; // java:实例化一个Date对象,对象名称为birthday
//然而在C++中的实际含义如下,尽管形式一样
Date* birthday; // C++

并不只是面向对象这部分,Java中的变量并没有进行赋值,而是一个变量指向了一个数据,由此可见,与其说Java是摒弃了指针的语言,不如说是全程都在用(简化版的)指针的语言(真香啊
在面向对象的程序中,应该尽量避免一个方法能够修改对象属性,甚至是直接访问对象属性,这都是不安全的,因此java类的设计通常有以下指定功能的方法:

  • 构造器:最朴实无华的构造器,简单粗暴的new一个新对象,传统中的传统
  • 静态工厂的构造器:不使用传统的构造器构造对象,而是使用这种特殊的方法(接口)构造一个新对象并且作为返回值可以赋值给对象变量
  • 更改器:看似是修改了对象的属性,其实是返回了一个属性不一样的新对象
  • 访问器:只访问对象而不修改对象的方法被称为访问器

听起来似乎花里胡哨,但本质上他们都是一个类/对象的方法罢了,只不过是人们根据他们的功能或特性起了一个(听起来很高端的)新名字,下面这个程序案例可以充分展示上述中三个新方法的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package MyJava;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.Scanner;

public class Example
{
public static void main(String[] args)
{
//这里是静态工厂
//使用静态工厂实例化一个表示现在日期的对象,对象名称为date
LocalDate date = LocalDate.now();

//通过date对象,得到int型的月份和日期(这个月的第几天)
int month = date.getMonthValue();
int today = date.getDayOfMonth();

//date的日期更新为该月的第一天,也就是减去today再+1,赋值给date,再通过DayOfWeek类与方法返回星期数,并用value得到值
date = date.minusDays(today - 1); // 这里是更改器
DayOfWeek weekday = date.getDayOfWeek();
int value = weekday.getValue(); // 这里是访问器

//输出表头,即格式部分
System.out.println("Mon Tue Wed Thu Fri Sat Sun");
for(int i=1; i<value; i++)
{
System.out.print(" ");
}
//输出日历主体,只要还在当月就继续循环
while(date.getMonthValue() == month)
{
//输出日期
System.out.printf("%3d",date.getDayOfMonth());
//判断是否为今天
if(date.getDayOfMonth() == today)
{
System.out.print("*");
}
else
{
System.out.print(" ");
}
//更新date
date = date.plusDays(1);
if(date.getDayOfWeek().getValue() == 1)
{
System.out.println();
}
}
if(date.getDayOfWeek().getValue() != 1)
{
System.out.println();
}
}
}

//输出结果如下
/*
Mon Tue Wed Thu Fri Sat Sun
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23* 24 25 26 27
28 29 30 31
*/

使用这些类,接口和方法,可以轻松处理日历的很麻烦的问题(尤其是不用考虑一大堆边界条件,因为大犇们已经提前造好轮子了),

II 用户自定义类

与c++结构类似,其区别第一在于java的各种属性与方法有着很长的前缀,其二在于java在声明方法的时候定义方法。不过,前者只是将C++的访问权限展开罗列,后者将每个类定义在一个java文件中,所以综上所述java的自定义类和C++结构依然相似。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//一个最简单的java类定义
class ClassName
{
//属性
//field1
//field2

//构造器
//constructor1
//constructor2

//方法
//method1
//method2
}

为了防止域值(对象的属性)出错或者随意修改,应该在针对一个属性拥有以下三个内容:

  • 一个私有的数据域
  • 一个公有的域访问器的方法
  • 一个公有的域更改器的方法

这样有很多好处,一是内部实现不会影响其他代码,二是更改其可以执行错误检查
同时为了防止数据域的出错,在需要返回一个可变对象的时候需要进行克隆:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class OutPut
{
private Date theDay;

//以下是不安全的做法
public Date getDay()
{
return theDay;
}
//以下是安全的做法
public Date getDay()
{
return (Date) theday.clone();
}
}

III 静态域与静态方法

静态前缀修饰符static即将变量的作用域设置为静态,这个修饰符可以作用于以下内容:

  • 静态域:

将成员属性设置为静态,即类中所有对象都公用一个的属性,在其他面向对象的语言中也会将其称为“类域”,对于静态域的使用有以下经典用法:

1
2
3
4
5
6
7
8
9
10
11
12
class Employee
{
private static int nextID = 1;
private int id;

//自动设置雇员的id
public void setId()
{
id = nextId;
nextId++;
}
}
  • 静态常量

即在常量中用static修饰,最经典的例子就是在Math类中的静态常量PI。
静态常量因为不能被修改,所以设置成公有也是没有问题的。

  • 静态方法

静态方法是一种不能对对象实施操作的方法,也就是没有this参数的方法,例如,Math终端pow方法,在使用时不许呀哦任何Math对象。
虽然静态方法不能访问实例域,但是可以访问静态域,也可以通过类名待用这个方法。

  • 静态工厂

类似LocalDate和NumberFormat的类使用静态工厂方法来构建对象,以NumberFormat为例,不使用构造器有以下两点原因:
一方面构造器的名字必须与类名相同,而实际希望将得到两种不同的名字
另一方面使用构造器时无法改变所构造的对象类型,而Factory方法将返回一个DecimalFormat类的对象,也就是NumberFormat的子类。

  • main方法

调用静态方法不需要对象,同理。main方法也是一个静态方法,main函数不需要对任何对象进行操作。
事实上,程序启动的时候没有任何一个对象,静态的main方法将执行并创建程序所需要的对象。
注: 每一个类都可以有一个main方法,常用于对类进行单元测试

  • 声明时赋值与初始化块

虽然java可以使用常规的构造器初始化数据域,与C++不同,java可以在类声明属性的时候就对其进行初始化,除此之华还有“初始化块”这个第三种机制,只要构造类的对象,这些块就会被执行,对于静态域进行初始化的初始化块可以在花括号前单独加一个static前缀
类中调用构造器的顺序为:
数据域初始化为默认值 -> 按照声明中出现的次序执行初始化块 -> 若构造器中有其他构造器,先执行内部构造器,再执行外部构造器

IV 包

包是java中管理类的容器,一个类可以使用所属包中的所有类,以及其他包中的公有类,我们可以有两种方法访问其他包中的类:

1
2
3
4
5
6
7
8
9
10
11
12
//第一种方法 显然比较繁琐
java.time.LocalDate todaty = java.time.LocalDate.now();

第二种方法,*代表导入这个包内的所有类,当然也可以只导入特定的类
import java.until.*;
LocalDate today = LocalDate.now();
```

除此之外,import语句不仅可以导入类,还可以用来导入静态方法和静态域(不过这样会很大程度降低代码的清晰度)
同理,设计好的类也需要放在包中。可以用以下语句实现:
```java
package nameOfpackage;

如果在包的目录中没有对应的类,也可以进行编译,但是最终程序无法正常运行,因此需要先将所有类文件移到正确的位置上。
除此之外,类文件也可以存储在JAR文件中,JAR文件可以理解为Java归档的ZIP格式组织文件,JDK提供了许多JAR文件的类库,同时JAR文件也方便其他第三方库的使用。