软件构造 | Data Type and Type Checking

软件构造 | Data Type and Type Checking

1 Data type in programming languages

  1. primitive types 基本数据类型
    1. intlongbooleandouble
  2. object types 对象数据类型

2 Static vs. dynamic data type checking

Conversion by casting 类型转换

在这里插入图片描述

Static Typing vs. Dynamic Typing

java是静态类型语言,所有变量的类型在编译时已知,因此编译器可以推导表达式类型,在编译阶段进行类型检查(动态类型语言: 在运行阶段进行类型检查)

静态检查 >> 动态动态 >> 无检查

静态检查:可在编译阶段发现错误,避免了将错误 带入到运行阶段,可提高程序正确性/健壮性

在这里插入图片描述

3 Mutability and Immutability

Changing a variable or its value

改变一个变量、改变一个变量的值,二者有何区别?

  1. 改变一个变量:将该变量指向另 一个存储空间。
  2. 改变一个变量的值:将该变量 当前指向的存储空间中写入一个新的值。

Immutability

不变数据类型:一旦被创建,其值不能改变

在这里插入图片描述

编译器进行静态类型检查时,如判断final变量首次赋值后发生了改变,会提示错误

  1. final类无法派生 子类
  2. final变量无法改变值/引用
  3. final方法 无法被子类重写

Mutability and Immutability

  1. 不变对象:一旦被创建,始终指向同一个值/引用
  2. 可变对象:拥有方法可以修改自己的值/引用

String是一种不变对象

String s = "a";
s = s.concat("b");

在这里插入图片描述

StringBuilder是一种可变对象

StringBuilder sb = new StringBuilder("a");
sb.append("b");

在这里插入图片描述

两者的区别:当只有一个引用指向该对象 ,二者没有区别;但是有多个引用的时候,差异就出现了。

String t = s;
t = t + "c";     // t + "c"就创建了新的对象,需要在次赋给t

StringBuilder tb = sb;
tb.append("c");

在这里插入图片描述

  1. 使用不可变类型,对其频繁修改会产生大量的临时拷贝(需要垃圾回收);不可变类型更“安全”, 在其他质量指标上表现更好
  2. 可变类型因 为最少化拷贝,可以提高效率;使用可变数据类型,可获得更好的性能;也适合于在多个模块之间共享数据。传递可变对象是一个潜在的错误源泉,一旦被 无意中改变,则这种错误非常难于跟踪和发现

在这里插入图片描述

How to modify the code?

  • 通过防御式拷贝,给客户端返回一个全新的Date对象(不可变类型 不需要防御式拷贝)

在这里插入图片描述

More Examples of Defensive Copying
在这里插入图片描述

在这里插入图片描述

Defensive Copying防御式拷贝

在Java编程中,防御式拷贝(Defensive Copying)通常用于确保数据对象的不可变性和安全性。下面通过一个示例来详细解释防御式拷贝在Java中的应用:

假设我们有一个Person类,其中包含一个List类型的属性List<String> phoneNumbers,表示电话号码列表。如果我们希望在Person类的方法中避免原始的phoneNumbers列表被修改,就可以使用防御式拷贝来处理。

示例代码如下所示:

import java.util.ArrayList;
import java.util.List;

public class Person {
    private List<String> phoneNumbers;

    public Person(List<String> phoneNumbers) {
        // 使用防御式拷贝,复制传入的phoneNumbers列表
        this.phoneNumbers = new ArrayList<>(phoneNumbers);
    }

    // 获取电话号码列表
    public List<String> getPhoneNumbers() {
        // 返回一个新的列表,而不是直接返回原始phoneNumbers列表
        return new ArrayList<>(phoneNumbers);
    }
}

在上面的示例中,Person类中的构造方法和getPhoneNumbers方法都使用防御式拷贝来处理phoneNumbers属性。在构造方法中,传入的phoneNumbers列表会被复制一份,而不是直接引用;在getPhoneNumbers方法中,同样也会返回一个新的列表的副本,而不是直接返回原始列表。

通过这种方式,无论在何时调用Person类的方法,都不会影响原始的电话号码列表,因为每次都是操作副本而不是直接操作原始数据。这样可以确保数据对象的不可变性,避免数据被意外修改或破坏。

