<java编程思想>(thing in java) 阅读笔记(第十三章至第十五章)

第13章 字符串 

13.1 不可变 String 
String对象是不可变的。String类中每一个看起来会修改String值的方法,实际上都是创建了一个全新的String 对象,以包含修改后的字符串内容。而最初的String对象则丝毫未动。

对于一个方法而言,参数是为该方法提供信息,而不是想让该方法改变自己的。

13.2 重载“+”与StringBuilder
String对象是不可变的,你可以给String对象加任意多的别名。因为String对象具有只读特性,所以指向它的任何引用都不可能改变它的值。因此,也就不会对其他的引用有什么影响。
不可变性会带来一定的效率问题。为String 对象重载的“+”操作符就是一个例子。重载的意思是,一个操作符在应用特定的类是,被赋予了特殊的意义。(用于String的“+”与“+=”是java中仅有的两个重载过的操作符,而java并不允许程序重载任何操作符)。

13.3无意识的递归  287 页最下面例子 不懂 
java中的每个类从根本上都是继承自Object,标准容器类自然也不例外。因此容器类都有toString()方法,并且覆写了该方法,使得它生成的String结果能够表达容器自身,以及容器所包含的对象。

graphic

当你创建了InfiniteRecursion对象,并将其打印出来的时候,你会得到一串非常长的异常。
这里发生了自动类型转换,由 InfiniteRecursion类型转换成String 类型。因为编译器看到一个String对象后面跟着一个“+”,而再后面的对象不是String,于是编译器试着将this转换成一个String,它怎么转换呢,正是通过调用this上的toString()方法,于是就发生了递归调用。

13.4 String
方法:构造器
重载版本:默认版本,String ,String-Builder,String-Buffer ,char数组,byte数组 
应用:创建String对象 
当需要改变字符串的内容时,String类的方法都会返回一个新的String对象。同时,如果内容没有发生改变,String的方法只是返回指向原对象的引用而已。

13.5格式化输出
13.5.1printf()
它使用一个简单的格式化字符串,加上要插入其中的值,然后将其格式化输出。printf()并不使用重载的“+”操作符来连接引号内的字符串或字符串变量,而是使用特殊的占位符来表示数据将来的位置。而且它还将插入格式化字符串的参数,以逗号分隔,排成一行。

13.5.3Formatter类
在java中,所有新的格式化功能都由java.util.formatter类处理。可以将Formatter看作一个编译器,它将你的格式化自发i穿与数据翻译成需要的结果。当你创建一个Formatter对象的时候,需要向 其构造器传递一些信息,告诉她最终的结果将向哪里输出。
%s,它表示插入的参数是String类型。这个例子使用的是最简单的格式修饰符,只具有转换类型而没有其他功能 

13.5.4格式化说明符 
应用不同类型的数据转换时,percision则不然,不是所有类型的数据都能使用precision,而且应用不同类新的数据转换时,precision的意义也不同。
在将precision应用于String时,它表示打印String时输出字符的最大数量。而将precision应用于浮点数时,它表示小数部分要显示出来的位数(默认是6位小数),如果小数位数过去则舍入,太少则在尾部补零。

13.5.5 Formatter转换
类型转换符  d:整数型(十进制)。e:浮点数(科学计数)。c:Unicode字符。x:整数(十六进制)。b:boolean值。h:散列码(十六进制)。s:String。%:字符%。f:浮点数(十进制)

程序中的每个变量都用到了b转换。虽然它对各种类型都是合法的, 但其行为却不一定与你想象的一致。对于boolean基本类型或Boolean对象,其转换结果是对应的,true或false。但是,对其他类型的参数,只要该参数不为null,那转换的结果就永远都是true。

13.6 正则表达式 

13.6.1 基础 
一般来说,正则表达式就是以某种方式来描述字符串,因此你可以说,如果一个字符串含有这些东西,那么它就是我正在找的东西。在其他语言中,\\表示“我想要在正则表达式中插入一个普通的(字面上)的反斜线,请不要给它任何特殊的意义。”而在java中,\\的意思是“我要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义。”例如,如果你想表示一位数字,那么正则表达式应该是\\d,如果,你想插入一个普通的反斜线,则应该这样\\\\。不过换行和制表符之类的东西只需要使用单反斜线。

