• Java之面向对象(8)


    前言(面向对象定义)

      面向对象是在面向过程之后出现的。当事件比较简单的时候,利用面向过程,注重的是事件的具体的步骤/过程,注重的是过程中的具体的行为,以函数为最小单位,考虑怎么做。

      而面向对象注重找“参与者”,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做,然后通过这个对象来使用这个对象所具有的方法属性等(比如:人(类或对象)有眼睛鼻子(属性),也可以吃饭(方法).所以我们说面向对象有一句通俗的话叫做万物皆对象)。

      下面我们就来详细介绍一下面向对象的概念和使用。

    1 面向对象基本使用和相关关键字

    1.1 类和对象的概念和使用

    的定义和代码结构

    1. 类是对一个实体(也就是对象)向上抽取出抽象的部分(公共的部分),这样的抽象实体就是类。类是抽象的,是一个模板。
    2. 具体来说类里面有两种特性,即属性方法。属性是类的静态特征,方法是类的动态特征。

    注意:第一次使用类的时候,会进行类的加载,初始化创建对象的时候,对象的属性没有给赋值,会有默认的初始化的值。而第二次使用这个类就不会进行类加载了

    完整代码结构:

    //类的定义
    [修饰符] class 类名{
    	//属性的定义
    	[修饰符]  属性类型  属性名 = [默认值] ;
    	//方法的定义
    	[修饰符]  方法返回值类型  方法名(形参列表) {
            // n条语句
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    对象的定义和代码结构

    1. 对象是一个类的具体实例。(EG:定义一个人的类,类里面有吃饭的方法。而对象是张三(一个具体的人),张三是站着吃饭的(具体的吃饭的方法)。)
    2. 创建一个对象需要有类的存在,通过类就可以创建一个对象。用new关键字来实现。

    完整代码结构是:

    [修饰符] 类名	类对象名 = new 类名(实参列表);
    
    • 1

    使用类和对象的实例
      1 创建一个类,抽象出人这个实体。
      2 用这个类来构造一个对象,并且使用这个对象的方法。

    /**
     * 创建类:人类
     */
    public class Person {
        //名词---》属性---》成员变量---》放在类中方法外(注意:我们只把有需要的内容写到代码里,不相关的东西不要放在代码中)
        int age ;//年龄
        String name;//姓名
        double height;//身高
        double weight;//体重
        //动词---》方法
        //吃饭
        public void eat(){
            int num = 10;//局部变量:放在方法中
            System.out.println("我喜欢吃饭");
        }
        //睡觉:
        public void sleep(String address){
            System.out.println("我在"+address+"睡觉");
        }
        //自我介绍:
        public String introduce(){
            return "我的名字是:"+name+",我的年龄是:"+age+",我的身高是:"+height+",我的体重是:"+weight;
        }
    }
    
    /**
     * 测试类,使用刚才创建的Person类
     */
    public class Test {
        //这是一个main方法,是程序的入口:
        public static void main(String[] args) {
            //创建一个人类的具体的对象/实例:
            //创建一个对象,对象的名字叫:zhangsan
            //Person 属于 引用数据类型
            //第一次加载类的时候,会进行类的加载,初始化创建对象的时候,对象的属性没有给赋值,有默认的初始化的值。
            Person zs= new Person();
            zs.name = "张三";
            zs.age = 19;
            zs.height = 180.4;
            zs.weight = 170.4;
            //再创建一个对象:名字叫:lisi
            //再次创建类的时候,就不会进行类的加载了,类的加载只在第一次需要的时候加载一次
            Person ls = new Person();
            ls.name = "李四";
            ls.age = 18;
            ls.height = 170.6;
            ls.weight = 160.5;
            //对属性值进行读取:
            System.out.println(zs.name); // 张三
            System.out.println(ls.age); // 18
            //对方法进行操作:
            //不同的对象,属性有自己的特有的值,但是方法都是调用类中通用的方法。
            //属性:各个对象的属性是独立的,
            //方法:各个对象的方法是共享的。
            zs.eat(); // 我喜欢吃饭
            ls.eat(); // 我喜欢吃饭
            zs.sleep("教室");  // 我在教室睡觉
            /*String str = zs.introduce();
            System.out.println(str);*/
            System.out.println(zs.introduce()); // 我的名字是:张三,我的年龄是:19,我的身高是:180.4,我的体重是:170.4
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    1.2 局部变量和成员变量的区别

    • 区别1:代码中位置不同
        局部变量:方法中定义的变量
        成员变量:类中(方法外)定义的变量

    • 区别2:代码的作用范围不同
        局部变量:当前的方法内
        成员变量:作用于当前类的所有方法中

    • 区别3:变量是否有默认值
        局部变量:无
        成员变量:有,↓为成员变量的默认值

    • 区别4:变量是否要初始化
      局部变量:一定需要,不然使用的时候会报错
      成员变量:不需要,可以使用的时候对它进行赋值

    • 区别5:内存中的位置不同
      局部变量:栈内存
      成员变量:堆内存

    • 区别6:作用时间不同
      局部变量:当前方法从开始到执行结束
      成员变量:当前类的对象从创建到销毁的整个过程

    1.3 构造器

      在创建对象的过程中,是通过类内的一个特殊方法(构造器)来实现创建的。

    具体过程见例子:

    class Test {
        public static void main(String[] args) {
            //创建一个Person类的具体的对象/实例/实体:
            /*
                创建对象的过程:
                    1.第一次遇到Person的时候,进行类的加载(只加载一次)
                    2.创建对象,为这个对象在堆中开辟空间
                    3.为对象进行属性的初始化动作
                        new关键字实际上是在调用一个方法,这个方法叫构造方法(构造器)
                        调用构造器的时候,如果你的类中没有写构造器,那么系统会默认给你分配一个构造器,只是我们看不到罢了。
                        可以自己显式 的将构造器编写出来:
                            构造器的格式:
                                [修饰符] 构造器的名字(){
                                }
                                
                构造器和方法的区别:
                    1.没有方法的返回值类型
                    2.方法体内部不能有return语句
                    3.构造器的名字很特殊,必须跟类名一样
                    
                构造器的作用:不是为了创建对象,因为在调用构造器之前,这个对象就已经创建好了,并且属性有默认的初始化的值。
                
                调用构造器的目的:给属性进行赋值操作的。
                
                注意:我们一般不会在空构造器中进行初始化操作,因为那样的话每个对象的属性就一样了。
                实际上,我们只要保证空构造器的存在就可以了,里面的东西不用写
             */
            Person p = new Person();
            System.out.println(p.age);
            System.out.println(p.name);
            System.out.println(p.height);
            Person p2 = new Person();
            System.out.println(p2.age);
            System.out.println(p2.name);
            System.out.println(p2.height);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

      构造器是一个特殊方法,所以也可以重载。但是当你显示的写了一个类的重载构造器之后,默认的空参构造器就需要显示写出来,不然会报错。

      想要指向类对象中的属性时,使用this关键字。this代表你创建的那个对象

    public class Person {
        //属性:
        String name;
        int age;
        double height;
        //空构造器
        public Person(){
        }
        public Person(String name,int age,double height){
            //当形参名字和属性名字重名的时候,会出现就近原则:
            //在要表示对象的属性前加上this.来修饰 ,因为this代表的就是你创建的那个对象
            this.name = name;
            this.age = age;
            this.height = height;
        }
        public Person(String a,int b){
            name = a;
            age = b;
        }
        //方法:
        public void eat(){
            System.out.println("我喜欢吃饭");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    1.4 简单代码的内存分析

    public class Person {
            int id;
            int age;
            String school;
            public Person (int a,int b,String c){
                    id=a;
                    age=b;
                    school=c;
            }
            public static void main(String args[]){
                    Person p= new Person(1,20, "");
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

      点击运行时从main函数开始的:

    (1)先开辟三个空间:,和方法区(方法区内有一个地方为常量池)。
    (2)在栈中开辟一个空间main方法。
    (3)首先看到new Person,在方法区中加载这个Person字节码文件(只有第一次会加载),然后在中开辟一个空间来创建一个Person对象(这个对象有一个地址)。这个对象也会反过来指向它的字节码文件,用来指定自己是依靠谁来创建的。
    (4)创建好了对象之后,new Person(1,20,"海淀")就去执行它的构造方法。会在栈中开辟一个空间Person构造器方法。
    (5)在栈实参传入构造器方法的过程中。
      如果是基本数据类型,那么直接传的就是这个值本身(值传递)。
      如果是引用数据类型,比如String,那么会先在常量池中开辟一个空间来放置"海淀"这个字符串,然后把地址传给c(地址传递)。
    (6)构造器执行完毕后,就会在栈中销毁,然后转入main方法。
    (7)Person p = 。。。 之后把这个对象的地址赋值给p。(之后就可以通过p来操作这个对象)

    整个程序运行的内存图解

    1.5 this,static,代码块

    this指的是当前对象

      当属性名字和形式参数发生重名的时候,或者属性名字和局部变量的名字重名的时候,都会发生就近原则,所以我们如果要直接使用属性的话,前面就要用this修饰这个变量。(如果没有重名就无所谓使用了)

    this关键字使用的图解

    static关键字特点

    1. static可以修饰:属性,方法,代码块,内部类(没有类)
    2. 在方法区内有一个区域为静态域,用于存放static修饰的东西
    3. 用它修饰会在加载某类字节码文件同时加载到静态域内。先于对象存在
    4. 应用场景:某些特定的数据想要在内存中共享时就使用static来修饰即可

    代码实例:

    /**
     * static的使用
     */
    public class Demo1 {
        int id;
        static int sid;
        public void a(){
            System.out.println(id);
            System.out.println(sid);
            System.out.println("------a");
        }
        // 1.static和public都是修饰符,并列的没有先后顺序,先写谁后写谁都行
        static public void b(){
            //System.out.println(id); // 2.在静态方法中不能访问非静态的属性
            //a(); // 3.在静态方法中不能访问非静态的方法
            //System.out.println(this.id); // 4.在静态方法中不能使用this关键字
            System.out.println(sid);
            System.out.println("------b");
        }
        //这是一个main方法,是程序的入口:
        public static void main(String[] args) {
            //5.非静态的方法可以用对象名.方法名去调用
            Demo1 d = new Demo1();
            d.a();
            //6.静态的方法可以用   对象名.方法名去调用  也可以用  类名.方法名 (推荐)
            Demo1.b();
            d.b();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    代码块的特点

    1. 类的组成是:属性,方法,构造器,代码块和内部类
    2. 代码块的分类是:普通块,构造块,构造器块,静态块,同步块(多线程)
    3. 代码块的执行顺序是:
        1.最先执行静态块,而且只在类加载的时候执行一次。(一般用于执行一些全局性的初始化操作)
        2.然后执行构造块
        3.再执行构造器方法块
        4.最后执行方法中的普通块
    4. 代码块详细:
        普通块:就是普通方法内的内容
        构造块:在类中单独一个大括号内部的内容
        构造器块:特殊用于构造类对象的方法
        静态块:在类中用一个static{ }结构包裹的内容

    代码实例:

    /**
     * 代码块的执行顺序
     */
    public class Demo1 {
        //属性
        int a;
        static int sa;
        //方法
        public void a(){
            System.out.println("我是普通方法a");
            {
                //普通块限制了局部变量的作用范围
                System.out.println("我是普通方法里面的普通块");
            }
        }
        public static void b(){
            System.out.println("我是静态方法b");
        }
        //构造块
        {
            System.out.println("我是类下的构造块,{}");
        }
        //静态块
        static{
            System.out.println("我是类下的静态块,static{}");
            //在静态块中只能方法:静态属性,静态方法
            System.out.println("我是类下的静态块,只能调用静态属性"+sa);
        }
        //构造器
        public Demo1(){
            System.out.println("我是类下的空构造器");
        }
        public Demo1(int a){
            this.a = a;
        }
        //这是一个main方法,是程序的入口:
        public static void main(String[] args) {
            Demo1 t = new Demo1();
            t.a();
            System.out.println("------------------第二次---------------");
            Demo1 t2 = new Demo1();
            t2.a();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    1.6 包,import

    包的定义和特点

    1. 包的作用:就是为了分类,然后也为了解决重名问题权限问题其实就相当于电脑盘符的目录)
    2. 包的命名
      (1)名字全部小写
      (2)中间用 . 隔开
      (3)一般都是公司域名倒着写 : com.jd com.uestc
      (4)加上模块名字:com.jd.login com.jd.register
      (5)不能使用系统中的关键字:nul con com1com9
      (6)包声明的位置一般都在非注释性代码的第一行:
    3. 想使用同一个包下的类不需要导包,可以直接使用。使用不同包下的类要需要导包: import **.**.**; 例如:import java.util.Date;
    4. java.lang包下的类,可以直接使用无需导包。
    System.out.println(Math.random());
    
    • 1
    1. IDEA中导包快捷键:alt+enter
    2. import 。。。.*代表所有
    import java.util.*
    
    • 1
    1. 导包中的目录没有包含和被包含的关系
    import com.jayden.*  //只代表jayden这一层的类,如果里面有还有包,那么这个包并没有加入
    import com.jayden.sub.D //因为如果jayden下如果分别有两个包,两个包都有sub方法就会导致歧义
    
    • 1
    • 2
    1. 包可以静态引用
    //静态导入:
    import static java.lang.Math.*;
    //导入:java.lang下的Math类中的所有静态的内容
    public class Test {
        public static void main(String[] args) {
            System.out.println(random());
            System.out.println(PI);
      		System.out.println(round(5.6));
        }
        //在静态导入后,同一个类中有相同的方法的时候,会优先走自己定义的方法。
        public static int round(double a){
            return 1000;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2 三大特性

      面向对象的三大特性是指:封装,继承,多态

    2.1 封装(Encapsulation)

    封装的定义:封装就是具体的内部不给你看,只提供有限的接口供你使用的一种设计思想。

    封装的特点

    1. 设计程序是要求“高内聚,低耦合”。
        高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
        低耦合:仅对外暴露少量的方法用于使用。
        隐藏对象内部的复杂性,只对外提供有限的接口。方便外界调用,从而提高系统的安全性、可扩展性、可维护性。通俗来说就是该隐藏的隐藏,该暴露的暴露。
    2. 代码中通过private权限修饰符来限制
    3. 权限修饰符

      一般属性用private修饰,方法用public修饰

    代码实例:

    /**
     * 用private封装属性
     */
    public class Girl {
    
        //属性: private:私有化,外界不可以直接访问
        private int age;
    
        //读取年龄: public:外界可以直接访问
        public int getAge(){
            return age;
        }
        //设置年龄:
        public void setAge(int age){
            if(age >= 30 ){
                this.age = 18;
            }else{
                this.age = age;
            }
        }
    }
    
    public class Demo1 {
        public static void main(String[] args) {
            //创建一个Girl类的对象:
            Girl g = new Girl();
            /*g.age = 33;
            System.out.println(g.age);*/
            //设置年龄:
            g.setAge(31);
            //读取年龄:
            System.out.println(g.getAge());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

      上面的代码,对于属性age来说,我加了修饰符private,这样外界对它的访问就受到了限制,现在我还想加上其他的限制条件,但是在属性本身上没有办法再加了,所以我们通过定义方法来进行限制条件的添加。
      以属性为案例:
    进行封装:
    (1)将属性私有化,被private修饰(加入权限修饰符)
    一旦加入了权限修饰符,其他人就不可以随意的获取这个属性
    (2)提供public修饰的方法让别人来访问/使用
    (3)即使外界可以通过方法来访问属性了,但是也不能随意访问,因为咱们在方法中可以加入限制条件。

    2.2 继承(Inheritance)

    继承的定义:继承就是对类的进一步抽象,多个类可以能有相同的属性和方法,这个时候就可以对这几个类抽取出所谓的父类。

    EG: 
    **学生类**Student:
    	属性:姓名,年龄,身高,学生编号
    	方法:吃饭,睡觉,喊叫,学习
    **教师类**Teacher:
    	属性:姓名,年龄,身高,教师编号
    	方法:吃饭,睡觉,喊叫,教学
    共同的东西:**人类**Person
    	属性:姓名,年龄,身高
    	方法:吃饭,睡觉,喊叫
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

      学生和老师都是人,如果还有100种职业的话,比如都有属性姓名属性和吃饭的方法,那么每一个类中都要重新写一遍就太麻烦了,所以定义它们的父类Person,只要用字段entend关键字继承Person,就会获得Person的所有共有属性和方法。

    继承的特点

    1. 继承的好处:提高了代码的复用性可扩展性。类定义的内容,子类可以直接拿过来用就可以了,不用代码上反复重复定义了。也是多态的前提。
    2. 继承只能有一个直接父类
    //报错,只能有一个直接父类
    public class Student extends Person,Boss{
    }
    
    • 1
    • 2
    • 3
    1. 继承具有传递性:EG:Student继承自Person,Person继承自Object(所有的类都直接或者间接的继承自Object)

    内存分析:在new一个子类对象的时候,会先加载这个子类所有父类的字节码,然后再加载这个子类的字节码。

    注意:父类private修饰的内容,子类实际上也继承,只是因为封装的特性阻碍了直接调用,但是提供了间接调用的方式,可以间接调用。

    方法重写的定义和特点:

    1. 发生在子类和父类中,当子类对父类提供的方法不满意的时候,要对父类的方法进行重写。
    2. 子类的方法名字和父类必须一致,参数列表(个数,类型,顺序)也要和父类一致。

    代码实例:

    /**
     * 继承中的重写
     */
    class Person {
        public void eat(){
            System.out.println("吃食物");
        }
    }
    class Student extends Person {
        @Override
        public void eat(){
            System.out.println("我喜欢吃小龙虾喝啤酒。。");
        }
    }
    public class Demo1 {
        public static void main(String[] args) {
            //创建一个Student类的对象:
            Student s = new Student();
            //打印的是子类Student中重写的的eat方法,而不是父类Person的
            s.eat(); // 我喜欢吃小龙虾喝啤酒。。
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    重载和重写的区别:重载(Overload)是在同一个类中方法名相同,单参数列表不同的多个方法构成的结构。而重写(Override)是在不同类中,方法名和参数名都必须相同注意:重写中父类的修饰符权限一定要低于子类的,返回值类型要大于子类的)

    super关键字的定义和特点

    1. 在子类的方法中,可以通过 super.属性 super.方法 的方式,显示的去调用父类提供的属性,方法。在通常情况下,super.可以省略不写。
    2. 在特殊情况下,当子类和父类的属性和方法重名时,你要想使用父类的属性,必须加上修饰符super.,只能通过super.属性来调用。
    3. 其实我们平时写的构造器的第一行都有(super();)代码。作用是调用父类的空构造器(只是我们一般都省略不写)。如果构造器中已经显示的调用super父类构造器,那么它的第一行就没有默认分配的(super();)了
    4. 在构造器中,super调用父类构造器和this调用自身构造器只能存在一个。(因为super和this修饰构造器都要放在第一行)
    public Student(int age,String name,double score){
    	super(age,name);
    	this(score);//调用子类的public Student(double score)构造器报错
    }
    
    • 1
    • 2
    • 3
    • 4

    2.3 多态(Polymorphism)

    多态的定义:多态就是对同一个方法的不同实现。(继承是多态的前提)

    多态的特点

    1. 多态只和方法有关,和属性无关。
    2. 多态的好处:提高了代码的可扩展性,符合面向对象的设计原则:开闭原则(可以扩展,但是原来存在的不准修改)

    代码实例:多态的应用

    class Animal {//父类:动物:
        public void shout(){
            System.out.println("我是小动物,我可以叫。。。");
        }
    }
    class Cat extends Animal{//子类1:猫
        //喊叫方法:
        public void shout(){
            System.out.println("我是小猫,可以喵喵叫");
        }
    }
    class Dog extends Animal{//子类2:狗
        int age;
        int weight;
        //喊叫:
        public void shout(){
            System.out.println("我是小狗,我可以汪汪叫");
        }
        public void eat(){
            System.out.println("我是小狗,我爱吃东西。。");
        }
    }
    class Girl { //多态应用1:原本要写两个方法现在只需要写一个
        //跟猫玩耍:
        /*public void play(Cat cat){
            cat.shout();
        }*/
        //跟狗玩耍:
        /*public void play(Dog dog){
            dog.shout();
        }*/
        //跟小动物玩耍:
        public void play(Animal an){
            an.shout();
        }
    }
    public class Demo1 {
        public static void main(String[] args) {
            //eg1:转化为父类根据对象使用对应的方法
            Cat cat = new Cat();
            Dog dog = new Dog();
            //Animal an = ?
            Animal animal = dog;//转型:向上转型(Dog转成Animal)
            //多态应用2:下面代码就不用变了
            animal.shout();
            //注意an只能用Animal中存在的方法,虽然它的对象是Dog,但在编译期的时候因为Animal没有eat属性所以会报错
    //        an.eat();
            
            //eg2:转化为子类后可以用子类的方法
            Dog dog2 = (Dog)animal ;//转型:向下转型(Animal转为Dog)
            dog2.eat();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    2.4 简单工厂模型(多态应用)

      多态的应用中,不仅可以使用父类做方法的形参,还可以使用父类做方法的返回值类型,真实返回的对象可以是该类的任意一个子类对象。
      这种方法是简单工厂模式的实现,它是解决大量对象创建问题的一个解决方案。将创建和使用分开,工厂负责创建,使用者直接调用即可。简单工厂模式的基本要求是
    (1)定义一个static方法,通过类名直接调用
    (2)返回值类型是父类类型,返回的可以是其任意子类类型
    (3)传入一个字符串类型的参数,工厂根据参数创建对应的子类产品
    代码实例:

    /**
     * 多态应用2,返回值抽象成父类
     */
    class PetStore {//宠物店 ---》工厂类
        //方法:提供动物
        public static Animal getAnimal(String petName){//多态的应用场合(二)
            Animal an = null;
            if("猫".equals(petName)){//petName.equals("猫") --》这样写容易发生空指针异常
                an = new Cat();
            }
            if("狗".equals(petName)){
                an = new Dog();
            }
            return an;
        }
    }
    class Demo2 {
        public static void main(String[] args) {
            Girl g = new Girl();
            //Cat c = new Cat();
            //Dog d = new Dog();
            Animal an = PetStore.getAnimal("狗");
            g.play(an); // 我是小狗,我可以汪汪叫
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    曲线的凹凸性与拐点【高数笔记】
    自定义类型 struct/class(类、对象与成员变量)
    uniapp webview 修改ua(UserAgent、user-agent)
    Tensorflow.keras 常用方法总结
    Typora+MinIO+Python代码打造舒适协作环境
    10.5号作业
    vb.net picturebox中的内容保存到文件
    酷早报:9月6日Web3加密行业新闻大汇总
    【MindSpore 入门教程】01 张量Tensor
    Javascript知识【jQuery-基本操作】上篇
  • 原文地址:https://blog.csdn.net/qq_34516746/article/details/126018720