Java核心技术·卷一
第四章:对象与类
4.1面向对象程序设计概述
对于面向过程:算法+数据结构=程序
而对于面向对象:数据结构+算法=程序
4.1.1类
封装:亦称之为数据隐藏。对象中的数据称为实例字段,操作数据的过程称之为方法。
4.1.2对象
包括:行为,状态,标识
4.1.4类之间的关系
- 依赖(”uses-a“),一个类的方法使用或者操纵另一个类的对象,也称之为耦合性。如果类A不需要知道类B的存在,那么类B的修改不会对类A造成bug。
- 聚合(”has-a”)包含关系。
- 继承(“is-a”)
4.2使用预定义类
再次提醒,声明和创建内存是分开的。也可以两个变量名指向同一个对象内存。本质上也是一个指针。
对象变量没有实际包含一个对象,它只是引用了一个对象
在Java中,任何对象变量的值都是对储存在另外一个地方的某个变量的引用
4.2.2Java中的LocalDate类
保存时间与给定时间点命名分开。故Date用于表示时间点,用LoaclDate表示时间命名。
LocalDate a = LocalDate.now();
System.out.println(a.getYear());
LocalDate b = LocalDate.of(2002,05,13);//采用工厂的形式来构造,不要自己new一个对象
System.out.println(a.toString());
//实际上date也有得到年月日的方法,但是已经废弃不用,废弃不用的方法我们应该避免使用,以免以后官方完全删除这些方法造成程序Bug
DayofWeek dayofWeek = a.getDayOfWeek();
dayOfweek.getValue();
//1为周一,7为周日
更改器方法与访问器方法:是否对状态进行修改
4.3自定义类
所有的Java对象都是在堆中构造的,所有的构造函数的都要配合new使用
隐式参数,显式参数
void Employee(int a){
}
//显式参数为a,隐式参数为this指针,即当前的变量。
var关键字
可以使用var关键字来声明方法中的局部变量。参数和实体字段的类型必须声明
var harry = new Employee("njq",5000,2021,6,1);
Object类的requirNotNull方法可以实现对null参数的拒绝,然后报错(这样相对于常规报错,更容易定位到错误)
class Demo{
private int a;
public Demo(Integer a) {
Objects.requireNonNull(a);
this.a = a;
}
}
//如果传null值,就会报错Exception in thread "main" java.lang.NullPointerException
在c++中通常在类的外部定义方法
void Employee::raiseSalary(doule byPercent){
....
}
//如果在类的内部定义方法,这个方法将自动成为内联的
class Employee{
int getName(){return name}//inline
};
但是Java所有的方法都要在类的内部定义,是否内联由虚拟机决定。
对getter和setter理解:更好的实现了封装性,还可以实现数据检查,修改字段数据需要调用方法的好处是对破环的溯源。
不要编写返回可变对象引用的访问器方法。这样会破坏封装性,应该返回应该副本
class Employee{
...
public Date getHireDate(){
return (Date)hireDate.clone();
}
}
私有的方法由于除了本类不会有其他调用,在确保本类不会出错误的情况下,可以删去private方法。而对于public方法,需要谨慎。
final关键字:不可变的。但是对于可变的类,使用final修饰符可变类,只是说明这个变量不会指向另一个对象。
private final StringBuilder evaluations;
//构造器中:
evaluations = new StringBuilder();
//某一个方法中:
public void giveGoldStar(){
evaluations.append(LocalDate.now()+GoldStar);
}
4.4静态字段与静态方法
静态字段===>“类字段”。
**仅做了解:System类的out实体变量为static final,但是却有一个setOut的方法,这里是一个原生方法,可以绕过访问控制机制,并不是在Java语言中实现的。
对象是可以调用静态方法的。但是不推荐这样做,因为调用这个静态方法的对象与这个静态方法是没有太大关系的。推荐还是用类名来调用。
工厂方法:
为什么不使用构造器:
- 无法命名构造器。构造器的名字是确定的,但是有可能希望得到不同类型的实例,使用无法重命名的构造器会造成混乱
- 无法改变所构造对象的类型。new + 构造器返回的一定是该类,但是有可能需要返回子类这种需求。
4.5方法参数
按值传递与按引用传递:
对对象的传递,本质上是传递了一个指针副本。
Test a = new Test();
Test b = new Test();
public void Swap(Test a, Test b){
Test temp = new Test();
a = temp;
a = b;
b = temp;
}
//但是a,b并没有交换。
public void Change(Test a){
a.setName("njq");
}
//a对象的名字属性改变了。
C++便可以加入”&“按照引用传递。
4.6对象构造
4.6.1重载
方法的签名:方法名以及参数类型。返回类型不是签名的一部分。也就是说不能有名字相同,形参相同,但是返回类型不同的两个方法。
4.6.2默认字段初始化
实体字段会默认进行初始化(0,null,flase…),而局部变量必须显示的初始化。
public static void main(String[] args) {
int a;
System.out.println(a);
}
//报错,java: 可能尚未初始化变量a
public class Application {
public static void main(String[] args) {
Test test = new Test();
System.out.println(test.n);
}
}
class Test{
public int n;
}
//ok的,输出为0
仅仅当类没有其他构造器的时候,才会自动生成一个无参构造器。
初始值不一定是常量值,还可以调用方法:
public class Application {
public static void main(String[] args) {
Test test1 = new Test();
Test test2 = new Test();
System.out.println(test1.getA());
System.out.println(test2.getA());
}
}
class Test{
static private int count = 0;
private int a = initialize();
static public int initialize(){
return count++;
}
public int getA(){
return a;
}
}
4.6.6调用另一个构造器
this指示一个方法的隐式参数。不过,当形如this(…)为构造器的第一个语句时,它会调用同一个类的另一个构造器。
public Employee(double s){
this("Employee #"+nextId,s);//调用Employee(String,double)构造器
nextId++;
}
4.6.7初始化块
除了:
- 构造器初始化
- 声明中初始化(public int a = 0;)
还可以用初始化块。可以包含多个代码块,只要构造这个类的对象,这些块就会被调用。
构造处理步骤:
-
如果构造器的第一行用this(…)调用了另一个构造器,则转到另一个构造器
-
否则:
- 所有字段初始化为默认值
- 按照在类声明中出现的顺序,执行所有的字段初始化方法和初始化块
3.执行构造器主体代码
public class Application {
public static void main(String[] args) {
Test test1 = new Test();
Test test2 = new Test();
System.out.println(test1.getA());
System.out.println(test2.getA());
}
}
class Test{
static private int count;
private int a = initialize();
static public int initialize(){
return count++;
}
public int getA(){
return a;
}
static {
count = 10;
System.out.println("static Construct block");
}
{
System.out.println("simple Construct block of "+count);
}
}
/* result:
static Construct block
simple Construct block of 11
simple Construct block of 12
10
11
*/
//注意这里的simple为11和12,这也印证了上面提到的构造顺序
Java.Util的Random类
Random r = new Random();
System.out.println(r.nextInt(1000));
//返回一个0到999的整数
Java中没有析构函数…
4.7包
4.7.1包名
从编译器的角度来讲,嵌套的包没有任何的关系。例如:java.lang与java.util毫无关系
4.7.2类的导入
一个类可以使用所属包中的所有类,以及导入包的公共类
实际上import更多的是相当于C++中的命名空间
4.7.3静态导入
可以直接使用静态方法和字段
但是感觉没什么卵用
import static java.lang.System.*;
public class Application {
public static void main(String[] args) {
out.println("static import");
}
}
//在某种程度上可以使代码清晰
sqrt(pow(x,2) + pow(y,2))
//比下面清晰
Math.sqrt( Math.pow(x,2) + Math.pow(y,2) )
4.7.4在包中增加类
将包的名字放在源文件的开头
package com.werun.njq;
如果没有package语句,就属于无名包
源文件应该放在与包名对应的文件夹下。上面那个包就应该放在com/werun/njq目录下。如果包与路径不匹配,JVM就找不到类。
用“-classpath”来表示类路径
4.8 JAR文件
用 Jar -cvf 打包的文件名(带后缀jar) 打包的文件 打jar包
更普遍的形式是 jar options file1 file2…
c | 创建一个新的或者空的存档文件并加人文件。如果指定的文件名是目录,jar 程序将会对它们进行递归处理 |
---|---|
C | jar cvf jarFileName.jar -C classes *.class切换到classes子目录以便增加类文件 |
e | 在清单文件中创建一一个人口点 |
f | 指定JAR文件名作为第二个命令行参数。如果没有这个参数,jar 命令会将结果写至标准输出(在创建 JAR文件时)或者从标准输人读取(在解压或者列出 JAR文件内容时) |
i | 建立索引文件(用于加快大型归档中的查找) |
m | 将一个清单文件添加到JAR文件中。清单是对归档内容和来源的一个说明。 每个归档有一个默认的清单文件。但是,如果想验证归档文件的内容,可以提供自己的清单文件 |
M | 不为条目创建清单文件 |
t | 显示内容表 |
u | 更新一个已有的JAR文件 |
v | 生成详细的输出结果 |
x | 解压文件。如果提供个或多个文件名, 只解压这些文件:否则,解压所有文件 |
o | 存储,但不进行ZIP压缩 |
4.9文档注释
javadoc生成HTML文档
4.9.1注释的导入
标记+自由格式文本
可以使用HTML修饰符,例如用于强调的em,着重强调的strong,用于项目符号列表的ul,li;用于图像的img,用于等宽代码用{@code},这样可以避免<>的转意
链接文件需要放到doc-files目录,javadoc会自动提取这个目录的内容到文档目录中,例如:
<img src="doc-files/uml.png" alt=UML diagram>
4.9.2类注释
类注释必须放在import语句之后,类定义以前。
4.9.3方法注释
必须放在所描述的方法值之前
/**
* 返回两个数的最大值
* @param a 给参数部分添加一个条目,可以占据多行,可以使用html标记。所有@param标记必须放在一起
* @param b
* @return 返回部分,可以占据多行,可以使用html标记
* @throws 表示这个方法有可能抛出异常
*/
public int max(int a ,int b){
return a>b?a:b;
}
4.9.4字段注释
只需要对公共字段(通常指的是静态变量)建立文档
/**
* The Count of id
*/
public static final int COUNT = 1;
4.9.5通用注释
@since,@author @version
@see reference ,reference有以下选择:
- package.class#feature label
- <“a href=”…">label</"a> (那两个引号是为了避免转义)
- ”text"
@see com.werun.njq.AdminController#login(...)
//注意要使用#来分割类名与方法名,或者类名与常量名
@see <a href="182.92.225.218:8080">MyWeb</a>
@see "MyWeb"可以有多个@see标记,但是必须放在一起。
可以用{@link 在任何地方增加一个链接,用法与@see类似}
@author 作者是{@link com.werun.njq....#...}
4.9.6包注释
- 提供一个名为package-info.java的文件,文件里面全为文档注释,不能有其他内容
- 提供一个package.html的html文件。javadoc会收取<'body></'body>之间的内容
4.9.7注释抽取
基本方法:
javadoc -d docDirectory nameOfPackage
javadoc -d MyDoc com.njq.werun