黑马程序员-java继承

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------


一、继承概述

1、继承的优点:

1)提供了代码的复用性;

2)让类与类之间产生了关系。有了这个关系,才有了多态的特性。

注意:千万不要为了获取其他类的功能,简化代码而继承。必须是类与类之间有所属关系才可以继承。


java只支持单继承,不支持多继承。因为多继承容易带来安全隐患:当多个父类中定义了相同方法,而方法内容不同时,子类对象不确定要运行哪个。

但java保留了这种机制,并用另一种形式来表示:多实现

java支持多层继承,也就是一个继承体系。


2、如何使用一个继承体系中的方法:

先查阅体系父类的描述,因为父类中定义的是该体系中共性方法。通过了解共性方法,就可以知道该体系的基本功能。然后,具体调用时创建最子类的对象。因为1)父类可能不能创建对象;2)子类对象可以使用更多的方法,包括基本的也包括特有的。


3、继承的原则:

1)子类继承了其父类中不是私有的成员变量和成员方法,作为自己的成员变量和方法。

2)子类中定义的成员变量和父类中定义的成员变量相同时,则父类中的成员变量被隐藏

3)子类中定义的成员方法,它的名字、返回类型及参数个数和类型都与父类中的某个成员方法完全相同时,则父类的成员方法被覆盖。


二、变量

如果子类中出现非私有的同名成员变量时,子类要访问本类中的变量,用this语句;子类要访问父类中的同名变量,用super语句

例1:

class Parent
{
	int i=1;
}
class Child extends Parent
{
	int i=2;
	public int getParentI()
	{
		return super.i;  //访问父类同名变量
	}
	public int getChildI()
	{
		return this.i;  //访问子类同名变量
	}
}
class ExtendsDemo
{
	public static void main(String[] args) 
	{
		Child c=new Child();
		System.out.println(c.getParentI());
		System.out.println(c.getChildI());
	}
}

输出结果:

1
2


三、方法

如果子类中的成员方法的名字、返回类型及参数个数和类型都与父类中的某个成员方法完全相同时,则父类的成员方法被覆盖(重写)。

覆盖:

当子类继承父类,沿袭了父类的功能到子类中。虽然子类具备了该方法,但想要的方法内容却和父类的不一致。这时,没有必要定义新方法,而是使用覆盖特性,保留父类的方法定义,并重写方法内容。


覆盖原则:

1)子类覆盖父类,必须保证子类的权限大于等于父类的权限,否则编译失败。

2)静态只能覆盖静态。

3)子类覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法只能抛出父类的异常货该异常的子类;如果父类抛出多个异常,那么子类的覆盖方法只能抛出父类异常的子集;如果父类方法中没有异常抛出,那么子类的覆盖方法也不可以抛出异常。如果子类的覆盖方法发生了异常,就必须进行try处理,绝对不能抛出。


覆盖常用于功能扩展:

class Tel
{
	void show()
	{
		System.out.println("number");
	}
}
class NewTel extends Tel
{
	void show()
	{
		super.show();
		System.out.println("name");
		System.out.println("pic");
	}
}

四、构造函数

1)在对子类对象进行初始化时,父类的构造函数也会运行。因为子类的所有构造函数默认第一行有一条隐式的语句:super();

2)如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。注意:super语句必须在子类构造函数的第一行。

3)当父类中没有空参数的构造函数时,子类必须手动定义super语句来指定访问父类中的构造函数,否则编译失败。

4)子类的构造函数第一行也可以手动指定this语句来访问本类的其他构造函数。

例2:

class Person
{
	private String name;
	Person(String name)
	{
		this.name=name;
	}
	Person()
	{
		this.name="AAA";
	}
	public void getStudent()
	{
		System.out.println("name="+this.name);
	}
}
class Student extends Person
{
	private String id;
	Student(){}               //默认第一行为super();
	Student(String name)
	{
		super(name);      //手动指定super语句访问父类构造函数
	}
	Student(String name,String id)
	{
		this(name);       //手动this语句访问本类其他构造函数
		this.id=id;
	}
	public void getStudent()  //覆盖父类getStudent()方法
	{
		super.getStudent();
		System.out.println("id="+this.id);
	}
}
class ExtendsDemo
{
	public static void main(String[] args)
	{
		Student s1=new Student();
		Student s2=new Student("BBB");
		Student s3=new Student("CCC","001");
		s1.getStudent();
		s2.getStudent();
		s3.getStudent();
	}
}

输出结果:

name=AAA
id=null
name=BBB
id=null
name=CCC
id=001

五、继承中容易出现的错误

例3:

class Parent
{
	int a = 1;
	public int getA()
	{
		return this.a;
	}
}

class Child extends Parent 
{
	int a = 2;
}

public class ExtendsDemo 
{
	public static void main(String[] args) 
	{
		Child c =new Child();
		System.out.println(c.getA());
		System.out.println(c.a);
	}
}

输出结果:

1
2
此例中,Child继承了Parent,根据继承原则2),父类中的int a变量被隐藏,所以直接打印c.a,结果为2。但因为Child中没有同名的getA()方法,所以c.getA()中调用的getA()方法是继承自Parent类的,Parent类中的getA()方法调用的是Parent类中的a,所以结果为1。

例4:

class Parent
{
	int a = 1;
	public int getA()
	{
		return this.a;
	}
}

class Child extends Parent 
{
	int a = 2;
	public int getA()
	{
		return this.a;
	}
}

public class ExtendsDemo 
{
	public static void main(String[] args) 
	{
		Child c =new Child();
		System.out.println(c.getA());
		System.out.println(c.a);
	}
}

