你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

JavaSE——面向对象

2021/12/25 4:06:02

面向对象概述

面向对象和面向过程的区别

面向过程:
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展
面向对象:
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低
面向过程是具体化的,流程化的,解决一个问题,你需要一步一步的分析,一步
一步的实现。
① OOA:面向对象分析(Object-Oriented Analysis)
② OOD:面向对象设计(Object-Oriented Design)
③ OOP:面向对象编程(Object-Oriented Programming)
面向对象是模型化的,你只需抽象出一个类,这是一个封闭的盒子,在这里你拥有数据也拥有解决问题的方法。需要什么功能直接使用就可以了,不必去一步一步的实现,至于这个功能是如何实现的,管我们什么事?我们会用就可以了。
面向对象的底层其实还是面向过程,把面向过程抽象成类,然后封装,方便我们使用的就是面向对象了。

面向对象三大特性

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

  • 封装 : 封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如
    果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没 有提供给外界访问的方法,那么这个类也没有什么意义了。
  • 继承 : 继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新
    的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用 继承我们能够非常方便地复用以前的代码。
    关于继承如下 3 点请记住:
    1. 子类拥有父类非 private 的属性和方法。
    2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
    3. 子类可以用自己的方式实现父类的方法。
  • 多态 : 所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出
    的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到
    底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的 方法,必须在由程序运行期间才能决定。
    在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口 (实现接口并覆盖接口中同一方法)。
    其中Java 面向对象编程三大特性:封装 继承 多态
    **封装:**隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便 于使用,提高复用性和安全性。
    **继承:**继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以 增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通 过使用继承可以提高代码复用性。继承是多态的前提。
    关于继承如下 3 点请记住:
    1. 子类拥有父类非 private 的属性和方法。
    2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
    3. 子类可以用自己的方式实现父类的方法。
      **多态性:**父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。提 高了程序的拓展性。
      在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口 (实现接口并覆盖接口中同一方法)。
      方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重 写(override)实现的是运行时的多态性(也称为后绑定)。
      一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是 哪个类中实现的方法,必须在由程序运行期间才能决定。运行时的多态是面向对 象精髓的东西,要实现多态需要做两件事:
  • 方法重写(子类继承父类并重写父类中已有的或抽象的方法);
  • 对象造型(用父类型引用子类型对象,这样同样的引用调用同样的方法就会根据 子类对象的不同而表现出不同的行为)。

什么是多态机制?Java语言是如何实现多态的?

所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出 的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒 底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的 方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这 样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而 导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时 所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。 多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的 重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不 同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来 实现的,也就是我们所说的多态性。
多态的实现
Java实现多态有三个必要条件:继承、重写、向上转型
**继承:**在多态中必须存在有继承关系的子类和父类。
**重写:**子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的 方法。
**向上转型:**在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具 备技能调用父类的方法和子类的方法。
只有满足了上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现 代码处理不同的对象,从而达到执行不同的行为。
对于Java而言,它多态的实现机制遵循一个原则:当超类对象引用变量引用子类 对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但 是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。

面向对象五大基本原则

单一职责原则SRP(Single Responsibility Principle)类的功能要单一,不能包罗万象,跟杂货铺似的。
开放封闭原则OCP(Open-Close Principle)
一个模块对于拓展是开放的,对于修改是封闭的,想要增加功能热烈欢迎,想要修改,哼,
一万个不乐意。
里式替换原则LSP(the Liskov Substitution Principle LSP)子类可以替换父类出现在父类能够出现的任何地方。比如你能代表你爸去你姥姥家干活。哈哈~~
依赖倒置原则DIP(the Dependency Inversion Principle DIP)高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。就是你出国要说你是中国人,而不能说你是哪个村子的。比如说中国人是抽象的,下面有具体的xx省,xx市,xx县。你要依赖的抽象是中国人,而不是你是xx村的。
接口分离原则ISP(the Interface Segregation Principle ISP)
设计时采用多个与特定客户类有关的接口比采用一个通用的接口要好。就比如一个手机拥有
打电话,看视频,玩游戏等功能,把这几个功能拆分成不同的接口,比在一个接口里要好的
多。

对象的创建和使用

对象和引用
堆栈图

案例1:

public class User{
    // 用户id
    // 访问id不能这样:User.id (这是错误的,实例变量不能用类名访问。)
    // id的访问必须先造对象,然后对象有了,才能访问对象的id
    int id; //成员变量,实例变量(对象变量,一个对象一份。)
    // 用户名
    String username; // 成员变量可以不手动赋值,系统赋默认值。
    // 密码
    String password;
}

// 第一步:类加载
// 第二步:调用UserTest类的main方法(方法调用要压栈。)
public class UserTest{

    // 方法体外声明的变量叫做成员变量。
    //User u1; //成员变量。(实例变量)

