Skip to content

Latest commit

 

History

History
executable file
·
386 lines (255 loc) · 13.5 KB

ch6_4.md

File metadata and controls

executable file
·
386 lines (255 loc) · 13.5 KB

[TOC]

继承

继承是一种由已有的类创建新类的机制。 利用继承,我们可以先创建一个拥有共同属性的一般类,根据该一般类再创建具有特殊属性的新类。

类具有继承性,子类对父类的继承关系体现了现实世界中特殊和一般的关系。 ==通过继承可以更有效地组织程序结构,明确类间关系,并充分利用已有的类来完成更复杂、深入的开发。==

由继承而得到的类称为子类(subclass), 被继承的类称为父类(或叫超类,superclass)。

  • 直接或间接被继承的类都是父类。
  • 子类继承父类的状态和行为,同时也可以修改父类的状态或重写父类的行为,并添加新的状态和行为。

==Java中不支多重继承。==

创建子类

Java中的所有类都是java.lang.Object类的子类。

通过在类的声明中加入__extends__子句来创建一个类的子类,其格式如下:

class SubClass extends SuperClass{
     ……
}

- 把SubClass声明为SuperClass的直接子类。SuperClass为唯一的父类 - 如果SuperClass又是某个类的子类,则SubClass同时也是该类的(间接)子类。 - 子类可以继承父类的成员变量和方法。 - 如果缺省extends子句,则该类为java.lang.Object的子类。 - 子类可以继承父类中访问权限设定为publicprotecteddefault的成员变量和方法。 - 但是不能继承访问权限为private的成员变量和方法。

继承的简单例子

class Father{ //父类
	private int money;
	float weight,height;
	String head;
	
	String speak(String s) {
		return s ;
	}
}

class Son extends Father{ //子类
	String hand, foot;
}

public class TestExtend {             
	public static void main(String args[]){
    	Son boy=new Son();
    	boy.weight=120f; 
    	boy.height=1.8f;
    	boy.head="一个头"; 
    	boy.hand="两只手";
    	boy.foot="两只脚";
     
    	System.out.println("我是儿子");
    	System.out.println("我有:"+boy.hand+"、"+boy.foot+"、"+ boy.head
+"、重"+boy.weight+"、高"+boy.height);
	}
}

程序运行结果如下:

我是儿子 我有:两只手、两只脚、一个头、重120.0、高1.8

成员变量的隐藏和方法的重写

  • 如果子类和父类不在同一个包中,那么,子类可以继承了父类的protected、public修饰的成员变量做为子类的成员变量,并且也可以继承了父类的protected、 public修饰的方法作为子类的方法。

  • 另外子类和父类不在同一个包中,则子类不能继承父类的default变量和default方法。

数据成员的隐藏

数据成员的隐藏是指在子类中重新定义一个与父类中已定义的数据成员名完全相同的数据成员,即子类拥有了两个相同名字的数据成员,一个是继承父类的,另一个是自己定义的。 当子类要操作继承自父类的同名数据成员时,可使用关键字super引导。

成员方法的覆盖

子类可以重新定义与父类格式完全相同(包括方法名、方法形参的个数和类型、方法返回值的类型)的成员方法,实现对父类方法的覆盖。

  • 只有当实例方法能被访问时,才能进行方法覆盖。私有方法不能被覆盖.
  • 静态方法(类方法)能被继承,但不能被覆盖。
  • 子类在重新定义父类已有的方法时,应保持与父类完全相同的方法名、返回值类型和参数列表,否则就不是方法的覆盖,而是子类定义自己特有的方法,与父类的方法无关。

继承不同包中的类的简例子

// HouseHold.java
package xing.house;
public class HouseHold {    //家类
	protected String address;   //地址
	public  String surnname;  //姓
	
	String givenname; //名
  
	public HouseHold(String  add) { address =add;}
	protected String getAddress(){return address;}
	void setMoney(String newadd) {address=newadd;}
	void setAddressString add){address=add;}
}
// Mikey.java:
package xing.friend;
import xing.house.HouseHold;

