面向对象概念和三大特性

本文介绍了面向对象编程的基本概念,包括面向对象的编程思想、类与对象的关系、类的组成、构造方法、封装、继承和多态。通过实例详细阐述了如何在Java中创建和使用对象,以及this和super关键字的用途。此外,还讨论了Java中的final关键字和访问修饰符,以及方法覆盖和多态的实现。

面向对象概念

面向对象

编程思想

概念:建立解决问题的思路,即编程思想

常见的编程思想

  1. 面向过程: 自顶向下,逐步求精,注重解决问题的过程,将解决问题分为一个一个的步骤,只要按照步骤执行就一定能解决问题

  2. 面向对象: 分析解决问题需要的哪些功能,而哪些对象具有这种功能;注重对象之间的配合调用

问题: 回家

面向过程: 拿着地图,分析回家需要经过哪些路口,每条路上走多远,遇到路口往哪儿拐,一步一步就走到家了

面向对象:只需要调用出租车(对象)的载客功能,由出租车送我们回家

注意:面向对象与面向过程并不是完全对立!面向对象是在面向过程的基础上实现的

面向对象

认识对象

什么是对象?

Java眼中的对象: 一切客观存在的事物都是对象!世界就是由对象组成的!—万物皆对象

例如:水杯、桌子、电脑、空气、错误等等都是对象

程序中的对象: 内存中的一块存储空间,用来存储对现实对象的关注的部分,从而代表现实中的对象

  • 对象的组成:

    • 特征(属性): 有什么

    • 行为(方法): 干什么

    注意:有时候,一个对象的属性可能也是一个对象;对象的调用者可能也是一个对象
    例如:硬盘是电脑的属性,硬盘本身也是对象;程序员使用电脑开发,程序员也是对象

  • 对象间的关系

    • 一个对象继承了另一个对象 is a 例如:狗是一个动物

    • 小对象组成大对象 has a 例如:电脑由硬盘、cpu组成

    • 一个对象使用另一个对象 use a 例如:程序员使用电脑

认识类

类与对象的关系
  1. 类是对象的模板,对象是类的实例

  2. 类是人们对一系列相同相似事物(现实中的对象)的认识,抽取出共性定义在类中

  3. 对象就是根据类创建出来的实例,类似于根据模板生产物品

  4. 类由属性与方法组成

例如: Computer类 ------》电脑对象

类的组成
  1. 属性: 有什么 成员变量\实例变量

    位置: 类的内部,方法的外部

    语法:

    1. 声明属性: 数据类型 属性名;    例如:String name;
    2. 声明并初始化: 数据类型 属性名 = 值;  例如: int age = 18;

      错误语法: 先声明,再赋值
      例如: double score;
      score = 100;//错误语法!!

      class Student{
      String name;
      int age = 18;
      double score;
      //score = 100;错误
      }

    属性具有默认值:即属性的数据类型的默认值!

    属性的作用范围:至少为整个类的内部

    注意:当属性与局部变量命名冲突,局部变量优先!

    补充:局部变量与属性的区别

    局部变量属性
    位置定义在函数(方法)内部类的内部,方法的外部
    默认值没有默认值具有默认值
    作用范围从定义的那一行开始,到定义所在代码块的结束至少整个类的内部
    命名冲突作用范围重合内,不允许命名重复当属性与局部变量命名冲突,局部变量优先
  2. 方法: 干什么 成员方法 对象的具体的行为:吃喝拉撒睡、工作、学习、玩耍、等等

    位置: 类的内部,其他方法的外部

    语法:

    修饰符 返回值类型 方法名(形参列表){
    //方法实现
    }
    注意:函数是特殊的方法!函数声明中需要写上static修饰符;而方法一般不写static

    class Student{
    String name = “小郭”;
    int age = 18;
    double score;
    //score = 100;错误

    public void study(){
    //局部变量优先!
    //String name = “小红”;
    System.out.println(name+“沉迷学习无法自拔”);
    }
    public void eat(){
    System.out.println(“吃饭饭”);
    }
    public void sleep(){
    System.out.println(“睡觉觉”);
    }
    }

  3. 构造方法: Java规定的具有特殊功能的方法

    作用: 可以用来配合new关键字创建对象,并且可以在创建对象的过程中选择是否为对象属性赋值!

    分类:

    • 无参构造方法 没有形参列表,在创建对象时无法为对象属性赋值;可以在对象创建成功后,通过其他方式赋值

      语法: public 类名(){}

    • 有参构造方法 具有形参列表,在创建对象时可以将形参的值赋值给属性

      语法: public 类名(形参列表){ //赋值语句 }

    注意:

  4. 构造方法的方法名必须和类名相同!

  5. 构造方法没有返回值类型,注意连void都没有

  6. 构造方法无法手动调用,只有在创建对象时被调用一次

  7. 若代码中没有定义任何构造方法,默认提供一个公开无参构造方法

    经验:

  8. 一般的,若类中具有属性,需要提供有参、无参构造方法

  9. 可以定义多个形参个数不同的有参构造方法

    //学生类
    class Student{
    //属性
    String name = “小红”;
    int age = 18;
    double score;
    //score = 100;错误
    //普通的行为方法
    public void study(){
    //局部变量优先!
    //String name = “小郭”;
    System.out.println(name+“沉迷学习无法自拔”);
    }
    public void eat(){
    System.out.println(“吃饭饭”);
    }
    public void sleep(){
    System.out.println(“睡觉觉”);
    }

    //无参构造方法
    public Student(){}
    //具有两个参数的有参构造方法
    public Student(String n,double s){
    name = n;
    score = s;
    }
    //具有三个参数的有参构造方法
    public Student(String n,int a,double s){
    name = n;
    age = a;
    score = s;
    }
    }

思考:普通方法与构造方法的区别?

答: 普通方法是人为定义的一些具体功能、行为方法,例如吃喝拉撒睡、工作学习等等

而构造方法它的作用是Java实先规定好的,是用来创建对象的;它们的关系就好像主函数与普通函数的关系

java中常见类的使用
  1. Math    常见数学函数

  2. Arrays 操作数组的工具类,toString,sort方法

  3. Random 随机数,种子的概念

  4. String,Integer 查看内部状态,与相应的方法

  5. ArrayList 可以代替数组

创建对象

创建对象的方式:

创建对象的过程称为实例化,对象的属性和方法称为实例(instance)成员

  1. 通过无参构造方法创建对象

    语法: 类名 引用 = new 类名(); 例如:Student s1 = new Student();

  2. 通过有参构造方法创建对象

    语法: 类名 引用 = new 类名(实参列表); 例如:Student s2 = new Student(“小郭”,20,80);

使用对象:

  • 使用对象的属性:

    • 通过 引用.属性名; 获取该对象的该属性

    • 通过 引用.属性名 = 值; 完成对该对象的该属性的再次赋值

  • 使用对象的方法:

    • 通过 引用.方法名(实参列表) 调用该对象的该方法

class TestObject{
public static void main(String[] args){
//通过无参构造方法创建对象
Student s1 = new Student();
System.out.println(s1.name+" “+s1.age+” “+s1.score);
s1.study();
s1.score = 100;
System.out.println(s1.name+” “+s1.age+” "+s1.score);

//通过有参构造方法创建对象
Student s2 = new Student(“小郭”,20,80);
System.out.println(s2.name+" “+s2.age+” "+s2.score);
s2.study();

}
}
//学生类
class Student{
//属性
String name = “小红”;
int age = 18;
double score;
//score = 100;错误
//普通的行为方法
public void study(){
//局部变量优先!
//String name = “小郭”;
System.out.println(name+“沉迷学习无法自拔”);
}
public void eat(){
System.out.println(“吃饭饭”);
}
public void sleep(){
System.out.println(“睡觉觉”);
}

//无参构造方法
public Student(){}
//具有两个参数的有参构造方法
public Student(String n,double s){
name = n;
score = s;
}
//具有三个参数的有参构造方法
public Student(String n,int a,double s){
name = n;
age = a;
score = s;
}
}

重点:

  1. 程序中的对象与现实中的对象的的关系

  2. 类与对象的关系

  3. 类的组成,每个组成部分的作用及其语法、使用、注意事项

  4. 普通方法与构造方法的区别

  5. 无参构造方法与有参构造方法的区别,以及根据二者创建出来的对象的区别

  6. 创建对象的语法,对象的属性与方法的调用

  7. 明白int a = 10; 与 JavaKid jk = new JavaKid()的异同

  8. 基本数据类型变量与引用数据类型变量的异同

  9. 方法重载的语法及要求

this关键字

public Worker(String n,int a,double s){
name = n;
age = a;
salary = s;
}
存在问题: 不满足标识符命名规范,无法望名知意!
修改后:
public Worker(String name,int age,double salary){
name = name;
age = age;
salary = salary;
}
存在问题: 形参也属于局部变量,此时形参名与属性名重复,局部变量优先,即形参优先!那么此时 name = name中的两个name都是形参name,所以无法完成对属性赋值
需要使用this将局部变量形参与属性区别开!!!

this的用法:

  1. 用在构造方法或普通方法中,代表当前实例对象

    • this.属性名; 代表当前对象的属性

    • this.方法名(实参); 代表调用当前对象的方法

    public Worker(String name,int age,double salary){
    this.name = name;
    this.age = age;
    this.salary = salary;
    }
    public void method(){
    int age = 20;
    System.out.println(“局部变量:age”+age);
    System.out.println(“属性:age”+this.age);
    }

  2. 必须要用在构造方法中,表示调用本类的其他构造方法

    • this(实参) 代表调用本类中具有相应形参的构造方法

    • 注意: 该语句,必须写在构造方法中的第一行

    public Student(String name,int age){
    this.name = name;
    this.age = age;
    }
    public Student(String name,int age,double score){
    this(name,age);
    this.score = score;
    }

class TestThis{
public static void main(String[] args){
Student s = new Student(“zhangs”,18,100);
s.study();
s.method();
}
}
//模板
class Student{
String name ;
int age;
double score;

public Student(){}
public Student(String name,int age){
    this.name = name;
    this.age = age;
}
public Student(String name,int age,double score){
    this(name,age);
    this.score = score;
}

public void study(){
    System.out.println(name+"沉迷学习无法自拔");
}
public void method(){
    int age = 20;
    System.out.println("局部变量:age"+age);
    System.out.println("属性:age"+this.age);
}

}

final关键字

final关键字修饰变量,则该变量值不能改变,对基本数据类型与引用变量均适用

final int i=1;final Circle c1 = new Circle();

什么时候使用final?

当希望获得一个常量,如Math.PI

final常与staic联合使用,作为类常量

或者希望某个引用类型变量只能指向一个指定对象

创建对象时必须初始化final属性

name属性在初次赋值后就不能改变,即不能指向其他String对象

final修饰类

  1. 该类不可被继承
  2. 一般用于创建不可变类(immutable)

final修饰方法

  1. 该方法是最终方法,不能被子类覆盖
  2. 保护该方法的代码不会被修改
  3. Object中的很多方法都使用final修饰

三大特性

封装

问题:目前,可以随意的调用、修改一个对象的属性,这种行为是不合理的!!!

概念:是一个边界,是一个屏障,可以保护对象内部的数据不被外界随意访问

实现封装:借助访问(权限)修饰符–private 私有的

访问修饰符作用范围
public 公共的、公开的外界可以随意访问
private 私有的只能在本类中使用
  • 属性封装

    • 语法:private 数据类型 属性名;

    • 注意:一般的,当将属性私有化后,需要提供公开的get\set方法,供外界使用,从而去获取及修改属性

      即使用封装隐藏内部状态,所有的通信都通过对象的方法来实现

    注意:

    1. 每个私有属性都必须有自己独立的get(获取属性)\set(修改属性)方法
    2. get\set命名规范:get\set+属性名(首字母大写)
    3. 若属性的数据类型为boolean类型,get方法可以命名为:is+属性名(首字母大写);set方法不变
    4. 可以根据需求,选择是否提供get\set方法    
    

    class TestDog{

    public static void main(String[] args){
        Dog d = new Dog();
        d.setName("旺财");
        d.setAge(2);
    
        System.out.println(d.getName()+"  "+d.getAge());
    }
    

    }
    class Dog{

    private String name;
    private int age;
    private boolean sex;
    
    public Dog(){}
    public Dog(String name,int age){
        this.name = name;
        this.age = age;
    }
    public Dog(String name,int age,boolean sex){
        this(name,age);
        this.sex = sex;
    }
    
    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 boolean isSex(){
        return sex;
    }
    public void setSex(boolean sex){
        this.sex = sex;
    }
    

    }

思考:为什么将属性封装后,还提供get\set方法?

答: 封装只是起到保护的作用,而不是完全隔绝!

  • 方法封装 将一些只用于本类,并不希望被外界调用的方法私有化,禁止外界调用!

重点:

  1. this.与this(实参)的区别及使用注意事项

  2. 对象的创建过程,明白属性的三个赋值时机

  3. 明白引用中存储的内容,及对象的存储地方

  4. 基本数据类型变量与引用数据类型变量的区别:存储、传递

  5. private与public的区别,作用范围

  6. get\set方法的作用及语法及注意事项

继承

生活中的继承:一般的,指儿子继承爸爸的一些物品、财产

Java中的继承:指一个类(子类)可以继承(使用)另一个类(父类)的某些属性和方法

概念: 父类是子类共性的抽取!在父类中只能定义所有子类的共性部分;父类—子类是中“一般–特殊”的关系

语法:class 子类类名 extends 父类类名{}

继承的特点:当继承关系建立后,子类可以直接使用父类中可以被子类继承的方法与属性;子类中也可以定义自己独有的属性与方法;子类继承父类的所有非私有成员(不包含构造器)

继承的好处:减少代码冗余,提高代码的可复用性

注意:

  1. 一般的,只有满足 is a的关系,才能建立继承关系

  2. 构造方法不能被继承

  3. Java是单继承—一个子类只能有一个直接父类!但是可以多级继承,可以被继承的属性或方法也是叠加的

补充: 访问修饰符

访问修饰符本类同包其他包子类不同包不子类继承性
public公开的可以被继承
protected受保护的可以被继承
default默认的同包子类可继承
private私有的不能被继承

从宽到严: public -> protected -> default -> private

注意:

  1. 以上四个访问修饰符都可以修饰 属性、方法、构造方法

  2. 只有个public与default可以修饰类

  3. 以上四个均不可以修饰局部变量

package d10;
public class ClassA{
//公开的
public int a = 10;
//受保护的
protected int b = 20;
//默认的
int c = 30;
//私有的
private int d = 40;

}

package d10;
//同包子类
public class ClassB extends ClassA{
public ClassB(int a,int b,int c,int d){
this.a = a;
this.b = b;
this.c = c;
this.d = d;//无法继承
}

}

package p10;
import d10.ClassA;
//不同包子类
public class ClassC extends ClassA{
public ClassC(int a,int b,int c,int d){
this.a = a;
this.b = b;
this.c = c;//无法继承
this.d = d;//无法继承
}

}

package p10;
import d10.ClassA;
//不同包不子类
public class ClassD{
public static void main(String[] args){
ClassA ca = new ClassA();
System.out.println(ca.a);
System.out.println(ca.b);//无法访问
System.out.println(ca.c);//无法访问
System.out.println(ca.d);//无法访问
}
}

class TestAnimal{
public static void main(String[] args){
Dog d = new Dog(“旺财”,2,“红色”);
d.eat();
d.sleep();
d.lookDoor();
System.out.println(d.name+" “+d.age+” "+d.color);
}
}
//父类 只能定义所有子类的共性
class Animal{
String name;
int age;

public void eat(){
    System.out.println("吃饭饭");
}
public void sleep(){
    System.out.println("睡觉觉");
}

public Animal(){}
public Animal(String name,int age){
    this.name = name;
    this.age = age;
}

}
//子类
class Dog extends Animal{
String color;

public void lookDoor(){
    System.out.println("看门门");
}

public Dog(){}
public Dog(String name,int age,String color){
    this.name = name;
    this.age = age;
    this.color = color;
}

}
//子类
class Bird extends Animal{
String pinZhong;

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

public Bird(){}
public Bird(String name,int age,String pinZhong){
    this.name = name;
    this.age = age;
    this.pinZhong = pinZhong;
}

}
//子类
class Fish extends Animal{
double size;

public void swim(){
    System.out.println("划水水");
}

public Fish(){}
public Fish(String name,int age,double size){
    this.name = name;
    this.age = age;
    this.size = size;
}

}

方法覆盖

概念:OverRide,又称为方法重写,即在子类中可以将从父类继承到的方法进行重新实现

语法要求:

  1. 方法三要素(返回值类型、方法名、形参列表)必须完全相同

  2. 访问修饰符相同或更宽

注意:

  1. 子类中覆盖后的方法优先执行

  2. 若父类中有一个方法使用private修饰,此时子类中有一个方法三要素完全与之相同、但访问修饰符为public的方法,此时不构成方法覆盖,因为子类继承不到该方法

  3. @Override注解

    告诉编译器这里覆盖了父类的方法,如果在父类中未找到相应方法,会报错

class TestAnimal{
public static void main(String[] args){
Dog d = new Dog(“旺财”,2,“红色”);
d.eat();
d.sleep();
d.lookDoor();
System.out.println(d.name+" “+d.age+” "+d.color);
}
}
//父类 只能定义所有子类的共性
class Animal{
String name;
int age;

public void eat(){
    System.out.println("吃饭饭");
}
public void sleep(){
    System.out.println("睡觉觉");
}

public Animal(){}
public Animal(String name,int age){
    this.name = name;
    this.age = age;
}

}
//子类
class Dog extends Animal{
String color;

public void eat(){
    System.out.println("狗吃骨头");
}
public void lookDoor(){
    System.out.println("看门门");
}

public Dog(){}
public Dog(String name,int age,String color){
    this.name = name;
    this.age = age;
    this.color = color;
}

}

对象的创建过程

  • 无父类的创建过程

    1. 分配空间,为属性赋默认值

    2. 属性初始化,为属性赋初始值

    3. 执行构造方法,完成对象的创建,为属性赋构造参数值

  • 有父类的创建过程

    1. 分配空间(父类+子类),为父子类属性赋默认值

    2. 初始化父类属性,为父类属性赋初始值

    3. 执行父类构造方法,完成父类对象的创建,为父类属性赋构造参数值

    4. 初始化子类属性,为子类属性赋初始值

    5. 执行子类构造方法,完成子类对象的创建,为子类属性赋构造参数值

  • 总结:

    • 创建子类对象时,必须先创建父类对象

    • 子类对象由父类对象加上子类独有内容共同组成

class TestAnimal{
public static void main(String[] args){
Animal a = new Animal(“动物”,1);
System.out.println(a.getName());

    Dog d1 = new Dog("狗1",2,"黑色");
    // d1.setName("狗1");
    System.out.println(d1.getName());//狗1
    Dog d2 = new Dog();
    System.out.println(d2.getName());//null
}

}

super关键字

当子类中定义了与父类中同名的属性时(属性遮蔽)或发生了方法覆盖时,需要加以区分,才能专项访问

子类中的属性名与父类中的属性名相同,这父类中的属性被屏蔽(隐藏)

只是被隐藏但还存在,可使用super关键字访问

super的两种用法:

  1. 用在构造方法、普通方法中,代表父类对象

    • super.属性名; 获取父类属性

    • super.方法名(); 调用父类方法

    注意:该用法中的属性与方法必须是子类可以继承到的

    //子类
    class Dog extends Animal{
    String color;

    public void eat(){

       System.out.println("狗吃骨头");
    

    }
    public void lookDoor(){

       System.out.println("看门门");
    

    }

    public Dog(){}
    public Dog(String name,int age,String color){

       super.name = name;
       super.age = age;
       this.color = color;
    

    }

}

class TestMyClass{
public static void main(String[] args){
MyClassB mb = new MyClassB();
mb.method();
mb.print();
}
}
class MyClassA{
int value = 10;

  public void print(){
      System.out.println(value);
  }

}
class MyClassB extends MyClassA{
int value = 20;

  public void print(){
      //调用父类中被覆盖的print方法
      super.print();
      System.out.println(value);
  }
  public void method(){
      int value = 30;
      System.out.println("局部变量:"+value);//30
      System.out.println("子类属性:"+this.value);//20
      System.out.println("父类属性:"+super.value);//10

  }

}

  1. 必须用在子类构造方法中, 用来制定创建父类对象时,使用父类的哪个构造方法

    • super(); 调用父类无参构造方法

    • super(实参); 调用父类有参构造方法

    注意:

  2. super(…)必须写在子类构造方法中的第一行

  3. this(…)与super(…)不能同时出现

  4. 若子类构造方法第一行不是this(…)也不是super(…),则默认为super();

    class Dog extends Animal{
    String color;

    public void eat(){

       System.out.println("狗吃骨头");
    

    }
    public void lookDoor(){

       System.out.println("看门门");
    

    }

    public Dog(){}
    public Dog(String name,int age,String color){

       super(name,age);
       this.color = color;
    

    }

    }

面向对象代码规范写法:

//父类
class Animal{
//父类私有属性
private String name;
private int age;
//get、set方法
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 Animal(){}
public Animal(String name,int age){
this.name = name;
this.age = age;
}
//普通方法
public void eat(){
System.out.println(“吃饭饭”);
}
public void sleep(){
System.out.println(“睡觉觉”);
}
}
//子类继承父类
class Dog extends Animal{
//子类独有私有化属性
private String color;
//子类get\set方法
public String getColor(){
return color;
}
public void setColor(String color){
this.color = color;
}
//子类有参无参构造方法
public Dog(){}
public Dog(String name,int age,String color){
//调用父类有参构造方法创建父类对象,完成父类name与age属性的赋值
super(name,age);
this.color = color;
}
//子类覆盖父类中的eat方法
public void eat(){
System.out.println(“狗吃骨头”);
}
//子类独有方法
public void lookDoor(){
System.out.println(“看门门”);
}
}

多态

一个动物------> 狗

一个动物------> 猫

Dog d = new Dog(); 狗 是狗

Animal a = new Dog(); 狗 是动物

概念:父类的引用可以指向子类的对象

语法:父类类名 引用 = new 子类类名(实参);

特点:

  1. 父类的引用指向子类对象,并不会改变对象的实际类型

  2. 当使用多态时,只能调用引用类型中具有的方法

  3. 当使用多态时,会优先执行子类中覆盖后的方法

总结:方法调用看引用类型;方法的执行结果看对象的实际类型(子类类型)

多态性:相同形态,不同行为,使程序在运行中具备动态选择代码执行的能力

注意:方法具有多态,属性没有多态!调用属性的引用是什么类型,就会获取什么类型中的属性。

class TestAnimal{
public static void main(String[] args){
Animal a = new Animal(“a”);
a.eat();//吃饭饭
a.sleep();//睡觉觉
System.out.println(a.name);//a

    Dog d = new Dog("d");
    d.eat();//狗吃骨头
    d.sleep();//睡觉觉
    d.lookDoor();//看门门
    System.out.println(d.name);//null
    Animal d1 = d;
    System.out.println(d1.name);//d

    Animal dog = new Dog("dog");
    dog.eat();//狗吃骨头
    dog.sleep();//睡觉觉
    //错误,只能调用引用类型中具有的方法
    //dog.lookDoor();
    System.out.println(dog.name);//dog
}

}
class Animal{
String name;

public Animal(){}
public Animal(String name){
    this.name = name;
}

public void eat(){
    System.out.println("吃饭饭");
}
public void sleep(){
    System.out.println("睡觉觉");
}

}
class Dog extends Animal{
//注意:一般情况下,子类中不会定义与父类中同名的属性
//String name;

public Dog(){}
public Dog(String name){
    super(name);
}

public void eat(){
    System.out.println("狗吃骨头");
}
public void lookDoor(){
    System.out.println("看门门");
}

}

引用类型的类型转换

  1. 子类类型引用可以直接赋值给父类类型引用

    Dog d = new Dog();
    Animal a = d;

  2. 在多态场景下,父类类型引用赋值给子类类型引用,需要强转

    Animal a = new Dog();

    Dog d = (Dog)a;//强转的目的:编译器在编译代码时,无法准确得知引用a指向的对象的实际类型,而将Animal类型转为Dog类型,可能会出错,所以需要强转,告诉编译器,允许转换

    Animal a = new Cat();

    Dog d = (Dog)a;//由于强转,导致编译通过;但是解释器在工作的时候,会确认a指向的对象的实际类型,由于a指向的Cat类型对象,此时强转为Dog类型,所以会运行报错!!–java.lang.ClassCastException类型转换异常

    Animal a = new Animal();

    Dog d = (Dog)a;//编译通过,运行报错,原因同上!!

    Cat c = new Cat();
    Dog d3 = (Dog)c;//编译报错,不兼容类型!因为Cat不可能转换为Dog!

  3. 没有继承关系的两种类型,不允许相互转换

    Person p = new Person();
    Dog d = (Dog)p;//编译报错,不兼容类型!因为Person不可能转换为Dog!

instanceof关键字

语法: 引用 instanceof 类名 注意:这是一个boolean表达式,结果要么为true,要么为false

作用:判断引用指向的对象的实际类型与关键字后面的类名类型是否兼容,若兼容,则返回true,表示可以强转;若不兼容,则返回false,表示不能强转;

例如:
if(a instanceof Dog){
Dog d = (Dog)a;
}else{
Cat c = (Cat)a;

}

====================================================================================

class TestInstanceof{
public static void main(String[] args){
//多态的应用场景1:定义父类类型的数组,可以存储子类类型的对象
int[] arr = {1,2,3,4,5};
Animal[] as = new Animal[4];

    as[0] = new Dog();
    as[1] = new Cat();
    as[2] = new Dog();
    as[3] = new Cat();

    //需求:调用数组as中所有Dog对象的lookDoor方法,Cat对象的catchMouse方法

    for(int i=0;i<as.length;i++){
        if(as[i] instanceof Dog){
            Dog d = (Dog)as[i];
            d.lookDoor();
        }else{
            Cat c = (Cat)as[i];
            c.catchMouse();
        }
    }
}

}

多态的应用场景

  1. 应用在数组上,定义父类类型数组,可以存储子类类型对象;

    Animal[] as = {
    new Animal(),
    new Dog(),
    new Cat()
    }

  2. 应用在方法形参上,方法形参定义为父类类型,调用方法时,传入实参的类型可以为父类对象或其子类对象

  3. 应用在返回值类型上,方法返回值类型定义为父类类型,方法执行结束时,可以返回父类对象或其子类对象

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

       Dog d = new Dog();
       Cat c = new Cat();
       Wolf w = new Wolf();
       Tiger t = new Tiger();
    
       feed(d);
       feed(c);
       Animal a = sell(2000000);
       System.out.println(a);
    

    }
    //避免了方法重载,提高了代码的可重用性
    public static void feed(Animal a){

       a.eat();
    

    }
    public static Animal sell(double money){

       if(money>1000000){
           return new Tiger();
       }else if(money>10000){
           return new Wolf();
       }else{
           return new Dog();
       }
    

    }
    /*
    public static void feed(Dog d){

       d.eat();
    

    }
    public static void feed(Cat c){

       c.eat();
    

    } */
    }
    class Animal{
    public void eat(){

       System.out.println("吃饭饭");
    

    }
    }
    class Dog extends Animal{
    public void eat(){

       System.out.println("狗吃骨头");
    

    }
    }
    class Cat extends Animal{
    public void eat(){

       System.out.println("猫吃鱼");
    

    }
    }
    class Wolf extends Animal{
    public void eat(){

       System.out.println("狼吃喜洋洋");
    

    }
    }
    class Tiger extends Animal{
    public void eat(){

       System.out.println("老虎吃人");
    

    }
    }

多态的好处

  1. 减少代码冗余,提高代码的可重用性

  2. 解耦合(依赖倒转原则)

重点:

  1. 多态的概念及语法,三个特点

  2. 理解:方法具有多态,属性没有多态

  3. instanceof的真正用法,及其判断的内容

  4. 多态的三个应用场景

抽象类

使用abstract修饰的类为抽象类
  1. 抽象类不能实例化,只能作为其他类的父类

  2. 生来就应该被继承

    public abstract class Shape{}
    
使用abstract修饰的方法为抽象方法
  1. 只有声明,没有实现方法

    public abstract double get();
    

​ 2.意味着该方法需要到子类才能确定其实现

子类继承抽象类,一般要实现父类的抽象方法
  1. 体现子类的特有实现
子类如果只实现部分抽象方法,依然是抽象类
  1. 一个类中只要有抽象方法,必然是抽象类
  2. 此类必须声明为abstract
  3. 一个抽象类中可以有非抽象方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值