黑马程序员——Java面向对象之继承与多态笔记

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------



一,继承


继承概述
  多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,
那么多个类无需再定义这些属性和行为,只要继承那个类即可。
  通过extends关键字可以实现类与类的继承
class 子类名 extends 父类名 {}  
单独的这个类称为父类,基类或者超类;这多个类可以称为子类或者派生类。
有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,
还可以定义自己的新成员.


继承的好处


1)提高了代码的复用性
多个类相同的成员可以放到同一个类中
2)提高了代码的维护性
如果功能的代码需要修改,修改一处即可
让类与类之间产生了关系,是多态的前提
其实这也是继承的一个弊端:类的耦合性很强


继承的特点


1),Java只支持单继承,不支持多继承。
一个类只能有一个父类,不可以有多个父类。


2),Java支持多层继承(继承体系)
class A{}
class B extends A{}
class C extends B{}


3)继承中类之间体现的是:”is a”的关系


继承的注意事项


1)子类只能继承父类所有非私有的成员(成员方法和成员变量)
其实这也体现了继承的另一个弊端:打破了封装性.


2)子类不能继承父类的构造方法,但是可以通过super(后面讲)关键字去访问父类构造方法。

   3)不要为了部分功能而去继承.
示例:

class Fu{
	int num=4;
}
class Zi extends Fu{
	int num=5;
	void show(){
		System.out.println(num);
	}
}
class ExtendsDemo{
	public static void main(String[]args){
		Zi z=new Zi();
		z.show();
	}
}
结论:
在子类方法中访问一个变量
首先在子类局部范围找
然后在子类成员范围找
最后在父类成员范围找(肯定不能访问到父类局部范围)
如果还是没有就报错。



super关键字


super的用法和this很像


this代表本类对象的引用。
super代表父类存储空间的标识(可以理解为父类引用)


用法(this和super均可如下使用)


访问成员变量
this.成员变量 super.成员变量


访问构造方法(子父类的构造方法问题讲)
this(…) super(…)


访问成员方法(子父类的成员方法问题讲)
this.成员方法() super.成员方法()

示例:访问成员变量时

class Fu{
	int num=4;
}
class Zi extends Fu{
	int num=5;
	void show(){
		//第一个输出为5,第二个输出为4
		System.out.println(this.num+"  "+super.num);
	}
}
class ExtendsDemo{
	public static void main(String[]args){
		Zi z=new Zi();
		z.show();
	}
}

继承中构造方法的关系

1) 子类中所有的构造方法默认都会访问父类中空参数的构造方法

因为子类会继承父类中的数据,可能还会使用父类的数据。

所以,子类初始化之前,一定要先完成父类数据的初始化。

每一个构造方法的第一条语句默认都是:super().

2),子类通过super去显示调用父类其他的带参的构造方法

子类通过this去调用本类的其他构造方法

本类其他构造也必须首先访问了父类构造


一定要注意:
super(…)或者this(….)必须出现在第一条语句上

否则,就会有父类数据的多次初始化.

示例:

class Fu{
		public int num = 10;
		public Fu(){
			System.out.println("fu");
		}
	}
	class Zi extends Fu{
		public int num = 20;
		public Zi(){
			System.out.println("zi");
		}
		public void show(){
			int num = 30;
			//输出30
			System.out.println(num);
			//输出20
			System.out.println(this.num);
			//输出10
			System.out.println(super.num);
		}
	}
	class Test {
		public static void main(String[] args) {
			Zi z = new Zi();
			z.show();
		}
	}
      方法重写概述

子类中出现了和父类中一模一样的方法声明,也被称为方法覆盖,方法复写。
使用特点:

如果方法名不同,就调用对应的方法.
如果方法名相同,最终使用的是子类自己的.

方法重写的应用

当子类需要父类的功能,而功能主体子类有自己特有内容时,
   可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。

方法重写的注意事项

父类中私有方法不能被重写
子类重写父类方法时,访问权限不能更低
父类静态方法,子类也必须通过静态方法进行重写。
示例:
/*
子父类中构造函数对对象的初始化过程
内存中的方法区位置的所有方法,是在程序编译时进驻内存的. 
所以在运行时没有"方法存在的先后"之说,只有"方法被调用的先后"之说.
*/
class Fu{

