栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【zyc的从零开始】面向对象编程笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【zyc的从零开始】面向对象编程笔记

面向对象语言的基本特征 1、封装

1)语法:使用private关键字,修饰字段和方法。只能在当前类中使用。
2)意义 :安全、对类的谁用皇城呢个本降低了。

2、继承

1)什么是继承:就是将共性进行抽取,使用关键字extends来实现继承。
设计一个类表示动物

// Animal.java 
public class Animal { 
 public String name; 
 
 public Animal(String name) { 
 this.name = name; 
 } 
 
 public void eat(String food) { 
 System.out.println(this.name + "正在吃" + food); 
 } 
} 
// Cat.java 
class Cat { 
 public String name; 
 
 public Cat(String name) { 
 this.name = name; 
 } 
 
 public void eat(String food) { 
 System.out.println(this.name + "正在吃" + food); 
 } 
} 
// Bird.java 
class Bird { 
 public String name; 
 
 public Bird(String name) { 
 this.name = name; 
 } 
 
 public void eat(String food) { 
 System.out.println(this.name + "正在吃" + food); 
 } 
 
 public void fly() { 
 System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿"); 
 } 
} 
  • 这三个类都具备一个相同的 eat 方法, 而且行为是完全一样的.
  • 这三个类都具备一个相同的 name 属性, 而且意义是完全一样的.
  • 从逻辑上讲, Cat 和 Bird 都是一种 Animal (is - a 语义).

此时我们就可以让 Cat 和 Bird 分别继承 Animal 类, 来达到代码重用的效果.

2)优点:代码可以进行重用。
3)关键字super:
super表示对父类对象的应用
1.super.data //访问父类的成员变量
2.super.func() //访问父类的成员方法
3.super() //访问父类的成员构造方法
对于this和super的区别请看这里: Java中super和this的详解

4)关键字protected

详情请看这里: 关键字protected详解

tips:
在Java中,只能通过extends来继承一个类,不能继承多个类。【单继承】

想详细了解Java继承请戳这里: Java继承详解

3、组合

和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果。
例如表示一个学校

public class Student { 
 ... 
} 
public class Teacher { 
 ... 
} 
public class School { 
 public Student[] students; 
 public Teacher[] teachers; 
} 

组合并没有涉及到特殊的语法(诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段
组合表示 has - a 语义
在刚才的例子中, 我们可以理解成一个学校中 “包含” 若干学生和教师.
继承表示 is - a 语义
在上面的 “动物和猫” 的例子中, 我们可以理解成一只猫也 “是” 一种动物

4、多态

多态是同一个行为具有多个不同表现形式或形态的能力。

在了解多态前需要先了解几个概念:重写、向上转型、向下转型、动态绑定

1)重写(Override) ······ //搬运

重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!

class Animal{
   public void move(){
      System.out.println("动物可以移动");
   }
}
 
class Dog extends Animal{
   public void move(){                               //外壳不变
      System.out.println("狗可以跑和走");  //核心重写
   }
}
 
public class TestDog{
   public static void main(String args[]){
      Animal a = new Animal(); // Animal 对象
      Animal b = new Dog(); // Dog 对象
 
      a.move();// 执行 Animal 类的方法
 
      b.move();//执行 Dog 类的方法
   }
}

以上实例编译运行结果如下:

动物可以移动
狗可以跑和走

在上面的例子中可以看到,尽管 b 属于 Animal 类型,但是它运行的是 Dog 类的 move方法。
这是由于在编译阶段,只是检查参数的引用类型。
然而在运行时,Java 虚拟机(JVM)指定对象的类型并且运行该对象的方法。
因此在上面的例子中,之所以能编译成功,是因为 Animal 类中存在 move 方法,然而运行时,运行的是特定对象的方法。

思考以下例子:

class Animal{
   public void move(){
      System.out.println("动物可以移动");
   }
}
 
class Dog extends Animal{
   public void move(){
      System.out.println("狗可以跑和走");
   }
   public void bark(){
      System.out.println("狗可以吠叫");
   }
}
 
public class TestDog{
   public static void main(String args[]){
      Animal a = new Animal(); // Animal 对象
      Animal b = new Dog(); // Dog 对象
 
      a.move();// 执行 Animal 类的方法
      b.move();//执行 Dog 类的方法
      b.bark();
   }
}

