Java 面向对象设计的基本思想

目录

1、Java中面向对象设计的扩展

2、Java中面向对象设计的基本要素

3、何为面向对象的思想?

4、如何使用对象编程?

5、面向对象在Java编程中的实践举例


        接口和抽象类是 Java 面向对象设计的两个基础机制。

        接口是对行为的抽象,它是抽象方法的集合,利用接口可以达到 API 定义和实现分离的目的。接口不能实例化;不能包含任何非常量成员,任何 Field 都是隐含着 public static final 的意义;同时,没有非静态方法实现,也就是说要么是抽象方法,要么是静态方法。Java 标准类库中,定义了非常多的接口,比如 java.util.List//Java8后接口中还可以有默认方法

        抽象类是不能实例化的类,用 abstract 关键字修饰 class,其目的主要是代码重用。除了不能实例化,形式上和一般的 Java 类并没有太大区别,可以有一个或者多个抽象方法,也可以没有抽象方法。抽象类大多用于抽取相关 Java 类的共用方法实现或者是共同成员变量,然后通过继承的方式达到代码复用的目的。Java 标准库中,比如 Collection 框架,很多通用部分就被抽取成为抽象类,例如 java.util.AbstractList

1、Java中面向对象设计的扩展

        Java 相比于其他面向对象语言,如 C++,设计上有一些基本区别,比如 Java 不支持多继承。这种限制,在规范了代码实现的同时,也产生了一些局限性,影响着程序设计结构。Java 类可以实现多个接口,因为接口是抽象方法的集合,所以这是声明性的,但不能通过扩展多个抽象类来重用逻辑。//多实现单继承的局限性

        在一些情况下存在特定场景,需要抽象出与具体实现和实例化无关的通用逻辑,或者纯调用关系的逻辑,但是使用传统的抽象类会陷入到单继承的窘境。以往常见的做法是,实现由静态方法组成的工具类(Utils),比如 java.util.Collections//突破单继承的窘境的努力

        设想,为接口添加任何抽象方法,相应的所有实现了这个接口的类,也必须实现新增方法,否则会出现编译错误。对于抽象类,如果我们添加非抽象方法,其子类只会享受到能力扩展,而不用担心编译出问题。//抽象类比接口具有更好的拓展和兼容能力

        接口的职责也不仅仅限于抽象方法的集合,其实有各种不同的实践。有一类没有任何方法的接口,通常叫作 Marker Interface,顾名思义,它的目的就是为了声明某些东西,比如我们熟知的 Cloneable、Serializable 等。这种用法,也存在于业界其他的 Java 产品代码中。//接口除了收集抽象方法,也可以用作标记接口

        从表面看,这似乎和 Annotation 异曲同工,也确实如此,它的好处是简单直接。对于 Annotation,因为可以指定参数和值,在表达能力上要更强大一些,所以更多人选择使用 Annotation//注解是比接口更强大的标记接口

        Java 8 增加了函数式编程的支持,所以又增加了一类定义,即所谓 functional interface,简单说就是只有一个抽象方法的接口,通常建议使用 @FunctionalInterface Annotation 来标记。Lambda 表达式本身可以看作是一类 functional interface,某种程度上这和面向对象可以算是两码事。我们熟知的 Runnable、Callable 之类,都是 functional interface,这里不再多介绍了,有兴趣你可以参考:https://www.oreilly.com/learning/java-8-functional-interfaces //变革!!!函数式接口

        还有一点可能让人感到意外,严格说,Java 8 以后,接口也是可以有方法实现的!

        从 Java 8 开始,interface 增加了对 default method 的支持。Java 9 以后,甚至可以定义 private default methodDefault method 提供了一种二进制兼容的扩展已有接口的办法。比如,我们熟知的 java.util.Collection,它是 collection 体系的 root interface,在 Java 8 中添加了一系列 default method,主要是增加 Lambda、Stream 相关的功能。类似 Collections 之类的工具类,很多方法都适合作为 default method 实现在基础接口里面。你可以参考下面代码片段:// 变革!!!默认方法 -> 想想接口中可以写方法,是不是更趋向于多继承了呢?


  public interface Collection<E> extends Iterable<E> {
     default Stream<E> stream() { //可以有具体的方法实现
         return StreamSupport.stream(spliterator(), false);
     }
  }

