栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【Java核心技术卷I笔记】第四章 类和对象

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【Java核心技术卷I笔记】第四章 类和对象

对象与类 4.1 面向对象程序设计概述

面向对象程序设计(OOP)是当今主流的程序设计范型。Java是完全面向对象的
面向对象的程序由对象组成,每个对象包含对用户公开的特定功能和隐藏的实现部分。在OOP中,不必关心对象的具体实现,只要能满足用户需求即可

4.1.1 类

类是构造对象的模板,由类构造对象的过程称为创建类的实例
封装从形式上看,是将数据和行为组合在一个包中,并对对象的使用者隐藏了数据的实现方式,对象中的数据称为实例域,操纵数据的过程叫做方法。对每个特定的类实例都有一组特定的实例域值,这些值的集合就是这个对象的当前状态
实现封装的关键在于绝不能让类中的方法直接访问其他类的实例域。程序仅仅通过对象的方法和对象数据进行交互。封装给对象赋予了“黑盒”特征。
继承是OOP的另一个工具。继承的类拥有被继承类的全部方法和属性,在新类中只需提供适用于这个新类的方法和数据域即可。

4.1.2 对象 4.1.3 识别类 4.1.4 类之间的关系
  • uses-a 依赖
  • has-a 聚合
  • is-a 继承
4.2 使用预定义类

Java中,没有类就无法做任何事。例如前面接触的Math类,但它不具有面向对象特征。下面给出更典型的Date类为例

4.2.1 对象与对象变量

要使用对象,就必须先构造对象,指定初始状态。然后应用对象方法。
Java中使用构造器构造新实例。构造器是一种特殊的方法,用来构造并初始化对象。
构造器的名字和类名相同,因此Date类的构造器名为Date。要构造一个Date对象,需要在构造器前面加上new操作符

Date birthday = new Date();
// Date有toString方法 返回日期的字符串描述
System.out.println(birthday.toString());

4.2.2 Java类库中的LocalDate类

标准Java类库包含两个类:用来表示具体时间点的Date类,以及日历表示法的LocalDate类
应当使用静态工厂方法调用构造器

LocalDate.now()	// 返回调用这个构造器时的时间
LocalDate newYearsEve = LocalDate.of(1999, 12, 31);	// of方法,用年月日构造一个特定日期的对象

可以获取LocalDate对象的信息,修改信息

int year = newYearsEve.getYear();	// 1999
newYearsEve = newYearsEve.plusDay(1000);	// 增加1000天
4.2.3 更改器方法和访问器方法

更改器方法:改变原先对象的状态,而不是生成新的对象
访问器方法:只访问而不修改对象状态
一个简单的例子,感受使用类的接口编程

import java.time.DayOfWeek;
import java.time.LocalDate;

public class Test {
    public static void main(String[] args) {
        LocalDate date = LocalDate.now();
        int month = date.getMonthValue();
        int today = date.getDayOfMonth();

        date = date.minusDays(today-1); // 月初
        DayOfWeek weekday = date.getDayOfWeek();    // WEDNESDAY (...
        int value = weekday.getValue(); // 星期几 3-Wed

        System.out.println("MontTuetWedtThutFritSattSun");
        for(int i=0; i
            System.out.print("t"); // 空出前面的部分
        }
        while(date.getMonthValue() == month){
            System.out.print(date.getDayOfMonth());
            if(date.getDayOfMonth() == today){
                System.out.print("*t");
            }else{
                System.out.print("t");
            }
            date = date.plusDays(1);
            if(date.getDayOfWeek().getValue() == 1) System.out.println();   // 换行
        }
        if(date.getDayOfWeek().getValue() != 1) System.out.println();
    }
}


4.3 用户自定义类

现在开始设计复杂应用程序所需的各种类,这些类通常没有main方法,但有自己的实例域和实例方法。
创建一个完整的程序,应当将若干类组合在一起,其中只有一个类有main方法

import java.time.LocalDate;