以上实例编译运行结果如下:

TestDog.java:30: cannot find symbol
symbol : method bark()
location: class Animal
b.bark();
^

该程序将抛出一个编译错误,因为b的引用类型Animal没有bark方法。

方法的重写规则
  • 参数列表与被重写方法的参数列表必须完全相同。

  • 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。

  • 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。

  • 父类的成员方法只能被它的子类重写。

  • 声明为 final 的方法不能被重写。

  • 声明为 static 的方法不能被重写,但是能够被再次声明。

  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。

  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。

  • 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。

  • 构造方法不能被重写。

  • 如果不能继承一个类,则不能重写该类的方法。

Super 关键字的使用

当需要在子类中调用父类的被重写方法时,要使用 super 关键字。

class Animal{
   public void move(){
      System.out.println("动物可以移动");
   }
}
 
class Dog extends Animal{
   public void move(){
      super.move(); // 应用super类的方法
      System.out.println("狗可以跑和走");
   }
}
 
public class TestDog{
   public static void main(String args[]){
 
      Animal b = new Dog(); // Dog 对象
      b.move(); //执行 Dog类的方法
 
   }
}

以上实例编译运行结果如下:

动物可以移动
狗可以跑和走

重载(Overload)

重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

重载规则:
  • 被重载的方法必须改变参数列表(参数个数或类型不一样);
  • 被重载的方法可以改变返回类型;
  • 被重载的方法可以改变访问修饰符;
  • 被重载的方法可以声明新的或更广的检查异常;
  • 方法能够在同一个类中或者在一个子类中被重载。
  • 无法以返回值类型作为重载函数的区分标准。
实例
public class Overloading {
    public int test(){
        System.out.println("test1");
        return 1;
    }
 
    public void test(int a){
        System.out.println("test2");
    }   
 
    //以下两个参数类型顺序不同
    public String test(int a,String s){
        System.out.println("test3");
        return "returntest3";
    }   
 
    public String test(String s,int a){
        System.out.println("test4");
        return "returntest4";
    }   
 
    public static void main(String[] args){
        Overloading o = new Overloading();
        System.out.println(o.test());
        o.test(1);
        System.out.println(o.test(1,"test3"));
        System.out.println(o.test("test4",1));
    }
}
重写与重载之间的区别

总结

方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。

(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。

2)向上/下转型 向上转型:父类引用 引用子类对象 向下转型:与向上转型相对应的就是向下转型了。向下转型是把父类对象转为子类对象(请注意!这里是有坑的)

来看代码:

class Animal {
    protected String name;
    private int age;

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

    public void eat() {
        System.out.println(name+" eat()");
    }
}

class Dog extends Animal{

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

   public void eat() {
        System.out.println("Dog::eat()");
   }
}

class Bird extends Animal{

    public String selfFiled;

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

    public void eat() {
        System.out.println("Bird::eat()");
    }

    public void fly() {
        System.out.println(name+"fly()");
    }

}

public class TestDemo {
    
    public static void main6(String[] args) {
        Animal animal = new Dog("xiaogou",14);
        animal.eat();//父类引用 引用子类对象
    }


    
    public static void main5(String[] args) {
        Animal animal2 = new Bird("小鸟",18,"self");
        animal2.eat();
        //向下转型
        Bird bird = (Bird)animal2;
        bird.fly();

        System.out.println("=========向下转型不安全========");
        Animal animal = new Dog("xiaogou",14);
        //向下转型 但是不安全的
        if(animal instanceof Bird) {//instanceof表判断
            Bird bird2 = (Bird)animal;
            bird2.fly();
        }
    }
}
向下转型注意事项
  • 向下转型的前提是父类对象指向的是子类对象(也就是说,在向下转型之前,它得先向上转型)
  • 向下转型只能转型为本类对象(狗不会飞)
3)动态绑定

用上面的代码来说

当执行main函数时,输出为:

Dog::eat()

两个eat方法,程序为什么会选择Dog类呢,这里我们就引出了运行时绑定。
首先需要知道:运行时绑定绑定的是这个和父类相同的子类的方法。

发生运行时绑定的条件