public class Mikey extends HouseHold {
	public Mikey(){  super("Star flight street 110");  }

	public static void main(String args[]){
		Mikey mikey=new Mikey();
		
		//mikey.givenname=“Johnson”;     //非法
		mikey.surnname="Math";                   //合法.
		mikey.address="Star flight street 110"; //合法.
		String m=mikey.getAddress();             //合法
		//mikey.setAddress("Star flight street 110");  //非法
		System.out.println(mikey.surnname+":"+m);
	}
}

super关键字

super表示的是当前对象的直接父类对象,是当前对象的直接父类对象的引用。

super的使用方法有三种:

  • (1) 访问直接父类隐藏的数据成员,其使用形式如下:

super.数据成员

  • (2) 调用直接父类中被覆盖的成员方法,其使用形式如下:

super.成员方法(参数)

  • (3)调用直接父类的构造方法,子类可以调用父类声明的构造方法。其使用形式如下:

super([参数列表])

注意

  • super 语句必须是子类构造方法的第一条语句。
  • 不能在子类中使用父类构造方法名来调用父类构造方法。
  • 父类的构造方法不被子类继承。

调用父类的构造方法的唯一途径是使用 super 关键字,如果子类中没显式调用,则编译器自动将 super(); 作为子类构造方法的第一条语句。这会形成一个构造方法链。

静态方法中不能使用 super 关键字。

this关键字

this关键字表示当前对象。

可用于:

  • 调用当前类的构造方法,并且必须是方法的第一条语句。如:this(); 调用默认构造方法。this(参数); 调用带参构造方法。
  • 限定当前对象的数据域变量。
  • 一般用于方法内的局部变量与对象的数据域变量同名的情况。 如 this.num = num。this.num 表示当前对象的数据域变量 num,而 num 表示方法中的局部变量。

对象的上转型对象

protected关键字

protected修饰的成员变量可以被三种类所引:该类自身、与它在同一个包中的其它类及在其它包中的该类的子类。

final关键字

使用final关键字可以定义常量。

定义类时,在class关键字前加关键字final,表示此类是最终类,不能被其它类继承,不能做父类。 用final修饰成员方法,表示此方法不能被它的子类覆盖。

final 修饰类中的属性或者变量

无论属性是基本类型还是引用类型,final 所起的作用都是变量里面存放的"值"不能变。 这个值,对于基本类型来说,变量里面放的就是实实在在的值,如 1,"abc" 等。 而引用类型变量里面放的是个地址,所以用 final 修饰引用类型变量指的是它里面的地址不能变,并不是说这个地址所指向的对象或数组的内容不可以变,这个一定要注意。

例如: 类中有一个属性是

final Person p=new Person("name"); 那么你不能对 p 进行重新赋值,但是可以改变 p 里面属性的值 p.setName('newName');

final 修饰属性,声明变量时可以不赋值,而且一旦赋值就不能被修改了。 对 final 属性可以在三个地方赋值:声明时、初始化块中、构造方法中,总之一定要赋值。

final修饰类中的方法

作用:可以被继承,但继承后不能被重写。

final修饰类

作用:类不可以被继承。

native修饰的方法

称为本地方法,此方法使用的目的是为了将其它语言(例如,C、C++、FORTRAN、汇编等)嵌入到Java语言中。这样可以充分利用已经存在的其它语言的程序功能模块,避免重复编程。

多态性

多态(Polymorphism)的意思就是用相同的名字来定义不同的方法。在Java中,普通类型的多态为重载,这就意味着可以使几个不同的方法使用相同的名字,这些方法以参数的个数不同、参数的类型不同等方面来进行区分,以使得编译器能够进行识别。

也可以这样讲,重载是同一个方法具有不同的版本,每个版本之间在参数特征方面有差异。==重载是Java实现多态性的方式之一。==

例如:

family()方法可以有三个版本,如下:

family() {
}

family(String ch) { 
	address=ch; 
}

family(String ch,float n) { 
	address=ch; 
	pay=n; 
}

