Java基础之面向对象的概念 继承---组合----枚举类

来源:互联网 发布:手机淘宝设置在哪里 编辑:程序博客网 时间:2024/06/07 06:50
    

                                                                Java基础之面向对象的概念 继承---组合----枚举类

本章作为面向对象概念的最后一篇,但是作为一名java程序员在面向对象的这条路上还很长。

 

一、继承与组合简介

     继承是实现类重用的重要手段,但是继承的最大缺点是破坏了封装性,组合也是实现类重用的重要手段之一,这种方式则能提供更好的封装性。本章的上半部分将讲解继承和组合的区别和联系。

     1.1使用继承应该注意的地方。

子类继承父类之后可以访问父类的变量和方法,因此严重破坏了父类的封装性,封装性是指:每个类都应该封装它内部的信息和实现细节,而只暴漏必要的方法给其他类使用。

子类访问父类的变量和方法的同时也造成了子类和父类的严重耦合。

为了解决这类的问题,我们应从父类的设计入手,有如下规则

  1.尽量隐藏父类的内部数据,就是将父类的变量设置为private修饰。子类不能够直接访问。

  2.不要让子类可以随意访问,修改父类的方法。可以使用访问修饰符(public   protected private ),final修饰符进行方法的访问权限设定,来达到不同的访问需求。

  3.尽量不要在父类中调用将被子类重写的方法,

  4.如果将父类设计为不可继承的状态,则需要使用Final修饰,或者使用private修饰这个类的所有构造函数,则可以保证子类无法调用父类的构造函数,子类就无法继承该类。对于该类创建对象的问题,可以创建一个静态方法,返回该类的实例。

