面向对象三大特征

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

1.1 封装

1.1.1 封装简介

封装,英文单词Encapsulation。

从广义的角度来说,将一块经常要使用的代码片段,定义到方法中,是封装。将多个方法和多个状态数据定义到类体中,也是一种封装。

从狭义的角度来说,java的封装,就是把类的属性私有化(private修饰),再通过公有方法(public)进行访问和修改;

1.1.2 属性封装

1)为什么封装?

一个类中的某一些属性,不希望直接暴露给外界,让外界直接操作。因为如果让外界直接操作的话,对 这个属性进行的值的设置,可能不是我们想要的(可能不符合逻辑)。此时就需要将这个属性封装起来,不让外界直接访问。

下面使用案例来演示一下不合逻辑的地方:

class Person {
   String name;
   int age;
}
class Program {
   public static void main(String[] args) {
      // 实例化一个Person对象
      Person xiaoming = new Person();
      xiaoming.name = "xiaoming";
      xiaoming.age = -10000;
   }
}

2)如何封装

1、为了不让外界直接访问某些属性,用关键字private修饰这些属性。

2 、提供共有的getter/setter的方法,用来操作这个被私有化的属性。

public class Person {
   String name;
   private int age;        // 1、将属性私有化起来,不让外界直接访问
​
   // 2、给要访问的属性,设置对应的  setter/getter 方法
   public void setAge(int age) {
      this.age = age;
   }
​
   public int getAge() {
      return this.age;
   }
}

3)疑问:为什么私有化起来的属性,不希望外界访问,还需要再提供setter和getter

答:可以通过指定的方式访问属性,在这些方法中,可以添加一些数据处理操作

public void setAge(int age) {
   if (age >= 0 && age <= 120) {
      this.age = age;
   }
}

2.2 单例设计模式

2.2.1 简介