public class Test {
    public static void main(String[] args){
        Employee[] staff= new Employee[3];
        // 构造
        staff[0] = new Employee("Carl Cracker", 75000, 1987, 12, 15);
        staff[1] = new Employee("Harry Cracker", 50000, 1989, 10, 1);
        staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15);
        // 调用raiseSalary方法
        for(Employee e : staff){
            e.raiseSalary(5);
        }
        // 调用访问器
        for(Employee e :staff){
            System.out.println("name = "+e.getName()+", salary = "+e.getSalary()+", hireDay = "+e.getHireDay());
        }
    }
}
class Employee {
    private String name;
    private double salary;
    private LocalDate hireDay;
    // 构造器
    public Employee(String n, double s, int year, int month, int day) {
        name = n;
        salary = s;
        hireDay = LocalDate.of(year, month, day);
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public LocalDate getHireDay() {
        return hireDay;
    }

    public void raiseSalary(double byPercent) {
        double raise = salary * byPercent / 100;
        salary += raise;
    }
}
  1. 方法标记为public意味着任何类的任何方法都可以调用这些方法
  2. private实例域确保只有Employee类自身的方法能够访问这些实例域,其他类的方法不可以
  3. 构造器与类同名。构造对象时,构造器会运行,将实例域初始化为所希望的状态。构造器总是伴随着new操作符的调用而执行,不能对一个已经存在的对象调用构造器来达到重新设置实例域的目的。构造器没有返回值,一个类可以有多个构造器
4.3.5 隐式参数
public void raiseSalary(double byPercent) {
        double raise = this.salary * byPercent / 100;
        this.salary += raise;
    }
}
  1. 每一个方法中,关键字this表示隐式参数,为调用该方法的对象
  2. Java中所有方法都必须在类的内部定义。不同于C++,这些方法不一定inline。这取决于Java虚拟机
4.3.6 封装的优点 4.3.7 基于类的访问权限

一个方法可以访问所属类的所有对象的私有数据

class Employee{
	...
	public boolean equals(Employee other){
		return name.equals(other.name);
	}
}
// 调用
if(harry.equals(boss)){...}

这个方法访问harry和boss的私有域,这是合法的,他们都是Employee对象

4.3.8 私有方法

private方法指出。
不会被外部的其他类调用

4.3.9 final实例域

将实例域定义为final,在构建对象时必须初始化,并且之后不能再修改。

4.4 静态域和静态方法 4.4.1 静态域

如果将域设置为static,每个类中只有一个这样的域。静态域属于类,不属于任何独立对象

4.4.2 静态常量

例如

public class Math{
	...
	public static final double PI = 3.14159265358979323846;
	...
}
// 可以直接用 Math.PI 获得这个常量

public class System{
	...
	public static final PrintStream out = ...;
	...
}
4.4.3 静态方法

静态方法是一种不能向对象实施操作的方法,例如Math类的pow方法就是一个静态方法。

Math.pow(x, a);	// 不使用任何Math对象

静态方法不能访问实例域,因为他不能操作对象;但是静态方法可以访问自身类中的静态域。可以通过类名调用
以下情况需要使用静态方法

  1. 一个方法不需要访问对象状态,其参数都是显式提供,例如Math.pow(x,a)
  2. 一个方法只需访问类的静态域
4.4.4 工厂方法

静态方法常见用途:静态工厂方法。例如LocalDate.now和LocalDate.of方法构造对象
为什么不用构造器呢?

  1. 无法命名构造器,构造器的名字必须和类名相同,有时候希望采用不同的名字
  2. 使用构造器时无法改变对象的类型,在继承时可能需要构造子类对象
4.4.5 main方法

main方法也是一个静态方法,他不对任何对象进行操作。事实上在启动程序时没有任何一个对象,静态main方法将执行并创建程序所需的对象

每一个类都可以有一个main方法,常用来对类进行单元测试。

以下代码总结了static用法以及main方法做单元测试