实例代码1:

     package cn.com.basicFour;/** * @author fcs * 2014年8月28日 * 说明:子类重写的方法出现在父类的构造器中引发错误 */class Base{public Base(){test();}public void test(){System.out.println("我是父类中将被子类重写的方法。。。。");}}public class Sub extends Base {    public static void main(String[] args) {Sub sub = new Sub();}    private String name;    //父类中的构造方法调用的是子类重写的方法,但是name的长度是一个null,运行报错。    public void test(){   System.out.println("我是子类重写的方法。。。。name的长度: "+name.length());      }}

那么什么时候需要继承呢?

说明:不仅需要保证子类是一种特殊的父类。而且需要下面两个条件之一

   1.子类需要额外增加属性,而不仅仅是属性值的改变。

   2.子类需要增加自己独有的行为方式,(包括增加新的方法或重写父类的方法)

上面说明了继承的注意事项,如果只是考虑到继承的复用性,那也可以使用组合实现。

组合仅需将对象引用置于新类中即可

实例代码2:简单的组合演示

package cn.com.basicFour;class Animal{private void best(){System.out.println("心脏跳动。。。");}public void breath(){best();System.out.println("戏一口气。。。。吐一口气。。。");}}class Bird{//将Animal类嵌入可以继承的子类中private Animal a;public Bird(Animal animal){this.a = animal;}//重新定义自己的方法public void breath(){//直接使用Animal提供的方法。a.breath();}public void fly(){System.out.println("我在天空飞翔。。。。。");}}class Wolf{//将Animal类嵌入可以继承的子类中private Animal a;public Wolf(Animal animal){this.a = animal;}//重新定义自己的方法public void breath(){//直接使用Animal提供的方法。a.breath();}public void run(){System.out.println("我在陆地上奔跑。。。。。");}}public class CompositeTest {     public static void main(String[] args) {//此时需要显示的创建被嵌入的对象     Animal  al = new Animal();     Bird bird = new Bird(al);     bird.fly();bird.breath();     Wolf wolf = new Wolf(al);     wolf.breath();     wolf.run();}}

结论:

 继承是对已有的类进行改造,来达到某些特定的需求。

如果两个类之间有明确的整体和部分的关系,可以使用组合关系实现复用。

继承要表达的思想是一种“is-A”关系,组合要表达的思想是“has-A”关系。

一、枚举类

说明:在某些情况下,一个类的对象是有限而且固定的。在java里被称为枚举类。

  1.1枚举类说明:

  1. Java5 新增了一个enum关键字(与class ,interface地位相同),用以定义枚举类。

  2.枚举类是一种特殊的类,可以有自己的属性,方法,可以实现一个或者多个接口,也可以定义自己的构造函数,

  3.一个java源文件最多只能定义一个public 访问权限的枚举类,该源文件名必须和该枚举类名相同。

  4.枚举类与普通类的区别

    1.使用ENUM定义的枚举类默认继承了java.long.Enum类,

而不是继承Object类,java.long.Enum类实现了java.long.Serializable和java.long.Comparable接口。、

     2.使用ENUM定义的非抽象的枚举类默认会使用Final修饰,因此枚举类不能派生子类。

    3.枚举类的构造函数只能使用private访问修饰符。

  4.枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远都不能产生实例,系统会自动为这些实例添加public static Final修饰。

1.2手动实现枚举类

设计方式

 1.通过private将构造函数隐藏起来。

 2.将这个类的所有可能的实例使用public static Final修饰。

 3.可以提供一些静态方法,允许其他程序根据特定的参数来获取与之匹配的实例。

实例代码3:

 package cn.com.basicFour;/** *  * @author fcs * 2014年8月28日 * 说明:手动实现枚举类 * 该类是一个不可变类 */public class Season {      private final String name;      private final String desc;      public Season(String name, String desc) {this.name = name;this.desc = desc;}public static final Season SPRING = new Season("春天","出游踏春");public static final Season SUMMER = new Season("夏天","夏日炎炎");public static final Season FALL = new Season("秋天","秋风飒飒");public static final Season WINTER = new Season("冬天","围炉赏雪");public String getName() {return name;}public String getDesc() {return desc;}     //静态工厂方法实现返回season的实例public static Season getSeason(int seasonNum){switch(seasonNum){case 1:return SPRING;case 2:return SUMMER;case 3: return FALL;case 4:return WINTER;default: return null;}}}public class SeasonTest {      public SeasonTest(Season season){      System.out.println(season.getName()+" 是一个"+season.getDesc()+"的季节。。。。");      }      public static void main(String[] args) {//直接使用Season的FALL常量代表一个Season实例     new SeasonTest(Season.FALL);  }} 

说明:使用枚举类可以使程序更加健壮,避免创建对象的随意性。

可以使用这种方式:静态常量

Public  static final int SEASON_SPRING = 1;

....

这种方式存在一些问题:

  1.类型不安全,因为这些常量可以相加。

  2.没有命名空间,容易与其他常量或者变量混淆。

  3.打印输出的意义不明确。1指的是什么

实例代码4

//枚举类演示

public enum Gender {    MALE,FEMALE;    private String name;   //定义public修饰的变量    public void setName(String name){    switch(this){    case MALE:      if(name.equals("男")){      this.name = name;      }else{      System.out.println("参数错误。。。。。");      }      break;    case FEMALE:    if(name.equals("女")){    this.name = name;    }else{    System.out.println("参数错误。。。。。。");    }    break;    }    }    public String getName(){    return this.name;    }}package cn.com.basicFour;public class GenderTest {      public static void main(String[] args) {Gender g = Enum.valueOf(Gender.class, "FEMALE");g.setName("女");System.out.println(g+"代表 : "+g.getName());g.setName("男");System.out.println(g+"代表: "+g.getName());}}

说明:应该将上面的程序设计为不可变类。将所有的变量都使用final修饰符来修饰。

应该使用构造函数指定初始值,列出枚举值时就必须对应的传入参数。

实例代码5:

//枚举类与构造函数public enum Gender2 {//枚举类必须使用对应的构造函数实现 MALE("男"),FEMALE("女"); private final String name;     private Gender2(String name){     this.name = name;     }     public String getName(){     return this.name;     }}1.3实现接口的枚举类实例代码6:public enum Gender3 implements GenderDesc{//;//必须带上这个分号,当没有枚举类型时。MALE("男"){    //这种方式相当于一个匿名内部子类    public void info() {System.out.println("这个枚举值代表男性。。。。。");    }},FEMALE("女"){  public void info() {System.out.println("这个枚举值代表女性。。。。");    }};public void info() {System.out.println("这是用于定义性别的枚举类。。。。。");} private final String name;     private Gender3(String name){     this.name = name;     }     public String getName(){     return this.name;     }}

注意:非抽象的枚举类才是默认使用final修饰的,对于一个抽象的枚举类而言,只要包含了抽象方法就是抽象枚举类,系统默认使用abstract修饰,而不是final修饰。

编译后程序会生成三个class文件:Gender.class,Gender$1.class,Gender%2.class

1.3包含抽象方法的枚举类

  package cn.com.basicFour;/** *  * @author fcs * 2014年8月28日 * 说明:正常的枚举计算 */public enum Operation {    PLUS,MINS,TIMES,DIVIDE;    //为枚举类定义一个方法,用于实现不同的运算    double eval(double x,double y){    switch(this){    case PLUS:     return x+y;    case MINS:     return x - y;    case TIMES:    return x * y;    case DIVIDE:    return x / y;    default: return 0;    }    }    public static void main(String[] args) {System.out.println(Operation.PLUS.eval(23,22.1));System.out.println(Operation.MINS.eval(2233,22.1));System.out.println(Operation.TIMES.eval(2,22.1));System.out.println(Operation.DIVIDE.eval(22,22.1));}}带抽象方法的枚举类package cn.com.basicFour;/** * @author fcs * 2014年8月28日 * 说明:带抽象类的枚举 * ,每个枚举值都必须实现该抽象类 */public enum Operation2 { PLUS{@Overridepublic double eval(double x, double y) {return x+y;} }, MINES{ @Overridepublic double eval(double x, double y) {return x - y;} }, TIMES{ @Overridepublic double eval(double x, double y) {return x * y;} }, DIVIDES{ @Overridepublic double eval(double x, double y) {return x / y;} };     public abstract double eval(double x,double y);     public static void main(String[] args) { System.out.println(Operation.PLUS.eval(23,22.1)); System.out.println(Operation.MINS.eval(2233,22.1)); System.out.println(Operation.TIMES.eval(2,22.1)); System.out.println(Operation.DIVIDE.eval(22,22.1)); }}

说明:

枚举类里定义抽象方法时不能使用abstract关键字将枚举类定义成抽象类(因为系统自动回为它添加abstract关键字),但因为枚举类需要显示创建枚举值,而不是作为父类,所以定义每个枚举值时必须为抽象方法提供实现,否则将出现编译错误

0 0