总结来说,防御式拷贝在Java中常用于确保数据对象的安全性和不可变性,通过复制数据对象的副本而不是直接操作原始数据,可以有效避免程序中因为数据修改而导致的问题。

4 Snapshot diagram as a code-level, run-time, and moment view

在这里插入图片描述

Snapshot diagrams

在Java编程中,“Snapshot diagrams”(快照图)通常指的是程序在某个时间点上(通常是调试或分析的目的)的内存状态的图形表示。这种图形表示可以帮助开发人员理解程序的内部状态、数据结构和对象之间的关系。

下面详细分析Java中快照图的概念和用途:

  1. 概念

    • 快照图是一种用于展示程序在某一时刻内存状态的图形表示方式,通常以树状结构或其他形式展示对象和数据结构之间的关系。
    • 快照图可以显示每个对象在内存中的位置、属性值、引用关系等信息,有助于开发人员理解程序的执行过程和数据流动。
  2. 用途

    • 调试分析:通过快照图,开发人员可以检查程序在某个特定时间点的内存状态,有助于排查bug和理解程序的行为。
    • 性能优化:快照图可以帮助开发人员监视内存使用情况,识别内存泄漏或不必要的对象创建,以优化程序性能。
    • 数据结构分析:通过快照图,开发人员可以更好地理解数据结构的组织和关联,从而优化数据处理算法。
  3. 工具支持

    • Java生态系统中有许多工具可以生成和显示快照图,如Java Heap Profiler、VisualVM等。这些工具通常提供了丰富的功能,帮助开发人员可视化内存状态和进行深入分析。
  4. 示例

    • 下面是一个简单的示例代码,在调试时可以使用快照图来观察对象的状态:
    public class Person {
        private String name;
        private int age;
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public static void main(String[] args) {
            Person p1 = new Person("Alice", 30);
            Person p2 = new Person("Bob", 25);
    
            // 这里可以生成快照图来观察p1和p2对象的内存状态
        }
    }
    

总而言之,快照图在Java开发中是一个重要工具,有助于开发人员理解程序的内存状态和数据结构,进而提高代码质量和性能。通过使用工具生成快照图,开发人员可以更好地管理内存资源,优化程序的执行效率。

  • Primitive values 基本类型的值

在这里插入图片描述

  • Object values 对象类型的值

在这里插入图片描述

不可变对象:用双线椭圆

在这里插入图片描述

不可变的引用:用双线箭头

在这里插入图片描述

引用是不可变的,但指向 的值却可以是可变的,可变的引用,也可指向不可变的值.

在这里插入图片描述
在Java中,垃圾回收(Garbage Collection)是一种自动内存管理机制,用于管理和释放不再被程序所引用的内存对象,从而避免内存泄漏和提高内存利用率。以下是对Java中垃圾回收机制的简要解释:

  1. 内存分配和管理

    • 在Java程序中,内存分为几个区域:堆(Heap)、栈(Stack)、方法区(Method Area)等。其中,堆是用于存储对象的内存区域。
  2. 标记-清除算法

    • Java的垃圾回收机制通常采用标记-清除算法(Mark and Sweep)来识别和释放不再被引用的对象。
    • 当Java程序中的对象不再被任何变量或引用所指向时,垃圾回收器会对这些对象进行标记,并在适当的时机将其清除(释放其占用的内存)。
  3. 垃圾回收器

    • Java的垃圾回收器是Java虚拟机(JVM)的一部分,负责执行实际的垃圾回收操作。
    • JVM会周期性地触发垃圾回收,但具体的回收时机和策略取决于具体的垃圾回收器实现。
  4. 对象的可达性分析

    • 垃圾回收器通过可达性分析来确定哪些对象是“活跃”的(即仍然被程序所引用),然后清除其他对象。
    • 对象的可达性是通过根对象(如线程栈中的变量、静态变量等)作为起点,对程序中的对象进行遍历,来判断对象是否可以被访问到。
  5. 不同类型的垃圾回收器

    • Java虚拟机提供了不同类型的垃圾回收器,如串行回收器(Serial Garbage Collector)、并行回收器(Parallel Garbage Collector)、CMS回收器(Concurrent Mark-Sweep Garbage Collector)和G1回收器(G1 Garbage Collector)等。
    • 这些回收器有不同的适用场景和性能特点,可以根据具体的应用程序需求进行选择和配置。

