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

    千次阅读 多人点赞 2020-03-25 20:45:49
    本文关键字:Java、面向对象、三大特征、封装。封装是面向对象编程中的三大特征之一,在对封装性进行解释时我们有必要先了解一些面向对象的思想,以及相关的概念。

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

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

    Java面向对象编程三大特征 - 封装

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


    封装是面向对象编程中的三大特征之一,在对封装性进行解释时我们有必要先了解一些面向对象的思想,以及相关的概念。当我们想要去描述一系列的关系时我们要用到的最基本结构就是类,其中存在着成员变量和方法,用于记录属性和表达行为,相关知识请进传送门:Java中的基本操作单元 - 类和对象

    一、名词解读

    为了解释封装的概念和作用,需要先来了解一下几个相关的概念,这有助于我们接下来的理解。

    1. 权限修饰符

    当我们在一个类中定义成员变量时,会指定一个变量的类型,除此之外,还会有修饰符的部分,在此给出定义成员变量的规范格式:

    // 定义变量
    [修饰符] 变量类型 变量名称;
    [修饰符] 变量类型 变量名称 = 初始值;
    

    修饰符起到的作用从字面就可以解释,起到一个修饰和限定的作用,可以使用在成员变量之前的修饰符可以是:publicprotectedprivate、final、static。
    修饰符与修饰符之间的顺序没有强制要求,其中public、protected、private被称为权限修饰符,可以用来限定类的属性和方法的访问权限,指明在哪些包的哪些类中能够调用到这些属性或方法,是一种一定会存在的修饰符。需要注意的是,这三个单词不能同时出现,当这三个单词都不出现的时候会被认为是默认访问权限,所以权限修饰符一共有四种:private、默认、protected、public。

    2. 权限对应关系表

    • private:私有权限,只能在定义的类中访问,在其他类中创建的实例均无法访问
    • 默认:同包可访问权限,在没有声明权限修饰符时为默认权限,允许在同包的其他类访问
    • protected:受保护权限,允许有继承关系的子类访问
    • public:公共权限,允许任何类访问

    3. 属性访问

    由于权限修饰符在封装特性中的作用只是实现封装性的一种途径,所以在这里只演示private与public的作用,权限修饰符的其他作用将在后续的文章中继续介绍。

    src
    └──edu
        └──sandtower
            └──bean
                │    Person.java
            └──test
                │    Test.java
    

    以上为实体类与测试类所在的目录结构,Person实体类所在包:edu.sandtower.bean,Test测试类所在包:edu.sandtower.test,相应代码如下:

    package edu.sandtower.bean;
    
    public class Person{
        // 声明公开属性
        public String name;
        // 声明私有属性
        private double money;
    }
    
    package edu.sandtower.test;
    
    import edu.sandtower.bean.Person;
    
    public class Test{
        public static void main(String[] args){
            // 在test包中的Test类中创建Person实例
            Person person = new Person();
            person.name = "小张";// 编译通过,可以访问name属性
            person.money = 500.0;// 编译失败,无法访问money属性
        }
    }
    

    从上面的例子可以看出,虽然依然是使用Person自己的实例在进行属性的调用,但是我们是在另外一个包中的类发生的调用,所以是不能够访问到private修饰的属性的,在刚开始学习时一定要注意区分。

    二、概念阐释

    1. 封装有什么用

    通过使用权限修饰符,我们可以限定类的成员的被访问权限,那为什么要这样做呢?在很多场景下,我们需要确保我们对属性值的操作均是有效操作,不能违背某些规则。
    比如,我们定义了一个Person类,具有name和money两个属性,在买东西时需要扣掉相应的金额,原始写法如下:

    public class Person{
        public String name;
        public double money;
    }
    
    public class Test{
        public static void main(String[] args){
            Person person = new Person();
            person.money = 500;// 初始金额500元
            System.out.println("购买一张桌子,花费200元");
            person.money -= 200;
            System.out.println("购买二手PSP,花费350元");
            person.money -= 350;
            System.out.println("目前余额为:" + person.money);// -50
        }
    }
    

    可以看到,经过代码操作以后可能会导致money的属性为负。看官甲:你自己不加判断赖代码?没错,这个问题我们可以增加判断代码来解决,由于这个操作是对money属性值的操作,我们将它封装成一个方法写在实体类中,于是有了改进之后的代码:

    public class Person{
        public String name;
        public double money;
    
        // 定义一个方法,用于设置money属性的值
        public void setMoney(double money){
            if(money >= 0){
                this.money = money;
            }
        }
    }
    
    public class Test{
        public static void main(String[] args){
            Person person = new Person();
            person.money = 500;// 初始金额500元
            System.out.println("购买一张桌子,花费200元");
            person.setMoney(person.money - 200);
            System.out.println("购买二手PSP,花费350元");
            person.setMoney(person.money - 350);
            System.out.println("目前余额为:" + person.money);// 300
        }
    }
    

    经过上面的改进,我们可以确保money的值不为负数,同时可以看到,当在实体类中定义方法后,使用者需要修改属性值时直接调用方法就可以保证不出问题。但是由于属性值依然可以被直接访问,还不能保证万无一失,于是我们利用权限修饰符使得变量不能被直接访问,同时需要定义一个能够取得属性值的方法。

    public class Person{
        public String name;
        // 声明money属性为private权限
        private double money;
    
        // 定义一个方法,用于设置money属性的值
        public void setMoney(double money){
            if(money >= 0){
                this.money = money;
            }
        }
        // 定义一个方法,用于获取money属性的值
        public double getMoney(){
            return this.money;
        }
    }
    
    public class Test{
        public static void main(String[] args){
            Person person = new Person();
            person.money = 500;// 初始金额500元
            System.out.println("购买一张桌子,花费200元");
            person.setMoney(person.getMoney() - 200);
            System.out.println("购买二手PSP,花费350元");
            person.setMoney(person.getMoney() - 300);
            System.out.println("目前余额为:" + person.getMoney());// 300
        }
    }
    

    通过以上的案例,我们可以看到进行封装有以下几个作用:

    • 防止类的属性被外部代码随意的修改和访问,保证数据的完备性
    • 将对属性的操作转换为方法,更加灵活和安全
    • 使用封装可以隐藏实现的细节:使用者只需要作用,不需要知道过程
    • 在类的定义结构中修改,提高了代码的可维护性,同时又可以不影响外部的使用
    • 通过封装方法可以有效减少耦合
      • 耦合:模块与模块之间,代码与代码之间的关联程度,对属性封装后,和调用相关的代码就会变得相对简单,可以降低耦合

    2. 如何进行封装

    在进行封装时都是出于对属性保护的考虑,可以按照以下两个步骤来进行:

    • 使用权限修饰符
      • 使用private作用在属性上,关闭直接访问的入口
      • 使用public作用在方法上,提供调用的入口
    • 定义与属性存取相关的方法

    在属性关闭后,我们需要通过方法来获取属性的值以及对属性值进行修改。由于有了方法结构,我们就可以对存入的数据进行判断,对不符合逻辑的数据进行处理。

    3. 常规封装方法

    明白了封装的作用后,我们可以通过自定义方法的方式完成对属性的封装。封装方法和类中定义的其他方法在结构上没有任何的区别,同样都是普通的方法,区别主要在于体现在用途方面:

    • 普通方法主要表达该类所能产生的行为
    • 封装方法主要为属性的访问和使用提供了一个入口,作用相对单一

    在进入到框架的学习之后,很多对实体类属性自动赋值的操作都是通过调用封装方法实现的,所以我们必须要知道常规封装方法的名称定义和类型设置规则。
    对于属性来说我们只会进行两种操作:存和取。那么相应的封装方法应该有一对儿

    • get代表取用:既然是取值,那么就要把属性值进行返回,方法的返回值类型与属性类型相同
    • set代表存储:既然是存值,那么就要在参数列表中接收想要存入的值,类型与属性类型相同

    对于命名方面只要遵从驼峰命名法就好,以get或set开头,大写属性名称的首字母,其余不变,看下面一个例子:

    public class Person{
        // 使用private声明属性
        private String name;
        private double money;
    
        // 使用public声明方法,作为操作属性的入口
        public void setName(String name){
            this.name = name;
        }
        public String getName(){
            return this.name;
        }
        public void setMoney(double money){
            // 如有需要,可以在方法中可以自定义其他逻辑
            this.money = money;
        }
        public double getMoney(){
            return this.money;
        }
    }
    

    由于常规封装方法定义的格式和名称都相对固定,所以一般的编译器都自带自动生成封装方法的功能,这样既方便又能降低出错率,大家一定要掌握。

    • Eclipse:

    属性定义完成后,选择source菜单 -> Generate Getters and Setters…
    在这里插入图片描述
    点击Select All(选择所有属性) -> Generate
    在这里插入图片描述

    • IDEA:

    与Eclipse类似,在定义好类的私有属性后,使用快捷键:Alt + Insert

    可以一次性选择多个属性 -> 点击OK

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

    在这里插入图片描述

    展开全文
  • Java面向对象编程三大特征 - 多态

    千次阅读 多人点赞 2020-04-06 11:43:48
    本文关键字:Java、面向对象、三大特征、多态。多态是面向对象编程的三大特征之一,是面向对象思想的终极体现之一。在理解多态之前需要先掌握继承、重写、父类引用指向子类对象的相关概念。

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

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

    Java面向对象编程三大特征 - 多态

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


    多态是面向对象编程的三大特征之一,是面向对象思想的终极体现之一。在理解多态之前需要先掌握继承、重写、父类引用指向子类对象的相关概念,对继承还没有完全明白的同学可进传送门:Java面向对象编程三大特征 - 继承

    一、抽象类

    在继承中,我们已经了解了子父类的关系以及如何对子父类进行设计,如果已经存在多个实体类,再去定义父类其实是不断的抽取公共重合部分的过程,如果有需要将会产生多重继承关系。在抽取整理的过程中,除了属性可以复用,有很多方法一样也可以复用,假如以图形举例:矩形、圆形,都可以具有周长和面积两个方法,但是计算的方式完全不同,矩形和圆形之间肯定不能构成子父类关系,那么只能是同时去继承一个父类,那么问题就来了,这两个类都有什么共同点?

    除了都是图形好像并没有什么共同点,矩形有两组边长,圆形是通过半径来描述,如果非要往一起联系的话。。。Wait a moment(灵光一闪中,请勿打扰)!!!难道说是都可以计算出周长和面积?细细想来,也是能说出一番道理的,但是这好抽象啊!
    如果真的是这样,也只能有一个模糊的思路,既然描述图形的属性不能够共用那就分别放在两个子类中吧,那么计算周长和面积的方法要怎么搞?如果在父类中定义相应的方法,那参数列表怎么写?方法体怎么填?这个坑好像有点大,接下来,我们就要华丽地将这个坑填平。

    1. 抽象与抽象类

    在上面的例子中,我们遇到了一个情况,有两个在逻辑上看似相关的类,我们想要把他们联系起来,因为这样做可以提高效率,但是在实施的过程中发现这个共同点有点太过模糊,难以用代码描述,甚至于还不如分开用来的方便,这时就要引出抽象的概念,对应的关键词为:abstract。

    • abstract可以修饰方法,修饰后被称为抽象方法
    • abstract可以修饰类,修饰后被称为抽象类
    • abstract不能与static修饰符同时使用
    • abstract不能与final修饰符同时使用

    那么使用了abstract又能如何呢?这代表指定的方法和类很难表述,那么。。。就不用表述了!对于矩形类(Rectangle)与圆形类(Circle)的父类:图形类(Figure),我们只能总结出他具有计算周长和面积的方法,而具体的实现方法我们无法给出,只有明确了图形之后,才能给出具体的实现,于是我们使用抽象来描述这两个方法,被abstract修饰的方法不需要有方法体,且不能为private,由于抽象方法没有方法体,那么如果被代码调用到了怎么办呢?以下两个限制规则可以杜绝这个问题:

    • 抽象方法只能存在于抽象类中(接口在另外的文章中讨论)
    • 抽象类无法被直接实例化(匿名内部类的用法暂不做讨论)

    既然抽象类不能被实例化,那么自然也就不会调用到没有方法体的那些方法了,那这些方法该怎么被调用呢?我们需要一步一步的来梳理,至少目前我们已经能够清晰的得到如下的关系图了:

    2. 抽象类的特点

    抽象类的本质依然是一个类(class),所以具备着一个普通类的所有功能,包括构造方法等的定义,总结一下,抽象类具有以下的几个特点:

    • 抽象类由abstract修饰
    • 抽象类中允许出现抽象方法
    • 抽象类不能通过构造器直接实例化
    • 可以在抽象类中定义普通方法供子类继承

    现在,我们已经可以将抽象父类用代码描述出来:

    // 定义抽象类:图形类
    public abstract class Figure{
        // 定义计算周长的抽象方法:getC()
        public abstract double getC();
        // 定义计算面积的抽象方法:getS()
        public abstract double getS();
        // 定义描述图形的非抽象方法:print()
        public void print(){
            System.out.println("这是一个图形");
        }
    }
    

    3. 天生的父类:抽象类

    现在我们已经有了一个抽象类,其中也定义了抽象方法,抽象类不能被直接实例化保证了抽象方法不会被直接调用到。回忆一下我们的出发点,费劲巴力的弄出个抽象类就是为了提取出两个类比较抽象的共同点,那么下一步自然是继承了。

    • 抽象类不能直接实例化,是天生的抽象类
    • 如果一个类继承了抽象类,那么必须重写父类中的抽象方法
    • 如果抽象类中定义了构造方法,可以被子类调用或在实例化子类对象时执行
    • 如果抽象类的子类依然是抽象类,可以不重写抽象方法,将重写操作留给下一级子类

    二、重写

    重写指的是子父类之间方法构成的关系,当子类继承父类时,父类中可能已经存在了某些方法,那么子类实例就可以直接进行调用。在有些时候由于子父类之间的差异,对于已经存在的方法想要做一些修改,这个时候我们可以利用重写,在子类中定义一个与父类中的方法完全相同的方法,包括返回值类型和方法签名(方法名 + 参数列表),此时就会构成重写。这样,子类实例在调用方法时就可以覆盖父类中的方法,具体的过程在后半部分阐述。

    1. 重写与重载的区别

    我们在刚开始接触方法的时候了解到了一个概念:重载,与重写有些类似,容易混淆,如果知识点已经模糊可以进传送门:Java程序的方法设计。总结一下,重写和重载有以下区别:

    • 重载是同一个类中方法与方法之间的关系
    • 重写是子父类间(接口与实现类间)方法与方法之间的关系
    • 构成重载:方法名相同,参数列表不同,返回值类型可以不同
    • 构成重写:方法名相同,参数列表相同,返回值类型相同或为对应类型的子类
    • 构成重载的方法之间权限修饰符可以不同
    • 重写方法的权限修饰符一定要大于被重写方法的权限修饰符

    有关于权限修饰符的作用如果不明确可以进传送门:Java面向对象编程三大特征 - 封装。明确了重写的含义之后,我们终于可以再度提笔,完成我们之前的例子:

    // 定义矩形类
    public class Rectangle extends Figure{
        // 定义构造器
        public Rectangle(double height, double width) {
    		this.height = height;
    		this.width = width;
    	}
        // 定义长和宽
        public double height;
        public double width;
    
        // 重写计算周长方法
        @Override
    	public double getC() {
    		return 2 * (this.height + this.width);
    	}
    
        // 重写计算面积方法
    	@Override
    	public double getS() {
    		return this.height + this.width;
    	}
    
        // 可选覆盖
        @Override
    	public void print(){
            System.out.println("矩形");
        }
    }
    
    // 定义圆形类
    public class Circle extends Figure{
        // 定义构造器
        public Circle(double radius) {
    		this.radius = radius;
    	}
    
        // 定义半径
    	public double radius;
    	
        // 重写计算周长方法
    	@Override
    	public double getC() {
    		return 2 * Math.PI * this.radius;
    	}
    
        // 重写计算面积方法
    	@Override
    	public double getS() {
    		return Math.PI * Math.pow(this.radius, 2);
    	}
    
        // 可选覆盖
        @Override
    	public void print(){
            System.out.println("圆形");
        }
    }
    

    2. 方法重写的规则

    • 重写的标识为@Override
    • 方法的重写发生在子类或者接口的实现类中
    • 被final声明的方法不能被重写
    • 被static声明的方法不能被重写,只能声明同结构的静态方法,但是此时不构成重写
    • 受限于权限修饰符,子类可能只能重写部分父类中的方法

    3. 父类方法的显式调用

    从上面的代码中可以看到,子类继承父类后,如果存在抽象方法则比如重写,由于父类中的方法是抽象的,所以无法调用。对于普通的方法,可以选择性的重写,一旦重写我们可以认为父类的方法被覆盖了,其实这样的形容是不准确的,在初学阶段可以认为是覆盖。
    比较规范的说法是:通过子类实例无法直接调用到父类中的同名方法了,但是在内存中依然存在着父类方法的结构,只不过访问不到而已。另外,我们同样可以在子类中显式的调用出父类方法,这要用到super关键字。

    • super指代父类对象
    • super可以调用可访问的父类成员变量
    • super可以调用可访问的父类成员方法
    • super可以调用可访问的父类构造方法
    • 不能使用super调用父类中的抽象方法
    • 可以使用super调用父类中的静态方法

    如果我们需要在子类中调用父类方法或构造器,可以将代码修改如下:

    // 定义抽象类:图形类
    public abstract class Figure{
        // 在抽象类中定义构造器,在子类实例创建时执行
        public Figure(){
            System.out.println("Figure init");
        }
        // 定义计算周长的抽象方法:getC()
        public abstract double getC();
        // 定义计算面积的抽象方法:getS()
        public abstract double getS();
        // 定义描述图形的非抽象方法:print()
        public void print(){
            System.out.println("这是一个图形");
        }
    }
    
    // 定义矩形类
    public class Rectangle extends Figure{
        // 定义构造器
        public Rectangle(double height, double width) {
            super();// 会调用默认的无参构造,代码可省略
            this.height = height;
            this.width = width;
    	}
        // 定义长和宽
        public double height;
        public double width;
    
        // 重写计算周长方法
        @Override
    	public double getC() {
    		return 2 * (this.height + this.width);
    	}
    
        // 重写计算面积方法
    	@Override
    	public double getS() {
    		return this.height + this.width;
    	}
    
        // 可选覆盖
        @Override
    	public void print(){
            super.print();// 调用父类方法
            System.out.println("矩形");
        }
    }
    
    // 定义圆形类
    public class Circle extends Figure{
        // 定义构造器
        public Circle(double radius) {
            super();// 会调用默认的无参构造,代码可省略
            this.radius = radius;
    	}
    
        // 定义半径
    	public double radius;
    	
        // 重写计算周长方法
    	@Override
    	public double getC() {
    		return 2 * Math.PI * this.radius;
    	}
    
        // 重写计算面积方法
    	@Override
    	public double getS() {
    		return Math.PI * Math.pow(this.radius, 2);
    	}
    
        // 可选覆盖
        @Override
    	public void print(){
            super.print();// 调用父类方法
            System.out.println("圆形");
        }
    }
    

    三、父类引用指向子类对象

    前面提到的概念消化完毕后,我们看一下子父类对象实例化的形式以及方法的执行效果。

    1. 父类引用指向父类对象

    如果父类是一个抽象类,则在等号右侧不能直接使用new加构造方法的方式实例化,如果一定要得到父类实例,就要使用匿名内部类的用法,这里不做讨论。
    如果父类是一个普通类,那么我们在初始化时,等号左侧为父类型引用,等号右侧为父类型对象(实例),这个时候其实和我们去创建一个类的对象并没有什么分别,不需要想着他是某某类的父类,因为此时他不会和任何子类产生关系,只是一个默认继承了Object类的普通类,正常使用就好,能调用出的内容也都是父类中已定义的。

    2. 子类引用指向子类对象

    在进行子类实例化时,由于在子类的定义中继承了父类,所以在创建子类对象时,会先一步创建父类对象。在进行调用时,根据权限修饰符,可以调用出子类及父类中可访问的属性和方法。

    public class Test{
        public static void main(String[] args){
            Rectangle rectangle = new Rectangle(5,10);
            // 调用Rectangle中定义的方法,以子类重写为准
            rectangle.print();
            System.out.println(rectangle.getC());// 得到矩形周长
            System.out.println(rectangle.getS());// 得到矩形面积
            Circle circle = new Circle(5);
            // 调用Circle中定义的方法,以子类重写为准
            circle.print();
            System.out.println(circle.getC());// 得到圆形周长
            System.out.println(circle.getS());// 得到圆形面积
        }
    }
    

    3. 引用与对象之间的关系

    在刚开始学习编程时,我们接触了基本数据类型,可以直接用关键字声明,定义变量赋值后使用,并不需要使用new关键字。对于引用与对象的关系可以先参考之前的文章回顾一下:Java中的基本操作单元 - 类和对象。在这里我们重点要说明的是:等号左侧的引用部分,与等号右侧的部分在程序运行层面有怎样的关联。
    与基本数据类型不同,在类中可以定义各种属性和方法,使用时也需要先创建对象。等号左侧的部分依然是一个类型的声明,未赋值时虽然默认情况下是null,但在程序编译运行时,也会在栈中进行存储,记录了相应的结构信息,他所指向的对象必须是一个和它兼容的类型。
    类的声明引用存放在栈中,实例化得到的对象存放在堆中

    • 在代码编写阶段,能够调用出的内容以等号左侧类型为准
    • 在程序运行阶段,具体的的执行效果以等号右侧实例为准

    下图为引用与实例在内存中的关系示意图,有关于Java对象在内存中的分布将在另外的文章中说明:

    4. 父类引用指向子类对象

    了解了引用与对象的关系之后,就有了一个疑问,如果等号左侧的声明类型与等号右侧的实例类型不一致会怎么样呢?如果我们要保证程序能够通过编译,并且顺利执行,必须要保证等号两边的类型是兼容的。完全不相关的两个类是不能够出现在等号左右两边的,即使可以使用强制类型转换通过编译,在运行时依然会抛出异常。
    于是我们就联想到了子父类是否有可能进行兼容呢?会有两种情况:子类引用指向父类对象,父类引用指向子类对象,下面我们来一一讨论。

    • 子类引用指向父类对象为什么无法使用

    子类引用指向父类对象指的是:等号左侧为子类型的声明定义,等号右侧为父类型的实例。首先,结论是这种用法是不存在的,我们从两方面来分析原因。
    第一个方面,是否符合逻辑?也就是是否会有某种需求,让Java语言为开发者提供这样一种用法?显然是否定的,我们定义子类的目的就是为了扩展父类的功能,结果现在我们却在用老旧的、功能贫乏的父类实例(等号右侧)去满足已经具备了强劲的、功能更为强大的子类声明(等号左侧)的需要,这显然是不合理的。
    另一方面,在程序运行时是否能够办到?如果我们真的写出了相关的代码,会要求我们添加强制转换的语句,否则无法通过编译,即使通过,在运行时也会提示无法进行类型转换。这就相当于把一个只能打电话发短信的老人机强制转换为能安装各种APP的智能机,这显然是办不到的。

    • 父类引用指向子类对象有什么样的意义

    父类引用指向子类对象指的是:等号左侧为父类型的定义,等号右侧为子类型的实例。这种情况是会被经常使用的,类似的还有:接口指向实现类。那么,这种用法应该如何解释,又为什么要有这样的用法呢?
    首先,我们先来理解一下这代表什么含义,假如:父类为图形,子类为矩形和圆形。这就好比我声明了一个图形对象,这个时候我们知道,可以调用出图形类中定义的方法,由于图形类是一个抽象类,是不能直接实例化的,我们只能用他的两个子类试试看。

    public class Test{
        public static void main(String[] args){
            // figure1指向Rectangle实例
            Figure figure1 = new Rectangle(5,10);
            System.out.println(figure1.getC());// 得到矩形周长
            System.out.println(figure1.getS());// 得到矩形面积
            // figure2指向Circle实例
            Figure figure2 = new Circle(5);
            System.out.println(figure2.getC());// 得到圆形周长
            System.out.println(figure2.getS());// 得到圆形面积
        }
    }
    

    从上面的结果来看,这好像和子类引用指向子类对象的执行效果没什么区别呀?但是需要注意此时使用的是父类的引用,区别就在于,如果我们在子类中定义了独有的内容,是调用不到的。在上面已经解释了运行效果以等号右侧的实例为准,所以结果与直接创建的子类实例相同并不难理解。
    重点要说明一下其中的含义:使用Figure(图形)声明,代表我现在只知道是一个图形,知道能执行哪些方法,如果再告知是一个矩形,那就能算出这个矩形的周长和面积;如果是一个圆形,那就能算出这个圆形的周长和面积。我们也可以这样去描述:这个图形是一个矩形或这个图形是一个圆形。
    如果从程序运行的角度去解释,我们已经知道,子类对象在实例化时会先实例化父类对象,并且,如果子类重写了父类的方法,父类的方法将会隐藏。如果我们用一个父类引用去指向一个子类对象,这就相当于对象实例很强大,但是我们只能启用部分的功能,但是有一个好处就是相同的指令,不同的子类对象都能够执行,并且会存在差异。这就相当于一部老人机,只具备打电话和发短信的功能,小米手机和魅族手机都属于升级扩展后的智能机,当然保有手机最基本的通讯功能,这样使用是没问题的。

    四、多态

    学习了上面的内容后,其实你已经掌握了多态的用法,现在我们来明确总结一下。

    1. 什么是多态

    多态指的是同一个父类,或同一个接口,发出了一个相同的指令(调用了同一个方法),由于具体执行的实例(子类对象或实现类对象)不同,而有不同的表现形态(执行效果)。
    就像上面例子中的图形一样,自身是一个抽象类,其中存在一些抽象方法,具体的执行可以由子类对象来完成。对于抽象类的抽象方法,由于子类必须进行重写,所以由子类去执行父类的抽象方法必然是多态的体现,对于其他的情况则未必构成多态,因此总结了以下三个必要条件。

    2. 多态的必要条件

    • 存在子父类继承关系
    • 子类重写父类的方法
    • 父类引用指向子类对象

    只有满足了这三个条件才能构成多态,这也就是文章前三点用这么长的篇幅来铺垫的原因。

    3. 多态的优点

    使用多态有多种好处,特别是一个抽象类有多个子类,或一个接口存在多个抽象类时,在进行参数传递时就会非常的灵活,在方法中只需要定义一个父类型作为声明,传入的参数可以是父类型本身,也可以是对应的任意子类型对象。于是,多态的优点可以总结如下:

    • 降低耦合:只需要与父类型产生关联即可
    • 可维护性(继承保证):只需要添加或修改某一子类型即可,不会影响其他类
    • 可扩展性(多态保证):使用子类,可以对已有功能进行快速扩展
    • 灵活性
    • 接口性

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

    在这里插入图片描述

    展开全文
  • 面向对象概念和基本特征

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

    1 ,什么是面向对象?

    正如每个人心中都有一个哈姆雷特一样,每个人都有对面向对象的不一样的理解

    所谓的面向对象就是基于对象概念,以对象为中心,以类和继承为构造机制,来认识、理解刻画客观世界和设计、构建相应的软件系统。

    OO (Object Oriented, 面向对象)是当前计算机界关心的重点,它是90年代软件开发的主流。面向对象的概念和应用已经超越了程序设计和软件开发,扩展到很宽的范围。如数据库系统、交互式界面、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。

    OO方法(Object-Oriented Method,面向对象方法)面向对象的方法是一种把面向对象的实现应用于软件开发过程中,指导开发活动的系统方法,简称OO(Object-Oriented)方法,Object Oriented是建立在“对象”概念基础上的方法学。对象是由数据和容许的操作组成的封装体,与客观实体有直接对应关系,一个对象类定义了具有相识性质的一组对象。而每继承性是具有层次关系的类的属性和操作进行共享的一种方式。所谓的面向对象就是基于对象概念,以对象为中心,以类和继承为构造机制,来认识、理解刻画客观世界和设计、构建相应的软件系统。

    (我会在介绍完js中对象的特征后面继续介绍面向对象方法,转的一句话说明什么事面向对象方法,让你对面向对象有一个更深入的了解!)

    2 ,JS中的面向对象的基本特征

    面向对象的三个基本特征是:封装、继承、多态。在js中面向对象只有封装和继承,没有多态。

    封装:通过将一个方法或者属性声明为私用的,可以让对象的实现细节对其他对象保密以降低对象之间的耦合程度,可以保持数据的完整性并对其修改方式加以约束这样可以使代码更可靠,更易于调试。封装是面向对象的设计的基石。

    JavaScript是一门面向对象的语言,可它并不具备将成员声明为公共或私用的任何内部机制,所以我们只能自己想办法实现这种特性。下面还是通过一台完整的代码去分析,介绍什么是私有属性和方法,什么是特权属性和方法,什么是公有属性和方法,什么是公有静态属性和方法。

    私有静态属性和方法: 函数有作用域,在函数内用var 关键字声明的变量在外部无法访问,私有属性和方法本质就是你希望在对象外部无法访问的变量。

    特权属性和方法:创建属性和方法时使用的this关键字,因为这些方法定义在构造器的作用域中,所以它们可以访问到私有属性和方法;只有那些需要直接访问私有成员的方法才应该被设计为特权方法。

    公有属性和方法:直接书写在原型prototype上的属性和方法,不可以访问构造器内的私有成员,可以访问特权成员,子类会继承所有的公有方法。

    公有静态属性和方法:最好理解的方式就是把它想象成一个命名空间,实际上相当于把构造器作为命名空间使用。

     /* -- 封装 -- */
        var _packaging =function(){
           //私有属性和方法
          var name ='Darren';
           var method1 =function(){
              //...
           }
           //特权属性和方法
           this.title ='JavaScript Design Patterns' ;
          this.getName =function(){
             return name;
          }
       }
       //公有静态属性和方法
       _packaging._name ='Darren code';
       _packaging.alertName =function(){
          alert(_packaging._name);
       }
       //公有属性和方法
       _packaging.prototype = {
          init:function(){
             //...
          }
       }

    继承:继承是指这样一种能力:他可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

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

    被继承的类称为“基类”、“父类”  或者  “超类”。

    ① 构造函数绑定

    这一种方法最简单,使用call 或者 apply 方法,将父对象的构造函数绑定在子对象上,既在子对象构造元素中加一行代码:

    父类构造函数名.apply(this, arguments);

    Person.prototype.getName = function(){
        returnthis.name;
    }
    function Programmer(name,sex) {
        Person.call(this,name);  //在这里添加
        this.sex = sex;
    }

    ② 原型式继承 让子类的prototype对象指向父类的实例,相当于完全删除了prototype对象的原来的值,但是我们需要在原型继承js语句后面加上段代码 : 子类. prototype.constructor = 子类  ,不然子类的原型对象的构造函数属性就会指向所继承的父类prototype上的constructor(直接继承父类的原型的时候),继承父类的实例的时候回丢失constructor属性,继承完成之后当前这个子类的实例也都可以继承父类的prototype上的方法

       //直接继承父类原型
        function Parent(name) {
     		this.name = name;
     	}
     	Parent.prototype.sayHello = function() {
     		console.log("hello");
     	}
     	function Son(name) {
     		this.name = name;
     	}
     	Son.prototype = Parent.prototype;
     	Son.prototype.constructor = Son;//不加这句话Son.prototype.constructor指向Parent
     	var s = new Son();
     	s.sayHello()//hello
    // 原型式继承改进 
     	function Parent(name) {
     		this.name = name;
     	}
     	Parent.prototype.sayHello = function() {
     		console.log("hello");
     	}
     	function Son(name, age) {
     		this.name = name;
     		this.age = age;
     	}
     	Son.prototype.sayHi = function() {
     		console.log("hi");
     	}
     	Son.prototype = new Parent(); //原型继承改进继承父类的实例
     	Son.prototype.constructor = Son;//不加这句话Son.prototype.constructor会丢失
     	var s = new Son();
     	var p = new Parent();
     	s.sayHello()//hello
     	console.log(Son.prototype)
     	p.sayHi()// Uncaught TypeError: p.sayHi is not a function,父类的实例不可以调用子类的方法了

    js中对象的组成

        方法——函数:过程、动态的
        属性——变量:状态、静态的

    下面列举一些别人描述面向对象的思想

    1 ,把一组数据结构和处理它们的方法组成对象(object),把相同的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization) / 泛化 (generalization),通过多态(polymorphism)实现基于对象类型的动态分派(dynamic dispatch)。

    2,一种尽量降低后续扩展代码引入 side-effect 可能的代码组织手段

    3,封装:把属性值、红蓝条、攻击、走位、放技能、清兵、游走等行为都塞在一个英雄里。

    ,继承:攻击+10 的装备可以升级到攻击+20,以后还可能升级到攻击+30 并带有吸血效果。不管升级成什么,都携带着攻击+10 这部分属性。

    多态:一个团队需要一个辅助,我们只需要一个辅助英雄,并不关心来的是哪个辅助英雄,能加血就行。

    具备这三种特性的编程思想,叫做面向对象。
     

    展开全文
  • java 面向对象特征(详解):

    千次阅读 2019-09-21 15:27:18
    首先阐述面向对象思想概述 概述 ...向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算 机事件的设计思想。 它区别于面向过程思想,强调的...

    首先阐述面向对象思想概述

    概述
    Java语言是一种面向对象的程序设计语言,而面向对象思想是一种程序设计思想,我们在面向对象思想的指引下,
    使用Java语言去设计、开发计算机程序。 这里的对象泛指现实中一切事物,每种事物都具备自己的属性和行为。面
    向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算
    机事件的设计思想。 它区别于面向过程思想,强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去
    操作实现

    什么是类?


    类:是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为 特征来描述该类事物。
        现实中,描述一类事物:
                 属性:就是该事物的状态信息。
                行为:就是该事物能够做什么。
               举例:小猫
                属性:名字、体重、年龄、颜色 行为:走、跑、叫


    什么是对象?


    对象:是一类事物的具体体现。对象是类的一个实例(对象并不是找个女朋友),必然具备该类事物的属性和行为.
            现实中,一类事物的一个实例:一只小猫
         举例:一只小猫
          属性:tom、5kg、2 years、yellow

          行为:溜墙根走、蹦跶的跑、喵喵叫


    类与对象的关系


    类是对一类事物的描述,是抽象的。
    对象是一类事物的实例,是具体的。
    类是对象的模板,对象是类的实体。

     

    面向对象的四大特征

    1) 继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继
    承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的
    重要手段。
      好处
          1. 提高代码的复用性。
          2. 类与类之间产生了关系,是多态的前提。
    继承的特点
       1. Java只支持单继承,不支持多继承。

       2. Java支持多层继承(继承体系)。

    class A{}
    class B extends A{}
    class C extends B{}

    顶层父类是Object类。所有的类默认继承Object,作为父类。
    3. 子类和父类是一种相对的概念。


    super和this解释

    父类空间优先于子类对象产生
    在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空
    间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构
    造方法调用时,一定先调用父类的构造方法。理解图解如下:

    super和this的含义
         super :代表父类的存储空间标识(可以理解为父亲的引用)。
         this :代表当前对象的引用(谁调用就代表谁)。
    super和this的用法
    1. 访问成员

    this.成员变量 ‐‐ 本类的
    super.成员变量 ‐‐ 父类的
    this.成员方法名() ‐‐ 本类的
    super.成员方法名() ‐‐ 父类的
    
    
    用法演示,代码如下:
    
    class Animal {
        public void eat() {
            System.out.println("animal : eat");
        }
    } 
    class Cat extends Animal {
        public void eat() {
            System.out.println("cat : eat");
       } 
        public void eatTest() {
            this.eat(); // this 调用本类的方法
            super.eat(); // super 调用父类的方法
        }
    }
    
    public class ExtendsDemo08 {
        public static void main(String[] args) {
            Animal a = new Animal();
            a.eat();
            Cat c = new Cat();
            c.eatTest();
        }
    } 
    
    /*
    输出结果为:
    animal : eat
    cat : eat
    animal : eat
    
    */
    
    /*
    
    2. 访问构造方法
    this(...) ‐‐ 本类的构造方法
    super(...) ‐‐ 父类的构造方法
    
    注意:子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。
    super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
    
    */
    
    
    

    2)封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象
    的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我
    们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程
    接口。
     原则
        将属性隐藏起来,若需要访问某个属性,提供公共方法对其访问。

    封装的操作步骤
    1. 使用 private 关键字来修饰成员变量。
    2.对需要访问的成员变量,提供对应的一对 getXxx 方法 、 setXxx 方法。

     

     


    3) 多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调
    用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。如果将对象的方法视为对象向外
    界提供的服务,那么运行时的多态性可以解释为:当 A 系统访问 B 系统提供的服务时, B 系统有多种提供服务的方式,
    但一切对 A 系统来说都是透明的。方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写
    (override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态需要做
    两件事: 1. 方法重写(子类继承父类并重写父类中已有的或抽象的方法); 2. 对象造型(用父类型引用引用子类型对
    象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。


    4) 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对
    象有哪些属性和行为,并不关注这些行为的细节是什么。

     

    展开全文
  • python中面向对象三大特征

    千次阅读 2018-09-13 20:21:05
    面向对象三大特征  1.封装:根据职责将属性和方法封装到一个抽象的类中  2.继承:实现代码的重用,相同的代码不需要重复的写  3.多态:以封装和继承为前提,不同的子类对象调用相同的方法,产生不同的执行结果 一...
  • C++:面向对象的基本特征

    千次阅读 2016-05-10 17:19:36
    具体而言,有三个基本特征:封装、继承和多态。封装:将客观事物抽象成类,每个类对自身的数据和方法实行保护。类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。C++中类是一种封装手段...
  • 面向对象特征有哪些方面

    千次阅读 2014-02-21 14:05:24
    1、抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而...对象的一个新类可以从现有的类中派生,这个过程称为继承。新类继承了原始类的特性
  • Java中面向对象三大特征总结

    万次阅读 多人点赞 2017-07-09 14:13:36
     面向对象具有继承性(Inheritance)  面向对象具有多态性(Polymorphism)  面向对象具有封装性(Encapsulation) 一、继承  多个类具有共同的属性(成员变量)与行为(成员方法)的时候,将这些共同的部分...
  • 面向对象特征

    千次阅读 2014-06-08 00:52:44
    面向对象特征 关键字:封装,继承,多态,抽象 1. 封装 ² 实现细节隐藏的过程称为封装。 ² 优点:1)隐藏细节,具备一定的安全性  2)代码重用 ²  访问修饰符: 1) public:访问权限最高 2) protected...
  •  面向对象的编程方法具有四个基本特征: 1.抽象:  抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分...
  • Java的面向对象思想及特征

    千次阅读 2017-07-09 19:00:18
    总结一下java面向对象的思想及相关基础知识
  • Java基础(四)——面向对象的四大特征 一、封装: 什么是封装? 通俗点即隐藏信息 提供使用接口给别人使用,不让别人直接操作属性或方法 封装有什么好处呢? 1.使代码简洁 2.提交程序的可维护性 3.解耦合 提高...
  • 面向对象三个基本特征

    千次阅读 热门讨论 2014-07-25 15:26:45
    定义:每个对象都包含它能进行操作需要的所有信息,这个特性称为封装。 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。 例如:房子就...
  • OOP(面向对象)的四个基本特征

    千次阅读 2018-05-28 10:30:00
     面向对象的编程方法具有四个基本特征:1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部...
  • 面向对象的程序设计特征

    千次阅读 2016-08-12 14:28:22
    面向对象程序设计
  • 面向对象特征有哪些方面?

    千次阅读 2018-04-12 15:44:54
    答:面向对象特征主要有以下几个方面:- 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。- 继承:继承是...
  • 面向对象主要有四大特性· 抽象o 忽略一个主题中与当前目标无关的东西,专注的注意与当前目标有关的方面.(就是把现实世界中的某一类东西,提取出来,用程序代码表示,...表示世界中一类事物的特征,就是对象的属性.比...
  • 1、面向对象特征有哪些方面? 答:面向对象特征主要有以下几个方面: 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些...
  • Java基础:面向对象三大特征、五大原则

    万次阅读 多人点赞 2015-06-22 13:57:20
    面向对象三大特征: (1)封装(Encapsulation) 所谓封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。封装是面向对象特征之一,是对象和类...
  • Java面向对象的四个特征

    千次阅读 2017-02-09 14:42:52
    Java面向对象的四个特征:抽象、继承、封装、多态性 抽象:忽略主题全部不打算把全部事件描述下来,只是抽取主要部分抽象化描述,可以理解抽象是一个接口类或一个抽象类!  比如:描述一个抽象功能,用接口表示,...
  • 面向对象编程的三大特征简述

    千次阅读 2018-01-04 20:38:15
    对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继 承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以...
  • Java面向对象特征

    千次阅读 2012-03-13 13:37:54
    一、面向对象特征有哪些方面  1.抽象:  抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。...
  • Java:面向对象编程的三大特征详述

    千次阅读 2018-02-23 21:06:06
    Java:面向对象编程的三大特征详述 面向对象编程OOP(Object Oriented Programming)三大特征含义、体现、比喻及作用详述如下: 一、说明 对象(Object):万物皆对象对象是一种实体,包含一定的数据结构和状态...
  • Java面向对象的三大特征——多态

    千次阅读 2013-12-17 12:00:13
    Java面向对象三大特征——多态 一、引言  Java面向对象的三大特征:继承、封装、多态。其中继承和封装我们平时用的做广泛,但是多态用到的却不多,这是因为多态是一种建立在不同设计模式基础之上的特征。要使用多态...
  • 面向对象的三个基本特征

    万次阅读 2018-09-05 17:11:31
    面向对象的三个基本特征是:封装、继承、多态。 封装 封装最好理解了。封装是面向对象特征之一,是对象和类概念的主要特性。 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的...
  • 面向对象语言的三大特征

    万次阅读 2018-11-06 21:02:45
    面向对象语言的三大特征是:封装 继承 多态 最近感觉,总结一下这个问题还是挺有必要的,所以转发了此篇文章的部分段落。   封装   封装是面向对象特征之一,是对象和类概念的主要特性。封装,也就是把客观...
  • 面向对象的三大特征——(面试题)

    千次阅读 多人点赞 2020-12-09 11:03:03
    面向对象的三大特征 一、封装: 将同一类事物的特征和功能包装在一起,只对外暴露需要调用的接口而已。想让你看到的你就看得到,不想让你看到的就见不到。对于封装也称为信息的隐藏,体现的最淋漓尽致的地方就是来...
  • java面向对象的几个基本特征

    千次阅读 2017-02-06 22:09:09
    一个类可以是多个一般类的特殊类,它从多个一般类中继承了属性与服务,这称为多继承。例如,客轮是轮船和客运工具的特殊类。在java语言中,通常我们称一般类为父类(superclass,超类),特殊类为子类(subclass)。但...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 537,224
精华内容 214,889
关键字:

对象所具有的特征称为