精华内容
下载资源
问答
  • Java面向对象编程三大特征 - 继承

    千次阅读 多人点赞 2020-03-31 18:24:08
    本文关键字:Java、面向对象三大特征、继承。继承是面向对象编程的三大特征之一,继承将面向对象的编程思想体现的更加淋漓尽致,允许类和类之间产生关联。

    写在前面:博主是一只经过实战开发历练后投身培训事业的“小山猪”,昵称取自动画片《狮子王》中的“彭彭”,总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域,如今终有小成,愿将昔日所获与大家交流一二,希望对学习路上的你有所助益。同时,博主也想通过此次尝试打造一个完善的技术图书馆,任何与文章技术点有关的异常、错误、注意事项均会在末尾列出,欢迎大家通过各种方式提供素材。

    • 对于文章中出现的任何错误请大家批评指出,一定及时修改。
    • 有任何想要讨论和学习的问题可联系我:zhuyc@vip.163.com。
    • 发布文章的风格因专栏而异,均自成体系,不足之处请大家指正。

    Java面向对象编程三大特征 - 继承

    本文关键字:Java、面向对象、三大特征、继承


    继承是面向对象编程的三大特征之一,继承将面向对象的编程思想体现的更加淋漓尽致,允许类和类之间产生关联,对于类和对象的基本知识可进传送门:Java中的基本操作单元 - 类和对象

    一、思想解读

    1. 什么是继承

    从类的概念出发,我们可以通过定义class去描述一类事物,具有相同的属性和行为。但在很多时候我们希望对类的定义能够进一步细化,这就相当于是一个大的分类下面有很多的子分类,如文具下面可以分为:写字笔、便签、文件管理等等。

    如果品类更加的复杂,可以先分为:学生文具、办公文具、财会用品,然后在每个品类下面再根据具体的作用去划分。这些被划分出来的子类别都一定具有父类别的某些共同特征或用途,并且有可能存在多级的分类关系,那么如果我们使用面向对象的语言去描述出这样一种关系就可以使用继承。

    下面我们将例子与面向对象中的概念进行对应:

    • 上述关系可以用子类别继承自父类别来描述
    • 父类别被称作父类或超类
    • 子类别被称作子类
    • 继承可以使子类具有父类的各种属性和方法,不需要再次编写相同的代码

    2. 继承有什么用

    如果我们将学生类进一步细化为:初中生、高中生、大学生。显然,细化之后的类与类之间一定是存在某些差异的,但是也一定会存在共同点。如果我们使用代码进行表示,三个类中会有很多相同的属性或方法,也会存在一些差异:

    // 定义类:初中生
    public class JuniorStudent{
        // 相同的属性
        public String name;
        public int age;
        public String school;
        public String grade;
        // 其他方法
    }
    
    // 定义类:高中生
    public class SeniorStudent{
        // 相同的属性
        public String name;
        public int age;
        public String school;    
        public String grade;
        // 不同的属性
        public String subject;// 科目:文理科
        // 其他方法
    }
    
    // 定义类:大学生
    public class UniversityStudent{
        // 相同的属性
        public String name;
        public int age;
        public String school;
        public String grade;
        // 不同的属性
        public String college;// 学院
        public String major;// 专业
        // 其他方法
    }
    

    上面只是列举了部分的属性,可以发现有很多属性是完全重合的,方法也有可能存在相同的现象。这个时候我们就们就可以将其中相同的属性和方法抽取出来,定义一个Student学生类,从而对每个类进行简化。

    // 定义类:学生
    public class Student{
        // 提取公共的属性
        public String name;
        public int age;
        public String school;
        public String grade;
        // 提取公共的方法
    }
    
    // 简化后的初中生
    public class JuniorStudent extends Student{
        // 其他方法
    }
    
    // 简化后的高中生
    public class SeniorStudent extends Student{
        // 不同的属性
        public String subject;// 科目:文理科
        // 其他方法
    }
    
    // 简化后的大学生
    public class UniversityStudent extends Student{
        // 不同的属性
        public String college;// 学院
        public String major;// 专业
        // 其他方法
    }
    
    // 定义测试类
    public class Test{
        public static void main(String[] args){
            JuniorStudent juniorStudent = new JuniorStudent();
            juniorStudent.name = "小明";// 正常使用,来自父类
            SeniorStudent seniorStudent = new SeniorStudent();
            seniorStudent.name = "小李";// 正常使用,来自父类
            seniorStudent.subject = "文科";// 自有属性,来自子类
            UniversityStudent universityStudent = new UniversityStudent();
            universityStudent.name = "小陈";// 正常使用,来自父类
            universityStudent.college = "XX大学";// 自有属性,来自子类
            universityStudent.major = "XX专业";// 自有属性,来自子类
        }
    }
    

    从上面的例子可以看出,子父类之间可以通过extends关键字建立继承关系。子类可以直接使用父类中定义的属性和方法,也可以覆盖父类中的方法,表现出子类自己的特点。使用继承有以下几个好处:

    • 减少代码量,子类可以继承父类的属性和方法
    • 提高复用性,便于维护
    • 子类可以通过覆盖父类的方法表达自身的特点
    • 可以使类和类之间产生关联,是多态的前提

    3. 继承的限制与规则

    在Java中,继承的使用存在一些限制,我们需要先明确使用规则才能更好的去设计子父类。一言以蔽之:Java不支持多继承,但支持多重继承(多级继承),从一个子类出发,只存在一条路找到最终的父类

    • 单继承
    class A{
        ...
    }
    class B extends A{
        ...
    }
    
    • 多重继承
    class A{
        ...
    }
    class B extends A{
        ...
    }
    class C extends B{
        ...
    }
    
    • 多子类继承同一父类
    class A{
        ...
    }
    class B extends A{
        ...
    }
    class C extends A{
        ...
    }
    

    4. 如何设计子父类

    当我们需要通过程序去描述某一个场景或实现某一个应用系统时,就需要构建很多相关的类,合理的使用继承可以使代码更加高效也更加利于维护。那么子父类的构建就可以从类本身所代表的意义出发,如果含义相似或相近,并且类与类之间没有较大的冲突,那么我们就可以把他们归为一类。另外一种情况就是原有构建的类不能满足新功能的需要,需要据此改进,那么我们就可以将原有类作为父类,扩充出他的子类,使整体的功能更加强大,同时又不会对已有的代码产生较大的影响。

    • 从多个相关联的类中提取出父类

    可以从管理员(AdminUser)、普通用户(NormalUser)、VIP用户(VIPUser)中提取相同的特征,得到父类:用户(User),同样具有用户名,密码,昵称等信息,同样存在登录方法,只不过各自的实现会有所不同。我们也可以混合使用多种继承方式,得到如下的类关系:

    • 从一个已有的类中扩充出子类

    对于一个简单的电商场景,产品类的设计会比较简单,只需要标识基本信息和价格即可。如果此时需要举行一个秒杀活动,要在购买页面中标识出原价、特价、活动时间、活动介绍等等信息,这就使得我们要对产品类做出升级,如果直接去修改产品类,会导致出现一些可能不会经常使用的属性和方法,因为这些属性和方法纯粹是为特价商品而设计的。比较好的做法是将原有的商品类(Product)作为父类,然后扩充出它的子类:特价商品类(SpecialProduct),在特价商品类中存放新出现的信息。

    • 子类是父类的一个扩充和扩展,使用extends关键字来表示真的是很恰当

    二、子父类的使用

    理解了相关的概念后,我们回到Java的语法中来,子父类间通过extends来建立继承关系,结构如下:

    // 定义父类
    public class Father{
        ...
    }
    
    // 定义子类,继承父类
    public class Son extends Father{
        ...
    }
    

    1. 权限修饰符

    在之前的文章中,已经介绍了权限修饰符的用法,不清楚的同学可以进传送门:Java面向对象编程三大特征 - 封装。当两个类建立了继承关系时,虽然父类当中的所有内容均会被子类继承,但是由于存在权限修饰符,无访问权限的属性或方法会被隐藏,无法被调用和访问(实例化子类对象时,父类对象也会一同被实例化,详细过程会在后面的文章中单独说明)。
    在子类中可以直接调用父类中被public和protected声明的属性和方法,如果是在测试类中,在进行属性调用时依然会受到权限修饰符的限制,看下面一个例子:

    src
    └──edu
        └──sandtower
            └──bean
                │    Father.java
                │    Son.java
            └──test
                │    Test.java
    

    以上为实体类与测试类所在的目录结构

    • Father实体类所在包:edu.sandtower.bean
    • Son实体类为Father的子类,与Father在同一包下
    • Test测试类所在包:edu.sandtower.test
    package edu.sandtower.bean;
    
    public class Father{
        // 父类中的私有属性
        private double ownMoney = 2000;// 私房钱
        // 父类中的受保护属性
        protected double money = 5000;
        // 父类中的公开属性
        public String name = "老李";
    }
    
    package edu.sandtower.bean;
    
    public class Son extends Father{
        // 子类中的独有属性
        ...
        // 测试方法
        public void test(){
            Son son = new Son();
            System.out.println(son.ownMoney);// 编译失败,无法访问私有属性,查看私房钱
            System.out.println(son.money);// 编译通过,在子类中可以访问protected属性
            System.out.println(son.name);// 编译通过,可以访问public属性
        }
    }
    
    package edu.sandtower.test;
    
    import edu.sandtower.bean.Son;
    
    public class Test{
        public static void main(String[] args){
            // 在test包中的Test类中创建Son实例
            Son son = new Son();
            son.name = "小李";// 编译通过,可以访问自父类继承的公开属性
            // 编译失败,在测试类中无法访问protected属性,因为Test类与Father类并无子父类关系
            son.money -= 500.0;
            // 对于Son的其他属性和Father的使用可以自行进行测试,不再赘述
        }
    }
    

    从上面的例子可以看到,权限修饰符所起的作用还是很大的。测试类对于子父类来说是一个处在不同包中的完全无关的类,在调用时会被权限修饰符所限制,所以这里也再度明确一下:权限修饰符是根据类的所在路径与类之间的结构关系进行限定的,不是说在任意一个地方使用子类实例都能调用出父类中的属性和方法。

    2. this与super

    明确了权限修饰符的作用规则后就带来了一个问题,既然在其他类中不能够访问某些属性,那应该如何赋值和使用呢?这时就可以使用封装的办法,同时结合this和super的使用来解决。

    • this:指代当前对象,可以调用当前类中的属性和方法
    • super:指代父类对象,可以调用父类中可访问的属性和方法,包括被子类覆盖重写的方法

    在使用子类实例时,如果我们想要使用某些父类的属性或方法,可以借助构造器和封装方法。将代码修改后,得到如下结果:

    package edu.sandtower.bean;
    
    public class Father{
        // 父类中的私有属性
        private double ownMoney = 2000;// 私房钱
        // 父类中的受保护属性
        protected double money = 5000;
        // 父类中的公开属性
        public String name = "老李";
    }
    
    package edu.sandtower.bean;
    
    public class Son extends Father{
        // 子类中的独有属性
        ...
        // 使用构造器为属性赋值
        public Son(String name,double money){
            super.name = name;
            super.money = money;
        }
        // 使用封装方法操作父类中的属性
        public void setMoney(double money){
            super.money = money;
        }
        public double getMoney(){
            return super.money;
        }
    }
    
    package edu.sandtower.test;
    
    import edu.sandtower.bean.Son;
    
    public class Test{
        public static void main(String[] args){
            // 在test包中的Test类中创建Son实例
            Son son = new Son("小李",3000);// 成功为父类继承而来的属性赋值
            // 以下代码编译通过
            double money = son.getMoney();
            System.out.println(money);
            son.setMoney(money - 500);
        }
    }
    

    3. final修饰符

    final修饰符可以用来修饰属性和方法,也可以用来修饰类。
    当修饰属性时,如果是基本数据类型,则可看做是定义了一个常量,值一旦被指定则不可变。如果是引用类型,则引用无法发生变化,即:可以修改数组或实例中的属性值,但是引用的指向不能再发生变化,无法再指向其他的实例和数组。

    由final修饰的方法被子类继承后不能被重写,有关于继承中子父类方法的关系将在论述多态的文章中详细讨论
    由final修饰的class不能被继承,如果我们把继承关系想象成一棵大树,父类为根,子类为枝的话,那么final class就一定只能做树叶了,因为确认不会有它的子类存在了。

    4. 终极父类:Object

    在刚接触面向对象时,我们可能就听说过一个类,那就是:Object,好像它无所不能装,大饼夹一切的存在。我们所有定义的class都会隐式的继承Object,即:如果我们的类没有使用extends关键字显示的指定父类,那么会自动认为Object是父类,这一过程是在JVM运行时完成的,所以我们不能通过反编译来进行验证。
    Object中提供了特别通用的方法,如:toString,hashCode,equals等等。那么为什么使用Object能装下一切呢?首先就是因为Object类一定是最终父类的存在,换句话说就是树根本根!因为如果一个类显示的指定了另外一个类作为父类,那么他的父类或者父类的父类,一定会在某一级隐式的继承Object。
    如果想进一步了解为什么任意类型的对象实例都能使用Object类型的引用接收可以查看另外一篇文章:Java面向对象编程三大特征 - 多态

    扫描下方二维码,加入官方粉丝微信群,可以与我直接交流,还有更多福利哦~

    在这里插入图片描述

    展开全文
  • 一: 面向对象三大特性: 1.封装:对外部不可见 封装的实现:java中实现封装性有多种方式,而private只是其中一种比较常用的方式.类中每一个属性都必须封装. private 属性类型 属性名称; private 方法返回值...

    一: 面向对象三大特性:

    1.封装:对外部不可见

    封装的实现:java中实现封装性有多种方式,而private只是其中一种比较常用的方式.类中每一个属性都必须封装.

    private 属性类型 属性名称;

    private 方法返回值类型 方法名称(参数列表){}

    package Nsbc.club;

    public class Person {

    public static void main(String[] args) {

    Persons persons = new Persons();

    persons .name = "nishui";

    persons .age = 21;

    persons.tell();

    }

    }

    class Persons{

    private String name;

    private int age ;

    public void tell(){

    System.out.println("姓名:" + this.name +",年纪:" +this.age);//加上this代表本类中的属性或者方法

    }

    }

    以上代码为错误代码,这体现了类的封装性(private  私有的),就是外部不可访问,但是我们设定的这些属性肯定要表示出一些实际的意义,那么这些属性就应该由外部设定,怎样访问?Java中提供了setter(设置内容)方法和getter(获得设置内容)方法,方法如下:

    package Nsbc.club;

    public class Person {

    public static void main(String[] args) {

    Persons persons = new Persons();

    persons.setName("nishui");

    persons.setAge(21);

    persons.tell();

    }

    }

    class Persons{

    private String name;

    private int age ;

    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 void tell(){

    System.out.println("姓名:" + this.getName() +",年纪:" + this.getAge());//加上this代表本类中的属性或者方法

    }

    }


    2.继承:扩展类的功能

    3.多态:

    (1)方法的重载

    (2)对象的多态性


    二:类和对象

    (1) 类:是对某一事物的描述,是抽象的。

    (2)对象:是实际存在的,为该类事物的每个个体 ,也称为实例。

    类就相当于模版,而根据模版生产出来的东西就为对象;比如:如果类为一张汽车设计图纸的模版,那么生产出来的汽车就为对象!

    一个类产生之后,不能直接使用,要产生对象才能使用;

    (1)类名   对象名称  =  null;//声明对象

    对象名称  =  new   类名();//实例化对象就是为其开辟堆内存空间

    (2)类名  对象 = new 类名();

    声明对象:是在栈内存中声明的,跟数组一样,数组名也是存放在栈内存中的.但是只开辟了栈内存空间,对象是无法使用的,必须有其堆内存的引用才可以使用;

    实例化对象:new  类名();在堆内存中开辟空间,在其内存放的变量值都为默认值(没有对变量赋值);

    例如:

    String:是一个字符串类型,但他本身为一个类,是一个引用数据类型,所以此时他的默认值为null;

    int是一个整型,代表的是一个数,所以他是一个基本数据类型,此时的默认值为0.

    对象是保存在栈内存中,属性是保存在堆内存中.在程序中方法是保存在全局代码区中的.

    此区中的内容是所有对象共享的!

    注意:在使用对象的时候,对象必须是被实例化之后才可以使用,而对象的实例化不仅仅只是使用new关键字实现,只要有其堆内存的空间指向,则就表示实例化成功.(如果不实例化对象,就使用它,就会发生空指向异常!)一个栈内存只能指向一个堆内存空间.


    展开全文
  • Java面向对象三大特性

    2019-07-30 15:34:32
    JAVA基础–面向对象三大特性 参考:https://www.cnblogs.com/hysum/p/7100874.html#_labelTop 1 封装 1.1 封装的概念 将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏...

    JAVA基础–面向对象三大特性

    参考:https://www.cnblogs.com/hysum/p/7100874.html#_labelTop

    1 封装

    1.1 封装的概念

    将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。

    好处:

    • 只能通过规定的方法访问数据。

    • 隐藏类的实现细节,方便修改和实现。

    1.2 封装的使用

    封装实现的步骤:

    在这里插入图片描述

    **注意:**对封装的属性不一定要通过get/set方法,其他方法也可以对封装的属性进行操作。当然最好使用get/set方法,比较标准。


    1.2.1 访问修饰符

    在这里插入图片描述

    从表格可以看出从上到下封装性越来越差。

    1.2.2 this关键字

    1. this关键字代表当前对象
    • this.属性 操作当前对象的属性

    • this.方法 调用当前对象的方法。

    1. 封装对象的属性的时候,经常会使用this关键字。

    2. 当getter和setter函数参数名和成员函数名重合的时候,可以使用this区别。如:

    public void setNumber(float number){
        this.number = number;
    }
    

    1.2.3 Java 中的内部类

    内部类( Inner Class )就是定义在另外一个类里面的类。与之对应,包含内部类的类被称为外部类。

    那么问题来了:那为什么要将一个类定义在另一个类里面呢?清清爽爽的独立的一个类多好啊!!

    答:内部类的主要作用如下:

    1. 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。

    2. 内部类的方法可以直接访问外部类的所有数据,包括私有的数据

    3. 内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便。

    内部类可分为以下几种:

    • 成员内部类

    • 静态内部类

    • 方法内部类

    • 匿名内部类

    各个内部类的具体使用请转移到另一篇随文:http://www.cnblogs.com/hysum/p/7101974.html

    2 继承

    2.1 继承的概念

    继承是类与类的一种关系,是一种“is a”的关系。比如“狗”继承“动物”,这里动物类是狗类的父类或者基类,狗类是动物类的子类或者派生类。如下图所示:

    在这里插入图片描述

    **注意:**Java中的继承是单继承,即一个类只有一个父类。

    好处:

    子类拥有父类的所有属性和方法(除了private修饰的属性不能拥有)从而实现了实现代码的复用;

    2.2 继承的使用

    只要在子类加上extends关键字继承相应的父类就可以了:

    //class 子类 extends 父类
    class Dog extends Animal{
        ······
    } 
    

    2.2.1 方法的重写

    子类如果对继承的父类的方法不满意(不适合),可以自己编写继承的方法,这种方式就称为方法的重写。当调用方法时会优先调用子类的方法。

    注意:

    • 返回值类型

    • 方法名

    • 类型及个数

    都要与父类继承的方法相同,才叫方法的重写。

    重载和重写的区别:

    • 方法重载:在同一个类中处理不同数据的多个相同方法名的多态手段。

    • 方法重写:相对继承而言,子类中对父类已经存在的方法进行区别化的修改。


    2.2.1 继承的初始化顺序

    1、初始化父类再初始化子类

    2、先执行初始化对象中属性,再执行构造方法中的初始化。

    基于上面两点,我们就知道实例化一个子类,java程序的执行顺序是:

    父类对象属性初始化---->父类对象构造方法---->子类对象属性初始化—>子类对象构造方法

    下面有个形象的图:

    在这里插入图片描述


    2.2.3 final关键字

    使用final关键字做标识有“最终的”含义。

    1. final 修饰类,则该类不允许被继承。

    2. final 修饰方法,则该方法不允许被覆盖(重写)。

    3. final 修饰属性,则该类的该属性不会进行隐式的初始化,所以 该final 属性的初始化属性必须有值,或在构造方法中赋值(但只能选其一,且必须选其一,因为没有默认值!),且初始化之后就不能改了,只能赋值一次。

    4. final 修饰变量,则该变量的值只能赋一次值,在声明变量的时候才能赋值,即变为常量。


    2.2.4 super关键字

    在对象的内部使用,可以代表父类对象。

    1、访问父类的属性:super.age

    2、访问父类的方法:super.eat()

    super的应用:

    首先我们知道子类的构造的过程当中必须调用父类的构造方法。其实这个过程已经隐式地使用了我们的super关键字。这是因为如果子类的构造方法中没有显示调用父类的构造方法,则系统默认调用父类无参的构造方法。那么如果自己用super关键字在子类里调用父类的构造方法,则必须在子类的构造方法中的第一行。


    2.2.5 Object类

    Object类是所有类的父类,如果一个类没有使用extends关键字明确标识继承另一个类,那么这个类默认继承Object类。 Object类中的方法,适合所有子类!!!

    Object类中主要的方法:

    1、toString()

    a. 在Object类里面定义toString()方法的时候返回的对象的哈希code码(对象地址字符串)。

    我们可以发现,如果我们直接用System.out.print(对象)输出一个对象,则运行结果输出的是对象的对象地址字符串,也称为哈希code码。如:

    com.imooc.Telphone@60ec2ea8
    

    哈希码是通过哈希算法生成的一个字符串,它是用来唯一区分我们对象的地址码,就像我们的身份证一样。

    b. 可以通过重写toString()方法表示出对象的属性。

    如果我们希望输出一个对象的时候,不是它的哈希码,而是它的各个属性值,那我们可以通过重写toString()方法表示出对象的属性。

    2、equals()

    a. equals() 返回值是布尔类型。

    b. 默认的情况下,比较的是对象的引用是否指向同一块内存地址-------对象实例化时,即给对象分配内存空间,该内存空间的地址就是内存地址。使用方法如:dog.equals(dog2);

    c. 如果是两个对象,但想判断两个对象的属性是否相同,则重写equals()方法。

    以Dog类为例,重写后的equals()方法如下(当然你可以根据自己想比较的属性来重写,这里我以age属性是否相同来重写equals()方法):

    public boolean equals(Object obj){
        if(this == obj)
            return true;
        if(obj == null)
            return false;
        if(getClass() != obj.getClass())
            return false;
        Dog other = (Dog)obj;
        if(age != other.age)
            return false;
        return true;
    }
    

    上面有四个判断,它们的含义分别是:

    1. 判断地址是否相同----if (this == obj),相同则返回true

    2. 判断对象是否为空----if (obj == null),为空则返回false

    3. getClass()可以得到类对象,判断类型是否一样-----if (getClass() != obj.getClass()),不一样则返回false

    4. 判断属性值是否一样----if (age != other.age),不一样返回false

    5. 如果地址相同,对象不为空,类型一样,属性值一样则返回true

    这里要注意的是,理解obj.getClass()得到的类对象和类的对象的区别,以下用图形表示:

    在这里插入图片描述

    可以看到,对于类对象我们关心它属于哪个类,拥有什么属性和方法,比如我和你都是属于“人”这个类对象;而类的对象则是一个类的实例化的具体的一个对象。比如我和你是两个不同的人。

    3 多态

    3.1 多态的概念

    面向对象的最后一个特性就是多态,那么什么是多态呢?多态就是对象的多种形态。

    java里的多态主要表现在两个方面:

    3.1.1 引用多态

    • 父类的引用可以指向本类的对象;

    • 父类的引用可以指向子类的对象;

    这两句话是什么意思呢,让我们用代码来体验一下,首先我们创建一个父类Animal和一个子类Dog,在主函数里如下所示:

    public static void main(String[] args){
        Animal obj1 = new Animal();//父类的引用指向本类
        Animal obg2 = new Dog();//父类的引用指向子类
    } 
    

    **注意:**我们不能使用一个子类的引用来指向父类的对象,如:

    Dog obj3 = new Animal();
    

    这里我们必须深刻理解引用多态的意义,才能更好记忆这种多态的特性。为什么子类的引用不能用来指向父类的对象呢?我在这里通俗给大家讲解一下:就以上面的例子来说,我们能说“狗是一种动物”,但是不能说“动物是一种狗”,狗和动物是父类和子类的继承关系,它们的从属是不能颠倒的。当父类的引用指向子类的对象时,该对象将只是看成一种特殊的父类(里面有重写的方法和属性),反之,一个子类的引用来指向父类的对象是不可行的!!

    3.1.2 方法多态

    根据上述创建的两个对象:本类对象和子类对象,同样都是父类的引用,当我们指向不同的对象时,它们调用的方法也是多态的。

    • 创建本类对象时,调用的方法为本类方法;

    • 创建子类对象时,调用的方法为子类重写的方法或者继承的方法;

    使用多态的时候要注意:如果我们在子类中编写一个独有的方法(没有继承父类的方法),此时就不能通过父类的引用创建的子类对象来调用该方法!!!

    注意: 继承是多态的基础。


    3.2 多态的使用

    3.2.1 引用类型转换

    了解了多态的含义后,我们在日常使用多态的特性时经常需要进行引用类型转换。

    引用类型转换:

    1. 向上类型转换(隐式/自动类型转换),是小类型转换到大类型。

    就以上述的父类Animal和一个子类Dog来说明,当父类的引用可以指向子类的对象时,就是向上类型转换。如:

    Dog dog = new Dog();
    Animal animal = dog;//自动类型提升 向上类型转换
    

    2. 向下类型转换(强制类型转换),是大类型转换到小类型(有风险,可能出现数据溢出)。

    将上述代码再加上一行,我们再次将父类转换为子类引用,那么会出现错误,编译器不允许我们直接这么做**,虽然我们知道这个父类引用指向的就是子类对象,但是编译器认为这种转换是存在风险的。**如:

    Dog dog = new Dog();
    Animal animal = dog;//自动类型提升 向上类型转换
    Dog dog2 = animal;
    

    那么我们该怎么解决这个问题呢,我们可以在animal前加上(Dog)来强制类型转换。如:

    Dog dog2 = (Dog)animal;//向下类型转换 强制类型转换
    

    但是如果父类引用没有指向该子类的对象,则不能向下类型转换,虽然编译器不会报错,但是运行的时候程序会出错,如:

    Dog dog2 = (Dog)new Animal();
    

    其实这就是上面所说的子类的引用指向父类的对象,而强制转换类型也不能转换!!

    还有一种情况是父类的引用指向其他子类的对象,则不能通过强制转为该子类的对象。如:

    Dog dog = new Dog();
    Animal animal = dog;//向上类型转换 自动类型转换
    Dog dog2 = (Dog)animal;
    Cat cat = (Cat)animal;//1. 编译时 Cat类型 2. 运行时Dog类型
    

    这是因为我们在编译的时候进行了强制类型转换,编译时的类型是我们强制转换的类型,所以编译器不会报错,而当我们运行的时候,程序给animal开辟的是Dog类型的内存空间,这与Cat类型内存空间不匹配,所以无法正常转换。这两种情况出错的本质是一样的,所以我们在使用强制类型转换的时候要特别注意这两种错误!!下面有个更安全的方式来实现向下类型转换。。。。

    **3. instanceof运算符,**来解决引用对象的类型,避免类型转换的安全性问题。

    instanceof是Java的一个二元操作符,和==,>,<是同一类东东。由于它是由字母组成的,所以也是Java的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据。

    我们来使用instanceof运算符来规避上面的错误,代码修改如下:

    if (animal instanceof Cat){
        Cat cat = (Cat)animal;//1. 编译时 Cat类型 2. 运行时Dog类型
    }
    

    利用if语句和instanceof运算符来判断两个对象的类型是否一致。

    **补充说明:**在比较一个对象是否和另一个对象属于同一个类实例的时候,我们通常可以采用instanceof和getClass两种方法通过两者是否相等来判断,但是两者在判断上面是有差别的。Instanceof进行类型检查规则是:你属于该类吗?或者你属于该类的派生类吗?而通过getClass获得类型信息采用==来进行检查是否相等的操作是严格的判断,不会存在继承方面的考虑

    **总结:**在写程序的时候,如果要进行类型转换,我们最好使用instanceof运算符来判断它左边的对象是否是它右边的类的实例,再进行强制转换。


    3.2.2 抽象类

    定义:抽象类前使用abstract关键字修饰,则该类为抽象类。

    使用抽象类要注意以下几点:

    1. 抽象类是约束子类必须有什么方法,而并不关注子类如何实现这些方法。

    2. 抽象类应用场景:

    a. 在某些情况下,某个父类只是知道其子类应该包含怎样的方法,但无法准确知道这些子类如何实现这些方法(可实现动态多态)。

    b. 从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为子类的模板,从而避免子类设计的随意性。

    1. 抽象类定义抽象方法,只有声明,不需要实现。抽象方法没有方法体以分号结束,抽象方法必须用abstract关键字来修饰。如:
    public abstract class Telephone{
    	public abstract void call();//抽象方法,方法以分号结束,只有声明,不需实现
    }
    

    4、包含抽象方法的类是抽象类。抽象类中可以包含普通的方法,也可以没有抽象方法。如:

    public abstract class Telephone{
        //这个抽象类可以没有抽象方法
        public void message(){
            System.out.println("我是抽象类的普通方法")
        }//抽象类中包含普通的方法
    }
    

    5、抽象类不能直接创建,可以定义引用变量来指向子类对象,来实现抽象方法。以上述的Telephone抽象类为例:

    1 public abstract class Telephone {
    2     public abstract void call();//抽象方法,方法体以分号结束,只有声明,不需要实现
    3     public void message(){
    4         System.out.println("我是抽象类的普通方法");
    5     }//抽象类中包含普通的方法
    6 }
    
    1 public class Phone extends Telephone {
    2 
    3     public void call() {//继承抽象类的子类必须重写抽象方法
    4         // TODO Auto-generated method stub
    5         System.out.println("我重写了抽象类的方法");
    6     }
    7     
    8 }
    

    以上是Telephone抽象类和子类Phone的定义,下面我们看main函数里:

    public class train{
        public static void main(String[] argus){
            Telephone t = new Telephone();//抽象类不能直接创建
            Telephone p = new Phone();//定义引用变量来指向子类对象
            p.cell();
            p.message()}
    }
    

    ​ 运行结果(排错之后):

    我重写了抽象类的方法
    我是抽象类的普通方法
    

    3.2.3 接口

    1、概念

    接口可以理解为一种特殊的类,由全局常量和公共的抽象方法所组成。也可理解为一个特殊的抽象类,因为它含有抽象方法。

    如果说类是一种具体实现体,而接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部数据,也不关心这些类里方法的实现细节,它只规定这些类里必须提供的某些方法。(这里与抽象类相似)

    2.接口定义的基本语法

    修饰符 interface 接口名 [extends父接口1,2....](多继承){
    	0…n常量 (public static final)                                          
    	0…n 抽象方法(public abstract)                                      
    }  
    

    其中[ ]里的内容表示可选项,可以写也可以不写;接口中的属性都是常量,即使定义时不添加public static final 修饰符,系统也会自动加上;接口中的方法都是抽象方法,即使定义时不添加public abstract修饰符,系统也会自动加上。

    3.使用接口

    一个类可以实现一个或多个接口,实现接口使用implements关键字。java中一个类只能继承一个父类,是不够灵活的,通过实现多个接口可以补充。

    继承父类实现接口的语法为:

    [修饰符] class 类名 extends 父类 implements 接口1,接口2...{
    	类体部分//如果继承了抽象类,需要实现继承的抽象方法;要实现接口中的抽象方法
    }
    

    注意:如果要继承父类,继承父类必须在实现接口之前,即extends关键字必须在implements关键字前

    补充说明:通常我们在命名一个接口时,经常以I开头,用来区分普通的类。如:IPlayGame

    以下我们来补充在上述抽象类中的例子,我们之前已经定义了一个抽象类Telephone和子类Phone,这里我们再创建一个IPlayGame的接口,然后在原来定义的两个类稍作修改,代码如下:

    1 public interface IPlayGame {
    2     public void paly();//abstract 关键字可以省略,系统会自动加上
    3     public String name="游戏名字";//static final关键字可以省略,系统会自动加上
    4 }
    
     1 public class Phone extends Telephone implements IPlayGame{
     2 
     3     public void call() {//继承抽象类的子类必须重写抽象方法
     4         // TODO Auto-generated method stub
     5         System.out.println("我重写了抽象类的方法");
     6     }
     7 
     8     @Override
     9     public void paly() {
    10         // TODO Auto-generated method stub
    11         System.out.println("我重写了接口的方法");
    12     }
    13     
    14 }
    
     1 public class train {
     2     
     3 
     4     public static void main(String[] args) {
     5         // TODO Auto-generated method stub
     6         IPlayGame i=new Phone();//用接口的引用指向子类的对象
     7         i.paly();//调用接口的方法
     8         System.out.println(i.name);//输出接口的常量
     9     
    10         
    11 
    12     }
    13 }
    

    运行结果:

    我重写了接口的方法
    游戏名字
    

    4.接口和匿名内部类配合使用

    接口在使用过程中还经常和匿名内部类配合使用。匿名内部类就是没有没名字的内部类,多用于关注实现而不关注实现类的名称。

    语法格式:

    1 Interface i =new interface(){
    2     Public void method{
    3         System.out.println(“利用匿名内部类实现接口1);
    4     }
    5 };
    6 i.method();
    

    还有一种写法:(直接把方法的调用写在匿名内部类的最后)

    1 Interface i =new interface(){
    2     Public void method{
    3         System.out.println(“利用匿名内部类实现接口1);
    4     }
    5 }.method();
    
    展开全文
  • 面向对象语言的三大特征

    千次阅读 2016-08-12 10:55:55
    面向对象语言的三大特征是:封装 继承 多态 最近感觉,总结一下这个问题还是挺有必要的,所以转发了此篇文章的部分段落。 封装 封装最好理解了。封装是面向对象特征之一,是对象和类概念的主要特性。 封装,...

    面向对象语言的三大特征是:封装 继承 多态

    最近感觉,总结一下这个问题还是挺有必要的,所以转发了此篇文章的部分段落。

    封装

    封装是面向对象的特征之一,是对象和类概念的主要特性。封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。示例代码:

    public class Department
    {
         private string departname;
    
         // 读方法
         public string GetDepartname(){
              return departname;
         }
    
         //写方法
         public void SetDepartname( string a){
              departname=a;
         }
    
    }

    继承

    面向对象编程 (OOP) 语言的一个主要功能就是继承。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

    通过继承创建的新类称为“子类”或“派生类”。

    被继承的类称为基类父类超类

    继承的过程,就是从一般到特殊的过程。

    要实现继承,可以通过继承Inheritance)和组合Composition)来实现。

    在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。

     

    继承概念的实现方式有三类:实现继承、接口继承和可视继承。

    ?         实现继承是指使用基类的属性和方法而无需额外编码的能力;

    ?         接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;

    ?         可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。

    在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是属于关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。

    抽象类仅定义将由子类创建的一般属性和方法,创建抽象类时,请使用关键字 Interface 而不是 Class

    OO开发范式大致为:划分对象抽象类将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。

    普通类继承关系示例代码:

    public  class A{
         int test=0;
         public A(){
              test = 5;
              Console.WriteLine("I am A 公有默认构造函数 ,test={0}", test);
         }
    }
    
    public class B : A {
    
    }
    
    public class InheritanceTest1 {
            
         public static void Main(string[] args){
                B b = new B();
                Console.Read();
          }
    }

    多态

    多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

    实现多态,有二种方式,覆盖,重载。

    覆盖,是指子类重新定义父类的虚函数的做法。

    重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

    其实,重载的概念并不属于面向对象编程,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_funcstr_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的(记住:是静态)。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!真正和多态相关的是覆盖。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。结论就是:重载只是一种语言特性,与多态无关,与面向对象也无关!引用一句Bruce Eckel的话:不要犯傻,如果它不是晚邦定,它就不是多态。

    那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用家谱中任一类的实例的某一属性时的正确调用。

        public class Animal
        {
            public virtual void Eat()
            {
                Console.WriteLine("Animal eat");
            }
        }
    
        public class Cat : Animal
        {
            public override void Eat()
            {
                Console.WriteLine("Cat eat");
            }
        }
    
        public class Dog : Animal
        {
            public override void Eat()
            {
                Console.WriteLine("Dog eat");
            }
        }
    
        class Tester
        {
            static void Main(string[] args)
            {
                Animal[] animals = new Animal[3];
    
                animals[0] = new Animal();
                animals[1] = new Cat();
                animals[2] = new Dog();
    
                for (int i = 0; i < 3; i++)
                {
                    animals[i].Eat();
                }
            }
        }


    文章会不断修改更新,不是最终版


    参考网站:

    http://www.360doc.com/content/12/0102/15/306774_176667425.shtml

    http://www.cnblogs.com/jiajiayuan/archive/2011/09/09/2172292.html

    http://blog.csdn.net/acmilanvanbasten/article/details/8625097

    http://www.cnblogs.com/jhxk/articles/1644018.html

    展开全文
  • 面向对象三大特性

    万次阅读 2018-08-11 18:31:57
    封装是把客观事物抽象成类,并且把自己的属性和方法让可信的类或对象操作,对不可性的隐藏。 继承 继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。 ...
  • 写在前面 作为自认为还年轻的一代,游戏这个被...所以这篇博客主要想借用大家熟悉的游戏为模型来讨论理解面向对象三大特性,你会感受到看似枯燥的代码在游戏的加持下会变得有血有肉,栩栩如生。 相关 相信
  • 面向对象程序设计的三大特征

    千次阅读 2012-06-19 16:30:11
    今天总结一下面向对象(OOP)的三大特征,方便自己的复习学习,也方便大家的参考。 (1)面向对象程序设计有三大特征:封装(Encapsulation)、继承(Inheritance)、多态(Polymorphism)。这个单词很常见,大家...
  • 面向对象特性

    千次阅读 热门讨论 2018-05-25 20:33:05
    一,封装解释:每个对象都包含它能进行操作所需要的所有信息,这个特性成为封装,因此对象不必依赖其他对象来完成自己的操作。好处:1.减少耦合。2.类内部的实现可以自由的修改。3.类具有清晰的对外接口。二,继承...
  • 面向对象概念和基本特征

    千次阅读 2018-07-23 16:24:51
    1 ,什么是面向对象? 正如每个人心中都有一个哈姆雷特一样,每个人都有对面向对象的不一样的理解 所谓的面向对象就是基于对象概念,以对象为中心,以类和继承为构造机制,来认识、理解刻画客观世界和设计、构建...
  • 面向对象

    2020-08-03 20:38:44
    面向过程 解决一个问题时,先列举出各个步骤 面向对象 先将问题域中的对象找出来,并确定之间的关系 优点:复用性更好;开发复杂项目时更具有优势; 3、面向对象中的概念 对象(Object):一切具体的事物都...
  • 而java的面向对象性是什么样的特性呢,同样面向对象性也有自己的特征,“封装”性就是其中一个,本篇文章主要是介绍面向对象特性封装知识详解 既然说到了java的面向对象性,那么把面向对象性的特征在这里都列举出来...
  • 第一章 面向对象

    千次阅读 2019-08-23 21:17:16
    文章目录(一)面向对象三大特性?(二)重写与重载的区别?(二)对java多态的理解 ?()Java中实现多态的机制是什么? (一)面向对象三大特性面向对象三大特性包括:封装、继承、多态。 封装是给对象提供...
  • 2. 继承:广义的继承有种实现形式:实现继承(指使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。前两种(类...
  • 封装是面向对象特征之一,是对象和类概念的主要特性。 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。 继承 面向对象编程 ...
  • 面向对象设计七原则

    千次阅读 2018-06-07 17:28:00
    然后对面向对象设计的一些原则进行了一些学习和整理。包括SOLID、合成复用原则与迪米特法则。可维护性Robert C.Martin认为⼀个可维护性较低的软件设计,通常由于如下四个原因造成:• 过于僵硬(Rigidity)• 过于脆弱...
  • 面向对象基础

    千次阅读 多人点赞 2021-03-24 19:24:47
    1、Java面向对象三大特征是什么 ? 封装、继承、多态 2、什么是封装? 封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制 在程序中属性的读和修改的访问级别;将抽象得到的数据和行 为(或功能)相结合,...
  • 面向对象编程

    千次阅读 2019-06-11 11:30:21
    面向对象三大特性: 封装 , 继承 和多态 一. 面向过程编程 1.定义: 是一种以过程为中心的编程思想。这些都是以什么正在发生为目标进行编程,不同于面向对象的是谁在受影响。于面向对象明显 的不同就是封装,...
  • Java基础四——面向对象思想

    万次阅读 多人点赞 2018-07-10 00:04:35
    (在已有对象的情况下,直接使用对象,而不再去考虑对象的内部构造) 面向对象的开发来讲也分为个过程:OOA(面向对象分析)、OOD(面向对象设计)、OOP(面向对象编程)1.2 了解对象的内部结构1.2.1 成员变量和...
  • C语言的面向对象设计方法,正如题记上面所说,面向对象是一种思想,而并非是一种语言,我们将会介绍C语言实现的面向对象开发方式。
  • 面向对象和面向过程

    2019-02-19 23:34:11
    写在开头的碎碎念: 在前面的笔记中,所列举的代码皆为面向过程的编程,这里借用一个例子:用代码实现一个功能可以类比为做一道菜,面向过程...通过面向对象的方法,我们创造了具有特定姓名和年龄的厨师这个对象,...
  • 面向对象的 C

    2015-01-27 16:01:59
    面向对象的语言更接近人的思维方式,而且在很程度上降低了代码的复杂性,同时提高了代码的可读性和可维护性,传统的 C 代码同样可以设计比较易读,易维护,复杂度较低的优美代码,本文将通过一个实际的例子来...
  • 面向对象的重新思考

    千次阅读 热门讨论 2014-09-05 22:24:34
    在刚开始学习一门面向对象语言的时候,我们是这样写代码的:  我们会先写一个小狗类,然后new它,最后调用它的方法实现功能。    例如:    Dog d=new Dog();//造个小狗  d.shout();//小狗看到stranger会叫  ...
  • java基础--面向对象详解

    千次阅读 多人点赞 2018-09-02 11:46:21
    1、面向对象(1) 1、什么叫面向对象面向对象(Object-Oriented,简称OO)就是一种常见的程序结构设计方法。 面向对象思想的基础是将相关的数据和方法放在一起,组合成一种新的复合数据类型,然后使用新创建的...
  • java面向对象思想

    千次阅读 2018-09-25 18:01:11
    1.1 什么是面向对象  面向对象思想就是不断的创建... 面向对象的开发来讲也分为个过程:OOA(面向对象分析)、OOD(面向对象设计)、OOP(面向对象编程) 1.2 了解对象的内部结构 1.2.1 成员变量和成员方法...
  • 使用java实现面向对象编程

    千次阅读 2017-05-29 20:50:32
    第一章:抽象和封装 学习面向对象,理解其中的概念只是前提,灵活应用才是目的 面向对象编程比较抽象,想要真正掌握...面向对象的核心是封装了属性和方法的类,以数据为中心,实现了类级别的代码重用,面向对象因为采
  • 可维护性的复用是以设计原则为基础的,下面列举7个重要的的面向对象设计原则: 单一职责原则(SRP):一个对象应该只包含单一的职责,并且该职责被完整的封装在一个类中。用于控制类的粒度大小,是实现高内聚、...
  • 面向对象的分析方法

    千次阅读 2015-05-11 21:55:16
    是在一个系统的开发过程中进行了系统业务调查以后,按照面向对象的思想来分析问题。OOA与结构化分析有较的区别。OOA所强调的是在系统调查资料的基础上,针对OO方法所需要的素材进行的归类分析和整理,而不是对管理...
  • 【Python】python面向对象编程

    千次阅读 2015-06-04 11:21:34
    在python中,面向对象编程主要有两个主题,就是类和类实例。 1、类 新式类—— class ClassName(bases): 'ClassName class doc string' class_suite 经典类—— class ClassName: 'ClassName class doc string' ...
  • 10. 比找女朋友还难的技术点,Python 面向对象

    千次阅读 多人点赞 2020-11-29 20:57:40
    有人跟我说他一学就会面向对象了,我上去就一个......赞。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 28,958
精华内容 11,583
关键字:

列举出面向对象的三大特性