==设计模式==(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的代码设计的经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。

总体来说设计模式分为三大类:

  • 创建型模式(5种):工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

  • 结构型模式(7种):适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

  • 行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

在这里,我们只介绍和学习其中的单例模式:

==单例模式==(Singleton Pattern)是一种常用的软件设计模式,属于创建型模式之一。它的目的是确保一个类只有一个实例,并提供一个全局访问点。

使用场景:

  • 频繁创建和销毁的对象:如果对象创建和销毁的成本较高,且在程序运行期间需要频繁访问,使用单例模式可以提高效率。

  • 控制资源访问:例如,数据库连接、日志对象、配置管理器等,这些资源通常希望在整个应用中只有一份实例。

  • 工具类:对于一些工具类,如缓存、对话框、注册表设置等,使用单例模式可以简化代码,避免重复实例化。

2.2.2 单例的饿汉模式

  1. 提供一个private权限的、静态的、当前类的属性,并在静态代码段中进行实例化。

  2. 构造方法私有化,杜绝从外界通过new的方式实例化对象的可能性。

  3. 提供一个public权限的静态方法,获取一个当前类的对象。

public class Boss {
   // 1、设计一个私有的、静态的、当前类的对象
   private static Boss instance;
   static {
      // 对instance静态对象进行实例化
      instance = new Boss();
   }
   
   // 2、将单例类的构造方法私有化,杜绝从外界通过new的方式实例化对象的可能性。
   private Boss() {
      System.out.println("一个Boss对象出现了 "); 
   }
​
   // 3、需要提供一个public权限的静态方法,可以获取一个当前类的对象。
   public static Boss getCurrentBoss() {
      return instance;
   }
}

2.2.3 单例的懒汉模式

  1. 提供一个private权限的、静态的、当前类的属性

  2. 构造方法私有化,杜绝从外界通过new的方式实例化对象的可能性。

  3. 提供一个public权限的静态方法,负责创建单例对象,第一次调用时会初始化单例,后续调用则直接返回已存在的单例。

public class Master {
​
   private static Master instance;
   
   private Master() {
      System.out.println("一个Chairman对象被实例化了 "); 
   }
   
   public static Master getMaster() {
      // 使用到instance对象的时候,判断是不是null
      if (instance == null) {
         // 实例化
         instance = new Master();
      }
      return instance;
   }
}

2.2.4 两者比较

基本都是一样的,都可以获取到一个类的唯一的对象。

1 、在没有使用获取当前类对象之前,懒汉式单例比饿汉式单例在内存上有较少的资源占用。

2 、懒汉式单例在多线程的环境下有问题。需要考虑线程安全(学到线程再说)

3.3 继承

3.3.1 继承简介

面向对象最显著的一个特征。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。

这种技术使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用。比如可以先定义一个类叫车,车有以下属性:车体大小,颜色,方向盘,轮胎,而又由车这个类派生出轿车和卡车两个类,为轿车添加一个小后备箱,而为卡车添加一个大货箱。

已有的类,叫==父类==,又叫基类,超类。 派生出来的新类,叫==子类==,也叫派生类。

使用关键字extends 来表示子类继承了父类,语法如下:

修饰词 class  子类名  extends 父类名{
     //子类的类体
}

例如:

class A{}  //父类
class B extends A{}  //B是A的子类
class C extends B{}  //C是B的子类

3.3.2 继承特点

1、Java只支持单继承,即一个类只能有一个父类;但是一个类可以有多个子类。

2 、java支持多重继承,即一个类在继承自一个父类的同时,还可以被其他类继承。 可见继承具有传递性

3、子类继承了父类的所有成员变量和方法,包括私有的(只不过没有访问权限),当然也包括静态成员。

4、子类不会继承父类的构造方法,只能调用父类里的构造方法,并且一定至少有一个子类构造器调用了父类的构造器。

5 、子类在拥有父类的成员的基础上,还可以添加新成员。

3.3.3 继承中的构造器

一个对象在实例化的时候,需要在堆上开辟空间,堆中的空间分为两部分,分别是从父类继承到的属性,子类特有的属性。而实例化父类部分的时候,需要调用父类中的构造方法。

值得注意的是,默认调用的是父类中的无参构造器。如果父类中没有无参构造器,那么子类需要显式调用父类中的某一个有参构造器。

在子类的构造方法中,使用==super(有参传参)==调用父类中存在的构造方法,而且==super(有参传参)==必须放在首行首句的位置上。因此可知,==super(有参传参)==和==this(有参传参)==不能在一个构造器中共存。

3.3.4 继承中的方法重写

重写,叫做override。在子类中,对从父类继承到的方法进行重新的实现。 这个过程中,子类重写该方法,会覆盖掉继承自父类中的实现方法,因此,重写又叫做覆写。

为什么重写呢?

因为父类的方法逻辑不能满足子类的需求了,因此子类需要修改逻辑(重写)

重写的特点:

-- 子类只能重写父类中存在的方法。
-- 重写时,子类中的方法名和参数要与父类保持一致。(区别于重载overload)
-- 返回值类型:必须和父类方法的返回值类型相同,或者是其子类型。
-- 访问权限:子类重写方法的访问权限必须大于等于父类方法的访问权限。

注解@Override

这个注解,用在重写的方法之前,表示验证这个方法是否是一个重写的方法。如果是,程序没有问题。如果 不是,程序会报错。  因为我们在进行方法重写的时候,没有什么提示的,因此,在进行重写之前,最好加  上去这个注解。
​
误区:加了@Override就是重写,没有加@Override就不是重写。 这种说法是错误的! @Override只是进行的一个语法校验,与是不是重写无关。

面试题 : 简述 Override 和 Overload 的区别

Override: 是重写,是子类对父类的方法进行重新实现。

Overload: 是重载,是对同一个类中的同名、不同参数方法的描述。

3.3.5 Object类型

Object类,是Java中的根类。所有的类都直接或者间接的继承自Object类。因为,所有的类都直接或者间接的继承自Object类,因此在Object类中定义的属性、方法,在所有的类中都有包含。 比如常用的方法 hashCode(),equals(),toString(),getClass(),wait(),notify(),notifyAll()等

1)toString()方法

源码如下:

public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

该方法的作用是将对象的信息变成字符串。 Object里返回的是 “类名@16进制” 。这个返回结果意义不大。看不到对象的成员变量的信息。因次一般都需要重写。

该方法。在使用输出语句打印对象的变量时,会自动调用。

@Override
public String toString() {
   return "Teacher{" +"name='" + name + '\'' +", age=" + age +", gender=" + gender +'}';
}

2)equals()方法

源码如下:

public boolean equals(Object obj){
   return this==obj;  //  == 比较的是地址值。  
}

上述的源码的意义所在:比较this和传进来的obj 是不是同一个对象,如果是,则返回true,不是的话,返回false。

