`
aihhd2008
  • 浏览: 25746 次
  • 性别: Icon_minigender_1
  • 来自: 合肥
最近访客 更多访客>>
社区版块
存档分类
最新评论

类和接口

    博客分类:
  • java
阅读更多
使类和成员的可访问能力最小化
public表示这个类在任何范围都可用。
protected表示只有子类和包内的类可以使用
private-package(default)表示在包内可用
private表示只有类内才可以用
在设计的时候应该尽可能的使每一个类或者成员不被外界所访问。在设计一个类的时候应该尽量的按照4321得顺序设计。如果一个类只是被另一个类使用,那么应该考虑把它设计成这个类的内部类。通常 public的类不应该有public得字段,不过我们通常会用一个类来定义所有的常量,这是允许的。不过必须保证这些字段要么是基本数据类型要么引用指向的对象是不可修改的。不然他们将可能被修改。例如下面的定义中data就是不合理的,其他人可以改变数组中的内容,有安全漏洞,后面两个没有问题。
public class Con    
{    
   public static final int[] data = {1,2,3};// it is bad    
   public static final String hello = "world";    
   public static final int i = 1;    
}    
public class Con { public static final int[] data = {1,2,3};// it is bad public static final String hello = "world"; public static final int i = 1; } 
 

解决data安全隐患的方法有两种:
①将公有方法替换为一个私有数组,以及一个公有的非可变列表
private static final Type[] PRIVATE_VALUES = {...};    
public static final List VALUES = Collections.unmodifiableLis(Arrays.asList(PRIVATE_VALUES));   
private static final Type[] PRIVATE_VALUES = {...}; public static final List VALUES = Collections.unmodifiableLis(Arrays.asList(PRIVATE_VALUES)); 


②把公有的数组替换为一个公有的方法,它返回私有数组的一份拷贝
private static final Type[] PRIVATE_VALUES = {...};    
public static final Type[] values(){    
    return (Type[])PRIVATE_VALUES.clone();    
}   
private static final Type[] PRIVATE_VALUES = {...}; public static final Type[] values(){ return (Type[])PRIVATE_VALUES.clone(); } 

总之,应该防止把任何杂散的累接口和成员变成API的一部分,除了公有静态final域的特殊情形之外,公有类不应该包含公有域,并且确保有静态final域所引用的对性是不可变的。
 
  支持非可变性
为了使一个类成为非可变类,要遵循下面五条规则:
①不要提供任何会修改对像的方法;
②保证没有可被子类改写的方法;
③使所有的域都是final的;
④使所有的域都成为私有的;
⑤保证对于任何可变组件的互斥访问。(如果一个类指向可变对象的域,则必须确保该类的客 户无法活得指向这些对象的引用,并且永远不要用客户提供的对象引用来初始化这样的域,也不要在任何一个访问方法中返回该对象的引用);
以上规则比真正的要求强了一点,为了提高性能可以有所方式,如:保证没有一个方法能够对对象的状态产生外部可见的改变,许多非可变的类拥有一个或者多个非final的冗余域,把一个开销昂贵的计算结果缓存在这些域中。
非可变对象本质上是线程安全的,它们不要求同步。非可变对象可以被自由地共享。你不仅可以共享非可变对象,甚至也可以共享它们的内部信息。非可变对象为其他对象--无论是可变的还是不可变的--提供了大量的构件。
非可变类真正唯一的缺点是,对于每一个不同的值都要求一个单独的对象。String就是这样的。通常有个解决的办法就是提供一个帮助类来弥补,例如StringBuffer类。
如果一个类不能被做成非可变类,那么你仍然应该尽可能地限制它的可变性。
构造函数应该创建完全初始话的对象,所有的约束关系应该在这时候建立起来,构造函数不应该吧“只构造看一部分的实例”传递给其他的方法,不应该在后者函数之外子踢狗一个公有的初始化方法。
使一个类成为非可变类有如下三种方法:
①将一个类声明为final类型的;
②让该类中的每一个方法都成为final的,这种方法的好处在于其子类可以继续扩展新的方法;
③把类的构造函数声明为私有的或者包级私有的,增加静态工厂方法,来代替公有的构造函数;(该方法虽然不常用,但却是最值得推荐的)
 
复合优先于继承
       实现代码重用最重要的办法就是继承,但是继承破坏了封装,导致软件的键壮性不足。如果子类继承了父类,那么它从父类继承的方法就依赖父类的实现,一旦他改变了会导致不可预测的结果。如果子类和超类在不同的包中,并且超类并不是为了扩展而设计的,那么继承会导致脆弱性。作者介绍了InstrumentedHashSet作为反例进行说明,原因就是没有明白父类的方法实现。作者给出的解决办法是通过化合来代替继承,尤其是当存在一个适当的接口来实现一个包装类的时候,用包装类和转发方法来解决问题。把想扩展的类作为本类的一个private final得成员变量。把方法参数传递给这个成员变量并得到返回值。这样做的缺点是这样的类不适合回掉框架。继承虽然好,我们却不应该滥用,只有我们能确定它们之间是is-a得关系的时候才使用。

要么专门为继承而设计,并给出文档说明,要么禁止继承
 对并没有文档说明的类进行继承是非常危险的,它的公有方法有可能被改变。在设计一个专门用来继承的类时必须注意以下几点(不适用于final类):
①必须精确地描述改写每个方法带来的影响,虽然这样的描述违法了文档格言“好的API文档应该描述一个方法做了什么工作,而不是描述它如何做”,但这也是继承破坏了程序的封装性而导致的。
②允许继承的类的构造函数一定不能调用可被改写的方法,无论是直接进行还是间接进行。因为超类的构造函数会在子类的构造函数之前运行,所以子类中改下版本的方法将会在子类的构造函数运行之前就被调用。如:
package com.ilibaba.test;    
import java.util.*;    
public class SubClass extends SuperClass {    
    private final Date date;    
    public SubClass() {    
        date = new Date();    
    }    
    public void m() {    
        System.out.println(date);    
    }    
    public static void main(String[] args) {    
        SubClass s = new SubClass();    
        s.m();    
    }    
}    
class SuperClass {    
    public SuperClass() {    
        m();    
    }    
    public void m() {    
        System.out.println("uuu");    
    }    
}   
package com.ilibaba.test; import java.util.*; public class SubClass extends SuperClass { private final Date date; public SubClass() { date = new Date(); } public void m() { System.out.println(date); } public static void main(String[] args) { SubClass s = new SubClass(); s.m(); } } class SuperClass { public SuperClass() { m(); } public void m() { System.out.println("uuu"); } } 
 

“函数一定不能调用可被改写的方法”的实现方式:把每个可改写的方法的代码体移到一个私有的“辅助方法”中,并且让每个可改写的方法调用他的私有辅助方法,然后用“直接调用可改写方法的私有辅助方法”来代替“可改写方法的每个自用调用”。
 在为了继承而设计类的时候,不推荐实现Cloneable和Serializable接口。clone和readObject方法在行为上和构造函数相似——“一定不能调用可被改写的方法,无论是直接进行还是间接进行”,对于readObject方法,子类中改写版本的方法将子类的状态被反序列化之前运行,而对于clone方法,改写版本的方法将在子类的clone方法有机会修正被克隆对象的状态之前被运行,无论哪种情形,它们都会不可避免的导致程序失败。
 对于既不是final类,也不是为了子类化而设计和编写文档的普通类而言,防止出现问题的最好方法是禁止子类化,方法有两种:
①把这个类声明为final的。
②把所有的构造函数变成私有的,或者包级私有的,并增加一个公有的静态工厂方法来代替构造函数。
继承的另一个替代方案就是利用包装类模式 
接口优于抽象类
接口和抽象类的区别:
①抽象类允许包含默写方法的实现,而接口是不允许的;
②一个类要实现抽象类,它必须成为抽象类的一个子类,而实现接口的类只要定义了所要求的方法,并遵守通用的约定,不管这个类位于类层次的哪个地方;
接口可以构造出非层次结果的类型框架,比如一个接口可以继承多个其他的接口。还可以安全地增加一个类的功能。
当然,也可以把接口和抽象类的有点结合起来,对于你期望导出的每一个总要的接口,头提供一个抽象的骨架实现类,这样,接口的作用仍然是定义类型,骨架实现类负责所以与接口实现相关的工作。
抽象类也有明显的优势,它可以在一个类的后续的版本中方便的增加一个新的方法,但不影响到其他相关的类,而接口则做不到这一点。
总之,接口通常是定义具有多个实现类型的最佳途径(例外的情况:当演化的容易性比灵活性和功能更为重要的时候,应该使用抽象类来定义类型,但也必须理解抽象类的局限性,并确保可以接受这些局限性),如果已经导出了一个重要的接口,那么,也应该考虑同时提供一个骨架实现类。最后,应该尽可能的谨慎设计所有的公有接口,并通过编写多个实现来对它们进行全面的测试。
 
接口只是被用于定义类型
接口只是用来定义一个类型,不要把接口用来做其他的事情(如在接口中定义常量,这种常量接口模式是对接口的不良使用)。
如果要导出常量,可以有以下几种方式:
①如果这些常量与某个已有的类或者接口有着紧密的联系,则可以把常量添加到这个类或者接口中。
②定义一个类型安全的枚举类,把这些常量看做枚举类型的成员。
③使用一个可以实例化的工具类(构造函数设为privat类型)来导出这些常量。

优先考虑静态成员类
嵌套类只为它的外围类提供服务。
嵌套类分为四种:静态成员类、非静态成员类、匿名类和局部类(后面三种称为内部类)
如果一个嵌套类的实例可以在它外类类的实例之外独立存在,则这个嵌套类应该设置成静态成员类(即:如果你声明的成员类不要访问外围实例,那么应该把static修饰符放到成员类的声明中)。
匿名类的用法:
①创建一个函数对象;
②创建过程对象;
③在静态工厂方法的内部使用;
④在复杂的类型安全枚举类型(它要求为每个实例提供单独的子类)中,用于公有的静态final域的初始化器中;
总结:四种不同的嵌套类都有自己不同的用途,如果一个嵌套类需要在单个方法之外任然是可见的,或者它太长,不适合放在一个方法内部,那么应该使用成员类,如果成员类的每个实例都需要一个指向其外围实例的引用,则把成员类做成非静态的;否则就做成静态的,假设一个嵌套类属于一个方法的内部,如果你只需要在一个地方创建它的实例,并且已经有了一个预先存在的类型可以说明这个类的特征,则把它做成匿名类;否则就做成局部类。
 




0
0
分享到:
评论

相关推荐

    C#类、接口、虚方法和抽象方法-抽象类和接口的相同点和区别

    在C#编程语言中,类、接口、虚方法和抽象方法是面向对象设计的重要组成部分。这里我们将详细讨论这些概念以及抽象类与接口之间的相同点和区别。 首先,让我们来看看抽象类(Abstract Class): 1. 抽象方法是只有...

    详细解析Java中抽象类和接口的区别

    ### 详细解析Java中抽象类和接口的区别 #### 引言 在面向对象编程中,Java作为一种广泛应用的编程语言,提供了多种方式来实现抽象的概念。其中最常用的两种机制是抽象类(abstract class)和接口(interface)。这...

    基于Java抽象类和接口实现疯狂动物城

    本项目主要包括项目开发环境搭建、不同功能的类的设计、抽象类的设计、接口的设计、及其继承抽象类重写和接口实现类等具体功能的实现。 ●工程项目搭建与游戏初始化功能实现(2学时) ; ●动物城成员列表与动物信息...

    Java中的类、抽象类和接口的区别和联系

    Java中的类、抽象类和接口的区别和联系

    c# 中抽象类和接口的学习

    在C#编程语言中,抽象类和接口是实现面向对象编程(OOP)中的多态性和代码重用的关键概念。本文将深入探讨这两个概念,并解释它们在C#中的使用方式和区别。 ### 抽象类 抽象类是一种特殊的类,它不能被实例化,其...

    抽象类和接口实验报告.doc

    抽象类和接口实验报告

    C#中抽象类和接口的区别.txt

    在C#编程语言中,抽象类和接口都是用于实现面向对象编程中多态特性的关键概念。它们都旨在为其他类提供一种定义行为和特性的模板或规范,但它们之间存在着重要的区别。本文将详细探讨C#中的抽象类与接口之间的差异,...

    抽象类和接口的PPT

    简单介绍了抽象类和接口

    抽象类和接口 java

    本次实验主要聚焦于Java编程语言中抽象类和接口的概念理解与实际运用,旨在深入掌握抽象类与抽象方法的定义,以及接口的定义与实现方法。通过具体实例,如形状类的创建与扩展,电话类的层次构建,以及教师类的排序...

    java中的抽象类和接口

    java中的抽象类和接口知识点总结: 一、抽象类的概念 在面向对象编程中,抽象类是一种特殊的类,它不能被直接实例化,必须通过继承来创建具体的子类。抽象类通常用于定义具有共同属性和行为的类的模板。如果一个类...

    抽象类和接口的详细区别和联系

    ### 抽象类和接口的详细区别和联系 在面向对象编程中,抽象类与接口是两种非常重要的概念,它们都是实现多态性的方式之一,但在使用场景、语法特性和设计哲学等方面存在明显的不同。本篇文章将深入探讨这两者的区别...

    java中的抽象类和接口的理解

    ### Java中的抽象类和接口的理解 #### 一、引言 在面向对象编程语言Java中,抽象类(Abstract Class)和接口(Interface)是实现多态性和代码复用的重要手段。它们都允许开发者定义行为规范而不需要具体实现细节,...

    抽象类和接口

    在Java编程语言中,抽象类和接口是两个核心的概念,它们在实现多态性和组织类结构方面起着重要作用。抽象类和接口虽然在某些方面看似相似,但它们在用法和含义上有着明显的区别。 首先,抽象类是一种特殊的类,它...

    抽象类和接口的区别

    在面向对象编程中,抽象类和接口是两种非常重要的概念,它们在软件设计和实现过程中发挥着不可替代的作用。本文将深入探讨这两种概念的区别,帮助开发者更好地理解和运用它们。 #### 一、定义 1. **抽象类**:抽象...

    Java中抽象类和接口的区别

    在Java语言中,abstract class和interface 是支持抽象类定义的两种机制。正是由于这两种机制的存在,才赋予了Java强大的面向对象能力。abstract class和interface之间在对于抽象类定义的支持方面具有很大的相似性,...

    浅析Java抽象类和接口的比较

    Java中的抽象类(abstract class)和接口(interface)都是用于创建抽象化模型的重要工具,它们在面向对象编程中扮演着核心角色。这两种机制都允许我们定义一组方法的签名,但不提供具体实现,从而实现“设计契约”...

    Java实验7抽象类和接口.pdf

    抽象类和接口 ...通过这个实验,我们学习了抽象类、接口、Cloneable 接口和多态性的概念,并掌握了如何使用它们来编写 Java 程序。我们还学习了浅拷贝和深拷贝的概念,并掌握了如何消除浅拷贝的方法。

    抽象类和接口的区别抽象类和接口的区别.pdf

    "抽象类和接口的区别" 抽象类和接口是 Java 语言中两种不同的机制,用于实现对象的抽象描述。虽然它们之间存在着一定的相似性,但它们在定义、实现和使用方面存在着很大的区别。 首先,抽象类是一个抽象的概念,...

Global site tag (gtag.js) - Google Analytics