目录
- 面向对象程序设计概述
- 使用预定义类
- 用户自定义类
- 静态字段和静态方法
- 方法参数
- 对象构造
- 包
- JAR文件
- 文档注释
概念名 | 解释 |
---|---|
class | 构造对象的模版或蓝图 |
instance | 由class构造对象的过程称为创建class的instance(实例) |
encapsulation(封装) | 将数据和行为组合在一个包中,并对对象的使用者隐藏具体的实现方式 |
实例字段 | 对象中的数据 |
方法 | 操作数据的过程 |
当前状态(state) | 作为一个类的实例,特定对象都有一组特定的实例字段值,这些值的集合就是当前对象的状态 |
更改器方法 | 对访问对象进行修改的方法 |
访问器方法 | 只访问对象而不对对象进行修改的方法 |
实现封装的关键:绝对不能让类中的方法直接访问其他类的实例字段,程序只能通过对象的方法与对象数据进行交互。(意味着一个类可以完全改变存储数据的方式,而对外操作的接口并没有发生改变。)
特性 | 解释 |
---|---|
对象的behavior | 可以对对象完成哪些操作,或者可以对对象应用哪些方法 |
对象的state | 当调用那些方法时,对象会做出什么响应 |
对象的identity | 如何区分具有相同行为和状态的不同对象 |
关系 | 解释 |
---|---|
依赖(uses-a) | 一个类使用或操作另一个类的对象 |
聚合(has-a) | 类A的对象包含类B的对象 |
继承(is-a) | 类A扩展类B |
要使用对象首先必须构造对象,并指定其初始状态,然后对对象应用方法
new Data();//构造器的名称应该与类名相同
Date birthday = new Data();//为了经常使用,把构造器构造的对象赋值给一个对象变量
如果没有对对象变量进行赋值的话不能使用这个对象的方法,出现编译错误。
class className{
field1
field2
...
contructor1
contructor2
...
method1
method2
}
NullPointerException
异常。这是一个很严重的错误,类似“索引越界”。定义一个类时,最好清楚地知道哪些字段可能为null。1. 宽容型
把null参数转换成一个适当的非null值
if(n==null) name="unknown"
else name = n;
java 9 提供了一个方法:
name = Objects.requireNonNullElse(n,"unknown");
2. 严格型
直接拒绝null参数
Objects.requireNonNull(n,"The name cannot be null");
name = n;
如果要接受一个对象引用作为构造参数,要考虑是不是真的希望接受可有可无的值,否则应该使用“严格法”
static <T> void requireNonNull(T obj);
static <T> void requireNonNull(T obj,String message);
static <T> void requireNonNull(T obj,Supplier<String> messageSupplier);
如果obj是null,这些方法会抛出一个NullPointerException异常而没有消息或给定的消息。
static <T> void requireNonNullElse(T obj,T defaultObj);
static <T> void requireNonNullElse(T obj,Supplier<T> messageSupplier);
如果obj不是null则返回obj,或者如果obj为null则返回默认对象
如果想要获得或者设置实例字段的值,需要提供下面三项内容:
1. 一个私有的数据字段
2. 一个公共的字段访问器方法
3. 一个公共的字段更改器方法
⚠️不要编写一个返回可变对象引用的访问器方法
如果这样的话可以在类外对该对象进行更改,破坏了封装性。因为两个指针指向同一片区域。
如果需要返回一个可变对象的引用,首先应该对它使用clone,对象克隆是指存放在另一个新位置上的对象副本。
class Emploee{
...
public Date getHireDay(){
return (Date) hireDay.clone();
}
}
⚠️如果需要返回一个可变数据字段的副本,就应该使用clone
final修饰符对于类型为基本类型或者不可变类的字段尤其有用
如果类中的所有方法都不会改变其对象,这样的类就是不可变的类。对于可变的类如果使用final可能会造成混乱。
class Employee{
private static int nextID = 1;
private int id;
...
}
所有类的实例共享nextID字段,而有各自的id字段
public static final double PI = 3.14
这样可以直接在类外使用Math.PI
可以直接使用类名来调用静态方法:
ClassName.staticMethod()
在下面两种情况下可以使用静态方法:
1. 方法不需要访问对象状态,因为它需要的所有参数都是通过显式参数提供
2. 方法只需要访问类的静态字段
1. 无法命名构造器。构造器的名字必须与类名相同,但是这里希望有两个不同的名字,分别得到货币实例和百分比实例
2. 使用构造器对象时,无法改变所构造对象的类型。而工厂方法实际上将返回DecimalFormat类的对象,这是一个NumberFormat的一个子类
java Employee(待测试的类)
Java程序设计语言总是采用按值调用,方法得到的是所有参数值的一个副本。方法不能修改传递给它的任何参数变量的内容。
有两种类型的方法参数:
1. 基本数据类型(数字、布尔)
2. 对象引用
方法得到的是对象引用的副本,原来的对象引用和这个副本的对象副本都引用同一个对象。对象引用是按值传递的。
方法不能修改基本数据类型的参数
方法可以改变对象参数的状态
方法不能让对象参数引用一个新的对象
签名(signature)
要完整地描述一个方法,需要指定方法名以及参数类型(返回值不算)
var messages = new StringBuilder();
var todolist = new StringBuilder("to do");
重载解析:查找匹配的过程
public Emploee(){
name = "";
salary = 0;
hireDay = LocalDate.now();
}
只有当class一个构造器都没有的时候系统才提供一个默认的构造器,其他情况都不会有,所以为了提供一个默认的,一般都自己写一个默认的。
class Employee{
private static int nextId;
private int id = assignId();
...
private static int assignId(){
int r = nextId;
nextId++;
return r;
}
this(其他构造器的参数格式);
//调用同一个类的另一个构造器
Java初始化数据字段的方法:
1. 在构造器中设置值
2. 在声明中赋值
3. 初始化块
首先运行初始化块,然后才运行构造器的主体部分。
⚠️调用构造器的具体步骤:
- 如果构造器的第一行调用了另一个构造器,则基于所提供的参数执行第二个构造器
- 否则,所有数据字段初始化为默认值;按照在类声明中出现的顺序,执行所有字段初始化方法和初始化块
- 执行构造器主体代码
使用包的主要原因是:确保类名的唯一性。从编译器的角度来看,嵌套的包之间没有任何的关系。java.util和java.util.jar包毫无关系。
import packageName;
import java.util.*;//只能用*导入一个包
在包中定位类是编译器的工作
import static java.lang.System.*;
//可以使用类中的静态方法和静态字段而不需要加类名前缀
package com.horstmann.corejava;
public class Employee{
...
}
(pothole)