	Fu(){

		show();	//会调用子类中的show(), 原因是子类覆盖了父类.
	}
	void show(){

			System.out.println("fu show");
	}
}

class Zi extends Fu{
	int num=8;

	Zi(){
			super();//这条是隐式语句
			System.out.println("在子类中的"+num);
	}
	void show(){
			System.out.println("zi show"+num);

}


class ExtendsDemo{
	public static void main(String[]args){

		//Fu f=new Fu();// 执行这条语句时, 不存在继承,更不存在覆盖的机制,
		                  //所以打印出 "fu show"

		Zi z=new Zi();// 输出结果为{ zi show 0  第二行为 在子类中的 8 } 
					//原因是对子类进行初始化时,必须在对父类进行初始化完成后,才对子类初始化.
		//z.show();

	}
}
结论:
    1)第一次执行System.out.println("在子类中的"+num);
是在对父类初始化时,此刻子类初始化还没有完成,num并没有开始被赋值.因此为0(系统默认赋值).
当父类初始化完成后,子类开始初始化,子类的初始化过程就是子类中的成员变量的赋值以及执行子类中对应的构造器.
2) 类中构造函数对本类对象的初始化过程,其实就是对本类中成员变量的赋值以及执行本类中对应的构造器.
先对成员变量显示赋值为8,再执行执行本类中对应的构造器.

重载与重写
1)重载在同一个类中,
如果两个方法的方法名\参数列表(包含参数类型与个数)均相同,
则为重载. 与方法的返回值类型无关
.
2),重写:在子类与父类关系中,
如果两个方法的方法名\参数列表(包含参数类型与个数)\返回值类型\均相同,
则为覆盖.子类中的覆盖父类中的方法.

3),注意:子类中的方法权限必须不小于父类中的方法权限,子类方法才能覆盖父类方法.
权限:private<默认 示例:
class Fu{
	 public int print(int x){
		System.out.println("fu"+x);	
		return x;
	}
}
class Zi extends Fu{
	 public int print(int x){
		System.out.println("zi");
		return x;
	}
}

class FugaiDemo{

	public static void main(String[] args){
		Zi z=new Zi();
		z.print(5);
		
	}
}

final关键字

final关键字是最终的意思,可以修饰类,成员变量,成员方法。
修饰类,类不能被继承
修饰变量,变量就变成了常量,只能被赋值一次
修饰方法,方法不能被重写
基本类型,是值不能被改变
引用类型,是地址值不能被改变

静态修饰符:
类中的成员
 static修饰静态成员
 没有static修饰的非静态成员

静态与非静态成员变量的区别:

从内存上看
 静态成员跟随类进入方法区中的静态去,优先于对象加载
 非静态成员 跟随对象的加载进入到堆内存

从所属上看
 静态成员只属于自己的类
 非静态成员属于对象

从调用方式上看
 静态成员可以被类名直接调用,也可以被对象调用(不推荐)
 非静态成员只能被对象调用

从生命周期
 静态成员跟随类的加载而加载,跟随类的消失而消失,生命最长
 非静态成员跟随对象的加载而加载,跟随对象的消失而消失,生命相对较短

使用方式
 静态成员是所有对象的共享数据
 非静态成员是对象自己的特有数据

  静态static修饰,应用场景

 静态成员变量
 静态成员方法

 静态成员变量:依据需要,具体分析,如果发现实现的功能中有对象的共享数据
 
 静态方法:定义方法的时候,方法中有没有访问过静态成员,如果有请你写静态
 如果你定义的方法中,访问过非静态的成员变量,这个方法也只能非静态
 方法中从来没有访问过任何成员变量,定义成静态方法.
示例:

/*
静态成员变量,有默认值

静态不能直接访问非静态  为什么呢
生命周期 静态优先非静态,静态成员调用的时候,还没有对象

静态方法中,不能写this也不能写super
*/

 class Student
{
	 String name;
	 int age ; 
	//静态区,无论new多少对象,只有一个
	 static String country ;

	public static void show(){
	   System.out.println(country);
	}

	public void method(){
	   System.out.println(name+age+country);
	}


}

class StaticDemo1 
{
	public static void main(String[] args) 
	{
		Student.show();
		//System.out.println(Student.country);
	}
}

