阿嘉的Java日志 [第三章:面向对象]

156 阅读13分钟

面向对象【Java语言的核心机制,最重要的内容,Java语言特色】

[内容时间:2021.08.09]

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

面向过程

主要关注点是实现的具体过程,因果关系【集成显卡的开发思路】

优点

对于业务逻辑比较简单的程序,可以达到快速开发,前期投入成本较低。

缺点

采用面向过程的方式开发很难解决非常复杂的业务逻辑,另外面向过程的方式导致软件元素之间的“耦合度”非常高,只要其中一环出问题,整个系统受到影响,导致最终软件的“扩展力”差。另外,由于没有独立体的概念,所以无法达到组件复用

面向对象

主要关注对象【独立体】能完成哪些功能。【独立显卡的开发思路】

优点

耦合度低,扩展力强,更容易解决现实世界当中更复杂的业务逻辑。组件复用性强 !

缺点

前期投入成本较高,需要进行独立体的抽取,大量的分析与设计。

面向对象的三大特征

封装

封装的好处

1、对于这个事物来说,看不到这个事物的复杂的一面,对外提供简单的操作入口。对于使用者来说不需要知道内部复杂的实现原理,只需要会简单的操作就行。

2、封装之后才会形成真正的“对象”,真正的“独立体”。

3、封装就意味着以后的程序可以重复使用,并且这个事物适应性比较强,任何场合都适用

4、封装之后,对于事物本身,提高了安全性。【安全级别高】

封装的步骤

1、所有属性私有化,用private关键字修饰,修饰的所有数据只能在本类中访问。

2、对外提供简单的操作入口

对外提供两个公开的方法,分别是set方法和get方法

3、set方法的命名规范:

/*public void set+属性名首字母大写(形参){  
}*/
public void setAge(int a){  
 age = a ;              //例如这样 
}

4、get方法命名规范

public int getAge(){
 return age;
}

继承

关于java语言当中的继承

//语法格式:
【修饰符列表】class 类名 extends 父类名{
    //类体
}

1、继承的“基本”作用是:代码复用。但继承最“重要”的作用是:有了继承才有了以后的“方法覆盖”和“多态机制”。

2、Java语言当中的继承只支持单继承,一个类不能同时继承很多类。【在C++中支持多继承】

3、关于继承的一些术语:

B类继承A类,其中:

A类成为:父类,基类,超类,superclass

B类称为:子类,派生类,subclass

4、在Java语言当中子类继承父类都继承哪些数据呢?

私有的不支持继承

构造方法不支持继承

其他数据都可以继承

5、虽然Java只支持单继承,但是一个类也可以间接继承其他类,例如:

C extends B{ }

B extends A{ }

A extends T{ }

那么C类直接继承B类,但是C类间接继承A类T类。

6、Java中假设一个类没有显示的继承任何类,该类默认继承JavaSE类库当中提供的java.lang.Object类。

Java语言中任何一个类都有Object类的特征

多态

单例模式(现在的水平不够 P149,先不讲 )

向下转型需要留意一下:↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

// instanceof语法格式:
// (引用 instanceof 数据类型名)
// 运算结果为布尔类型
Animal a1 = new Cat();
// 要访问子类特有对象时,要确定a1确实是个猫,那才能做向下转型,要不然会抛出异常,但不会报错
if(a1 instanceof Cat){
   Cat c1 = (Cat)a1; 
}

构造方法

关于java类的构造方法

1、构造方法又称为构造函数、构造器、Constructor

2、构造方法的语法结构

[修饰符列表] 构造方法名(形参列表){ }

3、回顾普通方法的语法结构

[修饰符列表] 返回值类型 方法名(形参列表){ }

4、对于构造方法来说,“返回值类型不需要指定”,而且也不能写void,写了就是普通方法了

5、构造方法的方法名必须与类名保持一致

6、构造方法的作用是:通过构造方法的调用,可以创建对象,并给实例变量赋值

7、怎样调用构造方法?

普通方法:有static时,类名.方法名(实参) ; 无static时,引用.方法名(实参)

构造方法:new 构造方法名(实参)

8、构造方法执行后有返回值吗?

每一个构造方法实际上执行结束后都有返回值,但是return这个语句不用写,因为返回值类型就是类本身,所以返回值类型也不用写

9、注释和取消注释:ctrl + /

10、当一个类中没有定义任何构造方法,系统默认给类提供一个无参数构造方法,称之为缺省构造器。