public class Test {
    public static void main(String[] args) {
        Employee[] staff = new Employee[3];
        staff[0] = new Employee("Tom", 40000);
        staff[1] = new Employee("Dick", 60000);
        staff[2] = new Employee("Harry", 65000);

        for (Employee e :
                staff) {
            e.setId();
            System.out.println("name="+e.getName()+", id="+e.getId()+", salary="+e.getSalary());
        }

        int n = Employee.getNextId();
        System.out.println("Next available id="+n);
    }
}
class Employee{
    private static int nextId = 1;
    private String name;
    private double salary;
    private int id;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public int getId() {
        return id;
    }

    public void setId(){
        id = nextId ++;
    }

    public static int getNextId() {
        return nextId;
    }

    public static void main(String[] args) {
        Employee e = new Employee("Harry", 50000);
        System.out.println(e.getName()+" "+e.getSalary());
    }
}

4.5 方法参数

Java程序设计语言总是采用按值调用
然而方法参数有两种类型:基本数据类型、对象引用。
一个方法不可能修改一个基本数据类型的参数,而对象作为引用就不一样了。
方法得到对象引用的拷贝,对象引用以及它的拷贝同时引用一个对象
方法不能让对象参数引用一个新的对象

下面程序演示了参数调用

public class Test{
    public static void main(String[] args) {
        
        System.out.println("Testing tripleValue:");
        double percent = 10;
        System.out.println("Before: percent="+percent);
        tripleValue(percent);
        System.out.println("After: percent="+percent);

        
        System.out.println("nTesting tripleSalary:");
        Employee harry = new Employee("Harry", 50000);
        System.out.println("Before: salary="+harry.getSalary());
        tripleSalary(harry);
        System.out.println("After: salary="+harry.getSalary());

        
        System.out.println("nTesting swap:");
        Employee a = new Employee("Alice", 70000);
        Employee b = new Employee("Bob", 60000);
        System.out.println("Before: a="+a.getName());
        System.out.println("Before: b="+b.getName());
        swap(a, b);
        System.out.println("After: a="+a.getName());
        System.out.println("After: b="+b.getName());
    }

    public static void tripleValue(double x){   // doesn't work
        x = 3*x;
        System.out.println("End of method: x="+x);
    }

    public static void tripleSalary(Employee x){    // works
        x.raiseSalary(200);
        System.out.println("End of method: salary="+x.getSalary());
    }

    public static void swap(Employee x, Employee y){
        Employee temp = x;
        x = y;
        y = temp;
        System.out.println("End of method: x="+x.getName());
        System.out.println("End of method: y="+y.getName());
    }

}

class Employee{
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public void raiseSalary(double byPercent){
        double raise = salary*byPercent/100;
        salary += raise;
    }
}

4.6 对象构造 4.6.1 重载

多个方法有相同的名字,不同的参数,便产生了重载。编译器根据各个方法给出的参数类型与特定方法调用所使用的值类型进行匹配挑出相应的方法。
Java允许重载任何方法而不只是构造器方法。完整描述一个方法需要指出方法名和参数类型,称为方法的签名。返回类型不属于签名的一部分,因此不能有两个名字相同、参数类型相同却返回不同类型值的方法。

4.6.2 默认域初始化

构造器中没有显式地给实例域赋值,那么会自动地赋为默认值:数值为0,布尔值为false,对象引用为null
不明确地对域初始化,不是一种好习惯。

4.6.3 无参数的构造器
  1. 当且仅当类没有提供任何构造器时,系统会自动提供一个无参数构造器,默认初始化实例域
  2. 如果提供了至少一个构造器,但没有提供无参数的构造器,那么构造对象时没有提供参数不合法。
4.6.4 显式域初始化

可以在类定义中,直接将一个值赋给任何域,例如

class Employee{
	private String name = "";
	...
}

在执行构造器之前,先执行赋值操作。
初始值不一定是常量值,可以调用方法对域进行初始化

class Employee{
	private static int nextId = 0;
	// 调用方法赋值初始化实例域
	private int id = assignId();
	...
	private static int assignId(){
		int r = nextId;
		nextId ++;
		return r;
	}
	...
}
4.6.5 参数名

为了可读性,建议参数变量用同样的名字将实例域屏蔽起来,实例域采用this.访问

4.6.6 调用另一个构造器
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/980882.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号