1…发生向上转型(父类引用 引用子类对象)
2.通过父类引用,调用父类和子类的重名覆盖(重写)方法

那为什么叫运行时绑定?因为在编译的时候,程序绑定的还是Animal的eat方法,只不过在运行的时候最终经绑定到Dog的eat方法

运行时绑定是多态的基础


OK那说了这么多,我们现在再来说多态

class Animal {
    protected String name;
    private int age;

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

    public void eat() {
        System.out.println(name+" eat()");
    }
}

class Dog extends Animal{

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

   public void eat() {
        System.out.println("Dog::eat()");
   }
}

class Bird extends Animal{

    public String selfFiled;

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

    public void eat() {
        System.out.println("Bird::eat()");
    }

    public void fly() {
        System.out.println(name+"fly()");
    }

}



public class TestDemo {
    public static void func(Animal animal) {
        animal.eat();
    }

    public static void main7(String[] args) {
        Dog dog = new Dog("xiaogou",14);
        func(dog);

        Bird bird = new Bird("小鸟",18,"self");
        func(bird);
    }
}

执行程序输出

Dog::eat()
Bird::eat()

在这里,通过Animal只是调用一个eat() 方法,因为传过去的参数不一样,所以他表现得形式也不一样,这种表现形式 就叫做多态,因此多态只是一个思想。

多态的特点

1.是通过一个Animal的引用 调用eat方法
2.传递的参数不一样,代表引用的对象不一样
3.通过这个引用,调用同一个方法,表现出了不同的形式,这种思想、这种情况,叫做多态

多态的条件

1.向上转型
2.方法重写

这里又引出了向上转移的三种形式
传参 、直接赋值 和 返回值


理解多态

有了面的向上转型, 动态绑定, 方法重写之后, 我们就可以使用 多态(polypeptide) 的形式来设计程序了.
我们可以写一些只关注父类的代码, 就能够同时兼容各种子类的情况

代码示例: 打印多种形状

class Shape { 
 public void draw() { 
 // 啥都不用干
 } 
} 
class Cycle extends Shape { 
 @Override 
 public void draw() { 
 System.out.println("○"); 
 } 
} 
class Rect extends Shape { 
 @Override 
 public void draw() { 
 System.out.println("□"); 
 } 
} 
class Flower extends Shape { 
 @Override 
 public void draw() { 
 System.out.println("♣"); 
 } 
} 
/我是分割线// 
// Test.java 
public class Test { 
 public static void main(String[] args) { 
 Shape shape1 = new Flower(); 
 Shape shape2 = new Cycle(); 
 Shape shape3 = new Rect(); 
 drawMap(shape1); 
 drawMap(shape2); 
 drawMap(shape3); 
 } 
 // 打印单个图形
 public static void drawShape(Shape shape) { 
 shape.draw(); 
 } 
}

在这个代码中, 分割线上方的代码是类的实现者编写的, 分割线下方的代码是类的调用者编写的。
当类的调用者在编写 drawMap 这个方法的时候, 参数类型为Shape (父类), 此时在该方法内部并不知道, 也不关注当前的shape引用指向的是哪个类型(哪个子类)的实例. 此时 shape 这个引用调用 draw 方法可能会有多种不同的表现(和 shape 对应的实例相关), 这种行为就称为多态

多态顾名思义, 就是 “一个引用, 能表现出多种不同形态”

如果面试官问什么是多态可以这样回答:父类引用,引用类对象,通过父类引用,调用父类和子类同名的覆盖方法。此时如果父类引用,引用的子类对象不一样,调用这个重写的方法,表现的行为也是不一样的。

使用多态的好处

使用多态的好处是什么?

  1. 类调用者对类的使用成本进一步降低.
    封装是让类的调用者不需要知道类的实现细节.
    多态能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可.
    因此, 多态可以理解成是封装的更进一步, 让类调用者对类的使用成本进一步降低.
    这也贴合了 <<代码大全>> 中关于 “管理代码复杂程度” 的初衷.
  2. 能够降低代码的 “圈复杂度”, 避免使用大量的 if - else
  3. 可扩展能力更强

本文某些部分为搬运和借鉴,所用链接放在这里
Java 重写(Override)与重载(Overload)
Java 中的多态
Java 多态

本文为个人理解,仅供参考!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/390438.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号