11、当一个类显示的构造方法定义出来了,那么系统则不再默认为这个类提供缺省构造器,建议开发中手动为当前提供无参数构造方法,因为无参数构造方法太常用了

12、构造方法支持重载机制,在一个类当中编写多个构造方法,这个多个构造方法显然已经形成方法重载机制

类和对象的概念

类的定义

JVM的内存划分

几种方法的用法

静态方法:

public static void 方法名(){ } 这个是普通的类的方法 // 用法是类名.方法名

实例方法:

public void 方法名(){ } 这个是可以访问类的private变量 方法入口 // 用法是引用.方法名

构造方法:

public 方法名(){ } 这个是构造方法 // 用法是new

this关键字

this能出现在实例方法和构造方法中

this() 在构造方法出现时只能出现在的第一行(它的上头不能有语句)作用是通过当前的构造方法去调用“本类”中的其他构造方法,目的是:代码复用

static关键字

静态变量

什么时候定义静态变量呢?

所有对象都有这个属性,而且所有对象的这个属性的值都是一样的,建议定义为静态变量,节省内存的开销

静态变量在类加载的时候初始化,内存在方法区中开辟。访问的时候不需要创建对象,直接使用 “ 类名.静态变量名 ” 的方式访问

public class Chinese{
    String id;
    String name;
    
    //国籍,所有对象的国籍都一样,这属于类级别,可以做变量前加static关键字修饰
    //静态变量在类加载的时候初始化,不需要创建对象,内存就开辟了
    static String country = "中国";
}

静态方法

1、方法什么时候定义为静态的?

方法描述的是动作,当所有对象执行这个动作的时候,最终产生的影响都是一样的,那就不属于某一个对象了,可以提升为类级别的动作,模板级别的动作

2、大多数方法都定义为实例方法,因为一般一个行为一个动作在发生的时候都需要对象的参与。【但也有例外】。大多数“ 工具类 ”中的方法都是静态方法,因为工具就是为了方便编程,方便调用。自然不用new是最好的

静态代码块

//语法格式:
static{
    //Java语句;
}

1、静态代码块在类加载的时候执行,并且只执行一次

2、静态代码块在一个类中可以编写多个,遵循自上而下依次执行

3、静态代码块的作用是?怎么用?用在哪?什么时候用?

和需求有关,例如项目中要求在类加载的时刻 / 时机执行代码完成日志记录,那么这段记录日志的代码就可以编写到静态代码块当中,完成日志记录

静态代码块是Java为程序员准备的一个特殊的时刻,这个特殊的时刻被称之为类加载时刻。若希望在此时刻执行一段特殊的程序,这段程序可以直接放到静态代码块当中。

4、通常在静态代码块当中完成预备工作,先完成数据的准备工具,例如:初始化连接池,解析XML配置文件……

【拓展】实例(代码块/语句块)

(类似static代码块的用法)【了解内容,使用非常少】

1、实例代码块可以编写多个,自上而下执行

2、实例代码块在构造方法执行之前执行,构造方法执行一次,实例代码块对应执行一次

3、实例代码块也是Java语言为程序员准备的一个特殊的时机,称之为:对象初始化时机

//语法为
public class Xx{
    {
        //java语句;
    }
}

4、想有输出,那在主方法里面new吧

方法覆盖

(又称之为方法重写,Override)

关于Java语言中的方法覆盖

1、什么时候使用方法覆盖?

当父类当中的方法已经无法满足当前子类的业务需求。子类有必要将父类中继承过来的方法进行重新编写,这就叫做方法覆盖

2、什么条件满足之后会发生方法覆盖呢?

①方法覆盖发生在具有继承的父子类之间

②方法覆盖的时候:返回值类型相同,方法名相同,形参列表相同

③方法覆盖的时候:访问权限不能更低,可以更高

④方法覆盖的时候:抛出异常不能更多,可以更少。【以后讲,讲完异常之后才能解释】

温馨提示:建议方法覆盖的时候尽量复制粘贴,自己编写容易出错,导致没有产生覆盖

3、注意事项

私有方法不能继承,所以不能覆盖

构造方法不能继承,所以不能覆盖

静态方法不存在覆盖

覆盖只针对方法,不涉及属性

