第十章 面向对象

第十章 面向对象

10.1 引言

10.2 类的抽象和封装

类的抽象是指将类的实现和类的使用分离开,实现的细节被封装并且对用户隐藏,这被称为类的封装。

Java提供了多层次的抽象。类抽象(class abstraction)是将类的实现和使用分离。类的创建者描述类的功能,让使用者明白如何使用类。从类外可以访问的public构造方法、普通方法和数据域的集合以及对这些成员预期行为的描述,构成了类的合约(class’s contract)。如图10-1所示,类的使用者不需要知道类是如何实现的。实现的细节通过封装对用户隐藏起来,这称为类的封装(class encapsulation)。例如:可以创建一个Circle对象,并且可以在不知道面积是如何计算出来的情况下,求出这个圆的面积。由于这个原因,类也称为抽象数据类型(AbstractData Type, ADT)

10.3 面向对象思想

面向过程的范式重点在于设计方法。面向对象的范式将数据和方法耦合在一起构成对象。使用面向对象范式的软件设计重点在对象以及对象上的操作。

10.4 类的关系

为了设计类,需要探究类之间的关系。类之间的关系通常有关联、聚合、组合以及继承。

10.4.1 关联

关联是一种常见的二元关系,描述两个类之间的活动。

10.4.2 聚集和组合

聚集是关联的一种特殊形式,代表了两个对象之间的归属关系。聚集对has-a关系进行建模。所有者对象称为聚集对象,它的类称为聚集类。而从属对象称为被聚集对象,它的类称为被聚集类。

10.5 示例学习:设计Course类

**需求:**假设需要处理课程信息。每门课程都有一个名称以及选课的学生,要能够向/从这个课程添加/删除一个学生。可以使用一个类来对课程建模,如图10-10所示。

请添加图片描述

**思路:**可以向构造方法Course(String name)传递一门课程的名称来创建一个Course 对象。可以使用 addstudent(String student)方法向某门课程添加学生,使用dropStudent(String student)方法从某门课程中删除一个学生,使用getStudents()方法可以返回选这门课程的所有学生。假设Course类是可用的。在一个测试类创建两门课程,并向课程中添加学生。

具体实现:

//Course类
package com.Javabook.Demo;

public class Course {
    private String courseName;
    private String[] students = new String[100];
    private int numberOfStudents;

    public Course(String courseName) {
        this.courseName = courseName;
    }

    public void addStudent(String student) {
        students[numberOfStudents] = student;
        numberOfStudents ++;
    }

    public String[] getStudents() {
        return students;
    }

    public int getNumberOfStudents() {
        return numberOfStudents;
    }
    public String getCourseName() {
        return courseName;
    }

    public void dropStudent(String student) {
        // 遍历学生数组,查找要删除的学生
        for (int i = 0; i < numberOfStudents; i++) {
            if (students[i] != null && students[i].equals(student)) {
                // 找到学生,开始删除操作
                // 将后面的学生向前移动一位,覆盖要删除的学生
                for (int j = i; j < numberOfStudents - 1; j++) {
                    students[j] = students[j + 1];
                }
                // 将最后一个元素设置为null,表示数组的结束
                students[numberOfStudents - 1] = null;
                // 更新学生数量
                numberOfStudents--;
                return; // 退出方法,因为我们已经找到了并删除了学生
            }
        }
        // 如果没有找到学生,可以选择打印一条消息或者什么都不做
        System.out.println("没有发现学生:" + student);
    }
}

//TestCourse类
package com.Javabook.Demo;

public class TestCourse {
    public static void main(String[] args) {
        Course course1 = new Course("Data Structures");
        Course course2 = new Course("Database Systems");

        course1.addStudent("Peter Jones");
        course1.addStudent("Kim Smith");
        course1.addStudent("Anne Kennedy");

        course2.addStudent("Peter Jones");
        course2.addStudent("Steve Smith");

        System.out.println("课程1的学生人数:" + course1.getNumberOfStudents());
        String[] students = course1.getStudents();
        for (int i = 0; i < course1.getNumberOfStudents(); i++) {
            System.out.print(students[i] + ",");
        }

        System.out.println();
        System.out.println("课程2的学生人数:" + course2.getNumberOfStudents());
    }
}

10.6 示例学习:设计栈类

**需求:**可以定义一个类建模栈。为简单起见,假设该栈存储int数值。因此,命名这个栈类为StackOfIntegers。这个类的UML图如下所示。

请添加图片描述

**思路:**栈中的元素都存储在一个名为elements的数组中。创建一个栈的时候,同时也创建了这个数组。类的无参构造方法创建一个默认容量为16的数组。变量 size 记录了栈中元素的个数,而size-1是栈顶元素的下标,如下所示对空栈来说,size为0。

请添加图片描述

具体实现:

//StackOfIntegers
package com.Javabook.Demo;

public class StackOfIntegers {
    private int[] elements;
    private int size;
    public static final int DEFAULT_CAPACITY = 16;

    public StackOfIntegers() {
        this(DEFAULT_CAPACITY);
    }

    public StackOfIntegers(int capacity) {
        elements = new int[capacity];
    }

    public void push(int value) {
        if (size >= elements.length) {
            int[] temp = new int[elements.length * 2];
            System.arraycopy(elements, 0, temp, 0, elements.length);
            elements = temp;
        }

        elements[size++] = value;
    }

    public int pop() {
        return elements[--size];
    }

    public int peek() {
        return elements[size - 1];
    }

    public boolean empty() {
        return size == 0;
    }

    public int getSize() {
        return size;
    }
}

//P338_TestStackOfIntegers
package com.Javabook.Demo;