\W,它的意思是非单词字符(如果W小写,\w,则表示一个单词字符)。

13.6.2 创建正则表达式
字符:B 指定字符B 。\xhh  十六进制值为oxhh的字符  。\uhhhh  十六进制表示为oxhhhh的Unicode的字符 。  \t 制表符 Tab  。\n 换行符 。 \r 回车 。\f换页 。\e 转义。

字符类:   .  任何字符。[abc] 包含a b 和c的任何字符 。[^abc]除了 a b和c之外的任何字符。
[a-zA-Z]从a到z或从A到Z的任何字符 (范围)。[abc [hij]]任意abchij字符。[a-z&&[hij]]任意h、i或j(交)。\s 空白符(空格、tab、换行、换页和回车)。\S 非空白符([^\s])。\d 数字[0-9]。\D 非数字[^0-9]。\w 词字符[a-zA-Z0-9]。\W 非词字符[^\w]

逻辑运算符
XY        Y跟着X后面 
X|Y       X或Y
(X)     

13.6.3 量词 
量词莫属了一个模式吸收输入文本的方式:
1、贪婪型:量词总是贪婪的,除非有其他的选项被设置。贪婪表达式会为所有可能的模式发生尽可能多的匹配。
2、勉强型:用问号来指定,这个量词匹配模式所需的最少字符数。因此也称作懒惰的,最少匹配的,非贪婪的,或不贪婪的。
3、占有型:目前,这些类型的两次只有在java语言中才可用,并且也更高级,因此我们大概不会立刻用到它。当正则表达式被应用于字符串时,它会产生相当多的状态,以便在匹配失败时可以回溯。

13.6.4Pattern和Matcher

13.6.7 reset()

13.7扫描输入 
input元素使用的类来自java.io。String reader将String转哈uwei可读的流对象,然后用这个对象来构造BufferReader对象,因为我们要使用BufferReader的readline()方法。最终,我们可以使用input对象一次读取一航文本,就像是从控制台读入标准输入一样。

Scanner的构造器可以接受任何类型的输入对象,包括File对象,InputStream、String或者像比例中的Readeble对象。Readable是java SE5中新加入的一个接口,表示“具有Read()方法的某种东西”。有了Scanner,所有的输入、分词以及翻译的操作都隐藏在不同类型的next方法中。普通的next方法返回下一个String。所有的基本类型都有对应的next方法。包括Bigdecimal和BigInteger。所有的Next方法,只有在找到一个完整的粉刺之后才会返回。

13.7.2 用正则表达式扫描 

第14章 类型信息  

运行时类型信息使得你可以在程序运行时发现和使用类型信息。
 314页
这是一个典型的类层次结构图,基类位于顶部,派生类向下扩展。面向对象变成中基本的目的是:让代码只操纵对基类(这里是shape)的引用。这样,如果要添加一个新类来扩展程序,就不会影响到原来的代码。在这个例子的shape接口中动态绑定了draw()方法,目的就是让客户端程序员使用泛化的shape因用来调用draw()。draw()在所有派生类里都会被覆盖,并且由于它是动态绑定的,所以即使是通过泛化的shape引用来调用,也能产生正确行为,这就是多态。

当从数组中取出元素时,这种容器——实际上它将所有的事物都当作Object持有——会自动将结果转型回Shape。这也是RTTI最基本的使用形式,因为在java中,所有的类型转换都是在运行时进行正确性检查的。这也是RTTI名字的含义:在运行时,识别一个对象的类型。

在这个例子中,RTTI类型转换并不彻底:Object被转型为Shape,而不是转型为Circle,Square或者Triangle。这是因为目前我们只知道这个List<Shape>保存的都是shape。在编译时,将由容器和java的泛型系统来强制确保这一点;而在运行时,由类型转换操作来确保这一点。