而我们大多时候的需求,并不是比较是不是同一个对象,而是比较两个对象的属性值是否相同。 因此:自定义类型时,应该重写eqauls方法。

重写要遵循一些原则:

1、如果  obj = null,一定要返回false。
2、如果  obj = this,一定要返回true。
3、如果两个对象的类型不同,一定要返回false。
4、如果  a.equals(b) 成立,则  b.equals(a) 也必须成立。
5、如果  a.equals(b), b.equals(c) 成立,则  a.equals(c) 也必须成立。

3)hashCode()方法

该方法返回的是一个int类型的值。 表示对象在内存堆中的一个算法值。   在自定义类型时,一般都需要重写该方法

4)getClass()方法

方法原型 : public final native Class<?> getClass();
方法作用 : 获取一个用来描述指定类型的Class类对象。获取一个指定的对象的类型。 这个方法,不能被重写。

3.3.6 final修饰词

String s1 = “你好”

String s2 = s1 + “中国”

String s3 = s1+s2

final是java语法中的修饰词,可以用来修饰类,属性,方法,局部变量。 final是“最后,最终”的含义

--1. 修饰类时,不能再有子类,即不能被继承, 比如String, 八大基本数据类型的包装类
--2. 修饰属性:
         只能赋值一次,即要么直接初始化,要么在构造器初始化。
--3. 修饰方法:
         final修饰的方法,不能被重写,
--4. 修饰局部变量, 只能赋值1次,不能第二次赋值。         

3.3.7 static修饰词

static修饰的内容,都是属于公有资源,不属于对象,而是属于类的,因此都是类名调用

1) static修饰属性

-- 属于类的,应该使用类名调用, 可以使用对象变量.调用,但是不合理。
   因为对象一旦对该属性进行了修改,势必会影响以后其他对象的使用。
   因此,static修饰的属性,迎合配合 final 一起修饰,来表示该属性是一个常量。
    
    常量: 命名时,字母都大写,多个单词使用下划线连接。应该使用 public static final修饰。

2) static修饰代码块

在类加载期间执行,只执行一次,适用于加载一些静态资源,比如图片,音频,视频等

3)static可以修饰方法

-- static修饰的方法,应该使用类名调用。
-- static修饰的方法,不能再被重写
package com.day11.static0;
​
public class Test03 extends A{
    public static void main(String[] args) {
        Test03.f1(); // 使用子类名调用父类里的静态方法不合理
        //应该使用f1所在类的类名调用
        A.f1();
    }
    //不是重写A里的f1
    public static void f1(){
        System.out.println("------Test03------");
    }
}
class A{
    public static void f1(){
        System.out.println("------A------");
    }
}

4)static修饰类

static可以修饰类,但必须是内部类,(类体中可以再定义类结构)

4.4 多态

4.4.1 简介

多态:从字面上理解,就是多种形态,多种状态的含义,在这里,指的是一个对象具有多种形态的特点。说的再简单点,就是一个对象可以从一种类型转换为另外一种类型。有向上转型和向下转型两种形式

4.4.2 向上造型(向上转型)

  • 父类型的变量引用子类型的对象。

  • 向上转型肯定会成功,是一个隐式转换。

  • 向上转型后的对象,将只能够访问父类中的成员(编译期间,看变量类型)

  • 如果调用的是重写过的方法,那么调用的一定是重写方法(运行期间,看对象)

  • 应用场景:在定义方法时,形式参数是父类型的变量。这样更加灵活,可以传任意子类型的对象

父类型   变量 =  new  子类型();
​
//"Is-a"的关系    
Animal a = new Cat()
a.noise();
​
​
noise(){
  .......
}
noise(){
   "汪汪汪"
}

4.4.3 向下转型

  • 父类型变量赋值给子类型的变量,需要强制转换,是一个显式转换。

  • 可能会失败,失败的话,会报类造型异常ClassCastException

  • 为了避免ClassCastException ,可以使用instanceof 来判断:变量指向的对象是否属于某一个类型。

子类型   变量名 = (子类型)父类型变量
// 1、实例化一个Dog对象,并且向上转型
Animal animal = new Dog();
​
// 2、判断类型,判断animal指向的对象是不是一个Cat类型
if (animal instanceof Cat) {
   System.out.println("animal 的确是一个Cat对象,可以进行向下转型 "); }
else {
   System.out.println("animal不是一个  Cat对象,不能进行向下转型 "); }
  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值