二,多态概述


某一个事物,在不同时刻表现出来的不同状态。

多态的前提和体现

有继承关系
有方法重写
有父类引用指向子类对象

多态的好处

提高了程序的维护性(由继承保证)
提高了程序的扩展性(由多态保证)

多态的弊端

不能访问子类特有功能





示例:

//父类为抽象类
abstract class Animal{

	public abstract void eat();
}
//
class Cat extends Animal{

	public void eat(){
	
		System.out.println("猫爱吃鱼");
	}
	// 猫的特有方法
	public void zhua(){
		System.out.println("猫抓老鼠");
	}
}
class Dog extends Animal{

	public void eat(){
		System.out.println("狗啃骨头");
	}
	public void count(){
		System.out.println("狗算数");
	
	}
}
class MaoDemo{
	public static void main(String[]args){

		method(new Dog());
		method(new Cat());
		
	}

	//通过方法来实现,来什么动物,就打印什么动物的吃法.
	public static void method(Animal a){  //此处省略了  Animal a = new Dog() 传递
		
		a.eat();// 可被复写的方法(非子类特有方法)

		//子类特有方法
		if(a instanceof Cat){ // 判断父类引用是否指向Cat子类. 

			Cat c=(Cat)a;//向下转型(强制)

			c.zhua();//子类特有方法
		}
		else if(a instanceof Dog){
			Dog d=(Dog) a;
			d.count();
		}	

	}
}

多态运行原理一:

任何程序的运行都是:

先编译,后运行. 这是两个独立的过程.

编译时,只看语法上是否有错误.比如,Fu f=new Zi(); f.看电影(); 

编译器会到父类中查看是否有看电影()的方法,有就通过,否则不通过.

运行时,以对象为主体,按照内存运行图进行,比如,先到子类中调用,再到父类中调用.
示例:

class Fu{
	void 讲课(){
		System.out.println("管理");
	
	}
	void 钓鱼(){
		System.out.println("钓鱼");
	
	}
}
class Zi extends Fu{
	void 讲课(){
		System.out.println("java");
	
	}
	void 看电影(){
		System.out.println("看电影");
	
	}

}
class FZDemo{

	public static void main(String []args){

		Fu f=new Zi();

		//Zi f=new Zi();

		f.讲课();// 编译时,编译器发现父类中有讲课方法,所以编译通过. 
				//但是运行时,运行过程还是先从子类中找该方法,如果有就调用,没有再去父类调用.

		f.看电影();//  是在编译时失败的. 原因是,编译器发现父类中根本没有看电影的方法().  所以编译不通过.

		f.钓鱼();
	}
}

多态运行原理二: 

子父类中,同名成员变量,运行时的内存位置.

在同一个对象里  有两个位置,分别标示为 this 和super 

this 指向本类,super 指向父类

示例:

class Fu{

	int num=7;
	void show(){
	
	System.out.println("fu");

	}
}
class Zi extends Fu{

	int num=9;
	
	void show(){
	System.out.println("zi");
	}
	
}
class FziDemo{ 
	public static void main(String[]args){
	
	Fu f=new Zi();
		//打印父类中的num, 因为有父类的标识
		System.out.println(f.num);
		//运行时,走子类重写的方法
	f.show();	

	}
}

结论:
  多态中,成员变量,成员方法特点
       1. 非静态的成员变量特点
 编译时期,看父类中有没有,如果有编译成功,没有编译失败
 运行时期,运行的是父类中的成员变量
 
2. 静态的成员变量特点
 编译时期,看父类中有没有,如果有编译成功,没有编译失败
 运行时期,运行的是父类中的成员变量
        3. 非静态成员方法特点
 编译时期,看父类中有没有,如果有编译成功,没有编译失败
 运行时期,运行的是子类重写的方法
4. 静态成员方法特点
 编译时期,看父类中有没有,如果有编译成功,没有编译失败
 运行时期,运行的是父类中的方法

简单方法:
  除了非静态的成员方法以外,编译运行都看父类
  只有非静态的成员方法,编译看父类,运行看子类

   Fu f = new Zi();
  编译看左边,运行看右边  非静态的成员方法
  编译看左边,运行看左边  成员变量,静态成员变量,静态成员方法





你可能感兴趣的:(笔记)