接下来就是多态机制的事了,shape对象实际执行什么样的代码,是由引用所指向的具体对象Circle,Square或者Triangle而决定的。

14.2  Class对象
要理解RTTI在java中的工作原理。首先必须知道类型信息在运行时是如何表示的。这项工作是由成为class对象的特殊对象完成的,它包含了与类有关的信息。事实上,Class对象就是用来创建类的所有常规对象的。Java使用Class对象来执行其RTTI,即使你正在执行的是类型转型这样的操作。Class类还拥有大量的使用RTTI的其他方式。

类是程序的一部分。每个类都有一个Class对象,换言之,每当编写并且编译了一个新类,就会产生一个Class对象。为了生成这个类的对象,运行这个程序的java虚拟机将使用被成为类加载器的子系统。

类加载器子系统实际上可以包含一条类加载器链,但是只有一个原生类加载器,它是jvm实现的一部分。
原生类加载器夹在的是所谓的可信类,包括 Java api类,它们通常是从本地盘加载的。在这条链中,通常不需要添加额外的类加载器,但是如果你有特殊需求(例如以某种特殊方式加载类),那么你有一种方式可以挂接额外的类加载器。

所有的类都是在对第一次使用时,动态加载到jvm中。当程序创建第一个对类的静态成员的引用时,就会加载这个类。这个证明构造器也是类的静态方法,即使在构造器之前并没有使用static关键。因此使用new 操作符创建类的新对象也会被当作对类的静态成员的引用。

java程序在它运行之前并非被完全加载,其各个部分是在必需时才加载的。

类加载器首先检查这个类的class对象是否已经加载。如果尚未加载,默认的类加载器就会根据类型查找.class文件。一旦某个类的class对象被载入内存,它就被用来创建这个类的所有对象。

Class.forName
这个方法是Class类(所有Class对象都属于这个类)的一个static成员。Class对象就和其他对象一样,我们可以获取并操作它的引用(这也就是类加载器的工作)。forName()是取得Class对象的引用的一种方法。它是用一个包含目标类的文本名的String做输入参数,返回的是一个Class对象的引用,上面的代码忽略了返回值。对forName()的调用是为了它产生的“副作用”:如果类Gum还没有被加载就加载它。

无论何时,只要你想在运行时使用类型系,就必须首先获得对恰当的Class对象的引用。
Class.forName()就是实现此功能的便捷途径,因为你不需要为了获得Class引用而持有该类型的对象。但是,如果你已经有了一个感兴趣的类型的对象,那就可以通过调用getClass()方法来获取Class引用了。

Class的newInstance()方法是实现“虚拟构造器”的一种途径,虚拟构造器允许你声明“我不知道你确切的类型,但是无论如何要正确地创建你自己。”在前面的示例中,up仅仅只是一个Class引用。

14.2.1 类字面常量
java还提供了另一种方法来生成对Class对象的引用,即使用类字面常量,对上述程序来说,就像下面这样:FancyToy.class。
类字面常量不仅可以应用于普通的类,也可以应用于接口、数组以及基本数据类型,另外,对于基本数据类型的包装器类,还有一个标准字段TYPE。TYPE字段是一个引用,指向对应的基本数据类型的Class对象。

当使用".class"来创建对Class对象的引用,不会自动地初始化该Class对象,为了使用类而做的准备工作实际包含三个步骤:
1、加载,这是由类加载器执行的。该步骤将查找字节码,并从这些字节码中创建一个Class对象
2、链接。在链接阶段将验证类中的字节码,并从这些字节码中创建一个Class对象
3、初始化。如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。
初始化被延迟到了对静态方法(构造器隐式地是静态的)或者非常数静态域进行首次引用时才执行。

如果一个static域不是final的,那么在对它访问时,总是要求在它被读取之前,要先进行链接(为这个域分配存储空间)和初始化(初始化该存储空间)