输出结果:

2
2

例4与例3 的区别在于,Child类中定义了同名的getA方法,它覆盖了Parent类中的同名方法。所以这里c.getA()中调用的getA()是Child类中的方法,它引用的是Child类中的a,所以结果为2。

例5:

class Parent
{
	int a = 1;
	public int getA()
	{
		return this.a;
	}
}

class Child extends Parent 
{
	public Child()
	{
		a=2;
	}
}

public class ExtendsDemo 
{
	public static void main(String[] args) 
	{
		Child c =new Child();
		System.out.println(c.getA());
		System.out.println(c.a);
	}
}

输出结果:

2
2

此例中,Child类继承Parent类中的变量int a和方法getA()。因此,Child类中的构造函数对a的赋值实际上是对继承自父类的变量a赋值,输出结果显示为2。

例6:

class Parent
{
	int a = 1;
	public int getA()
	{
		return this.a;
	}
}

class Child extends Parent 
{
	int a=2;
	public Child()
	{
		a=3;
	}
}

public class ExtendsDemo 
{
	public static void main(String[] args) 
	{
		Child c =new Child();
		System.out.println(c.getA());
		System.out.println(c.a);
	}
}

输出结果:

1
3

此例中,Parent类中的a被Child类中的同名变量a所隐藏。所以Child类中的构造函数初始化的是Child类中的a,所以打印c.a的结果为3。由于未覆盖Parent类中的getA(),所以c.getA()调用的仍是Parent类中的a,所以打印c.getA()的结果为1。

例7:

class Parent
{
	int i=1;
	Parent()
	{
		i=i+1;
		System.out.println(i);
		printI();
	}
	public void printI()
	{
		System.out.println("Parent.i="+i);
	}
}
class Child extends Parent
{
	int i=2;
	Child()
	{
		i=i+2;
		System.out.println(i);
		printI();
	}
}
class ExtendsDemo
{
	public static void main(String[] args)
	{
		Parent p=new Parent();
		Child c=new Child();
		c.printI();
		System.out.println(c.i);
	}
}

输出结果:

2
Parent.i=2
2
Parent.i=2
4
Parent.i=2
Parent.i=2
4

此例中,Parent类的构造函数访问的是Parent类中的i,printI()也毫无疑问的调用的是Parent类中的i,因此直接打印i的结果为2,而printI()的结果为2;

Child类的构造函数默认第一行为super(); 

Child类的构造函数访问的是Child类中的i,而printI()调用的是Parent类中的a,因此直接打印i的结果为4,而printI()的结果为2。

例8:

class Parent
{
	int i=1;
	Parent()
	{
		i=i+1;
		System.out.println(i);
		printI();
	}
	public void printI()
	{
		System.out.println("Parent.i="+i);
	}
}
class Child extends Parent
{
	int i=2;
	Child()
	{
		i=i+2;
		System.out.println(i);
		printI();
	}
	public void printI()
	{
		System.out.println("Parent.i="+i);
	}
}
class ExtendsDemo
{
	public static void main(String[] args)
	{
		Parent p=new Parent();
		Child c=new Child();
		c.printI();
		System.out.println(c.i);
	}
}

输出结果:

2
Parent.i=2
2
Parent.i=0
4
Parent.i=4
Parent.i=4
4

此例中,Parent类的构造函数访问的是Parent类中的i,printI()也毫无疑问的调用的是Parent类中的i,因此直接打印i的结果为2,而printI()的结果为2;

Child类的构造函数默认第一行为super(); 它调用Parent类的构造函数,此时构造函数访问的仍是Parent类中的i,而printI()因为覆盖所以调用的是Child类中的i,但注意此时Child类中i还未初始化,因此直接打印i的结果为2,而printI()的结果为0;

Child类的构造函数访问的是Child类中的i,而printI()调用的是Parent类中的a,因此直接打印i的结果为4,而printI()的结果为4。


总结:

1)如果调用父类方法,则父类方法中引用的变量一定是父类中的变量(如例3);

2)如果调用子类方法,那么:如果子类中没有同名变量(未实现隐藏),而在子类方法中引用了,则引用的是父类的变量;如果有同名变量(实现隐藏),则引用的是子类本身的变量;



---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------详细请查看:www.itheima.com

继承Java中的一种重要的面向对象编程概念,它允许一个(子继承另一个(父)的属性和方法。通过继承,子可以从父中获得已有的属性和方法,并且可以在此基础上进行扩展或者修改。 在Java中,使用关键字"extends"来实现继承。子通过继承,可以使用父中的非私有属性和方法。子可以直接访问父的公有属性和方法,也可以通过super关键字来访问父的构造方法和成员。 下面是一个简单的继承示例: ```java // 父 class Animal { private String name; public Animal(String name) { this.name = name; } public void eat() { System.out.println(name + "正在吃饭"); } } // 子 class Dog extends Animal { public Dog(String name) { super(name); } public void bark() { System.out.println("汪汪汪"); } } public class Main { public static void main(String[] args) { Dog dog = new Dog("旺财"); dog.eat(); // 调用父的eat方法 dog.bark(); // 调用子的bark方法 } } ``` 在上面的示例中,Animal是父,Dog是子。Dog通过关键字"extends"继承了Animal,从而可以使用Animal中的属性和方法。在main方法中,我们创建了一个Dog对象,并调用了eat方法(来自父)和bark方法(来自子)。 通过继承,子可以扩展父的功能,并且可以在需要时覆盖父的方法来实现特定的行为。继承Java中实现代码重用和多态性的重要手段之一。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值