您的位置:首页 > 职场人生

黑马程序员 第七篇 继承 (part 1) (Foundation; completed)

2015-08-01 15:47 666 查看
——- android培训java培训、期待与您交流! ———-

目录

1.什么是继承

2.修饰符

3.成员变量的覆盖

4.对象引用

5.方法的重写和重载

1.什么是继承

Java提供了单一继承,通过接口可以实现多重继承。

日常生活中,我们常遇到类似自行车和公路赛车的问题

自行车有颜色大小之分,公路赛车同样也有颜色大小之分,也多了一项特殊属性即速度的优势;自行车有的属性,公路车也有;公路车有的,自行车不一定有。

在Java中,遇到此类问题,可以通过继承来完成处理这类情况的功能。继承节省了定义新类中的大量工作,大大提高了代码的重用性。

//创建一个自行车类
public class bike
{
public Sstring color'
public int size;
}

//创建一个公路车赛车类
public class racing_cycle
{
public String color;
public int size;
public String speed;
}


代码解析:既然公路赛车是自行车的一种,我们可以用自行车类定义自行车,然后通过继承来使公路赛车获得自行车的所有属性,而公路赛车类只需要定义自己所特有的属性和状态。

就好比公路赛车是自行车的一个升级版本,很自然地拥有了自行车的特性。

通过继承来简化上面的程序

public class bike
{
public String color;
public int size;
}
public class racing_cycle extends bike
{
public String speed;
}


Java提供了单一继承,通过接口可以实现多重继承。

1.1 类的继承 (extends)

在Java中,被继承的类叫超类(super),继承超类的类叫做子类(subclass)。

因此,子类是超类的一个功能上的扩展,它继承了超类定义的所有属性和方法,并且添加了特有的功能方法。

所有的类都继承自Java.lang.Object类,所以说Object类是Java中所有类的父类。如果在定义的类中不做特殊的规定,自己定义的类均直接继承自Object类。

特性总结:

通过继承定义类,可以简化类的定义,让所需要的功能相应的子类去定义和实现

Java是单继承的,子类可以有很多,父类只能有一个。

Java的继承是多层继承,一个类可以有很多子类,而子类下面又可以有很多子类

父类里的属性和方法可以使子类同样拥有,而子类的不能调用父类的方法和属性,子类的午餐构造器默认是调用父类的无参构造器。

父类没有定义一个无参的构造器,那么编译器就默认生成一个午餐的构造器,也可以在子类构造器里显示使用super方法调用父类构造器,super方法里写几个参数就可以表示调用的是父类的哪一个构造器。

一般情况下,定义了一个有参的构造器,就应该定义一个无参的构造器

1.2 继承的语法

语法

class 类名 extends 父类名称


2.修饰符

在定义类的一个成员变量和方法时,必须要指定成员变量或方法的访问权限,这时就需要使用到修饰符。修饰符是修饰的当前成员变量的访问限制和状态的。Java中的修饰符主要有public,protected,private,static和final。

修饰成员变量的修饰符主要有4个 public private default protected



2、final修饰符

final的意思是不可变,他可以修饰类、字段、方法。修饰类后类不能被扩展(extends),也就是不能被继承。修饰字段后字段的值不能被改变,因此如果有final修饰字段,应该对字段进行手动初始化。修饰方法后该方法不能被改变,也就是重写。

3、abstract修饰符

abstract是抽象的意思,用来修饰类和方法,修饰类后,该类为抽象类,不能被实例化,必需进行扩展。修饰方法后,该方法为抽象方法必须被子类重写(override)。

4、static修饰符

static用来修饰内部类,方法,字段。修饰内部类说明该内部类属于外部类而不属于外部类的某个实例。修饰字段说明该字段属于类而不属于类实例。修饰方法说明该方法属于类而不属于类实例。

详细参考

Java修饰符全解:

http://www.cnblogs.com/leokou/archive/2011/06/30/2094282.html

3.成员变量的覆盖

class fruit
{

static String color = "蓝色";
String size = "大";

static String getFruirColor()
{
return color;
}

String getFruitSize()
{
return size;
}

}

public class apple extends fruit
{
static String appleColor = "红色";
String appleSize = "小";

static String getFruitColor()
{
return appleColor;
}

String getFruitSize();
{
return appleSize;
}

public static void main(String[] args)
{

fruit f = new apple();
System.out.println(f.getFruitColor());
System.out.println(f.getFruitSize());

}
}

输出结果

打印    蓝色
小


静态方法无法重写

所有getFruitColor 返回蓝色

getFruitSize被重写返回小

4.对象引用

外部可以调用类来创建对象,比如上面在Test类中:

Human aPerson = new Human(160);


创建了一个Human类的对象aPerson。

上面是一个非常简单的表述,但我们有许多细节需要深入:

首先看等号的右侧。new是在内存中为对象开辟空间。具体来说,new是在内存的堆(heap)上为对象开辟空间。这一空间中,保存有对象的数据和方法。

再看等号的左侧。aPerson指代一个Human对象,被称为对象引用(reference)。实际上,aPerson并不是对象本身,而是类似于一个指向对象的指针。aPerson存在于内存的栈(stack)中。

