黑马程序员-Java基础:继承和多态
2015-08-12 04:14
676 查看
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
举例:现实生活中当提到猫,狗时,我们都清楚它们是一种动物,那么就可以说猫,狗继承自动物。
类似这种: is-a关系都可以称为继承关系,被继承的类称为父类或超类(father/super class),继承自父类的类称为子类或基类(child/base class)。
例如:Cat is-a animal, 小明 is-a 学生…
注:判断是否使用继承:类之间是否具备is-a关系
2.继承的书写格式
Java中通过extends 关键字来实现类与类之间的继承关系
格式:class 子类名 extends 父类名 { }
示例:
注:该示例仅仅用于说明Java的继承书写格式,因此没有给出类的具体实现。
3.继承的简单案例
需求:
<1>定义一个类Person
该类中包含两个成员变量:name, age
两个构造方法:空参构造,带参构造
一个普通方法:show():用于打印所有成员变量
<2>定义一个类Student继承Person
<3>定义一个测试类Demo,在测试类中测试两个类。
代码:
测试结果:
4.继承的好处和弊端
好处:
<1>提高了代码的复用性
<2>提高了代码的可维护性
<3>让类与类之间产生了关系,是多态的前提条件
弊端:
<1>打破了类的封装性
<2>使类之间的耦合性增强,当父类发生改变时,会对子类产生影响
程序开发的原则:低耦合,高内聚
耦合是指类与类之间的关系,内聚是指类独立完成某项功能的能力
5.Java中继承的特点
<1>Java中只支持单继承,不支持多继承
<2>Java可以支持多层继承
代码解释:
6.继承中的注意事项
<1>子类不能继承父类的私有成员,因为私有成员只能在本类中访问
代码验证:
<2>子类无法继承父类的构造方法,但可以通过super访问
<3>不要为了部分功能而使用继承,需要确定类之间是否具备is-a关系
7.继承关系中子父类成员的关系
<1>成员变量的关系:
问题1:如何在子类方法中访问父类中的非私有成员变量?
代码解答:
情况1:子父类中成员变量不同名的情况
运行结果:
情况2:子类中成员变量与父类成员变量同名的情况
运行结果:
由上面的示例代码可知,要在子类中访问父类成员变量,可以使用super关键字
变量的就近访问原则:在方法内使用一个变量时,先在方法中寻找该变量,若没找到则在本类成员变量中寻找该变量,若仍然找不到则在父类成员变量中寻找该变量,以上位置都无法找到该变量,则报错。
类似的方法的调用也有就近访问原则,先在调用方法的对象中查找该方法,没有则到父类中找该方法,然后间接父类,依次类推,没找到就报错。
<2>构造方法的关系
A.虽然子类无法继承父类的构造方法,但子类的构造方法默认会去访问父类的空参构造方法,这样做的原因在于初始化子类数据之前先初始化父类的数据
代码示例:
运行结果:
从程序的运行结果可以看到,在调用子类构造方法创建对象时,默认会调用父类的空参构造方法。
B.父类中如果没有空参构造方法,那么子类需要显示调用父类的其他构造方法,通过super语句:super(参数…);
代码示例:对上面的代码示例做了如下修改
运行结果:
在上面的代码中如果不通过super语句显示调用父类的带参构造方法,则程序无法编译通过。
因此,子类的构造方法中一定会调用父类的构造方法。
super和this关键字对比:
this代表本类对象的引用,super代表父类对象的引用
this.成员变量(成员方法) : 在类中调用本类成员
super.父类成员 : 在子类中调用父类成员
this(参数) : 在本类构造方法中调用本类其他构造方法
super(参数) : 在子类构造方法中调用父类构造方法
<3>成员方法的关系
方法重写:子类中出现了和父类中一模一样的方法声明,即返回值类型,方法名,参数列表都相同,这种情况称为方法重写,或方法覆盖
使用特点示例:
思考如下代码:
运行结果:
从运行结果发现,最终调用的是子类的方法,而不是父类的方法,这种现象就是方法的重写。
方法重写的应用:当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
方法重写的注意事项:
父类中私有的方法不能重写,因为父类的私有方法子类无法继承;
子类重写父类方法时访问权限不能更低,最好与父类一致;
父类静态方法,子类也必须通过静态方法进行重写。
8.final 关键字
final 是最终的意思,可以用来修饰类,成员变量,成员方法
被final修饰的类无法被继承
被final修饰的变量只能被赋值一次,之后无法再改变,即称为常量
被final修饰的方法无法被子类重写
final修饰基本类型变量时,是值不能被改变;修饰引用类型变量时,是地址不能被改变
多态是指某一个事物在不同时刻表现出来的不同状态。
例如:水可以有气态,液态,固态,但它们的本质都是水
动物可以指猫,狗,老虎…..虽然它们名字不同,但都属于动物的一种
那么Java中如何体现多态呢?
下面用一个需求的两种解决方案来说明:
需求:分别定义一个猫类,狗类,老虎类,鸟类,来模拟现实中的动物,这些动物都有一个行为叫进食,创建它们的对象并让所有动物进食。
分析:很显然每一种动物都有各自喜欢吃的食物,所以虽然它们都有吃的行为,但具体的行为方式却不尽相同。
代码实现:非多态的方式
输出结果:
分析:通过结果,我们很开心的发现这段程序让所有动物吃到了想吃的东西,那么接下来需求改变了,又来了其他动物,马,大象,猪……不计其数的动物都要吃各自的喜欢的食物,我们当然可以用上面的代码去解决,再添加新的类,然后创建新动物类的对象,调用进食方法,但是可以想象代码的长度,而且每加入一个新的动物都要对代码做大量修改。
下面用一种更好的方式来改进上面的代码:
由于每种动物都要进食,而且它们都属于动物类型的一种,那么可以定义一个动物类,让所有的动物继承自这个类。
代码实现:多态方式
运行结果:
理解上面代码的关键就在于Animal类型的引用实际指向的对象是Animal的子类对象,而这正式多态的意义所在。可以看到通过多态改进后的代码可维护性和可扩展性都大大增强了,此时如果再加入其他动物类,只需要让新的动物继承Animal,重写eat()方法,然后将新动物的对象加入animals数组即可。
由上面的例子得出多态的前提条件:
有继承或者实现关系
有方法重写
父类引用指向子类对象
2.多态的好处和缺点
好处:提高了代码的可维护性和可扩展性
缺点:不能通过父类的引用使用子类特有的功能
向上转型:当父类引用指向子类对象时,编译器会将子类对象当作父类使用
向下转型:当需要在多态中使用子类特有方法时,需要将父类引用强制转型为子类引用,但这种操作的前提是必须保证父类引用实际指向的是子类对象,否则会发生类型转换异常。
3.多态中成员访问的特点
通过多态访问成员变量:通过父类的引用变量只能访问父类的成员变量,而不能访问子类的成员变量,否则编译不通过。
通过多态访问成员方法:通过父类引用只能调用父类中定义的方法,当子类重写了该方法时,则调用子类重写后的方法。无法调用子类特有的方法。
通过多态访问静态方法:因为静态的成员都是与类相关的,所有不存在多态的问题,通过类名调用即可。
构造方法:创建子类对象时需要调用子类构造方法,而子类构造方法中会默认调用父类构造方法。
格式:修饰符 abstract 方法名 (参数列表) ;
2.抽象类的概念:被abstract关键字修饰的类称为抽象类。
凡是包含抽象方法的类必须定义为抽象类,但抽象类中不一定包含抽象方法。
3.抽象类的特点:
<1>抽象类和抽象方法必须用abstract关键字修饰
<2>抽象类中不一定包含抽象方法,但有抽象方法的类必须是抽象类
<3>抽象类不能实例化,但可以有构造方法
<4>抽象类的子类要么声明为抽象类,要么重写父类所有抽象方法。
除了以上四个特点抽象类和普通类没有什么区别,也可以定义成员变量和成员方法。
一个简单的抽象类案例:
4.抽象类的几个小问题
<1>抽象类有构造方法,不能实例化,那么构造方法有什么用?
答:用于子类访问父类数据的初始化
<2>一个类如果没有抽象方法,却定义为了抽象类,有什么用?
答:为了禁止创建该类的对象
<3>abstract不能和哪些关键字共存?
答:final,private,static
在现实生活中有些人抽烟,有些人不抽烟,那么当我们要在Java中描述一个人类的时候(Person类),如何去表示哪些人抽烟哪写人不抽烟呢?
由于抽烟仅仅是一个行为,而这个行为不是所有人都具备,那么就可以将抽烟这个行为定义为一个接口,然后由抽烟的人去实现这个接口。
从上面的例子可以知道,当需要为某一个类添加扩展功能时,就需要为这个扩展功能定义一个接口,然后由需要扩展的类来实现具体的功能。
2.接口的特点
成员特点
<1>成员变量:只能是常量
接口中成员变量都有默认修饰符:public static final
<2>构造方法:没有构造方法
<3>成员方法:只能是抽象方法
接口中成员方法默认修饰符:public abstract
使用特点:
<1>定义一个接口时,用关键字interface修饰:
interface 接口名{ }
<2>一个类实现接口时,用implements 关键字
class 类名 implements 接口名 {}
<3>接口不能实例化
<4>接口的实现类特点:可以是抽象类或具体类,如果是具体类,必须重写(或者说实现更恰当)接口中所有方法
接口的简单示例
运行结果:
3.类与类,类与接口,接口与接口之间的关系
<1>类与类之间:继承关系,只能单继承,可以多层继承
<2>类与接口之间:实现关系,可以单实现,也可以多实现
还可以在继承一个类的同时实现多个接口
<3>接口与接口之间:继承关系,可以单继承,也可以多继承
举例:
如上面代码所示,类B就是A的内部类,其实内部类是类中成员的一种,类中的成员有成员变量,成员方法,当然也可以有成员类,就像人的体内有心脏,肝脏,那么心脏就属于人这个类的内部类。
2.内部类的访问规则
<1>在内部类中可以直接访问外部类的成员,包括私有成员,因为内部类本身也属于类的成员
<2>外部类要想访问内部类的成员,必须创建内部类的对象
3.内部类的分类
<1>成员内部类:位于类中方法外,和成员变量位置相同
创建内部类对象的方式:
非静态成员内部类:
外部类名.内部类名 对象名 = new 外部类名.new 内部类名();
静态成员内部类:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
<2>局部内部类:位于方法体内
局部内部类访问局部变量必须加final修饰。
4.匿名内部类
局部内部类的简化形式
前提:存在一个类或者接口
格式:
本质:匿名内部类的本质是继承该类或者实现该接口的子类的匿名对象。
代码示例:
1.作用:用于区分同名的类,并按照功能或模块对类进行分类管理
**2.定义方式:**package 包名;
注:多级包用 . 分隔
注意事项:
<1>package语句必须在文件中的第一条有效语句
<2>在一个java文件中,只能有一个package
<3>如果没有package,默认就是无包名
3.带包的编译和运行:
javac -d . HelloWorld.java
4.导包
<1>Java就提供了关键字import用于包的导入,如果在类中需要用到其他包中的类,则用import将需要的类导入。
<2>格式
import 包名…类名;
import 包名…*;(不建议)
<3>package,import,class的顺序
package > import > class
day08-09:继承和多态
一、继承
1.概述:提取多个类中的共性内容,组成一个新的类,这个新类和原来类之间就产生了一种关系,并且原来的多个类都具备新类中的内容,这种关系就称为继承。举例:现实生活中当提到猫,狗时,我们都清楚它们是一种动物,那么就可以说猫,狗继承自动物。
类似这种: is-a关系都可以称为继承关系,被继承的类称为父类或超类(father/super class),继承自父类的类称为子类或基类(child/base class)。
例如:Cat is-a animal, 小明 is-a 学生…
注:判断是否使用继承:类之间是否具备is-a关系
2.继承的书写格式
Java中通过extends 关键字来实现类与类之间的继承关系
格式:class 子类名 extends 父类名 { }
示例:
class Person {} class Student extends Person {} class Teacher extends Person {}
注:该示例仅仅用于说明Java的继承书写格式,因此没有给出类的具体实现。
3.继承的简单案例
需求:
<1>定义一个类Person
该类中包含两个成员变量:name, age
两个构造方法:空参构造,带参构造
一个普通方法:show():用于打印所有成员变量
<2>定义一个类Student继承Person
<3>定义一个测试类Demo,在测试类中测试两个类。
代码:
/* * Person类:父类 */ public class Person { /* * 成员变量 */ String name; int age; //空参构造 public Person() { } //带参构造 public Person(String name, int age) { this.name = name; this.age = age; } //普通方法show() public void show() { System.out.println("Person:" + name + "," + age); } }
/* * Student类:子类 * 在学生类中没有定义任何成员,主要为了说明继承的特点 */ public class Student extends Person { }
public class Demo { /* * 测试类:创建Student类对象,并通过Student对象访问Person类中的成员 * 1.访问Person类中的成员变量 * 2.调用Person类中的成员方法 */ public static void main(String[] args) { Student s = new Student(); // 1.访问Person类中的成员变量 s.name = "小明"; s.age = 18; System.out.println(s.name); System.out.println(s.age); // 2.调用Person类中的成员方法 s.show(); } }
测试结果:
小明 18 Person:小明,18
4.继承的好处和弊端
好处:
<1>提高了代码的复用性
<2>提高了代码的可维护性
<3>让类与类之间产生了关系,是多态的前提条件
弊端:
<1>打破了类的封装性
<2>使类之间的耦合性增强,当父类发生改变时,会对子类产生影响
程序开发的原则:低耦合,高内聚
耦合是指类与类之间的关系,内聚是指类独立完成某项功能的能力
5.Java中继承的特点
<1>Java中只支持单继承,不支持多继承
<2>Java可以支持多层继承
代码解释:
class A{ } class B{ } /* * 下面的代码编译无法通过 * 原因:Java只支持单继承,C无法既继承A,又继承B */ class C extends A,B{ }
class A{ } class B extends A{ } /* * 下面代码可以编译通过 * 原因:Java支持多层继承,C通过继承B间接继承了A */ class C extends B{ }
6.继承中的注意事项
<1>子类不能继承父类的私有成员,因为私有成员只能在本类中访问
代码验证:
class Father{ /*在父类中定义两个私有成员*/ private int num = 10; private void method(){ System.out.println("Father method."); } } //子类中没有成员 class Child extends Father{ } public class ExtendDemo{ public static void main(String[] args) { //在测试类中创建子类对象 Child c = new Child(); //通过子类对象变量调用父类的私有成员 //编译报错: num has private access in Father c.num = 200; //编译报错: cannot find symbol c.method(); c.method(); } }
<2>子类无法继承父类的构造方法,但可以通过super访问
<3>不要为了部分功能而使用继承,需要确定类之间是否具备is-a关系
7.继承关系中子父类成员的关系
<1>成员变量的关系:
问题1:如何在子类方法中访问父类中的非私有成员变量?
代码解答:
情况1:子父类中成员变量不同名的情况
class Father { int a = 100; } class Child extends Father { int b = 200; public void childMethod() { System.out.println("访问子类中的变量:" + b); System.out.println("访问父类中的变量:" + a); } } class ExtendDemo2 { public static void main(String[] args) { Child c = new Child(); c.childMethod(); } }
运行结果:
情况2:子类中成员变量与父类成员变量同名的情况
class Father { int a = 100; } class Child extends Father { int a = 200; public void childMethod() { int a = 300; System.out.println("访问子类中的局部变量:" + a); System.out.println("访问子类中的成员变量:" + this.a); System.out.println("访问父类中的成员变量:" + super.a); } } class ExtendDemo2 { public static void main(String[] args) { Child c = new Child(); c.childMethod(); } }
运行结果:
由上面的示例代码可知,要在子类中访问父类成员变量,可以使用super关键字
变量的就近访问原则:在方法内使用一个变量时,先在方法中寻找该变量,若没找到则在本类成员变量中寻找该变量,若仍然找不到则在父类成员变量中寻找该变量,以上位置都无法找到该变量,则报错。
类似的方法的调用也有就近访问原则,先在调用方法的对象中查找该方法,没有则到父类中找该方法,然后间接父类,依次类推,没找到就报错。
<2>构造方法的关系
A.虽然子类无法继承父类的构造方法,但子类的构造方法默认会去访问父类的空参构造方法,这样做的原因在于初始化子类数据之前先初始化父类的数据
代码示例:
class Father{ private int a; private int b; public Father() { System.out.println("父类空参构造方法被调用."); } public Father(int a, int b) { this.a = a; this.b = b; System.out.println("父类带参构造方法被调用."); } } class Child extends Father { public Child() { System.out.println("子类空参构造方法被调用."); } public Child(int a, int b) { System.out.println("子类带参构造方法被调用."); } } public class ExtendDemo2{ public static void main(String[] args) { Child c = new Child(); System.out.println("---------------"); Child c2 = new Child(1,2); } }
运行结果:
从程序的运行结果可以看到,在调用子类构造方法创建对象时,默认会调用父类的空参构造方法。
B.父类中如果没有空参构造方法,那么子类需要显示调用父类的其他构造方法,通过super语句:super(参数…);
代码示例:对上面的代码示例做了如下修改
class Father{ private int a; private int b; //注释掉父类的空参构造 // public Father() { // System.out.println("父类空参构造方法被调用."); // } //保留父类的带参构造 public Father(int a, int b) { this.a = a; this.b = b; System.out.println("父类带参构造方法被调用."); } } class Child extends Father { public Child() { //子类空参构造中显示调用父类带参构造 super(1,2); System.out.println("子类空参构造方法被调用."); } public Child(int a, int b) { //子类带参构造中显示调用父类带参构造 super(1,2); System.out.println("子类带参构造方法被调用."); } } public class ExtendDemo2{ public static void main(String[] args) { Child c = new Child(); System.out.println("---------------"); Child c2 = new Child(1,2); } }
运行结果:
在上面的代码中如果不通过super语句显示调用父类的带参构造方法,则程序无法编译通过。
因此,子类的构造方法中一定会调用父类的构造方法。
super和this关键字对比:
this代表本类对象的引用,super代表父类对象的引用
this.成员变量(成员方法) : 在类中调用本类成员
super.父类成员 : 在子类中调用父类成员
this(参数) : 在本类构造方法中调用本类其他构造方法
super(参数) : 在子类构造方法中调用父类构造方法
<3>成员方法的关系
方法重写:子类中出现了和父类中一模一样的方法声明,即返回值类型,方法名,参数列表都相同,这种情况称为方法重写,或方法覆盖
使用特点示例:
思考如下代码:
class Father{ //在父类中定义方法method() public void method() { System.out.println("父类中的方法"); } } class Child extends Father { //在子类中定义与父类相同声明的方法method() public void method() { System.out.println("子类中的方法"); } } public class ExtendDemo2{ public static void main(String[] args) { Child c = new Child(); //通过子类对象调用方法method() c.method(); } }
运行结果:
从运行结果发现,最终调用的是子类的方法,而不是父类的方法,这种现象就是方法的重写。
方法重写的应用:当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
方法重写的注意事项:
父类中私有的方法不能重写,因为父类的私有方法子类无法继承;
子类重写父类方法时访问权限不能更低,最好与父类一致;
父类静态方法,子类也必须通过静态方法进行重写。
8.final 关键字
final 是最终的意思,可以用来修饰类,成员变量,成员方法
被final修饰的类无法被继承
被final修饰的变量只能被赋值一次,之后无法再改变,即称为常量
被final修饰的方法无法被子类重写
final修饰基本类型变量时,是值不能被改变;修饰引用类型变量时,是地址不能被改变
二、多态
1.概述多态是指某一个事物在不同时刻表现出来的不同状态。
例如:水可以有气态,液态,固态,但它们的本质都是水
动物可以指猫,狗,老虎…..虽然它们名字不同,但都属于动物的一种
那么Java中如何体现多态呢?
下面用一个需求的两种解决方案来说明:
需求:分别定义一个猫类,狗类,老虎类,鸟类,来模拟现实中的动物,这些动物都有一个行为叫进食,创建它们的对象并让所有动物进食。
分析:很显然每一种动物都有各自喜欢吃的食物,所以虽然它们都有吃的行为,但具体的行为方式却不尽相同。
代码实现:非多态的方式
//定义一个猫类 class Cat{ public void eat() { System.out.println("猫吃鱼"); } } //定义一个狗类 class Dog{ public void eat() { System.out.println("狗吃骨头"); } } //定义一个老虎类 class Tiger { public void eat() { System.out.println("老虎吃肉"); } } //定义一个鸟类 class Bird { public void eat() { System.out.println("鸟吃虫子"); } } class DuotaiDemo{ public static void main(String[] args) { /* * 创建4种动物的对象 */ Cat cat = new Cat(); Dog dog = new Dog(); Tiger tiger = new Tiger(); Bird bird = new Bird(); /* * 调用各自的进食方法 */ cat.eat(); dog.eat(); tiger.eat(); bird.eat(); } }
输出结果:
分析:通过结果,我们很开心的发现这段程序让所有动物吃到了想吃的东西,那么接下来需求改变了,又来了其他动物,马,大象,猪……不计其数的动物都要吃各自的喜欢的食物,我们当然可以用上面的代码去解决,再添加新的类,然后创建新动物类的对象,调用进食方法,但是可以想象代码的长度,而且每加入一个新的动物都要对代码做大量修改。
下面用一种更好的方式来改进上面的代码:
由于每种动物都要进食,而且它们都属于动物类型的一种,那么可以定义一个动物类,让所有的动物继承自这个类。
代码实现:多态方式
//定义一个动物类 class Animal { //并没有给出到底吃什么 public void eat() { } } //定义一个猫类,继承动物类 class Cat extends Animal{ public void eat() { System.out.println("猫吃鱼"); } } //定义一个狗类,继承动物类 class Dog extends Animal{ public void eat() { System.out.println("狗吃骨头"); } } //定义一个老虎类,继承动物类 class Tiger extends Animal{ public void eat() { System.out.println("老虎吃肉"); } } //定义一个鸟类,继承动物类 class Bird extends Animal{ public void eat() { System.out.println("鸟吃虫子"); } } class DuotaiDemo{ public static void main(String[] args) { /* * 创建一个动物类型的数组 */ Animal[] animals = new Animal[4]; //将动物数组中的元素赋值为特定的动物对象 animals[0] = new Cat(); animals[1] = new Dog(); animals[2] = new Tiger(); animals[3] = new Bird(); /* * 让所有动物进食 */ for(int i=0; i<animals.length; i++) { animals[i].eat(); } } }
运行结果:
理解上面代码的关键就在于Animal类型的引用实际指向的对象是Animal的子类对象,而这正式多态的意义所在。可以看到通过多态改进后的代码可维护性和可扩展性都大大增强了,此时如果再加入其他动物类,只需要让新的动物继承Animal,重写eat()方法,然后将新动物的对象加入animals数组即可。
由上面的例子得出多态的前提条件:
有继承或者实现关系
有方法重写
父类引用指向子类对象
2.多态的好处和缺点
好处:提高了代码的可维护性和可扩展性
缺点:不能通过父类的引用使用子类特有的功能
向上转型:当父类引用指向子类对象时,编译器会将子类对象当作父类使用
向下转型:当需要在多态中使用子类特有方法时,需要将父类引用强制转型为子类引用,但这种操作的前提是必须保证父类引用实际指向的是子类对象,否则会发生类型转换异常。
3.多态中成员访问的特点
通过多态访问成员变量:通过父类的引用变量只能访问父类的成员变量,而不能访问子类的成员变量,否则编译不通过。
通过多态访问成员方法:通过父类引用只能调用父类中定义的方法,当子类重写了该方法时,则调用子类重写后的方法。无法调用子类特有的方法。
通过多态访问静态方法:因为静态的成员都是与类相关的,所有不存在多态的问题,通过类名调用即可。
构造方法:创建子类对象时需要调用子类构造方法,而子类构造方法中会默认调用父类构造方法。
三、抽象类
1.抽象方法的概念:没有方法体的方法就是抽象方法,即没有方法的具体实现。格式:修饰符 abstract 方法名 (参数列表) ;
2.抽象类的概念:被abstract关键字修饰的类称为抽象类。
凡是包含抽象方法的类必须定义为抽象类,但抽象类中不一定包含抽象方法。
3.抽象类的特点:
<1>抽象类和抽象方法必须用abstract关键字修饰
<2>抽象类中不一定包含抽象方法,但有抽象方法的类必须是抽象类
<3>抽象类不能实例化,但可以有构造方法
<4>抽象类的子类要么声明为抽象类,要么重写父类所有抽象方法。
除了以上四个特点抽象类和普通类没有什么区别,也可以定义成员变量和成员方法。
一个简单的抽象类案例:
/* 需求:定义一个抽象类Person 成员变量:姓名,年龄 成员方法:吃饭,睡觉 定义两个类继承Person,一个Student,一个Teacher Student有特有方法:study Teacher有特有方法:teach */ abstract class Person { //成员变量 private String name; private int age; //空参构造 public Person() {} //带参构造 public Person(String name, int age) { this.name = name; this.age = age; } //成员变量的getXxx()/setXxx()方法 public String getName(){ return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } //抽象方法 public abstract void eat(); public abstract void sleep(); } class Teacher extends Person { public Teacher(){} public Teacher(String name, int age) { super(name, age); } //重写父类的抽象方法eat() public void eat() { System.out.println(getName() + " is eating."); } //重写父类的抽象方法sleep() public void sleep() { System.out.println(getName() + " is sleeping."); } //子类特有方法 public void teach() { System.out.println(getName() + " is teaching."); } } class Student extends Person { public Student(){} public Student(String name, int age) { super(name, age); } //重写父类的抽象方法eat() public void eat() { System.out.println(getName() + " is eating."); } //重写父类的抽象方法sleep() public void sleep() { System.out.println(getName() + " is sleeping."); } //子类特有方法 public void study() { System.out.println(getName() + " is study."); } } //测试类 class PersonDemo { public static void main(String[] args) { Teacher t = new Teacher("比向东老师", 18); Student s = new Student("小明", 6); t.eat(); t.sleep(); t.teach(); s.eat(); s.sleep(); s.study(); } }
4.抽象类的几个小问题
<1>抽象类有构造方法,不能实例化,那么构造方法有什么用?
答:用于子类访问父类数据的初始化
<2>一个类如果没有抽象方法,却定义为了抽象类,有什么用?
答:为了禁止创建该类的对象
<3>abstract不能和哪些关键字共存?
答:final,private,static
四、接口
1.概述在现实生活中有些人抽烟,有些人不抽烟,那么当我们要在Java中描述一个人类的时候(Person类),如何去表示哪些人抽烟哪写人不抽烟呢?
由于抽烟仅仅是一个行为,而这个行为不是所有人都具备,那么就可以将抽烟这个行为定义为一个接口,然后由抽烟的人去实现这个接口。
从上面的例子可以知道,当需要为某一个类添加扩展功能时,就需要为这个扩展功能定义一个接口,然后由需要扩展的类来实现具体的功能。
2.接口的特点
成员特点
<1>成员变量:只能是常量
接口中成员变量都有默认修饰符:public static final
<2>构造方法:没有构造方法
<3>成员方法:只能是抽象方法
接口中成员方法默认修饰符:public abstract
使用特点:
<1>定义一个接口时,用关键字interface修饰:
interface 接口名{ }
<2>一个类实现接口时,用implements 关键字
class 类名 implements 接口名 {}
<3>接口不能实例化
<4>接口的实现类特点:可以是抽象类或具体类,如果是具体类,必须重写(或者说实现更恰当)接口中所有方法
接口的简单示例
interface Smoke { public abstract void smoke(); } interface Study { public abstract void study(); } class Worker implements Smoke { public void smoke(){ System.out.println("工人抽烟"); } } class Student implements Study{ public void study() { System.out.println("学生学习"); } } public class InterfaceDemo { public static void main(String[] args) { Worker w = new Worker(); w.smoke(); Student s = new Student(); s.study(); } }
运行结果:
3.类与类,类与接口,接口与接口之间的关系
<1>类与类之间:继承关系,只能单继承,可以多层继承
<2>类与接口之间:实现关系,可以单实现,也可以多实现
还可以在继承一个类的同时实现多个接口
<3>接口与接口之间:继承关系,可以单继承,也可以多继承
五、内部类
1.概念:把类定义在另一个类的内部,该类就被称为内部类举例:
class A { class B{ } }
如上面代码所示,类B就是A的内部类,其实内部类是类中成员的一种,类中的成员有成员变量,成员方法,当然也可以有成员类,就像人的体内有心脏,肝脏,那么心脏就属于人这个类的内部类。
2.内部类的访问规则
<1>在内部类中可以直接访问外部类的成员,包括私有成员,因为内部类本身也属于类的成员
<2>外部类要想访问内部类的成员,必须创建内部类的对象
3.内部类的分类
<1>成员内部类:位于类中方法外,和成员变量位置相同
创建内部类对象的方式:
非静态成员内部类:
外部类名.内部类名 对象名 = new 外部类名.new 内部类名();
静态成员内部类:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
<2>局部内部类:位于方法体内
局部内部类访问局部变量必须加final修饰。
4.匿名内部类
局部内部类的简化形式
前提:存在一个类或者接口
格式:
new 类名或接口名(){ 重写方法 }
本质:匿名内部类的本质是继承该类或者实现该接口的子类的匿名对象。
代码示例:
interface Person { public abstract void study(); } class PersonDemo { public void method(Person p) { p.study(); } } class PersonTest { public static void main(String[] args) { PersonDemo pd = new PersonDemo(); pd.method(new Person() { public void study() { System.out.println("好好学习,天天向上"); } }); } }
六、包
包其实就是文件夹1.作用:用于区分同名的类,并按照功能或模块对类进行分类管理
**2.定义方式:**package 包名;
注:多级包用 . 分隔
注意事项:
<1>package语句必须在文件中的第一条有效语句
<2>在一个java文件中,只能有一个package
<3>如果没有package,默认就是无包名
3.带包的编译和运行:
javac -d . HelloWorld.java
4.导包
<1>Java就提供了关键字import用于包的导入,如果在类中需要用到其他包中的类,则用import将需要的类导入。
<2>格式
import 包名…类名;
import 包名…*;(不建议)
<3>package,import,class的顺序
package > import > class
相关文章推荐
- 作为码农 ,我们为什么要写作
- 黑马程序员Java笔记——初识Java
- 黑马程序员--Java基础--集合(一)
- 黑马程序员——ios学习笔记 OC Foundation—NSArray&NSDictionary
- 黑马程序员——ios学习笔记 OC Foundation—NSString
- 黑马程序员——ios学习笔记 OC 分类
- 黑马程序员——ios学习笔记 OC 协议&Block
- 送上一篇应付Java 面试的秘籍(Java 初级)
- 黑马程序员——包、权限修饰符、内部类
- 黑马程序员
- 为什么程序员的业余项目大多都死了?
- 红杏: 为学者、程序员、外贸工作者打造的上网加速器,专治各种网站打不开和打开慢,而且,更棒的是,红杏
- 黑马程序员——面向对象(下)— 重写、多态、抽象、接口
- 黑马程序员
- 如果牛顿是程序员,那么?
- 黑马程序员——面向对象(上)— 类、对象、封装、继承、构造方法
- 黑马程序员--java基础学习笔记8
- 黑马程序员——Java基础---异常,IO流,File类
- 面试题
- 黑马程序员——Java基础---面向对象