14.2.2泛化的Class引用
class引用总是指向某个Class对象,它可以制造类的实例,并包含可作用于这些实例的所有方法代码,它还包含该类的静态成员,因此,Class引用表示的就是它所指向的对象的确切类型,而该对象便是Class类的一个对象。

当你将泛型语法用于Class对象是,会发生一件很有趣的事情:newInstance()将返回该对象的确切类型,而不仅仅只是在ToyTest.java中看到的基本的Object。

14.2.3 新的转型语法 
cast()方法接受参数对象,并将其转型为Class引用的类型。

14.3类型转换前先检查
迄今为止,我们已知的RTTI形式包括:
1)传统的类型转换,如,“(shape)”,由RTTI确保类型转换的正确性。
2)代表对象的类型的Class对象。通过查询Class对象可以获取运行时所需的信息。
3)RTTI在java中还有第三种形式,就是关键字instanceof。它返回一个布尔值,告诉我们对象不是不是某个特定类型的实例。

对instanceof有比较严格的限制:只可将其与命名类型进行比较,而不能与Class对象做比较。

14.3.2 动态的instanceof

14.4注册工厂 
你可能会考虑在每个子类中添加静态初始化器,以使得该初始化器可以将它的类添加到某个List中。遗憾的是,静态初始化器只有在类首先被加载的情况才能被调用:生成器在其列表中不包含这个类,因此它永远不能创建这个类的对象,而这个类也就不能被接在并至于这个列表中。

你被强制要求自己去手工创建这个列表,因此,你最佳的做法是,将这个列表置于一个位于中心的,位置明显的地方,而我们感兴趣的继承结构的基类可能就是这个最佳位置。

这里我们需要做的其他修改就是工厂方法设计模式,将对象的创建工作交给类自己去完成。工厂方法可以被多态地调用,从而为你创建恰当类型的对象。

14.5      instanceof 与class的等价性
使人放心的是,instanceof 和 isinstanceof()生成的结果完全一样,equals()和==也一样。但是这两组测试得出的结果却不相同。instanceof保持了类型概念,它指的是“你是这个类吗,或者你是这个类的派生类吗?”

14.6 反射:运行时的类信息
如果不知道某个对象的确切类型,RTTI可以告诉你。但是有一个限制,这个类型在编译时必须已知,这样才能使用RTTI识别它,并利用这些信息来做一些有用的事。换句话说,在编译时,编译器必须知道所有要通过RTTI来处理的类。

在编程时通过设置构件的属性值来配置它们。这种设计时的配置,要求构件都是可实例化的。并且要暴露其部分信息,以允许程序读取和修改构件的属性。此外,处理图形化用户界面事件的构建还必须暴露相关方法的信息,以便IDE能够帮主程序覆盖这些处理事件的方法。反射提供了一种机制——用来检查可用的方法,并返回方法名。java通过javaBeans提供了基于构件的编程结构。

人们想要在运行时获取类的信息的另一个动机,便是希望提供在跨网络的远程平台上创建和运行对象的能力。这被成为远程方法调用,它允许一个java程序将对象分布到多台机器上。于是这台机器就成为了描述这些动作的公共场所。

Class类与java.lang.reflect类库一起对反射的概念进行了支持,该类库包含了Field,Method,以及Constructor类(每个类都实现了Member接口)。这些类型的对象是由JVM在运行时创建的。
另外,还可以调用getFields(),getMethods()和getConstructors()等很便利的方法,以返回表示字段、方法以及构造器的对象的数组。这样,匿名对象的类信息就能在运行时完全确定下来,而在编译时不需要知道任何事情。

重要的是,要认识到反射机制并没有什么神奇之处。当通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类,(就像RTTI那样)。在用它做其他事情之前必须先加载那个类的Class对象。因此,那个类的.class文件对于JVM来说必须是可获取的:要么在本地机器上,要么可以通过网络取得。所以RTTI和反射之间的真正区别只在于,对RTTI来说,编译器在编译时打开和检查.class文件。(换句话说,我们可以用普通方式调用对象的所有的方法。)而对于反射机制来说,.class文件在编译时是不可获取的,所以在运行时打开和检查.class文件。

