OOP(二)继承与多态

继承

(is-a):子类扩展基类,并获取基类中的全部成员变量与方法(非private),因此在定义子类时只需指出与基类的不同之处即可。换句话说,通用的方法放在基类,特殊方法放在子类。 

Java在类中只支持单继承关系

public class Fruit {
	public double weight;

	public void info() {
		System.out.println("ClassFriut " + weight + " g");
	}
}


------------------------------------------------------------------
public class Apple extends Fruit {

	public static void main(String[] args) {
		Apple a = new Apple();
		a.weight = 100;
		a.info();

	}

方法重写(覆盖)

基本要求是子类与基类方法严格一致。另外,要求子类方法的返回值类型<=基类方法的返回值类型;子类中的异常类<=基类声明抛出的异常类;子类方法的访问权限>=父类的访问权限(基类的私有方法和静态方法以及final方法只能被重载不能被重写

重载和重写的比较
方法重载 方法重写
方法名相同 方法名相同
与修饰符无关 修饰符>=基类
返回值无关 返回值<=基类
参数列表不同(类型,个数,顺序[针对两个不同类型参数]) 参数列表相同
public class Bird {
	int a = 5;
	static int b = 16;

	public void fly() {
		System.out.println("Bird flying...");
	}

}

--------------------------------------------------------

public class OtherBird extends Bird {
	int a = 10;
	static int b = 15;

	public void fly() {
		System.out.println("I can't fly");
		System.out.println(a);
		System.out.println(b);
	}

	public void getSuperMethod() {
		// 访问基类方法
		super.fly();
		// 访问基类同名变量
		System.out.println(super.a);
		// 访问基类同名静态变量
		System.out.println(Bird.b);
	}

	public static void main(String[] args) {
		OtherBird ob = new OtherBird();
		ob.fly();// 子类重写(覆盖)基类方法
		ob.getSuperMethod();
	}
}

super关键字

super关键字用于调用基类的实例变量和方法。

情况一:子类与基类成员变量同名是,则基类的成员变量会被隐藏。此时需用super进行访问

情况二:子类对基类方法进行重写,需访问基类方法时使用super

情况三:在子类构造器中,显式调用基类构造器进行成员初始化,此时super语句要放子类构造器第一行

注:在访问某个成员变量x,但没有显式指定调用者的查找顺序是:

该方法中是否有该成员变量x--->当前类中是否有成员变量x--->父类中是否有成员变量x....--->直到Object类--->编译出错



public class HideTest {

	public static void main(String[] args) {
		Son s = new Son();
		// System.out.println(s.s);
		//子类只是隐藏基类的同名变量,不会完全覆盖
		//s.tag对象中,既有子类的值,也有基类的值
		System.out.println(((Parent) s).s);

	}

}

class Parent {
	public String s = "super";
}

class Son extends Parent {
	private String s = "son";
}
public class ConstructorTest extends B {

	public ConstructorTest() {
		super("cons", 2);
		System.out.println("C子类构造器");
	}

	public static void main(String[] args) {
		new ConstructorTest();
		//从最顶层开始执行
	}

}

class A {
	public A() {
		System.out.println("无参构造器");
	}
}

class B extends A {
	public B(String s) {
		System.out.println("B的构造器有参数" + s);
	}

	public B(String s, int i) {
		this(s);
		System.out.println("B的重载构造器" + i);
	}
}

子类在调用基类构造器时有以下几种情况:

  • 若没有super进行显式调用,子类会默认调用基类无参构造器一次
  • 若子类构造器第一行super显示调用了基类构造器,系统会根据参数列表自动选择构造器
  • 若子类第一行使用this调用另一个构造器,系统会在另一个构造器中调用基类构造器
super与this比较
this super
引用隐式参数 调用基类的方法或者成员变量
调用本类中其他构造器 调用基类的构造器
this是一个引用对象 super指示调用基类的关键字

多态

表面上看基类除了可以引用自己本身的对象,还可引用任何子类的对象。

从机制上看,在编译时类型由声明的变量决定,运行时由实际赋值给该变量的类型决定,即为多态

package inheritance;

public class SubClass extends BaseClass {
	public String book = "why";

	public void test() {
		System.out.println("重写方法");
	}

	public void sub() {
		System.out.println("subMethod");
	}

	public static void main(String[] args) {
		BaseClass bc = new BaseClass();
		System.out.println(bc.book);
		bc.base();
		bc.test();
		SubClass sc = new SubClass();
		System.out.println(sc.book);
		sc.base();
		sc.test();
		// 多态,向上转型,子类直接赋值给基类无须类型转换
		BaseClass bc2 = new SubClass();
		// 访问基类成员变量
		System.out.println(bc2.book);
		// 继承于基类
		bc2.base();
		// 访问当前类的test()方法
		bc2.test();
		// bc2.sub();
		// 原因是bc2编译时类型为BaseClass类型,基类中没有此方法
	}

}

class BaseClass {
	public int book = 6;

	public void base() {
		System.out.println("superMethod_1");
	}

	public void test() {
		System.out.println("superMethod_2");
	}
}

利用组合代替继承

package inheritance;

public class CompsiteTest {

	public static void main(String[] args) {
		//显式创建被组合对象
		Animal a1 = new Animal();
		Birddd b = new Birddd(a1);
		b.breath();
		b.fly();
		Animal a2 = new Animal();
		Wolf w = new Wolf(a2);
		w.breath();
		w.run();

	}

}

class Animal {
	private void beat() {
		System.out.println("herat beating...");
	}

	public void breath() {
		beat();
		System.out.println("breathing..");
	}
}

class Birddd {
	private Animal a;

	public Birddd(Animal a) {
		this.a = a;
	}

	public void breath() {
		a.breath();
	}

	public void fly() {
		System.out.println("flying...");
	}
}

class Wolf {
	private Animal a;

	public Wolf(Animal a) {
		this.a = a;
	}

	public void breath() {
		a.breath();
	}

	public void run() {
		System.out.println("running...");
	}
}

初始化块与静态初始化块

初始化块会先于构造器执行。

初始化块和对象声明的执行顺序是按代码顺序执行

静态初始化块与类相关,只在类加载时初始化一次。其他特点与初始化块一致

public class InitBlock {

	/*
	 * { a = 6; } int a = 9;
	 */
	// 静态初始化块(类初始化块)
	static int a = 10;
	static {
		a = 5;
	}

	// 初始化块,按照代码排列顺序执行
	// 初始化块可看作是对构造器的补充,初始化块总是在构造器之前执行
	public static void main(String[] args) {
		System.out.println(InitBlock.a);
	}

}
public class TextBlock {
	public static void main(String[] args) {
		new Leaf();
		new Leaf();
		// 静态初始化块,随类的产生只初始化一次
	}

}

class Root {
	static {
		System.out.println("root静态初始化块");
	}
	{
		System.out.println("root初始化块");
	}

	public Root() {
		System.out.println("root无参构造器");
	}
}

class Mid extends Root {
	static {
		System.out.println("mid静态初始化块");
	}
	{
		System.out.println("mid初始化块");
	}

	public Mid() {
		System.out.println("mid无参构造器");
	}

	public Mid(String s) {
		this();
		System.out.println("mid有参构造器" + s);
	}

}

class Leaf extends Mid {
	static {
		System.out.println("leaf静态初始化块");
	}
	{
		System.out.println("leaf初始化块");
	}

	public Leaf() {
		super("参数mid");
		System.out.println("leaf构造器");
	}
}

猜你喜欢

转载自blog.csdn.net/davemo/article/details/81081285