2、Java中面向对象设计的基本要素

        我们一定要清楚面向对象的基本要素:封装、继承、多态

        封装目的是隐藏事务内部的实现细节,以便提高安全性和简化编程。封装提供了合理的边界,避免外部调用者接触到内部的细节。我们在日常开发中,因为无意间暴露了细节导致的难缠 bug 太多了,比如在多线程环境暴露内部状态,导致的并发修改问题。从另外一个角度看,封装这种隐藏,也提供了简化的界面,避免太多无意义的细节浪费调用者的精力。

        继承代码复用的基础机制,类似于我们对于马、白马、黑马的归纳总结。但要注意,继承可以看作是非常紧耦合的一种关系,父类代码修改,子类行为也会变动。在实践中,过度滥用继承,可能会起到反效果。

        多态,你可能立即会想到重写(override)和重载(overload)、向上转型。简单说,重写是父子类中相同名字和参数的方法,不同的实现;重载则是相同名字的方法,但是不同的参数,本质上这些方法签名是不一样的,为了更好说明,请参考下面的样例代码:// 重写同名同参数,重载同名不同参数


public int doSomething() {
    return 0;
}
// 方法重载:输入参数不同,意味着方法签名不同
public int doSomething(List<String> strs) {
    return 0;
}
// 方法重写:不同实现,返回 1 而不是 0
public int doSomething() {
    return 1;
}

3、何为面向对象的思想?

        面向对象的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分。程序中的很多对象来自标准库,还有一些是自定义的。究竟是自己构造对象,还是从外界购买对象完全取决于开发项目的预算和时间。但是,从根本上说,只要对象能够满足要求,就不必关心其功能到底是如何实现的//面向对象 -> 程序组件化,不重复造轮子,使用第三方框架

        传统的结构化程序设计通过设计一系列的过程(即算法)来求解问题。一旦确定了这些过程,就要开始考虑存储数据的适当方式。这就是 Pascal 语言的设计者 Niklaus Wirth 将其著作命名为算法+数据结构=程序的原因。//结构化程序设计 -> 自己实现整个过程

        需要注意的是,在 Wirth 的这个书名中,算法是第一位的,数据结构是第二位的,这就明确地表述了程序员的工作方式。首先要确定如何操作数据,然后再决定如何组织数据的结构,以便于操作数据。而 OOP 却调换了这个次序,将数据放在第一位,然后再考虑操作数据的算法/

        理解面向对象的思想才能理解怎样使用Java进行编程,在大型企业应用中,大部分时间都在使用第三方封装的对象,该工作方式可以大大减轻编程的工作量,快速实现定制化需求。

        对于学习的意义,也在于能站在更高的角度去看待编程,从细节抽象到思想,从而提升一个新的知识层次。

        不过,有利也有弊,Java中使用框架的逻辑封装很多,大部分时间都是一种黑盒式的编程,往往很容易使人浮于表面,而不能深入一个技术的细节。

4、如何使用对象编程?

        要想使用OOP,一定要清楚对象的三个主要特性:

  • 对象的行为(behavior)——可以对对象完成哪些操作,或者可以对对象应用哪些方法?//方法
  • 对象的状态(state)——当调用那些方法时,对象会如何响应?           //成员变量/属性
  • 对象的标识(identity)——如何区分具有相同行为与状态的不同对象?//主键

        对象状态的改变必须通过调用方法实现,如果不经过方法调用就可以改变对象状态,只能说明破坏了封装性。

        如果不查看源代码,就不可能知道一个类内部的逻辑。当然,封装的意义就在于内部表示并不重要,重要的是类对外提供的方法//封装会带来黑盒

        用 public 标记实例字段会破坏一个对象的封装性,所以,强烈建议将实例字段标记为private//一个类的成员变量应该标记成私有的,如果需要对外暴露,就需要提供一个方法来实现,因为方法是类中的方法,所以可以在类中对该方法进行控制。

5、面向对象在Java编程中的实践举例

        看看下面这段代码,改编自某伟大公司产品代码,你觉得根据面向对象的思想可以如何改进?


public class VIPCenter {

  void serviceVIP(T extend User user>) {
     if (user instanceof SlumDogVIP) {     //SlumDogVIP
        //...
      } else if(user instanceof RealVIP) { //RealVIP
        //..
      }
      //...
  }

}

        这段代码的一个问题是,业务逻辑集中在一起,当出现新的用户类型时,比如,大数据发现了我们是肥羊,需要去收获一下, 这就需要直接去修改服务方法代码实现,这可能会意外影响不相关的某个用户类型逻辑。根据面向对象的思想,我们可以尝试改造为下面的代码:

public class VIPCenter {
   private Map<User.TYPE, ServiceProvider> providers;

   void serviceVIP(T extend User user) { //根据具体用户类型调用
      providers.get(user.getType()).service(user);
   }

 }

//通用接口
interface ServiceProvider{
   void service(T extend User user) ;
 }

//具体实现1
class SlumDogVIPServiceProvider implements ServiceProvider{
   void service(T extend User user){
     //...
   }
 }

//具体实现2
class RealVIPServiceProvider implements ServiceProvider{
   void service(T extend User user) {
     //...
   }
 } 

        上面的示例,将不同对象分类的服务方法进行抽象,把业务逻辑的紧耦合关系拆开,实现代码的隔离,方便了代码的扩展。

        至此,全文结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

swadian2008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值