Java学习之路之week4day1
作者:互联网
向Java程序员的目标前进!
day15
面向对象—续5
学习内容
多态的案例—“孔子装爹”
为了加深多态的成员特点的理解,我们将以伪代码的形式介绍。
故事背景:孔子是一名专门讲解论语的老师。而孔子的父亲是刚刚下岗的Java程序员,由于当时JavaSE非常火,很多人都需要学习,孔子的父亲就开始当老师讲JavaSE。有一天,孔子的爹被请去讲课了,孔子一个人在家。这时,还有一些人慕名前来找孔子爹学习JavaSE,孔子开始装他爹的模样,带上装备,黏上胡子,样子很像!1
伪代码:
class 孔子爹{
int age = 40;
//teach功能
public void teach(){
System.out.println("讲解JavaSE");
}
}
class 孔子 extends 孔子爹{
int age = 20;
@Override
public void teach(){
System.out.println("讲解论语");
}
//特有功能
public void playGame(){
System.out.println("会写书");
}
}
对以上伪代码在测试类中进行测试:
public class Test {
public static void main(String[] args) {
孔子爹 k1 = new 孔子() ;//向上转型
System.out.println(k1.age);//编译看左,运行看左。所以这里输出结果是40
k1.teach();//编译看左,运行看右。输出结果是“讲解论语”,一讲课就暴露了。
k1.playGame();//这句会报错,因为这是子类的特有功能,访问不了,为了节省内存空间,推荐使用向下转型。将父类的引用强制转换为子类的引用
孔子 k2 = (孔子) k1;
k2.playGame();//这样写不会报错
}
}
多态的练习
练习:台灯的多态版
/**
* 需求:设计一个台灯类(Lamp)其中台灯有灯泡类(Buble)这个属性,
* 还有开灯(on)这个方法。设计一个灯泡类(Buble),
* 灯泡类有发亮的方法,其中有红灯泡类(RedBuble)和绿灯泡类(GreenBuble)
* 他们都继承灯泡类(Buble)一个发亮的方法,请设计出一段代码可以使台灯开启灯泡发亮
* 红灯泡发红光,绿灯泡发绿光!(多态)
*
*/
//灯泡类
class Buble {
//定义一个发亮的方法
public void shine(){
System.out.println("灯泡可以发亮了...") ;
}
}
//绿灯泡
class GreenBuble extends Buble {
@Override
public void shine() {
System.out.println("灯泡可以发绿光了...");
}
}
//台灯类
class Lamp {
//台灯有灯泡类(Buble)这个属性
private Buble buble ;//灯泡类
//灯(on)这个方法,开灯---灯泡要"发亮"
public void on(Buble buble){
buble.shine() ;//调用灯泡的发亮的方法
}
}
//红灯泡
class RedBuble extends Buble {
@Override
public void shine() {
System.out.println("灯泡可以发红光了...");
}
}
//测试类
public class Test1 {
public static void main(String[] args) {
//创建台灯类对象
Lamp lamp = new Lamp() ;
Buble buble = new RedBuble() ;
//调用开灯方法
lamp.on(buble) ;
System.out.println("--------------------------------");
Buble buble2 = new GreenBuble() ;
lamp.on(buble2);
System.out.println("--------------------------------");
//匿名对象:子类匿名对象
lamp.on(new RedBuble());
lamp.on(new GreenBuble());
}
}
练习:动物类的多态版
/**
* 需求:定义一个动物类,里面有一个方法voice()
* 定义一个类Cat,实现voice方法
* 然后增加一种新的动物类型:Pig(猪),实现voice()方法。
* 定义一个Dog类,实现voice方法
* 定义一个Store(宠物店)类的getInstance方法:
* 如果传入的参数是字符串dog,则返回一个Dog对象;
* 如果传入pig,则返回一个Pig对象;否则,返回一个Cat对象。
*
*/
//动物类
class Animal {
public void voice(){
System.out.println("动物都需要发出声音...");
}
}
//猫类
class Cat extends Animal {
@Override
public void voice() {
System.out.println("猫发出喵喵叫的声音...");
}
}
//狗类
class Dog extends Animal {
@Override
public void voice() {
System.out.println("狗发出汪汪叫的声音");
}
}
//猪类
class Pig extends Animal {
@Override
public void voice() {
System.out.println("猪发出哼哼叫的声音...");
}
}
//宠物店类
class Store {
private Store(){} //构造方法私有化,外界不能new,然后功能加入static
public static Animal getInsance(String type){
if(type.equals("dog")){
return new Dog() ;
}else if(type.equals("pig")){
return new Pig() ;
}else{
return new Cat() ;
}
}
}
//测试类
public class Test2 {
public static void main(String[] args) {
Animal a = Store.getInsance("pig");//new Pig() ;
a.voice();
a = Store.getInsance("cat") ; //new Cat() ;
a.voice();
a = Store.getInsance("dog") ; //new Dog() ;
a.voice();
}
}
抽象类
回顾之前的猫狗案例,它们都有父类动物类。动物类是一个很抽象的事物,在定义时,我们只知道动物都会吃和睡。但是我们说一个动物,说的都是最具体的动物。只有说到具体的动物,我们才知道它吃的东西和其它的生活习性。
我们在开发中,应该将这种具有概括性的事物抽象化。并且,它们的吃或者睡的行为也不应该再给出具体的体现,应该只是声明出来(定义为没有方法提的方法),让具体的事物(子类)进行重写!
抽象方法
和之前学习定义成员方法是一样的,只是没有方法体和{}
而已
格式:
权限修饰符 abstract 返回值类型 方法名(参数列表);
抽象类的格式
abstract class 类名{}
抽象类的本质
一旦定义了抽象类和抽象方法,再定义子类时必须重写所有父类的抽象方法!
本质:强制子类必须做的事情!
抽象类的特点
抽象类不能实例化!(不能创建对象)
抽象类的注意事项
- 如果一个类中有抽象方法,那么这个类必须定义为抽象类!
- 抽象类中不一定有抽象方法,也可以是非抽象方法!
- 抽象类不能实例化,需要用具体的子类实例化
- 如果抽象类的子类是抽象类,就不能实例化。但是一定要存在一个最具体的子类,否则定义抽象类没有意义!
- 子类重写父类方法的时候,定义子类的方式时访问权限必须足够大,或者跟父类保持一致(不能更低),否则会报错!
抽象类的实例化
通过抽象类多态,父类引用指向子类对象。前提是要有最具体的子类,比如:
Fu fu = new Zi() ;
这里的Fu就是一个抽象类类型。
抽象类的成员特点
- 成员变量:既可以是变量,也可以是常量。被final修饰的变量不能再赋值。
- 成员方法:既可以是抽象方法,也可以是非抽象方法
- 构造方法:存在继承关系所以分层初始化。先让父类初始化,然后子类再进行初始化!
面试题:定义抽象类的意义
问题:如果一个类中没有抽象方法,那么把这个类定义为抽象类的意义何在?
解答:这是属于设计层面的问题。定义抽象类,目的是不想直接让这个类创建对象,因为最终还是有具体的子类存在的。
比如jdk提供的日历类Calendar,它是一个抽象类,不能创建对象。但是Calendar提供了静态方法,返回值就是它自己。所以调用其它成员方法的本质就是在创建具体的子类对象。
练习:台灯的抽象类版
/**
* 需求:设计一个台灯类(Lamp)其中台灯有灯泡类(Buble)这个属性,
* 还有开灯(on)这个方法。设计一个灯泡类(Buble),
* 灯泡类有发亮的方法,其中有红灯泡类(RedBuble)和绿灯泡类(GreenBuble)
* 他们都继承灯泡类(Buble)一个发亮的方法,请设计出一段代码可以使台灯开启灯泡发亮
* 红灯泡发红光,绿灯泡发绿光!
*
*/
//灯泡类
abstract class Buble {
public abstract void shine() ;
}
//绿灯泡类
class GreenBuble extends Buble {
@Override
public void shine() {
System.out.println("灯泡可以发绿光了");
}
}
//台灯类
class Lamp {
private Buble buble ;
public void on(Buble buble){
buble.shine() ;
}
}
//红灯泡类
class RedBuble extends Buble {
@Override
public void shine() {
System.out.println("灯泡可以发红光了");
}
}
//测试类
public class Test {
public static void main(String[] args) {
//创建台灯类对象
Lamp lamp = new Lamp() ;
Buble buble = new RedBuble() ;//抽象类多态
//调用开灯方法
lamp.on(buble) ;
System.out.println("--------------------------------");
Buble buble2 = new GreenBuble() ;
lamp.on(buble2);
System.out.println("--------------------------------");
//匿名对象:子类匿名对象
lamp.on(new RedBuble());
lamp.on(new GreenBuble());
}
}
面试题:abstract不能和哪些关键字冲突
abstract的应用场景:修饰类或成员方法
修饰成员方法时注意:
-
abstract不能和private关键字使用。因为被private修饰的成员只能在当前类访问,而加入abstract后需要让子类实现这个方法。这已经超出了当前访问的范围,所以这是冲突的。
-
abstract不能和final关键字使用。因为被final修饰的成员方法是不能被重写的,而abstract修饰的抽象方法需要被子类强制重写的,所以这是冲突的。
-
abstract不能和static关键字使用。因为被static修饰的方法是跟类相关的,修饰后会随着类的加载而加载。而抽象方法是需要被子类重写的,最终需要使用对象来实现抽象类多态。abstract和static一块使用抽象方法在父类中又没有方法体,加载进入内存就没有意义了。所以这是冲突的。
//定义一个抽象方法
//private abstract void show() ;//非法格式 需要子类实现show方法,而private修饰的方法只能在当前访问
//public final abstract void show() ;//非法格式
//public static abstract void show() ; //非法格式
//标准的格式
public abstract 返回值类型 方法名(参数列表);//参数列表可能空参/有参(基本类型/引用类型)
练习:动物类的抽象类版
/**
* 需求:定义一个动物类,里面有一个方法voice(),
* 定义一个类Cat,实现voice方法
* 然后增加一种新的动物类型:Pig(猪),实现voice()方法。
* 定义一个Dog类,实现voice方法
* 定义一个Store(宠物店)类的getInstance方法:
* 如果传入的参数是字符串dog,则返回一个Dog对象;
* 如果传入pig,则返回一个Pig对象;否则,返回一个Cat对象。
*/
//动物类
abstract class Animal { //这个动物类----抽象类
//优化
//动物都需要发声,将voice方法应该仅仅声明即可,需要让子类实现发声的方法
public abstract void voice() ;
}
//猫类
class Cat extends Animal {
@Override
public void voice() {
System.out.println("猫发出喵喵叫的声音...");
}
}
//狗类
class Dog extends Animal {
@Override
public void voice() {
System.out.println("狗发出汪汪叫的声音");
}
}
//猪类
class Pig extends Animal {
@Override
public void voice() {
System.out.println("猪发出哼哼叫的声音...");
}
}
//宠物店
class Store {
private Store(){} //构造方法私有化,外界不能new,然后功能加入static
public static Animal getInsance(String type){
if(type.equals("dog")){
return new Dog() ;
}else if(type.equals("pig")){
return new Pig() ;
}else{
return new Cat() ;
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
Animal a = Store.getInsance("pig");//new Pig() ;
a.voice();
a = Store.getInsance("cat") ; //new Cat() ;
a.voice();
a = Store.getInsance("dog") ; //new Dog() ;
a.voice();
}
}
练习:猫狗案例的抽象类版
/**
* 需求:用抽象类实现“猫狗”案例
* 定义一个动物类(Animal),属性有姓名、年龄、颜色,行为有吃和睡
* 再定义猫类(Cat)和狗类(Dog),都继承自动物类
* 猫和狗吃的不一样,重写它们的方法
* 分别定义方法 猫有特有功能:玩毛线;狗有特有功能:看门
*/
//动物类
abstract class Animal { //抽象类
//姓名,年龄,颜色
private String name ;
private int age ;
private String color ;
//无参构造方法
public Animal() {
}
//有参构造方法
public Animal(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
//公共的访问方法
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 String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
//动物的吃和睡觉,应该给出声明即可---抽象方法
public abstract String eat() ;
public abstract void sleep() ;
}
//猫类
class Cat extends Animal {
//构造方法
public Cat() {
}
public Cat(String name, int age, String color) {
super(name, age, color);
}
@Override
public String eat() {
return "猫吃鱼...";
}
@Override
public void sleep() {
System.out.println("猫舔着爪子睡觉...");
}
//特有功能
public void playGame() {
System.out.println("猫会玩毛线...");
}
}
//狗类
class Dog extends Animal {
//构造方法
public Dog() {
}
public Dog(String name, int age, String color) {
super(name, age, color);
}
@Override
public String eat() {
return "狗吃骨头";
}
@Override
public void sleep() {
System.out.println("狗趴着睡觉...");
}
//特有功能
public void catchRabit(){
System.out.println(this.getName()+"会抓兔子");
}
}
public class Test {
public static void main(String[] args) {
//测试类
//方式1:无参构造+setXXX()/getXXX()
//多态测试
Animal a = new Dog() ;//抽象类多态
String result = a.eat();
a.setName("黑子") ;
a.setAge(3) ;
a.setColor("棕色") ;
System.out.println(a.getName()+"---"+a.getAge()+"---"+a.getColor());
System.out.println(result);
a.sleep(); ;
//特有功能
//向下转型
Dog d = (Dog) a;
d.catchRabit() ;
System.out.println("--------------------------------------") ;
//方式2:有参构造方法+getXXX()
//猫类测试
Animal a3 = new Cat("橘猫", 3, "花色");
System.out.println(a3.getName()+"---"+a3.getAge()+"---"
+a3.getColor());
String str2 = a3.eat();
System.out.println(str2);
a3.sleep();
Cat cc = (Cat) a3;
cc.playGame();
}
}
接口
继续回到猫狗的例子,猫和狗属于动物的一种,但是它能够具有跳高、钻火圈、做数学题这些它们本身不具备的功能。这是经过后天学习和驯养员培养出来,才具备了这些额外的功能。通过这个例子我们可以总结出来,一个事物如果实现了额外的功能,那么它也就具备了这个功能。
回到Java中,接口的定义其实与上面的总结十分相似。接口是一种规范,如果类能够实现接口中额外的功能,那么就说明当前这个类具备这个功能。从宏观角度来说,接口能让事物实现额外的功能。接口是比抽象类还抽象的一种类型。
接口的格式
public interface 接口名{}
接口名和类名的命名规范是一致的,都遵循"大驼峰命名法"
类实现接口的格式
class 类名 implements 接口名{}
接口的特点
-
接口中的方法只能是抽象方法,不能有方法体
-
接口不能实例化(不能创建对象)
接口的实例化是通过接口多态实现的
接口的例子
/**
* 在AnimalArain接口中定义两个功能:
* jump():跳高
* compute():计算
* 最后编写测试类测试
*/
interface Jump{
// public void jump(){ //接口的方法只能是抽象方法
//
// }
public abstract void jump() ;
}
interface ComplateCal{//做计算的接口
public abstract void cal() ;
}
class Dog{
public void lookDoor(){
System.out.println("狗可以看门");
}
}
//跳高高它是狗,然后局部额外的功能,跳高
class JumpDog extends Dog implements Jump,ComplateCal{ //继承一个类的同时,可以实现多个接口
@Override
public void jump() {
System.out.println("狗可以跳高了...");
}
@Override
public void cal() {
System.out.println("狗可以做计算了...");
}
}
//测试类
public class InterfaceDemo {
public static void main(String[] args) {
//创建接口对象
// Jump jump = new Jump () ;//接口不能实例化,
//如何实例化呢:接口多态---提供接口的子实现类
Jump jump = new JumpDog() ;//接口类型---->子实现类对象 (接口类型)
jump.jump() ;
//向下转型
JumpDog jumpDog = (JumpDog) jump;
jumpDog.lookDoor() ;
jumpDog.cal();
ComplateCal cc = new JumpDog() ;//接口多态
cc.cal() ;
}
}
接口成员特点
- 成员变量只能是常量,默认修饰符是
public static final
,可以省略 - 没有构造方法,只是提供一些抽象方法,让子实现类实现这些方法
- 成员方法只能是抽象方法,默认修饰符是
public abstract
,可以省略
类与类,类与接口以及接口与接口的关系
-
类与类:继承关系,只能单继承,不支持多继承,但是可以多层继承
-
类与接口:实现关系,可以单(接口)实现,也可以多(接口)实现。还可以在继承一个类的同时实现多个接口
class 子实现类名 extends 父类名 implements 接口1,接口2,...{}
-
接口与接口:继承关系,不仅可以单继承,还可以多继承,也可以多层继承
面试题:接口和抽象类有什么区别
-
成员的区别
-
接口的成员:
- 成员变量:只能是常量,存在默认的修饰符public static final
实际开发中,如果要自定义一个常量,就先定义一个接口,里面再写常量即可!
- 成员方法:一般说的是抽象方法,可以是default默认方法或者静态方法,必须有方法体
- 构造方法:没有
-
抽象类的成员
- 成员变量:既可以是变量,也可以是常量。被final修饰的变量不能再赋值。
- 成员方法:既可以是抽象方法,也可以是非抽象方法,如果是抽象方法,方法中的abstract不能省略。
- 构造方法:存在有参构造/无参构造都可以。因为是继承关系,肯定是分层初始化的。
-
-
关系的区别
-
类与类的关系:继承关系
类可能是抽象类或具体类。继承关系只支持单继承,不支持多继承,可以多层继承
-
类与接口的关系:实现关系
一个类继承另一个类的同时可以实现多个接口
-
接口与接口的关系:继承关系
可以单继承,可以多继承,也可以多层继承
-
-
设计理念的区别
-
抽象类最终肯定有具体的子类,是继承关系,体现的是一种“is a”的关系,可以用抽象类多态描述
-
接口描述的是事物本身不具备的功能,是通过后台学习培养出来的额外的拓展功能。
接口的核心思想体现的是“like a”的关系
-
练习:运动员和教练
分析:
实现:
//篮球教练类
class BasketballCoach extends Coach{
public BasketballCoach() {
}
public BasketballCoach(String name, int age, String gender) {
super(name, age, gender);
}
@Override
public void teach() {
System.out.println("篮球教练教运动员怎么运球");
}
}
//篮球运动员类
class BasketBallPlayer extends Player {
public BasketBallPlayer() {
}
public BasketBallPlayer(String name, int age, String gender) {
super(name, age, gender);
}
@Override
public void study() {
System.out.println("篮球运动员学习如何运球和投篮");
}
}
//教练类
abstract class Coach extends Person{
public Coach() {
}
public Coach(String name, int age, String gender) {
super(name, age, gender);
}
@Override
public void eat() {
System.out.println("教练吃的是快餐");
}
public abstract void teach();
}
//人类
public abstract class Person { //抽象类
private String name ; //姓名
private int age ; //年龄
private String gender ; //性别
//无参
public Person() {
}
//有参构造
public Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
//公共访问方法
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 String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
//吃的东西不一样,所以让子类实现,仅仅给出声明即可
public abstract void eat() ;
}
//乒乓球教练类
class PingPangCoach extends Coach implements SpeakEnglish{
public PingPangCoach() {
}
public PingPangCoach(String name, int age, String gender) {
super(name, age, gender);
}
@Override
public void teach() {
System.out.println("乒乓球教练教运动员怎么打球");
}
@Override
public void speak() {
System.out.println("乒乓球教练会说英语!");
}
}
//乒乓球运动员类
class PingPangPlayer extends Player implements SpeakEnglish {
public PingPangPlayer() {
}
public PingPangPlayer(String name, int age, String gender) {
super(name, age, gender);
}
@Override
public void study() {
System.out.println("乒乓球运动员学习如何发球和接球");
}
@Override
public void speak() {
System.out.println("乒乓球运动员会说英语!");
}
}
//运动员类
abstract class Player extends Person { //抽象类
//构造方法
public Player() {
}
public Player(String name, int age, String gender) {
super(name, age, gender);
}
@Override
public void eat() {
System.out.println("运动员吃的是营养餐");
}
//学习的的内容不一样,只有见到具体的运动员才能知道学的是什么
//学习的功能---给出声明即可
public abstract void study() ;
}
//说英语的接口
interface SpeakEnglish {
//说英语
public abstract void speak() ;
}
//测试类
public class Test {
public static void main(String[] args) {
SpeakEnglish se = new PingPangPlayer() ;
se.speak(); //会说英语
PingPangPlayer pingPangPlayer = (PingPangPlayer)se;
pingPangPlayer.setName("马龙") ;
pingPangPlayer.setAge(30) ;
pingPangPlayer.setGender("男");
System.out.println(pingPangPlayer.getName()+"---"+pingPangPlayer.getAge()+"---"+pingPangPlayer.getGender()) ;
pingPangPlayer.eat() ;
pingPangPlayer.study();
System.out.println("--------------------------------------") ;
Coach pc = new PingPangCoach("刘国梁",40,"男");
PingPangCoach pingPangCoach = (PingPangCoach) pc;
pingPangCoach.speak();
System.out.println(pc.getName()+"---"+pc.getAge()+"---"+pc.getGender());
pc.eat();
pc.teach();
}
}
博客难免会产生一些错误。如果写的有什么问题,欢迎大家批评指正。
故事是完全虚构的。 ↩︎
标签:Java,String,void,之路,public,println,week4day1,抽象类,out 来源: https://blog.csdn.net/weixin_43527493/article/details/122691881