public class P338_TestStackOfIntegers {
    public static void main(String[] args) {
        StackOfIntegers stack = new StackOfIntegers();

        for (int i = 0; i < 10; i++) {
            stack.push(i);
        }

        while (!stack.empty()) {
            System.out.print(stack.pop() + " ");
        }
    }
}

10.7 将基本数据类型值作为对象处理

基本数据类型值不是对象,但是可以使用JavaAPI中的包装类来包装成一个
对象。

出于对性能的考虑,在Java中基本数据类型不作为对象使用。因为处理对象需要额外的系统开销。

然而,Java中的许多方法需要将对象作为参数。Java提供了一个方便的办法,即将基本数据类型合并为或者说包装成对象(例如,将int包装成Integer类,将double包装成Double类将char包装成Character类)。通过使用包装类,可以将基本数据类型值作为对象处理。

大多数基本类型的包装类的名称与对应的基本数据类型名称一样,第一个字母要大写。对应intInteger和对应charCharacter例外。

10.8 基本类型和包装类类型之间的自动转换

根据上下文环境,基本数据类型值可以使用包装类自动转换成一个对象,反
之也可以。

将基本类型值转换为包装类对象的过程称为装箱(boxing),相反的转换过程称为拆箱(unboxing)。

10.9 BigInteger和BigDecimal类

BigInteger 类和 BigDecimal类可以用于表示任意大小和精度的整数或者十进制数。

如果要进行非常大的数的计算或者高精度浮点值的计算,可以使用java.math包中的BigInteger类和BigDecimal类。它们都是不可变的。

10.10 String类

String对象是不可改变的。字符串一旦创建,内容不能再改变。

String 变量存储的是对String对象的引用,String对象里存储的才是字符串的值。

10.10.1 不可变字符串与驻留字符串

string 对象是不可变的,它的内容是不能改变的。

下列代码会改变字符串的内容吗?

String s ="Java";
S = "HTML";

答案是不能。

第一条语句创建了一个内容为"Java"string对象,并将其引用赋值给s。第二条语句创建了一个内容为"HTML"的新String对象,并将其引用赋值给s。赋值后第一个String对象仍然存在,但是不能再访问它了,因为变量s现在指向了新的对象,如下所示。

请添加图片描述

10.10.2 替换和拆分字符串

String类提供了替换和拆分字符串的方法,如下图所示。

请添加图片描述

10.10.3 使用模式匹配、替换和拆分

正则表达式(regular expression)(缩写regex)是一个字符串,用于描述匹配一个字符串集的模式。可以通过指定某个模式来匹配、替换或拆分一个字符串。这是一种非常有用且功能强大的特性。

10.10.4 字符串与数组之间的转换

字符串不是数组,但是字符串可以转换成数组,反之亦然。为了将字符串转换成一个字符数组,可以使用toCharArray方法。例如,下述语句将字符串"Java"转换成一个数组:

char[]chars = "Java".toCharArray();

因此,chars[0]J’,chars[1]'a'chars[2]'v'chars[3]'a'

10.10.5 将字符和数值转换成字符串

我们可以使用 Double.parseDouble(str)或者Integer.parseInt(str)将一个字符串转换为一个double值或者一个int值,也可以使用字符串的连接操作符来将字符或者数字转换为字符串。

请添加图片描述

10.10.6 格式化字符串

String类包含静态方法format,它可以创建一个格式化的字符串。调用该方法的语法是:

String.format(format,item1,item2.itemk);

这个方法与printf方法类似,只是format方法返回一个格式化的字符串。

10.11 StringBuilder类和StringBuffer类

StringBuilderStringBuffer类似于String类,区别在于String 类是不可改变的。
一般来说,使用字符串的地方都可以使用StringBuilder/StringBuffer类。StringBuilder/StringBuffer类比String类更灵活。

StringBuffer类中修改缓冲区的方法是同步的,这意味着只有一个任务被允许执行该方法,除此之外,stringBuilder类与StringBuffer类是很相似的。

10.11.1 修改StringBuilder中的字符串

可以使用下图中列出的方法,在字符串构建器的末尾追加新内容,在字符串构建器的特定位置插入新的内容,还可以删除或替换字符串构建器中的字符。

请添加图片描述

10.11.3 示例学习:判断回文串时忽略既非字母又非数字的字符等

**需求:**编写一个新程序检测一个字符串在忽略既非字母又非数字的字符时是否是一个回文串。

思路:

  1. 通过删除既非字母又非数字的字符过滤这个字符串。要做到这一点,需要创建一个空字符串构建器,将字符串中每一个字母或数字字符添加到字符串构建器中,然后从这个构建器返回所求的字符串。可以使用Character类中的isLetter0rDigit(ch)方法来检测字符ch是否是字母或数字。
  2. 倒置过滤后的字符串得到一个新字符串。使用equals方法对倒置后的字符串和过滤后的字符串进行比较。

具体实现:

//P355_PalindromeIgnoreNonAlphanumeric
package com.Javabook.Demo;

import java.util.Scanner;

public class P355_PalindromeIgnoreNonAlphanumeric {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("请输入一个字符串: ");
        String s = sc.nextLine();

        System.out.println("忽略非字母数字字符后,这个字符串" + s + "是一个回文吗? " + isPalindrome(s));
    }

    public static boolean isPalindrome(String s) {

        String s1 = filter(s);

        String s2 = reverse(s1);

        return s2.equals(s1);
    }

    public static String filter(String s) {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < s.length(); i++) {
            if (Character.isLetterOrDigit(s.charAt(i))) {
                sb.append(s.charAt(i));
            }
        }

        return sb.toString();
    }

    public static String reverse(String s) {
        StringBuilder sb = new StringBuilder();
        sb.reverse();
        return sb.toString();
    }
}

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值