对象与类
-
Java对象都是在堆中构造的,栈内存 存储一般的成员变量
-
强烈建议在重载任何构造函数时再写一遍空的构造函数!!!!!
-
不能在构造器中声明同名的局部变量
-
在C++中,通常在类的外面定义方法(成员函数);若在内部定义,则自动成为
inline
函数;Java则无这些要求,必须在内部定义方法。 -
在Java中,只有按值调用,没有按引用调用; 在C++中,有按引用调用(&)
// 在Java中,无法将参数中的两个值交换 public static void swap(Employee x, Employee y) { Employee temp = x; x = y; y = temp; }// 该方法结束时,x和y的值就被立刻回收了。
-
在Java中,有三种初始化数据字段的方法:在构造器中设置值、在声明中初始化值,初始化块
-
Java中,同一个包里面的类相当于C++中的友元
概述
- 类(class):可以理解为一种总称,是构造对象的模板。由类构造(construct)对象的过程称为创建对象的实例(instance)。对象中的数据称为实例字段(instance filed),操作数据的过程被称为方法(method)。
- 对象的三个特性:行为,状态,标识
- 类之间的关系:依赖,聚合,继承
- 面向对象的三个特性:封装,继承,多态
使用预定义类
对象与对象变量
以Date类为例子:
使用构造函数创建一个新的对象:new Date()
可以将这个对象传递给一个方法:System.out.println("new Date()");
也可以应用一个方法:String str = new Date().toString;
若想将构造的对象多次使用,则创建一个变量:Date birthday = new Date();
对象变量birthday
引用了新构造的对象
Java类库中的LocalDate
类
使用静态工厂方法(factory method)来构造LocalDate
类的对象
import java.time.*;
LocalDate newYearsEve = LocalDate.now();
更改器方法和访问器方法
// 这个plusDays方法并没有更改newYearEve的值,仅仅生成了一个新的LocalDate对象
LocalDate aThousandDaysLater = newYearEve.plusDays(1000);
// 这种只访问对象而不修改对象的方法称为访问器方法
// SomeDay的值改变了
GregorianCalendar SomeDay = new GregorianCalendar(1999, 11, 31);
someDay.add(Calendar.DAY_OF_MONTH, 1000);
// 修改对象的方法被称为更改器方法
用户自定义类
class ***ClassName
***{
f i e l d 1 field_{1} field1
f i e l d 2 field_{2} field2
……
c o n s t r u c t o r 1 constructor_{1} constructor1
c o n s t r u c t o r 2 constructor_{2} constructor2
……
m e t h o d 1 method_{1} method1
m e t h o d 2 method_{2} method2
……
}
public class Employee {
// instance fields
private String name;
private double salary;
private LocalDate hireDay;
// constructor
public Employee(String n, double s, int year, int month, int day){
this.name = n;
this.salary = s;
hireDay = LocalDate.of(year, month, day);
}
// a method
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public void raiseSalary(double byPercent) {
double raise = this.salary * byPercent / 100;
this.salary += raise;
}
}
剖析Employee
类
private关键字确保只有Employee
类的方法能够访问这些实例字段
- 构造器和类同名
- 每个类可以拥有一个以上的构造器
- 构造器可以有0个、1个以上的参数
- 构造器没有返回值
- 构造器伴随
new
关键字使用
####用var
关键字声明局部变量
如果可以从变量的初始值推导出它们的类型,那么可以用var
关键字声明局部变量
var harry = new Employee("Harry Potter", 50000, 1989, 10, 1);//这里能够推出harry是Employee类型的
而不是Employee harry = new Employee("Harry Potter", 50000, 1989, 10, 1);
null
引用
可以将对象显式地设置成null
,表明没有引用任何对象。
LocalDate birthday = null;
String s = birthday.toString();//NullPointerException
此时产生了一个NullPointerException
异常,在我们定义的Employee
构造函数中,我们不希望name
是null
的,Objects
类提供了一个方法:name = Objects.requireNonNullElse(n, "unknown");
####隐式参数与显式参数
public void raiseSalary(double byPercent) {
double raise = salary * byPercent / 100;
salary += raise;
}
raiseSalary
方法有两个参数
第一个为隐式参数:是Employee
对象的salary
实例字段
第二个是显式参数:方法名后面括号内的参数double byPercent
在每一个方法中,关键字this
指示隐式参数
public void raiseSalary(double byPercent) {
double raise = this.salary * byPercent / 100;
this.salary += raise;
}
使用这种方法,能够区分实例字段和局部变量。
封装的优点
-
字段访问器:get+私有变量
getName(), getSalary()
public void getName() { return name; }
-
字段更改器:set+私有变量
setName(),setSalary()
public void setName(String name) { this.name = name;// 关键字this说明了this.name是在类中定义的实例字段 }
关键字final
-
设置后就不能修改该字段
-
能被每一个对象调用
public class Employee { public final double PI = 3.1415926535; } Employee firstEmployee = new Employee("Jerry", "5500", 3, 2, 1); firstEmployee.PI;// 合法的
方法参数
对象引用
单纯的按值调用并不能修改基本数据类型的方法参数,对象引用却可以
public static void tripleSalary(Employee x)
{
x.Salary *= 3;
}
Employee harry = new Employee();
Employee.tripleSalary(harry);
静态字段和静态方法
静态字段
静态字段是作用于整个类的,即使没有对象,也存在该字段
静态常量
将作用于每一个对象的常量,变为作用于整个类的常量。通过类名.常量名来访问该常量
静态方法
静态方法不能访问类中的任何实例字段,因为这个方法是作用于整个类的。
以下两种情况可以使用静态方法
- 方法不需要访问对象状态,因为它需要的所有参数都通过显示参数提供
- 方法只需要访问类的静态字段
对象构造
-
重载:构造器有相同的名字,不同的参数
-
默认字段初始化:数值为0,布尔值为false,对象引用为null
-
无参构造器:若你在写一个类时没有编写构造器,就会为你提供一个默认的无参构造器。
public class Employee { Employee(){}// 再写一遍无参构造器 }
-
初始化块:只要构造一个类的对象,就会执行类中的块。这种机制不是必需的。
-
对象析构:Java会自动进行垃圾回收,故不支持析构;若一个资源需要就立刻回收,可以调用close方法。
包(package)
包名
功能:确立类的唯一性。在不同包中,类名相同不会冲突
类的导入
一个类可以使用所属包中的所有类,以及其他包的public类
若要访问另一个包中的公共类,常用 import导入一个包中的特定类
import 包名.类名
静态导入
import static 包名.类名
就可以直接使用该类的静态方法和静态字段
JAR文件
定义
类似于压缩包,可以包含类文件,也可以包含图像或者声音等文件。
导入JAR文件
IDEA直接拖入即可
文档注释
注释的插入
注释以/**
开始,以*/
结束。
每个文档注释由两部分组成:
- 标记:以
@
开始如,@param
- 自由格式文本:第一句应是概要性句子,可以用HTML修饰符。
类注释
类注释必须放在import
之后,类定义之前。
/**
* A {@code Card} object represents a playing card, such as "Queen of Hearts".
* A card has a suit (Diamond, Heart, Spade of Club) and a value (1 = Ace,2 ... 10, 11 = Jack, 12 = Queen, 13 = King)
*/
方法注释
每一个方法注释必须放在所描述的方法之前,一些常用标记:
-
@param
variable description这个标记将给当前方法的参数部分添加一个条目,所有
@param
标记必须放在一起 -
@return
description这个标记将给当前方法的返回值添加一个条目。
-
@throw
class description这个标记添加一个注释,表示这个方法有可能抛出异常
/**
* The method demonstrates raises the salary of an employee.
* @param byPercent the percentage by which to raise the salary
* @return the amount of the raise
*/
public double raiseSalary(double byPrecent)
{
double raise = this.salary * byPercent / 100;
this.salary += raise;
return rasie;
}
字段注释
通常只需要为静态常量建立
/**
* The "Hearts" card suit
*/
public static final int HEARTS = 1;
通用注释
@author
name
这个标记将产生一个作者条目
@version
text
这个标记将产生一个版本条目
包注释
需要在每一个包目录中添加一个单独的文件。
- 提供一个名为package-info.java的Java文件。这个文件当前仅当包含一个注释和一个package语句
- 提供一个名为package.html的HTML文件。会抽取
<body>...</body>
之间的全部文本
类设计技巧
-
一定要保证数据私有。
为了不破坏封装性
-
一定要对数据进行初始化
-
不要在类中使用过多的基本类型
-
不是所有的字段都需要单独的字段访问器和字段更改器
在对象中,常常包含一些不希望别人获得或者设置的实例字段
-
类的功能应该尽量唯一
-
类名和方法名应该体现它们的职责
类名/方法名应当是一个名词,或者是前面由形容词或者动名词修饰的名词
-
优先使用不可变的类
这里的不可变就是指不改变对象的状态。