当我们用等号赋值时,是将右侧new在堆中创建对象的地址赋予给对象引用。

这里的内存,指的是JVM (Java Virtual Machine)虚拟出来的Java进程内存空间。内存的堆和栈概念可参考Linux从程序到进程。



参考资料

作者:Vamei 出处:http://www.cnblogs.com/vamei

对象引用

http://www.cnblogs.com/vamei/archive/2013/04/01/2992484.html

5.方法的重写和重载

5.1 方法重写的特点
5.2 方法重载的特点
5.3 重写的返回类型
5.4 重写是基于继承的
5.5 静态方法是不能重写的
5.6  三者之间的关系
5.7 重写toString方法


5.1 方法重写的特点

方法重写也可以叫方法的覆盖,关键词为override。在子类本身的方法中,如果和所继承的方法具有相同的名称,便构成了方法的重写。重写的特点就是能够为各个子类定义特有行为。

静态方法不可重写。

关于自行车和公路赛车的相同点和不同点例子

class bike
{
public void move()
{
System.out.println("速度慢的!");
}

public class racing_cycle extends bike
{

public void move()
{
System.out.println("速度快的!");
}

public static void main(String[] args)
{

racing_cycle rc = new racing_cycle();
rc.move();

}

}

输出结果

打印    速度快的!


问题 如果把

racing_cycle rc = new racing_cycle();
换成

bike rc = new racing_cycle();


5.2 方法重载的特点

方法的重载就是指在一个类中,存在两个或者两个以上具有相同名称,不同参数的方法。

Java里重载的关键字为overload。

重载的方法名称相同,但方法的参数列表不同。如参数个数和参数类型等。

重载的方法和返回值可以相同也可以不相同。

示例代码如下

public String move() {}
public String move(String name) {}
public void move(String name, int spend) {}


代码说明:

虽然3种方法的名称是相同的,但这3种方法的参数列表,即个数和类型,是不相同的。

判断方法是否重载,看参数列别是很重要的。

一个重载的例子

class math
{
public int add(int i, int j)
{
return i+j;
}

public float add(float i, float j)
{
return i+j;
}

public static void main(String args[])
{
math m = new math();
System.out.println(m.add(1.0f, 2.3f));
System.out.println(m.add(1, 2));
}
}

输出结果:

打印  3.3
3


5.3 重写的返回类型

方法有它的返回类型,而重写的方法也有返回类型,并有一些相应的限制。方法被重写后,返回的类型为基本类型时,重写方法的返回要必须一样,否则会出现错误。

如果返回类型是对象引用类型,重写的方法返回类型可以相同,也可以派生(父类的派生类,即子类)自被重写方法的返回类型。

以下演示一段错误的使用返回类型的代码

class math
{
public int add()
{
return 10 + 15;
}

}

public class son extends math
{
public float add()
{
return 10 + 15;
}

public static void main(String[] args)
{
math m = new son();
System.out.println(m.add());
}
}

运行结果:

代码编译不通过
因为该代码重写add() 但返回类型改变了,所以出错。


5.4 重写是基于继承的

重写和重载的产生是基于继承,如果没有发生继承就不会产生重写和重载了。在实际开发中,一定要注意继承是重写的基础,没有继承就不会存在重写。

class door
{
]

class wood_Door extends door
{
}

class math
{
static public door getMes()
{
return new door();
}
}

public class Son extends math
{
static public wood_Door getMes()
{
return new wood_Door();
}

public static void main(String[] args)
{
math m = new Son();
System.out.println(m.getMes());
}
}

输出结果:

door@61de33

代码解析

父类和子类的getMes是被修饰为static类型的,static关键字表示这个方法为静态方法,具有唯一性,是个类方法。因为static类型的方法不能被重写,所以方法m.getMes() 调用的是父类的方法`getMes(){return new door();}`


5.5 静态方法是不能重写的

静态方法就是被修饰为static类型的方法,在类里声明具有唯一性,不是通过类的实例化而存在的,而是通过类的建立而存在,可以理解为用关键字new创建对象了,就是把这个对象实例化了。Java中的静态方法可以被继承,但是不可以被重写。

下面代码演示来说明静态方法不能被重写

class door
{
}

class wood_Door extends door
{
}

class math
{
static public door getMes()
{
return new door();
}

}

public class Son extends math
{
static public wood_Door getMes()
{
return new wood_Door();
]

public static void main(String[] args)
{
Son m = new Son();
System.out.println(m.getMes());
}
}

输出结果

打印  wood_Door@61de33

这里调用了wood_Door的getMes()方法,因为创建对象的时候使


Son m = new Son()

区别于上一段代码的


math m = new math()

调用door的getMes()方法

因为编译期为math类型,又因为getMes方法被修饰为static类型的。所以对象引用m在运行期被强制转型为math类型

总结
==

- 父类的静态方法可以被子类的静态方法覆盖
- 父类的非静态方法不能被子类的静态方法覆盖
- 父类的静态方法不能被子类的非静态方法覆盖
- 覆盖用于父类和子类之间
- 重写用在同一个类中,有相同的方法名,但参数不一样。

5.6 三者之间的关系
-----------

重写关键字是override,重载的关键字为overload,重写,重载,覆盖,都是基于继承的关系。当继承的关系放了,想用父类的方法就用super关键词来引用,如果想用新的方法了就重写,来完成新的功能需要。

对覆盖(重写也叫覆盖)的总结:
---------------

- 覆盖的方法的参数列表必须要和被覆盖的方法的参数列表完全相同,才能达到覆盖的效果
- 覆盖的方法的返回值必须和被覆盖的方法的返回值一致
- 覆盖的方法锁抛出的异常必须和被覆盖的方法所抛出的异常一致,或者是其子类
- 被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。

## 对重载的总结: ##

- 使用重载时只能定义不同的参数列表
- 不能通过重载的方法对返回类型,访问权限和抛出的异常来进行方法的重载

重写的总结如下
-------

- 重写的方法存在于父类中,也存在于子类中
- 重写的方法在运行期采用多态的形式
- 重写的方法不能比被重写的方法有更高的访问权限
- 重写的方法不能比被重写的方法有更多的异常

5.7 重写toString方法
----------------

toString方法是Java里Object类的方法,很多类都重写了该方法。该方法返回对象的状态信息。如果需要得到比较详细的状态信息,就需要对toString方法进行重写。

5.8 重写equals
------------

方法equals也是Object类的方法,很多类也进行了重写,一般重写equals方法是为了比较两个对象的内容是否相等。equals方法在两个比较的对象指向同一个对象的时候才会返回真值。如果需要进行比较详细的判断,就需要对equals方法进行重写。

equals的原型


public boolean equals(Object obj)

{

return (this == obj); //这里比较的是两个对象的引用

}

关于equals和==的区别总结
================


public class EqualTest {

public static void main(String[] args) {

//对于基本类型的变量。"=="和"equal"的区别
int t1=57;
int t2=67;
int t3=124;
int t4=124;

//“==”对于基本数据类型,判断两个变量的值是否相等。
Boolean result1=(t1==t2);
Boolean result2=((t1+t2)==t3);
Boolean result3=(t3==t4);

System.out.println("/n/n-----【t1==t2】"+result1+"/n-----【(t1+t2)=t3】"+result2+"/n-----【t3=t4】"+result3);
//“equal”不能用于基本数据类型。只能用于类变量。对于基本数据类型要用其包装类。
Integer i1=new Integer(t1);
Integer i2=new Integer(t2);
Integer i3=new Integer(t3);
Integer i4=new Integer(t4);

Boolean ri1=i1.equals(i2);
Boolean ri2=i3.equals(i1+i2);
Boolean ri3=i3.equals(i4);

System.out.println("/n/n-----【i1.equals(i2)】"+ri1+"/n-----【i3.equals(i1+i2)】"+ri2+"/n-----【i3.equals(i4)】"+ri3);

//对于对象变量,"=="和"equal"的区别

String st1="wasiker ";
String st2="is super man";
String st3="wasiker is super man";
String st4="wasiker is super man";

Boolean b1=(st1==st2);
Boolean b2=(st1+st2)==st3;
Boolean b3=(st3==st4);

System.out.println("/n/n-----【st1==st2】"+b1+"/n-----【(st1+st2)==st3】"+b2+"/n-----【st3==st4】"+b3);


//因为对象变量的存储的是对象在内存中的路径,即内存地址。所以用“==”比较时,即使

//对象的值相等,但是他们的内存地址不同,所以==的结果为false。故“==”用于比较两

//个变量的值是否相等,而不是变量引用的对象是否相等

Boolean r1=st1.equals(st2);
Boolean r2=(st1+st2).equals(st3);
Boolean r3=st3.equals(st4);

System.out.println("/n/n-----【st1.equals(st2)】"+r1+"/n-----【(st1+st2).equals(st3)】"+r2+"/n-----【st3.equals(st4)】"+r3);


//equal用于比较两个对象是否相同。

}

}

“`

运行结果为:

—–【t1==t2】false

—–【(t1+t2)=t3】true

—–【t3=t4】true

—–【i1.equals(i2)】false

—–【i3.equals(i1+i2)】true

—–【i3.equals(i4)】true

—–【st1==st2】false

—–【(st1+st2)==st3】false

—–【st3==st4】true

—–【st1.equals(st2)】false

—–【(st1+st2).equals(st3)】true

—–【st3.equals(st4)】true

总之:

**“==”比较的是值【变量(栈)内存中存放的对象的(堆)内存地址】

equal用于比较两个对象的值是否相同【不是比地址】**

【特别注意】Object类中的equals方法和“==”是一样的,没有区别,而String类,Integer类等等一些类,是重写了equals方法,才使得equals和“==不同”,所以,当自己创建类时,自动继承了Object的equals方法,要想实现不同的等于比较,必须重写equals方法。

“==”比”equal”运行速度快,因为”==”只是比较引用.

reference:

http://blog.csdn.net/xcysuccess3/article/details/6557771

——- android培训java培训、期待与您交流! ———-
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: