面向对象的三大特性

面向对象具体三大特性分别是 封装、继承、多态,几乎所有的设计模式都以此为基础,这些特性是面向对象语言所具备的, 面向过程不支持此语法

封装

隐藏对象内部细节,控制对象的修改访问权限

封装的必要性:

​ 在对象的外部为对象的属性赋值,可能存在非法输入;

​ 无法对属性的赋值进行加以控制

实现方式

​ 使用private修饰符,在属性和方法的前面声明。表示此属性和方法为私有,只能在本类中访问

​ 提供公共的访问方法

public class IGirl {
    //属性
    String name;
    private int age;
    int height;
    
    //set 赋值
    public void setAge(int age  ) {
        if (age < 0) {
            System.out.println("非法输入");
        } else {
            this.age = age;
        }
    }
    //get 取值
    public int getAge(){
        return this.age;
    }
}

public class TestGirl {

    public static void main(String[] args) {
        IGirl girl = new IGirl();
        
        //调用set方法赋值
        girl.setAge(-18);
        
        int age =   girl.getAge();
        System.out.println(age);
    }
}

get / set 方法是外界访问对象私有属性的唯一通道,方法内部可对数据进行检测和过滤

继承

继承关系

​ 两个类A 、B,存在A is a B , 可以把共性的属性和方法定义在B中,通过继承,A类可以重用B类中的共性方法和属性(公用代码)。继承的思想是重用和拓展,一个类通过继承获得属性和方法,同时也可以拓展出新的属性和方法

继承的语法

​ class B extends A {

​ }

​ A 为父类,B为子类

//父类
public class Animals {

    //属性
    String name;

    int age;

    String color;

    //构造器
    public Animals() {
    }

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

    //实例方法
    public void eat() {
        System.out.println(name+"吃东西");
    }
    public void sleep() {
        System.out.println(name+"睡觉");
    }

}

子类一

class Cat extends Animals {

    //构造方法
    public Cat() {
    }

    public Cat(String name, int age, String color) {
        super(name, age, color);
    }

    //实例方法
    public void run() {
        System.out.println(name+"跑......");
    }

}

子类二

class Bird extends Animals {

    //属性
    String kind;

    //构造函数
    public Bird() {
    }

    public Bird(String name, int age, String color, String kind) {
        super(name, age, color);
        this.kind = kind;
    }

    //实例方法
    public void fly() {
        System.out.println(name+"飞......");
    }

}

测试类

public class Test {

    public static void main(String[] args) {

        Cat cat = new Cat();
        //自己特有方法
        cat.run();
        //继承父类方法
        cat.eat();
        cat.sleep();


        Bird bird = new Bird();
        
        //继承父类方法
        bird.sleep();
        bird.eat();
        //自己独有方法
        bird.fly();


    }

}
单继承性

Java中,类是单继承的,一个类最多只有一个父类,但是可以支持多级继承,例如B继承A,C继承B

class A {
    int a;
    public void ma(){
        System.out.println("ma()");
    }
}
class B extends A {
    int b;
    public void mb(){
        System.out.println("mb()");
    }

}
class C extends B {
    int c;
    public void mc(){
        System.out.println("mc()");
    }

}

public class TestExtends {

    public static void main(String[] args) {
        C obj = new C();
    }

}

​ C类可以访问a b c三个属性 , ma mb mc 三个方法

不可继承的内容

一个类继承另一个类之后,并不是继承了全部的内容,有些内容不能够被继承

​ 如:

构造器不可继承;构造器是构建当前类对象的方法,要求与类同名,子类无法继承父类的构造器来充当自己的构造器,必须定义自己的构造器

私有属性不可继承;父类的私有属性,由于封装,只能在父类本类中使用,子类无法直接继承使用

​ 父类中的default属性和方法,子类在非同包,不可继承

由于default的访问性质,同包是极限,跨包无法访问,故子类不可直接继承使用

访问修饰符

Java中提供了3个关键词,四种访问级别控制

  1. private

    ​ 表示私有,被它修饰的属性、方法为私有,只能在本类中访问。

  2. default

    ​ 属性、方法前,不写!!!。表示默认,只能被同包访问

  3. protected

    ​ 表示受保护,兼容default性质,同时扩展了异包子类的可访问

  4. public

    ​ 表示公开,在任意位置都可以访问

**总结: 访问级别从严到宽 **

** private --> default --> protected-- public **

继承中对象创建对象的过程

在存在创建的关系中,创建子类对象,得先调用父类的构造方法,简单理解就是一个完整的子类对象中,包含一个父类对象的实例(父类的亚对象,不是对象),这个实例一般通过关键词super访问。

创建子类对象不会创建父类对象,但会调用父类的构造方法

1.创建对象指的是在堆区开辟空间(new)

2.编译器在运行子类构造器之前,必须先执行父类构造器;且调用父类构造器的语句必须在子类构造器的第一行。
3.构造方法的作用是为堆区中的对象的属性初始化,不是创建对象。

为什么创建子类对象要调用父类的构造方法?

子类继承父类后,获取到父类的属性和方法,这些属性和方法在使用前必须先初始化,所以须先调用父类的构造器进行初始化

在哪里调用父类的构造器?

答:在子类构造器的第一行会隐式的调用 super();,即调用父类的构造器 如果父类中没有定义空参的构造器,则必须在子类的构造器的第一行显示的调用super(参数); ,以调用父类中构造器

如果子类中构造器的第一行写了this();,则隐式的super()会消失,因为super()和this()都只能在构造器的第一行定义

Java 重写(Override)

重载(Overload)

重写

外壳不变,内容重写

当子类的从父类继承到的方法,并不适用,不能达到子类的要求,此时子类可对父类方法的内容进行重写,这个行为称为方法的重写或者覆盖

要求:

  1. 子类重写方法时,要求 方法名、参数、方法名 一致
  2. 子类重写方法后,访问权限不能更加严格
  3. 子类重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常

可以在子类重写后的方法上加 (Override) 注解,检验重写的正确性

demo:

//父类
class Animal{
   public void move(){
      System.out.println("动物可以移动");
   }
}
 //子类
class Dog extends Animal{
    //重写方法
    @Override
   public void move(){
      System.out.println("狗可以跑和走");
   }
}

重载

存在一种行为有多种实现的时候,可以通过方法的重载机制,实现多个方法使用相同的名字,通过参数来区分方法的效果,可提高代码的灵活性

要求

  1. 同一个类中,方法名相同
  2. 参数列表不同
  3. 与返回值类型、参数名字无关
class Demo{
    // 计算  n 以内的整数和  sum(int n)
    public double sum(int n){
        int sum = 0;
        for(int i=0;i<=n;i++){
            sum += i;
        }
        return  sum;
    }
    // 计算  m  n 内的整数和 sum(int m, int n)
    public double sum(int m , int n){
        int sum = 0;
        for(int i=m;i<=n;i++){
            sum += i;
        }
        return  sum;
    }
}

class TestDemo{
    public static void main(String[] args){
       Demo obj = new Demo();
       // 调用  public double sum(int n)
       double s1 =  obj.sum(10);  
        // 调用  public double sum(int m , int n)
       double s2 =  obj.sum(10,20);
       
    }
}
super关键字

super是Java中的一个关键字,可以在子类中直接使用,表示的是子类对象空间中对父类的引用,

  1. 可以用来区分分类和子类同名的实例变量和方法

    使用super.属性名 或者 super.方法名

  2. 可以调用父类的构造器,用来初始化从父类继承来的那部分属性(尤其是在父类属性被私有化的时候)

    ​ super()或者 super(参数)

    在子类构造方法中,使用super()或者super(参数)时,务必保证调用位于当前构造器 的第一行

多态

面向对象三大特性的最后一个,一种用法

父类类型的引用可以指向任何子类对象

Animal  animal  =  new Dog();

Animal  animal  =  new Cat();

Dog和Cat都是Animals的子类,所以多态是建立在继承上的一种属性,没有继承就没有多态

条件

​ 继承

​ 重写

​ 基类引用指向派生类对象

使用

​ 一旦使用多态语法,那么通过父类类型的引用就只能调用在父类中的方法和属性,子类特有的方法无法调用

屏蔽了子类的独有特点(属性和方法),只能调用父类中的共有行为

public static void main(String[] args){
      Person person = new Student("小红",20);
      //可以调用Person父类中定义的方法
      person.eat();
      person.sleep();
      person.sayHello();

      //不可调用 子类特有的属性和方法 ,编译看左边,运行看右边
      //person.lookAll(); 
      //person.study();
}

Student是Person的子类

应用

public class Animals {
    static String name = "animal";
    int num = 1;
    public static void sleep() {
        System.out.println("animal sleep");
    }
    public void run() {
        System.out.println("animal run");
    }
    
}
public class Cat extends Animals{

    static String name = "cat";
    int num = 2;
    public static void sleep() {
        System.out.println("cat sleep");
    }
    public void run() {
        System.out.println("cat run");
    }
}
public class Test {

    public static void main(String[] args) {

        Cat cat = new Cat();
        System.out.println(cat.num);

        Animals animals = new Cat();
        System.out.println(animals.num);

        animals.sleep();
        cat.sleep();
        
    }
}

2
1
animal sleep
cat sleep

调用成员变量以及静态方法时,“编译看左边,运行看左边”,

调用非静态方法时,编译看左边,运行看右边

类型转换

  • 自动类型提升

引用数据类型转换分为向上转型,向下转型,向上转型也叫自动类型提升,它就是多态的核心。简单的说就是可以直接把一个子类对象用父类类型的变量来保存。

Animal animal = new Dog();(向上转型)

这里 new Dog() 就是一个对象, 而这个对象被保存到一个 Animal的引用中。

  • 强制类型转换

把一个父类类型的引用 还原成它真实指向对象的类型的过程

Animal animal = new Dog();

Dog dog = (Dog)animal;

需要注意的是 务必保证 强转的合法性,因为父类类型引用只能转换成它真实指向对象的类型,如果不是会报错比如:

Animal animal = new Dog();

Cat cat = (Cat)animal;

这里将会引发 java.lang.ClassCastException 。因为 animal 这里真实指向的对象为 Dog 而不是 Cat,不能强转。

类型判断

因为强制类型转换存在安全性问题,所以强制转换前,需要提前判断使用 instanceof 关键字判断。

语法为 引用变量 instanceof 类型 如果前者是后者类型 返回true 否则 false

Animal animal = new Dog();

if ( animal instanceof Dog ){

​ Dog dog = (Dog)animal;

}

if ( animal instanceof Cat ){

​ Cat cat = (Cat)animal;

}

posted @ 2021-05-08 18:47  MrShangL  阅读(791)  评论(0)    收藏  举报