Java中的垃圾回收机制通过自动管理内存,使得程序员无需手动释放内存,从而减少了内存泄漏和提高了程序的可靠性。然而,开发人员也需要理解垃圾回收机制的原理和实际运行情况,以便更好地优化程序性能和内存利用。

5 Complex data types: Arrays and Collections

  • Array:Arrays are fixed-length sequences of another type T. 定长数组不可改变长度

  • List:Lists are variable-length sequences of another type T .

List<Integer> list = new ArrayList<Integer>();

在这里插入图片描述
在Java中,数组(Array)和列表(List)都是用来存储一组元素的数据结构,但它们有一些重要的区别和特性。以下是对Java中数组和列表的详细分析:

  • 数组(Array):
  1. 固定长度

    • 数组在创建时需要指定固定的长度,且长度不能动态改变。
  2. 类型必须一致

    • 数组中的元素类型必须一致,即数组中的所有元素必须是同一种数据类型。
  3. 直接存取

    • 数组可以通过索引直接存取元素,具有O(1)的读取时间复杂度。
  4. 内存分配

    • 数组在内存中是连续存储的,因此可以高效地利用缓存和内存。
  5. 长度不可变

    • 数组的长度一旦确定就不能改变。如果需要更改数组的长度,通常需要创建一个新的数组并复制原数组的元素。
  • 列表(List):
  1. 动态长度

    • 列表是一个接口,有多种实现类(如ArrayList、LinkedList等),可以根据需要动态改变长度。
  2. 元素类型可以不同

    • 列表可以存储不同类型的元素,因为它的元素类型是通过泛型参数来定义的。
  3. 灵活的操作

    • 列表提供了丰富的操作方法,如添加、删除、插入、查找等,使得对数据的操作更加灵活方便。
  4. 遍历

    • 列表提供了便利的遍历接口(如for-each循环、迭代器等),使得对列表中元素的访问更加方便。
  5. 内部实现不一定连续

    • 列表的内部实现可以是基于数组或链表等数据结构,因此它的内存分配和元素访问方式可能会有所不同。

综上所述,数组是一个静态的、固定长度且元素类型一致的数据结构,拥有高效的访问速度;而列表则是一个动态的、长度可变、元素类型不固定的数据结构,提供了更多的操作方法和灵活性。在实际应用中,开发人员需要根据具体需求来选择数组或列表来存储和处理数据。

Iterating

// Iterating an array
int max = 0;
for (int i=0; i<array.length; i++) {
	max = Math.max(array[i], max);
}

// Iterating a List
int max = 0;
for (int x : list) {
	max = Math.max(x, max);
}
  • Set:A Set is an unordered collection of zero or more unique objects.
s1.contains(e)      // test if the set contains an element
s1.containsAll(s2)  // test whether s1 ⊇ s2
s1.removeAll(s2)    // remove s2 from s1
  • Map:A Map is similar to a dictionary (key-value)
map.put(key, val)          // add the mapping key → val
map.get(key)               // get the value for a key
map.containsKey(key)       // test whether the map has a key
map.remove(key)            // delete a mapping

在这里插入图片描述

Declaration:

在这里插入图片描述

Note:We cannot create a collection of primitive types.

e.g. Set<int> does not work. but Set<Integer> numbers work)

Creating List, Set, and Map variables

List , Set , and Map are all interfaces:

  1. 它们定义了各自类型的工作方式,但不提供实施代码。
  2. 优势:用户有权在不同情况下选择不同的实现方式

Implementations of List , Set , and Map :

List( ArrayList and LinkedList)Set:(HashSet)Map( HashMap)

Note:The usual implementations of Java’s collections types — List, Set, Map — are all mutable: ArrayList, HashMap, etc.

An example iterator for ArrayList

在这里插入图片描述

在这里插入图片描述

  • 36
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值