final关键字

  • final 修饰的类无法被继承

  • final 修饰的方法无法被覆盖

  • final 修饰的变量只能赋一次值

  • final 修饰的引用一旦指向某个对象,则不能再重新指向其他对象,但该引用指向的对象内部的数据是可以修改的

  • final 修饰的实例变量必须手动初始化,不能采用系统默认值 NULL

  • final 修饰的实例变量一般和 static 联合使用,成为常量

    public static final double PI = 3.1415926 ;

抽象类

  • 抽象类由 abstract 修饰在 class 前面

  • 抽象类的作用是降低接口在实现类与接口之间的实现难度

  • 使用规则

    抽象类中可以声明抽象方法,也可以写具体方法

    抽象类声明抽象方法时,子类必须重写该抽象方法

    抽象类实现接口时,不需要对接口的方法进行重写

    抽象类由构造方法,但是不能使用

接口

接口是一种特殊的类文件(.class文件)

接口是一种引用数据类型

作用是

  • 制定规则
  • 降低耦合度

使用规则

  • 接口中的属性,默认都是静态常量属性(常量修饰符列表一般采用 public static final ,但接口里面的 public static final 可以省略不写)
  • 接口中的方法都是抽象方法,如果需要定义具体方法的实现,此时方法需要使用default修饰
  • 接口中的抽象方法定义时:public abstract 修饰符可以省略不写
  • 接口中的方法访问权限不能是private
  • 接口与接口之间可以实现多继承,但是接口之间不能相互实现
  • 接口中不存在构造方法

关于接口中的 static 方法( 静态方法 )

关于接口中的default方法( 默认方法 )

在java8以后,接口中可以添加使用default或者static修饰的方法,在这里我们只讨论default方法,default修饰方法只能在接口中使用,在接口中被default标记的方法为普通方法,可以直接写方法体。

一、概念

  1. 接口提供一个默认实现的方法,并且不强制实现类重写此方法
  2. 默认方法使用default关键字来修饰

二、引入背景

  1. 当一个接口添加新方法时,需要所有的实现类都重写新方法,影响到了已有的实现类,可能导致应用崩溃
  2. 默认方法可以不强制重写,也不会影响到已有的实现类
  3. 例如Iterable接口的foreach方法,就是一个默认方法,参数是一个Consumer对象

三、使用

  1. 当一个实现类实现了多个接口,多个接口里都有相同的默认方法时,实现类必须重写该默认方法,否则编译错误

a. 实现类自己重写逻辑

b. 实现类使用super关键字指定使用哪个接口的默认方法

  1. 接口静态方法

接口中支持定义静态方法,将默认方法的default关键字换成static即可

  1. 继承类可以直接使用接口中的static方法,也可以创建对象后使用接口中的default方法

======================

实现类会继承接口中的default方法。如果接口A中有default方法:

public interface A {
    public default void a(){
        System.out.println("这是A");
    }
}

Test类实现接口A:

public class Test implements A{
    
}

那么Test类将会继承接口A中的a方法:

public class Test2 {
    public static void main(String[] args) {
        Test t = new Test();
        t.a();
    }
}

2.如果一个类同时实现接口A和B,接口A和B中有相同的default方法,这时,该类必须重写接口中的default方法

为什么要重写呢?是因为,类在继承接口中的default方法时,不知道应该继承哪一个接口中的default方法。

接口A:

public interface A {
    public default void a(){
        System.out.println("这是A");
    }
}

接口B:

public interface B {
    public default void a(){
        System.out.println("这是B");
    }
}

Test类:

public class Text implement A,B{    //这行会提示报错,因为没重写 a()方法
    
}

3.如果子类继承父类,父类中有b方法,该子类同时实现的接口中也有b方法(被default修饰),那么子类会继承父类的b方法而不是继承接口中的b方法

接口A:

public interface A {
    public default void b(){
        System.out.println("AAA");
    }
}

类C:

public class C {
    public void b(){
        System.out.println("CCC");
    }
}

子类 Text 类:

public class Test extends C implements A{
    
}

测试类:

public class Text2{
    public static void main(String[] args){
        Text t = new Text();
        t.b();
    }
}

说明子类继承的b方法为父类C中的b方法,不是接口中的default b(){}方法。

Object类的常用方法

toString

如果输出类型是引用类型的话,系统自动调用toString方法

equals

内部类

匿名内部类

静态内部类

实例内部类

局部内部类,很少用,几乎不用

但匿名内部类又是局部内部类的一种

使用内部类编写的代码可读性很差,能不用尽量不用