这些方法并存于程序中,编译时,编译器根据实参的类型和个数来区分从而调用那个方法。如果这些方法作为函数或过程同时出现在其它语言的程序中,如C,那将导致灾难性的错误。

构造方法重载的例子

class person {
   String name="Johnson";    // 姓名
   int age=45;               // 年龄

   person(){ }
   
   person(String a) {
   		name=a;     
   	}
   
   person(String a,int b) {  
   		name=a;    
   		age=b;     
   	}

   public voiddisplay(){   
   	System.out.println("Name="+name+","+"Age="+age);    } 
   }

public class Poly{
    public static void main(String[] args) {
       person ko1=new person();
       person ko2=new person("Mike");
       person ko3=new person("Willian",50);
       
       ko1.display();
       ko2.display();
       ko3.display();
    }
}

多态是面向对象程序设计语言的一个重要特性,在Java中,可以在同一类或不同类中定义名称相同但是操作不同的多个方法,多态性指的是运行时判断应该执行哪个方法代码的能力,这种能力也叫动态绑定。

在Java语言中,多态性分为__编译时多态性__和__运行时多态性__。

  • 编译时的多态性是通过方法重载实现的,Java虚拟机根据传递给方法的参数个数和类型决定调用哪个重载方法。
  • 运行时的多态性是Java多态性的最重要的体现,在Java语言的继承关系中,子类对象与父类对象之间有一个重要特性:在任何需要父类对象的地方,都可以使用子类对象来代替,即子类对象可以作为父类对象来使用。一个对象可以通过引用子类的实例来调用子类的方法,通常,方法在父类中定义,在子类中覆盖,子类中调用哪个方法是在运行时决定的,取决于对象的类型,称为运行时的多态性。

如果子类重写了父类的方法,那么重写方法的调用原则如下:

  • Java运行时系统根据调用该方法的实例,来决定调用哪个方法。
  • 对子类的一个实例,如果子类重写了父类的方法,则运行时系统调用子类的方法;如果子类继承了父类的方法(未重写),则运行时系统调用父类的方法。

另外,方法重写时应遵循的原则如下:

  • 改写后的方法不能比被重写的方法有更严格的访问权限。
  • 改写后的方法不能比被重写的方法产生更多的异常。

进行方法重写时必须遵从这两个原则,否则编译器会指出程序出错。

方法重写的例子

class Parent{
    public void function(){ 
    	System.out.println("I am in Parent!");
    }
}

class Child extends Parent{
    private void function(){
        System.out.println("I am in Child!");
     }
}

public class RTpolyTest{
    public static void main(String args[]){
    	Parent pl=new Parent( );
    	Parent p2=new Child( );
       
       p1.function( );
       p2.function( );
    }
}

编译过程如下:

D:\user\chap05>Javac RTpolyTest.java RTpolyTest.java:8: function() in Child cannot override function() in Parent; attempting to assign weaker access privileges; was public private void function(){ ^ RTpolyTest.java:16: cannot find symbol symbol : variable p1 location: class RTpolyTest p1.function( ); ^ 2 errors

可以看出,该程序中实例p2调用function()方法时会导致访问权限的冲突。

#初始化块

初始化块用来和构造方法一起初始化对象。 初始化块是封装在一对大括号内的语句块,初始化块在类的定义体内,但不能包含在方法体内或构造方法体内。

初始化块分为对象(实例)初始化块和静态(类)初始化块。

静态数据成员、初始化块和构造方法的执行顺序如下:

  • (1) 当第一次使用类时,首先装载类,初始化静态数据成员,然后执行类的静态初始化块。
  • (2) 当使用new运算符创建类的对象时,按下述三个步骤执行:
  • ① 调用父类的构造方法(在调用父类的构造方法时,也按照这三个步骤执行)。
  • ② 初始化实例数据域,执行对象初始化块。
  • ③ 执行构造方法。

本文档 Github : https://github.com/bushehui/Java_tutorial

<script type="text/javascript" async src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML"> </script> <script type="text/x-mathjax-config"> MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}}); </script>