14.6.1 类方法提取器 

14.7 动态代理 
代理是基本的设计模式之一。它是你为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象。这些操作通常涉及“实际”对象的通信,因此代理通常充当着中间人的角色。

在任何时刻,只要你想要将额外的操作从实际对象中分离到不同的地方,特别是当你希望能够很容易作出修改,从没有使用额外操作转为使用这些操作,或者反过来,代理就显得很有用。(设计模式的关键就是封装修改——因此你需要修改事务以证明这种模式的正确性)。
java的动态代理,因为它可以动态地创建代理并动态地处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的对策。

通过调用静态方法Proxy.newProxyInstance()可以创建动态代理,这个方法需要得到一个类加载器(你通常可以从已经被加载的对象中获取其类加载器,然后传递给它),一个你希望该代理实现的接口列表,以及InvocationHandler接口的一个实现。

14.8空对象
空对象最有用之处在于它更靠近数据,因为对象表示的是问题空间内的实体。有一个简单的例子,许多系统都有一个Person类。通常你会使用一个null引用并测试它。与此不同的是,我们可以使用空对象。

14.8.1 模拟对象与桩 
空对象的逻辑编题是模拟对象与桩。模拟对象和桩都只是加班可以可以传递实际信息的存活对象,而不是像空对象那样可以成为null的一种更加智能化的替代物。
模拟对象和桩之间的差异在于程度不同。模拟对象往往是轻量级和自测试的。通常很多模拟对象被创建出来是我i饿了处理各种不同的测试情况。庄只是返回桩数据,它都是重量级的,并且经常在测试之间被复用。

14.10总结 
RTTI允许通过匿名基类的引用来发现类型信息。初学者极易误用它,因为在学会使用多态调用方法之前,这么做也很有效。

第15章 泛型 
一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚会很大。

如果一个方法的参数是一个接口,而不是一个类,这种限制就放松了很多,因为任何实现了该接口的类都能够满足该方法,这也包括暂时还不存在的类。

泛型实现了参数化类型的概念,使代码可以应用于多种类型,泛型这个术语的意思是:适用于许多许多的类型。泛型最初出现,目的是希望类或方法能够具备最广泛的表达能力,正是通过解耦类或方法与所使用的类型之间的约束。

15.2 简单泛型 
促成泛型的出现,而最引人注目的一个原因,就是为了创造容器类。容器,就是存放要使用的对象的地方。
泛型的主要的目的之一就是指定容器要持有什么类型的对象,而且由编译器来确保类型的正确性。

15.2.1 一个元组类库 
元组,它是将一组对象直接打包存储于其中的一个单一对象。这个容器对象允许读取其中元素,但是不允许向其中存放新的对象(这个概念也被称为数据传递对象或信使)。

15.2.2 一个堆栈类 

15.2.3 RandomList

15.3 泛型接口 
泛型也可以应用于接口。例如生成器,这是一种专门负责创建对象的类。实际上,这是工厂方法设计模式的一种应用。不过,当使用生成器

15.4 泛型方法 
泛型方法使得该方法能够独立于类而产生变化,以下是一个基本的指导原则:无论何时,只要你能做到,你就应该尽量使用泛型方法。也就是说,如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法。

注意:当使用泛型类时,必须在创建对象的时候指定类型参数的值,而使用泛型方法的时候,通常不必指明参数类型,因为编译器会为我们找出具体的类型。这称为类型参数推断。

15.4.1杠杆利用类型参数推断。
类型推断只对赋值操作有效,其他时候并不起作用。如果你将一个泛型方法调用的结果作为参数,传递给另一个方法,这是编译并不会执行类型推断。在这种情况下,编译器认为:调用泛型方法后,其返回值被赋给一个Object类型的变量。

15.4.2可变参数与泛型方法

15.4.4一个通用的Generator

15.4.5简化元组的使用

15.4.6 一个set实用工具 

15.7擦除的神秘之处 
当你开始更深入地钻研泛型时,会发现有大量的东西初看起来是没有意义的。例如,尽管可以声明ArrayList.class,但是不能声明ArrayList<Integer>.class。

在泛型代码内部,无法获得任何有关泛型参数类型的信息。

Java 泛型是使用擦除来实现的,这意味着当你在使用泛型时,任何具体的类型信息都被擦除了。你唯一知道的就是你在使用一个对象。这两种形式都被擦除成它们的原生类型。

当你希望使用的类型参数比某个具体类型更加泛化时——也就是说,当你希望代码能够跨多个类工作时,使用泛型才有所帮主,因此,类型参数和它们在有用的泛型代码中的引用,通常比简单的类替换要复杂。

15.7.2 迁移兼容性 
例如,当List<T>这样的类型注解将被擦除为List,普通的类型变量在未指定边界的情况下将被擦除为Object。

擦除的核心冬季是它使得泛化的客户端可以用非泛化的类库来使用,反之亦然,这也经常被成为“迁移兼容性 ”。
因此java泛型不仅必须支持向后兼容性,即现有的代码和类文件仍旧喝法,并且继续保持其之前的含义;而且还要支持迁移兼容性,使得类库按照它们自己的步调变为泛型,并且当某个类库变为泛型时,不会破坏依赖于它的代码和应用程序。

15.7.3 擦除的问题 
擦除的代价是显著的。泛型不能用于显式地应用运行时类型的操作之中,例如转型、instanceof操作和new表达式。因为所有关于参数的类型信息都丢失了,无论何时,当你在编写泛型代码时,必须时刻提醒自己,你只是看起来好像拥有有关参数的类型信息而已。

另外,擦除和迁移兼容性意味着,使用泛型并不是强制的。
即使擦除在方法和类内部移除了有关实际类型的信息,编译器仍旧可以确保在方法和类中使用的类型的内部一致性。
因为擦除在方法体中移除了类型信息,所以在运行时的问题就是边界:即对象进入和离开方法的地点。这些正式编译器在编译期执行类型检查并插入转型代码的地点。在泛型中的所有动作都发生在边界处,对传递进来的值进行额外的编译期检查,并插入对传递出去的值的转型。“边界就是发生动作的地方。”

15.8 擦除的补偿

15.8.1创建类型实例
java中的解决方法是传递一个工厂对象,并使用它来创建新的实例。最便利的工厂对象就是class对象,因此如果使用类型便签,那么你就可以使用newInstance()来创建这个类型的新对象。

15.8.2泛型数组
在运行时,它仍旧是Object数组,而这将引发问题。成功创建泛型数组的唯一方式就是创建一个被擦除类型的新数组,然后对其转型。

15.10 通配符 

15.10.2逆变
还可以走另外一条路,即使用超类型通配符。这里可以声明通配符是由某个特定类的任何基类来界定。方法是指定<? super MyClass>,甚至或者使用类型参数:<? super T >(尽管你不能对泛型参数给出一个超类型边界,即不能声明<T super MyClass>)。这使得你可以安全地传递一个类型对象到泛型类型中。
参数Apple是Apple的某种类型的List,这样你就知道向其中添加Apple或Apple的子类型是安全的,但是既然Apple是下界,那么你可以知道向这样的List中添加Fruit是不安全的,因为这将使List敞开扣字,从而可以向其中添加非Apple类型的对象,而这是违反静态类型安全的。

15.10.3无界通配符 
无界通配符<?>看起来意味着“任何事物”

15.11问题 

15.11.1任何基本类型都不能作为类型参数。
解决之道是使用基本类型的包装器类以及java  SE5的自动包装机制。

15.11.2 实现参数化接口 
一个类不能实现同一个泛型接口的两种变体。由于擦除的原因,这两个变体会成为相同的接口。

15.11.3 转型和警告 
使用带有泛型类型参数的转型或instanceof不会有任何效果,下面的容器在内容将各个值存储为Object。
并在获取这些值时,再将它们转型回T。

