面向对象的一个重要目标是对代码重用的支持。支持这个目标的一个重要的机制就是泛型机制:如果除去对象的基本类型外,实现的方法是相同的,那么我们就可以用泛型实现来描述这种基本的功能。

1.使用Object表示泛型

Java中的基本思想就是可以通过使用像Object这样超类来实现泛型类。

示例一:

(1)编写MemoryCell.java

package cn.pre.example;

public class MemoryCell {

    private Object storedValue;

    public Object rend() {

        return storedValue;

    }

    public void write(Object x) {

        storedValue=x;

    }

}

当我们使用这种策略时,有两个细节必须要考虑。第一个细节(2)中阐释,它描述一个main方法,该方法把“37”写到MemoryCell对象中,然后由从MemoryCell对象读出。为了访问这种对象的一个特定方法,必须要强制转换成正确的类型(当然了,在这个例子中可以不必强制转换,因为在程序中可以调用toString()方法。这种调用对任何对象都是能够做到的)。

(2)编写TestMemoryCell并运行该类

package cn.pre.example;

import java.lang.management.MemoryNotificationInfo;

import javax.swing.event.MenuDragMouseEvent;
/**
* 使用泛型MemoryCell类
* @author youcong
* @date 2018年12月25日20点19分
*/
public class TestMemoryCell { public static void main(String[] args) { MemoryCell m = new MemoryCell(); m.write("37"); String val = (String) m.rend(); System.out.println("Content are:"+val);
// System.out.println("Content are:"+m.rend().toString());
} }

第二个重要细节是不能使用基本类型。只有引用类型能够与Object相容。

说到这,回顾一下Java的基础知识,基本类型有哪些?引用类型有哪些?基本类型和引用类型的区别是?

基本类型:byte、short、int、long、double、float、boolean、char。

引用类型:Object、数组、String和枚举等。

基本类型和引用类型的区别是:基本数据类型和引用类型的区别主要在于基本数据类型是分配在栈上的,而引用类型是分配在堆上的

 

那么什么是堆?什么是栈?

堆(heap):是一个可动态申请的内存空间(其记录空闲内存空间的链表由操作系统维护).

在java中,所有使用new xxx()构造出来的对象都在堆中存储,当垃圾回收器检测到某对象未被引用,则自动销毁该对象.所以,理论上说java中对象的生存空间是没有限制的,只要有引用类型指向它,则它就可以在任意地方被使用
.

栈(stack):是一个先进后出的数据结构,通常用于保存方法(函数)中的参数,局部变量.

在java中,所有基本类型和引用类型都在栈中存储.栈中数据的生存空间一般在当前scopes内(就是由{...}括起来的区域).

下面的示例二(可以很好的说明这一点):

package cn.pre.example;

import java.awt.HeadlessException;

public class HeapStackExample {

    public static void stackTest() {
String s1 = "abc";
String s2 = "abc";
System.out.println(s1==s2);
} public static void heapTest() { String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1==s2);
} public static void main(String[] args) { HeapStackExample.stackTest();
HeapStackExample.heapTest();
} }

在Java中有六个不同的地方可供存数据:

(1) 寄存器(register)。这是最快的存储区,因为它位于不同于其他存储区的地方——处理器内部。但是寄存器的数量极其有限,所以寄存器由编译器根据需求进行分配。你不能直接控制,也不能在程序中感觉到寄存器存在的任何迹象。

(2)堆栈(stack)。位于通用RAM中,但通过它的“堆栈指针”可以从处理器哪里获得支持。堆栈指针若向下移动,则分配新的内存;若向上移动,则释放那些
内存。这是一种快速有效的分配存储方法,仅次于寄存器。创建程序时候,JAVA编译器必须知道存储在堆栈内所有数据的确切大小和生命周期,因为它必须生成
相应的代码,以便上下移动堆栈指针。这一约束限制了程序的灵活性,所以虽然某些JAVA数据存储在堆栈中——特别是对象引用,但是JAVA对象不存储其
中。

(3)堆(heap)。一种通用性的内存池(也存在于RAM中),用于存放所以的JAVA对象。堆不同于堆栈的好处是:编译器不需要知道要从堆里分配多少存储区
域,也不必知道存储的数据在堆里存活多长时间。因此,在堆里分配存储有很大的灵活性。当你需要创建一个对象的时候,只需要new写一行简单的代码,当执行
这行代码时,会自动在堆里进行存储分配。当然,为这种灵活性必须要付出相应的代码。用堆进行存储分配比用堆栈进行存储存储需要更多的时间。

(4)静态存储(static storage)。这里的“静态”是指“在固定的位置”。静态存储里存放程序运行时一直存在的数据。你可用关键字static来标识一个对象的特定元素是静态的,但JAVA对象本身从来不会存放在静态存储空间里。

(5)常量存储(constant storage)。常量值通常直接存放在程序代码内部,这样做是安全的,因为它们永远不会被改变。有时,在嵌入式系统中,常量本身会和其他部分分割离开,所以在这种情况下,可以选择将其放在ROM中 。

(6)非RAM存储。如果数据完全存活于程序之外,那么它可以不受程序的任何控制,在程序没有运行时也可以存在。

2.基本类型的包装

当我们实现算法的时候,常常遇到语言定型问题:我们已有一种类型的对象,可是语言的语法却需要一种不同类型的对象。

这种技巧阐释了包装类的基本主题。一种典型的用法是存储一个基本的类型,并添加一些这种基本类型不支持或不能正确支持的操作。

在Java中我们已经看到,虽然每一个引用类型都和Object相容,但是,8种基本类型却不能。于是,Java为这8种基本类型中的每一种都提供了一个包装类。

例如:int类型的包装是Integer。每一个包装对象都是不可变的(就是说它的状态绝不能改变),它存储一种当该对象被构建时所设置的原值,并提供一种方法以重新得到该值。包装类也包含不少的静态使用方法。

示例三(如何能够使用MemoryCell来存储整数):

package cn.pre.example;
/**
* 基本类型包装类
* @author youcong
* @date 2018年12月25日20点19分
*/
public class WrapperDemo { public static void main(String[] args) { MemoryCell m = new MemoryCell();
m.write(new Integer(37));
Integer wrapperVal = (Integer) m.rend();
int val = wrapperVal.intValue();
System.out.println("Contents are:"+val);
} }

基本数据对应的包装类:

byte -> Byte

short ->Short

int -> Integer

long ->Long

double ->Double

float ->Float

char ->Character

boolean -> Boolean

3.使用接口类型表示泛型

只有在使用Object类中已有的那些方法能够表示所执行的操作的时候,才能使用Object作为泛型类型来工作。

例如,考虑在由一些项组成的数组中找出最大项的问题。基本的代码是类型无关的,但是它的确需要一种能力来比较任意两个对象,并确定哪个是大的,哪个是小的。因此,我们不能直接找出Object的数组中的最大元素。最简单的想法是找出Comparable的数组中最大元。要确定顺序,可以使用compareTo()方法,我们知道,它对所有的Comparable都必然是现成可用的。

现在,提出几个忠告很重要。首先,只有实现Comparable接口的那些对象才能够作为Comparable数组的元素被传递。仅有compareTo()方法但并未宣称实现Comparable接口的对象不是Comparable的,它不具有必需的IS-A关系。因为我们也许会比较两个Shape的面积,因此假设Shape实现Comparable接口。

第二,如果Comparable数组有两个不相容的对象(例如一个String和一个Shape),那么Comparable方法将抛出异常ClassCastException(类型转换异常)。

第三,如前所述,基本类型不能作为Comparable传递,但是包装类则可以,因为它们实现了Comparable接口。

第四,接口究竟是不是标准的库接口倒不是必需的。

最后,这个方案不是总能够行得通,因为有时宣称一个类实现所需的接口是不可能的。例如,一个类可能是库中的类,而接口却是用户定义的接口。如果一个类是final类,那么我们就不可能扩展它以创建一个新的类。

4.数组类型的兼容性

语言设计中的困难之一是如何处理集合类型的继承问题。设Employee IS-A Person。那么,这是不是意味着数组Employee[] IS -A Person[]呢?换句话说,如果一个例程接受Person[]作为参数,那么我们能不能把Employee[]作为参数来传递呢?

咋看,该问题不值得一问,似乎Employee[]就应该是和Person[]类型兼容的。然而这个问题比想象中要复杂。假设除Employee外,我们还有Studeng IS-A Person,并设Employee[]是和Person[]类型兼容的。此时考虑下面两条赋值语句:

Person[] arr = new Employee["5];//编译:arrays are compatible

arr[0]= new  Student(...);//编译: Studeng IS-A Person

两句都编译,而arr[0]实际上是引用一个Employee,可是Student IS-NOT-A Employee。这样就产生了类型混乱。运行时系统(runtime system)不能抛出ClassCastException异常,因为不存在类型转换。

避免这种问题的最容易的方法是指定这些数组不是类型兼容的。可是,在Java中数组却是兼容的。这叫做协变数组类型。每个数组都明了它所允许存储的对象的类型。

如果将一个不兼容的类型插入到数组中,那么虚拟机将抛出ArrayStoreException异常(数组存储异常)。

示例代码我已经提交到我的Github,有需要可克隆下载。

代码地址为:https://github.com/youcong1996/The-Data-structures-and-algorithms/tree/master/Introduction

<数据结构与算法分析>读书笔记--实现泛型构件pre-Java5的更多相关文章

  1. <数据结构与算法分析>读书笔记--利用Java5泛型实现泛型构件

    一.简单的泛型类和接口 当指定一个泛型类时,类的声明则包括一个或多个类型参数,这些参数被放入在类名后面的一对尖括号内. 示例一: package cn.generic.example; public ...

  2. <数据结构与算法分析>读书笔记--函数对象

    关于函数对象,百度百科对它是这样定义的: 重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象.又称仿函数. 听起来确实很难懂,通过搜索我找到一篇 ...

  3. <数据结构与算法分析>读书笔记--最大子序列和问题的求解

    现在我们将要叙述四个算法来求解早先提出的最大子序列和问题. 第一个算法,它只是穷举式地尝试所有的可能.for循环中的循环变量反映了Java中数组从0开始而不是从1开始这样一个事实.还有,本算法并不计算 ...

  4. <数据结构与算法分析>读书笔记--运行时间计算

    有几种方法估计一个程序的运行时间.前面的表是凭经验得到的(可以参考:<数据结构与算法分析>读书笔记--要分析的问题) 如果认为两个程序花费大致相同的时间,要确定哪个程序更快的最好方法很可能 ...

  5. <数据结构与算法分析>读书笔记--数学知识复习

    数学知识复习是<数据结构与算法分析>的第一章引论的第二小节,之所以放在后面,是因为我对数学确实有些恐惧感.不过再怎么恐惧也是要面对的. 一.指数 基本公式: 二.对数 在计算机科学中除非有 ...

  6. <数据结构与算法分析>读书笔记--运行时间中的对数及其分析结果的准确性

    分析算法最混乱的方面大概集中在对数上面.我们已经看到,某些分治算法将以O(N log N)时间运行.此外,对数最常出现的规律可概括为下列一般法则: 如果一个算法用常数时间(O(1))将问题的大小削减为 ...

  7. <数据结构与算法分析>读书笔记--要分析的问题

    通常,要分析的最重要的资源就是运行时间.有几个因素影响着程序的运行时间.有些因素(如使用编译器和计算机)显然超出了任何理论模型的范畴,因此,虽然它们是重要的,但是我们在这里还是不能考虑它们.剩下的主要 ...

  8. <数据结构与算法分析>读书笔记--模型

    为了在正式的构架中分析算法,我们需要一个计算模型.我们的模型基本上是一台标准的计算机,在机器中指令被顺序地执行.该模型有一个标准的简单指令系统,如加法.乘法.比较和赋值等.但不同于实际计算机情况的是, ...

  9. <数据结构与算法分析>读书笔记--递归

    一.什么是递归 程序调用自身的编程技巧称为递归( recursion).递归做为一种算法在程序设计语言中广泛应用. 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的 ...

随机推荐

  1. JAVA项目工具包集合

    本文包括工具的下载以及配置,持续更新中…… 1 JDK 官网:https://www.oracle.com 下载:https://www.oracle.com/technetwork/java/jav ...

  2. js中Date 方法

    Date (对象) Date 对象能够使你获得相对于国际标准时间(格林威治标准时间,现在被称为 UTC-Universal Coordinated Time)或者是 Flash 播放器正运行的操作系统 ...

  3. Django中连接redis

    1. 安装 pip install django-redis 2. settings中配置 CACHES = { "default": { "BACKEND": ...

  4. Maven 那些破事

    deploy 只上传了pom 晚上输命令,打算打包上传到本地库里,然后去服务器上部署新版本 mvn clean package deploy 结果看着mvn的build过程只是上传了pom,去库服务器 ...

  5. PHP实现批量删除(封装)

    前台 <!DOCTYPE html> <html> <head> <title>批量删除</title> </head> < ...

  6. 解决ubuntu 16.04+ Qt 5.7.1无法输入中文的问题

    解决方法: 1.命令行安装fcitx-frontend-qt5 sudo apt-get install fcitx-frontend-qt5 结果显示如下图,说明我的fcitx-frontend-q ...

  7. Ubuntu16.10上安装NodeJS6.9.2

    1.下载 https://nodejs.org/en/download/ 2.解压 tar -xJf node-v6.9.2-linux-x64.tar.xz 3. 移到通用的软件安装目录 /opt/ ...

  8. Flutter 修改TextField的高度,以及无边框圆角

    修改TextField的高度可以通过decoration: InputDecoration的contentPadding进行修改,代码如下 new TextField( decoration: Inp ...

  9. Flutter开发中的几个常用函数

    几个Flutter开发中的常用函数 /** 返回当前时间戳 */ static int currentTimeMillis() { return new DateTime.now().millisec ...

  10. 浅谈文档协作在工程设计中的应用——共享excel计算书

    我们设计过程中大量采用excel计算书,因为很多经典的计算都可以用excel解决,最最基本的就是工程量计算啦.稍微复杂的比如钢管计算,埋地钢管结构计算,顶管计算,水力学计算,波浪爬高计算,堤防高程计算 ...