    public static void main(String[] args){
        //int i = 100;

        // 方法体当中声明的变量叫做局部变量
        User u1 = new User();
        // 实例变量怎么访问(属性怎么访问)?
        // 语法是:“引用.属性名”
        System.out.println(u1.id); //0
        System.out.println(u1.username); //null
        System.out.println(u1.password); //null

        u1.id = 11111;
        u1.username = "zhangsan";
        u1.password = "123";

        System.out.println(u1.id);
        System.out.println(u1.username);
        System.out.println(u1.password);

        User u2 = new User();
        u2.id = 22222;
        u2.username = "lisi";
        u2.password = "456";

        System.out.println(u2.id);
        System.out.println(u2.username);
        System.out.println(u2.password);
    }
}

案例1内存图内存图

案例2:

// 住址类
public class Address{

    // 一个家庭住址有3个属性。

    // 城市
    String city; // 实例变量

    // 街道
    String street;

    // 邮编
    String zipcode;
}

public class User{

    // 类=属性+方法
    // 以下3个都是属性,都是实例变量。(对象变量。)

    // 用户id
    // int是一种基本数据类型
    int id; // 实例变量

    // 用户名
    // String是一种引用数据类型
    String username; // 实例变量

    // 家庭住址
    // Address是一种引用数据类型
    // addr是成员变量并且还是一个实例变量
    // addr是否是一个引用呢?是。addr是一个引用。
    Address addr;
}

/*
    到目前为止,如果什么也没听懂,怎么写代码?
        记住一个知识点就行,可以后期慢慢学习画图。
            记住一句话:里面有什么就能“点”什么。

            所有的实例变量(属性)都是通过“引用.”来访问的。

    引用和对象怎么区分?
        “引用”是啥?是存储对象内存地址的一个变量。
        “对象”是啥?堆里new出来的。

    通俗一点:
        只要这个变量中保存的是一个对象的内存地址,那么这个变量就叫做“引用”。

    思考:
        引用一定是局部变量吗?
            不一定。
*/
public class Test{
    public static void main(String[] args){

        //报错了。id是实例变量,必须先创建对象,通过“引用.”的方式访问。
        /*
            User u = new User();
            u是引用。
        */
        //System.out.println(User.id);



        /*
        int i = 100;
        int j = i; // 原理:会将i中保存的100复制一份,传给j变量。
        */

        // 家庭住址对象
        Address a = new Address();
        a.city = "北京";
        a.street = "大兴区";
        a.zipcode = "121221";

        // 用户对象
        User u = new User();
        System.out.println(u.id); // 0
        System.out.println(u.username); // null
        System.out.println(u.addr); // null

        u.id = 11111;
        u.username = "zhangsan";
        u.addr = a;

        // 思考一个问题:
        // 我想直到zhangsan他是哪个城市的,代码应该怎么写?
        System.out.println(u.username + "是"+u.addr.city+"城市的!");

        // u.addr.city 这行代码可否拆分呢?u.addr.city 节省变量。
        // 拆分成以下代码和以上效果完全相同,原理完全相同,不同的是以下代码多了两个变量。
        Address ad = u.addr;
        String zhuZhi = ad.city;

        System.out.println(zhuZhi);

        //-----------------------是否理解以下代码---------------------------
        int x = 100;
        // = 代表赋值运算,“赋值”中有一个“值”
        // x变量中的值是100. 将100复制一份给y
        // 表示:将x变量中保存的值100复制一份给y
        int y = x;

        //-----------------------是否理解以下代码---------------------------
        Address k = new Address(); // Address k = 0x1111;
        Address m = k; // 这里表示将k变量中保存的0x1111复制了一份传给了m变量。
    }
}

案例2内存图内存图

空指针异常(NullPointerException)

空指针异常内存图
空指针异常案例:

/*
    空指针异常。(NullPointerException)

    关于垃圾回收器:GC
        在java语言中,垃圾回收器主要针对的是堆内存。
        当一个java对象没有任何引用指向该对象的时候,
        GC会考虑将该垃圾数据释放回收掉。

    出现空指针异常的前提条件是?
        "空引用"访问实例【对象相关】相关的数据时,都会出现空指针异常。
*/
public class NullPointerTest{
    public static void main(String[] args){
        // 创建客户对象
        Customer c = new Customer();
        // 访问这个客户的id
        System.out.println(c.id); // 0

        // 重新给id赋值
        c.id = 9521; // 终身代号
        System.out.println("客户的id是=" + c.id);

        c = null;
        // NullPointerException
        // 编译器没问题,因为编译器只检查语法,编译器发现c是Customer类型,
        // Customer类型中有id属性,所以可以:c.id。语法过了。
        // 但是运行的时候需要对象的存在,但是对象没了,尴尬了,就只能出现一个异常。
        System.out.println(c.id);
    }
}

class Customer{
    // 客户id
    int id; // 成员变量中的实例变量,应该先创建对象,然后通过“引用.”的方式访问。
}