15.11.4 重载 

15.12自限定的类型 
class  selfBounded<T extends SelfBounded<T>>{ }

15.12.1古怪的循环泛型
我在创建一个新类,它继承自一个泛型类型,这个泛型类型接受我的类的名字作为参数。它能够产生使用导出类作为其参数和返回类型的基类。它还能将导出类型用作其域类型,甚至那些将被擦除为Object的类型。
CRG的本质:基类用导出类替代其参数。这意味着泛型基类变成了一种其所有导出类的公共功能的模板,但是这些功能对于其所有参数和返回值,将使用导出类型。

15.12.2 自限定 
自限定将采取额外的步骤,强制泛型当作其自己的边界参数来使用,观察所产生的类可以如何使用以及不可以如何使用。

15.12.3参数协变 
自限定类型的价值在于它们可以产生协变参数类型——方法参数类型会随子类而变化。尽管自限定类型还可以产生于子类类型相同的返回类型。

15.13 动态类型安全 

15.14异常 
由于擦除的原因,将泛型应用于异常是非常受限的。catch语句不能捕获泛型类型的异常,因为在编译期和运行时都必须知道异常的确切类型,泛型类也不能直接或间接继承自Throwable(这将进一步阻止你去定义不能捕获的泛型异常)。
但是,类型参数可能会在一个方法throws子句中用到。

15.15 混型 
术语混型最基本的概念是混合多个类的能力,易产生一个可以辨识混型中所有类型的类。
混型的价值之一是它们可以将特性和行为一致的应用于多个类之上。

15.15.3 使用装饰者模式 
装饰器模式使用分层对象来动态透明地向单个对象中添加责任。装饰器指定包装在最初的对象周围的所有对象都具有相同的基本接口。某些事物是可装饰的,可以通过将其他类包装在这个可装饰对象的四周,来将功能分层。这使得对装饰器的使用是透明的——无论对象是否被装饰,你都拥有一个可以向对象发送的公共消息集。装饰类也可以添加新方法,但是正如你所见的,这将是受限的。
装饰器是通过使用组合和形式化结构(可装饰物/装饰器层次结构)来实现的。而混型是基于继承的。因此可以将基于参数化类型的混型当作是一种泛型装饰器机制。

15.15.4 与动态代理混合 
可以使用动态代理来创建一种比装饰器更贴近混型模型的机制。通过使用动态代理。所产生的动态类型将会是已经混入的组合型。

15.16 潜在类型机制 
泛型代码典型地将在泛型类型上调用少量方法,而具有潜在类型机制的语言只要求实现某个方法子集,而不是某个特定类或接口,从而放松了这种限制。因此,实际上一段代码可以声明:我不关心你是什么类型,只要你可以speak()和sit()即可。

15.17对缺乏潜在类型机制的补偿

15.17.1 反射 

15.17.3
15.17.4 用适配器仿真潜在类型机制 
潜在类型机制将爱这里实现什么?它意味着你可以编写代码声明:“我不关心我这里使用的类型,只要它具有这些方法即可。”实际上,潜在类型机制创建了一个包含所需方法的隐式接口。因此它遵循这样的规则:如果我们手工编写了必需的接口,那么它就应该能够解决问题。

从我们拥有的接口中编写代码来产生我们需要的接口,这是适配器设计模式的一个典型示例。

15.18 将函数对象用作策略 
策略设计模式将变化的事物完全隔离到一个函数对象中。函数对象就是在某种程度上行为像函数的对象,一般地会有一个相关的方法(在支持操作符重载的语言中,可以创建对这个方法的调用,而这个调用看起来和普通的方法调用一样)。函数的对象价值在于,与普通的方法不同,它们可以传递出去,并且还可以拥有在多个调用之间持久化的状态。但是函数对象主要是由其目的来区别的,这里的目的就是创建某种事务,使它的行为就像一个可以传递出去的单个方法一样,这样,它就喝策略设计模式紧耦合了,有时甚至无法区分。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值