Nrich's blog Nrich's blog
首页
  • Java
  • Golang
  • 深度学习
  • Git
  • Linux
  • DataStructure
  • CloudNative
  • Redis
  • MySQL
  • 路由劫持
GitHub (opens new window)

Nrich

小聪明
首页
  • Java
  • Golang
  • 深度学习
  • Git
  • Linux
  • DataStructure
  • CloudNative
  • Redis
  • MySQL
  • 路由劫持
GitHub (opens new window)
  • Java

    • 黑马Java入门基础-学习笔记

      • Java 基础语法
      • 数组
      • 方法
      • 面向对象基础
      • 常用API(String、ArrayList)
      • 面向对象进阶1(static、单例、代码块、继承)
        • 1. static 关键字
          • 1.1 static 是什么、修饰成员变量和成员方法的用法
          • 1.1.1 static 是什么
          • 1.1.2 成员变量
          • 1.1.3 成员方法
          • 1.2 static 修饰成员变量的内存原理
          • 1.3 static 修饰成员方法的内存原理
          • 1.4 static 访问的注意事项
        • 2. static 应用知识
          • 2.1 工具类
          • 2.1.1 什么是工具类
          • 2.1.2 工具类的好处
          • 2.1.3 为什么工具类中的方法不用实例方法做?
          • 2.1.4 工具类定义时的其他要求(锦上添花)
          • 2.2 代码块
          • 2.2.1 代码块概述
          • 2.2.2 代码块分类
          • 2.2.3 案例——斗地主游戏
          • 2.3 设计模式:单例
          • 2.3.1 什么是设计模式
          • 2.3.2 单例模式
          • 2.3.3 饿汉单例模式
          • 2.3.4 懒汉单例模式
          • 2.3.5 饿汉单例模式 VS 懒汉单例模式
        • 4. 面向对象三大特征之二:继承
          • 4.1 继承概述
          • 4.2 继承的设计规范
          • 4.3 继承的内存运行原理
          • 4.4 继承的特点
          • 4.5 继承之后的特点
          • 4.5.1 成员变量、成员方法的访问特点
          • 4.5.2 继承后:方法重写
          • 4.5.3 继承后:子类构造器的特点
          • 4.5.4 继承后:子类构造器访问父类有参构造器
          • 4.6 this、super 使用总结
      • 面向对象进阶2(包、权限修饰符、抽象类、接口)
      • 面向对象进阶3(多态、内部类、常用API)
      • 面向对象进阶4(常用日期API、正则、Arrays类、Lambda)
      • 面向对象进阶5(集合体系)
      • 面向对象进阶6(集合体系之Map)
      • 面向对象进阶-补充(可变参数)
    • 面向对象
    • 常用API
    • 并发、并行、异步、同步(暂存)
    • 《Spring Boot 进阶-郑天民》

  • GoLang

  • 开发
  • Java
  • 黑马Java入门基础-学习笔记
Nrich
2023-02-24
目录

面向对象进阶1(static、单例、代码块、继承)

# 面向对象进阶1(static、单例、代码块、继承)

# 1. static 关键字

# 1.1 static 是什么、修饰成员变量和成员方法的用法

# 1.1.1 static 是什么

  • static 是静态的意思,可用来修饰成员变量和成员方法

  • static 修饰成员变量表示该成员变量只在内存中只存储一份,可以被共享访问、修改。

# 1.1.2 成员变量

成员变量可以分为2类:

  • 静态成员变量

    • 有 static 修饰,属于类,内存中加载一次

    • 常表示如在线人数信息、等需要被共享的信息,可以被共享访问

    • 示例代码

      public class User {
          // 静态成员变量
          public static String onlineNumber= 161;
      }
      
      1
      2
      3
      4
    • 访问格式:推荐使用 类名.静态成员变量,不推荐 对象.静态成员变量

  • 实例成员变量

    • 无static修饰,存在于每个对象中
    • 常表示姓名name、年龄age、等属于每个对象的信息。属于每个对象,且每个对象的信息不同
    • 访问格式:对象.实例成员变量

# 1.1.3 成员方法

成员方法可以分为2类:

  • 静态成员方法
    • 有 static 修饰,属于类
    • 使用场景:如果该方法是以执行一个共用功能为目的,则可以申明成静态方法
    • 访问:建议用类名访问,也可以用对象访问。类名.静态成员方法
  • 实例成员方法
    • 无 static 修饰,属于对象
    • 使用场景:表示对象自己的行为的,且方法中需要访问实例成员的,则该方法必须申明成实例方法
    • 访问:只能用对象触发访问

# 1.2 static 修饰成员变量的内存原理

在加载一个类时,会先将此类加载到方法区,同时会在堆内存中开辟一块此类的静态变量区

-> 然后将 main 方法加载进栈内存运行

注:此时内存中还没有 name、age 实例成员变量,因为他们是属于对象的

image-20230224105808538

-> 在创建对象时,才在堆内存中开辟一个新的空间出来。

image-20230224110029735

-> 有新的对象创建时,在堆内存中开辟另一个空间表示此对象。

image-20230224110133132

提示

同一个类中访问静态成员变量时,类名可以省略不写。

# 1.3 static 修饰成员方法的内存原理

在初始加载类时,便会将该类的静态方法同时加载到内存的方法区中,但实例成员方法此时还不会加载进来。 执行图中的 getMax 方法时,将该方法提到栈内存中运行,输出结果之后进行下一步。

image-20230224110821618

-> 创建一个对象之后,在堆内存中开辟空间,并将实例成员方法 study 加载入方法区,在对象中指向对相应静态方法和该实例方法的引用。

image-20230224111345867

# 1.4 static 访问的注意事项

  • 静态方法只能访问静态的成员,不可以直接访问实例成员。

    • 只是不能直接在一个静态方法中访问实例成员变量,但可以先在此方法中先创建一个实例成员变量,然后访问其变量。
    • 同时也不能直接访问实例成员方法。
  • 实例方法可以访问静态的成员,也可以访问实例成员。

  • 静态方法中是不可以出现 this 关键字的。

    • 因为 this 代表当前对象,但是静态方法可以不通过对象来调用。

实例方法/变量 ∈ 对象,静态方法/变量 ∈ 类。 静态成员变量一般设为 public ,因为它全局只有一份。

# 2. static 应用知识

# 2.1 工具类

# 2.1.1 什么是工具类

工具类中定义的都是一些静态方法,每个方法都是以完成一个共用的功能为目的。

比如:一个系统的很多部分都需要生成验证码,此时可以开发一个生成验证码的静态方法。

# 2.1.2 工具类的好处

  • 调用方便
  • 提高了代码复用(一次编写,处处可用)

# 2.1.3 为什么工具类中的方法不用实例方法做?

实例方法需要创建对象调用,此时用对象只是为了调用方法,这样只会浪费内存。

# 2.1.4 工具类定义时的其他要求(锦上添花)

  • 工具类中都是静态方法,直接用类名访问即可。
  • 工具类无需创建对象,建议将工具类的构造器进行私有化。(用 private 阉割掉)

# 2.2 代码块

# 2.2.1 代码块概述

  • 代码块是类的5大成分之一(成员变量、构造器,方法,代码块,内部类),定义在类中方法外。

  • 在Java类下,使用 { } 括起来的代码被称为代码块 。

# 2.2.2 代码块分类

  • 静态代码块

    • 格式:static{}

    • 特点:需要通过 static 关键字修饰,随着类的加载而加载,并且自动触发、只执行一次

    • 作用:若要在启动系统时对数据进行初始化,建议使用静态代码块完成数据的初始化操作,代码优雅

    • 使用场景:在类加载的时候做一些静态数据初始化的操作,以便后续使用。比如,数据库的连接可以在 static { } 中执行,进行静态资源的初始化。

    • 代码示例:

      public class StaticDemo {
          
          public static void main(String[] args) {
              System.out.println("----main方法执行----");
          }
          
          /**
           静态代码块:有static修饰,属于类,与类一起优先加载一次,自动触发执行
          */
          static {
              System.out.println("----静态代码块被触发执行了----");
          }
      }
      
      // 以上代码中先触发静态代码块,然后执行main方法
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
  • 构造代码块(了解,用的少)

    • 也可以叫实例代码块
    • 格式:{}
    • 特点:每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且在构造器执行前执行
    • 使用场景:初始化实例资源。

# 2.2.3 案例——斗地主游戏

需求:

在启动游戏房间的时候,应该提前准备好54张牌,后续才可以直接使用这些牌数据。

分析:

①该房间只需要一副牌。

②定义一个静态的 ArrayList 集合存储 54 张牌对象,静态的集合只会加载一份。

③在启动游戏房间前,应该将 54 张牌初始化好

④当系统启动的同时需要准备好 54 张牌数据,此时可以用静态代码块完成。

总结:

需要提前准备数据,并且是只需要一份这个数据,因此使用 static 修饰的时候,需要用到静态代码块。

# 2.3 设计模式:单例

# 2.3.1 什么是设计模式

  • 开发中经常遇到一些问题,一个问题通常有n种解法的,但其中有一种解法是最优的,这个最优的解法被总结出来,称之为设计模式。

  • 设计模式有20多种,对应20多种软件开发中会遇到的问题,学设计模式主要是学2点:

    • 第一:这种模式用来解决什么问题。
    • 第二:遇到这种问题了,该模式是怎么写的,他是如何解决这个问题的。

# 2.3.2 单例模式

可以保证系统中,应用该模式的这个类永远只有一个实例,即保证一个类永远只能创建一个对象。例如任务管理器对象我们只需要一个就可以解决问题了,这样可以节省内存空间。

单例的实现方式有很多,如饿汉单例模式、懒汉单例模式,……

# 2.3.3 饿汉单例模式

  • 在用类获取对象的时候,对象已经提前为你创建好了。

  • 设计步骤:

    • 定义一个类,把构造器私有

    • 定义一个静态变量存储一个对象

  • 代码示例:

    /** a、定义一个单例类 */
    public class SingleInstance {
        /** c.定义一个静态变量存储一个对象即可:属于类,与类一起加载一次 */
        public static SingleInstance instance = new SingleInstance();
        
        /** b.单例必须私有构造器*/
        private SingleInstance(){
            System.out.println("创建了一个对象");
        }
    }
    
    
    /** 
      别的类中的使用:直接获取该单例的对象
    */
    SingleInstance s1 = SingleInstance.instance;
    
    // 当有多条语句创建s1、s2、s3...时,他们都是相等的,是同一个对象
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

# 2.3.4 懒汉单例模式

  • 在真正需要该对象的时候,才去创建一个对象(延迟加载对象)。

  • 设计步骤:

    • 定义一个类,把构造器私有
    • 定义一个静态变量存储一个对象
    • 提供一个返回单例对象的方法
  • 代码示例:

    /** 定义一个单例类 */
    class SingleInstance{
        /** 定义一个静态变量存储一个对象即可:属于类,与类一起加载一次 */
        private static SingleInstance instance ; // 默认值:null
        
        /** 单例必须私有构造器*/
        private SingleInstance(){
            
        }
        
        /** 必须提供一个方法返回一个单例对象  */
        public static SingleInstance getInstance(){
            if(instance == null){
            	// 第一次来拿对象:此时需要创建对象    
    	        instance = new SingleInstance();
            }
            return instance;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    ⚠️ 上述代码中,line 4 中用 private 修饰对象,此时无法从该类外部使用 SingleInstance s1 = SingleInstance.instance 获取对象了,只能使用SingleInstance s1 = SingleInstance.getInstance() 获取。否则可能会拿到 null 值,不符合需求。

# 2.3.5 饿汉单例模式 VS 懒汉单例模式

懒汉单例模式节省内存,在不需要创建对象时,不会在栈内存中创建内存。

饿汉单例模式更快些,当需要对象时,已经在内存中了。

# 4. 面向对象三大特征之二:继承

# 4.1 继承概述

  • 关键字:extends,建立两个类之间的父子关系

  • 基本使用:public class Student extends People {} 。 Student 称为子类(派生类),People 称为父类(基类 或超类)。

  • 好处:子类继承父类后,可以直接使用父类公共的属性和方法了。因此,用好这个技术可以很好的我们提高代码的复用性

# 4.2 继承的设计规范

子类们相同特征(共性属性,共性方法)放在父类中定义,子类独有的的属性和行为应该定义在子类自己里面。

# 4.3 继承的内存运行原理

创建一个子类对象时,实际会在堆内存中分为父类空间和子类空间两部分;对外是一个对象,对内会分为两部分,整个代表一个子类对象。

image-20230226105900152

# 4.4 继承的特点

  • 子类可以继承父类的属性和行为,但是子类不能继承父类的构造器。

    • 子类有自己的构造器,父类构造器用于初始化父类对象。
  • Java是单继承模式:一个类只能继承一个直接父类。

    • 为啥?反证,当多个爸爸中让你复习不一样的内容,那你就不知道该复习哪门啦,出现了二义性
  • Java不支持多继承、但是支持多层继承。

    • 多层继承:儿子 -> 爸爸 -> 爷爷
    • 基于就近原则使用父类中的成员变量和成员方法
  • Java中所有的类都是 Object 类的子类。

    • 要么直接继承了Object , 要么默认继承了Object , 要么间接继承了Object
    • Object是祖宗类。

一些小注意点

  1. 子类是否可以继承父类的私有成员?

    子类可以继承父类的私有成员,但不能直接访问。如,大头儿子继承了小头爸爸的保险柜,但他没有密码,打不开。

  2. 子类是否可以继承父类的静态成员?

    有争议的知识点。

    子类可以直接使用父类的静态成员(共享)。

    但黑马认为:子类不能继承父类的静态成员。(共享并非继承)。如,爸爸的车给儿子开,这只是共享,不是继承,车没有给儿子。

# 4.5 继承之后的特点

# 4.5.1 成员变量、成员方法的访问特点

  • 在子类方法中访问成员(成员变量、成员方法)满足:就近原则
    • 子类局部范围找 → 子类成员范围找 → 父类成员范围找,如果父类范围还没有找到则报错。
  • 在子类中使用父类的重名成员怎么办?
    • 子类父类出现重名成员,优先使用子类的
    • 可通过 super 关键字,指定访问父类的成员,格式:super.父类成员变量/方法

# 4.5.2 继承后:方法重写

方法重写的基本知识:

  • 什么是方法重写?
    • 在继承体系中,子类出现了和父类中一模一样的方法声明,称子类的这个方法是重写的方法。
  • 方法重写的应用场景
    • 子类需要父类的功能,但父类的该功能不完全满足自己的需求时
  • 案例:
    • 旧手机的功能只能是基本的打电话,发信息
    • 新手机的功能需要能够:基本的打电话下支持视频通话。基本的发信息下支持发送语音和图片

@override 注解:

  • @Override 是放在重写后的方法上,作为重写是否正确的校验注解。
  • 加上该注解后如果重写错误,编译阶段会出现错误提示。
  • 建议重写方法都加 @Override 注解,代码安全,优雅!

方法重写注意事项和要求:

  • 重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致(申明必须一样,访问权限也一般都是一样的)。
    • 申明不变,重新实现
  • 私有方法不能被重写。
  • 子类重写父类方法时,访问权限必须大于或者等于父类 (private < 缺省 < protected < public)
  • 子类不能重写父类的静态方法,如果重写会报错的。
    • 原因:子类本身就没有继承到父类的静态方法,只是共享,因此无法覆盖它。调用中也是父类的静态方法用 “父类名.父类静态方法名” 调用,子类的用子类对应的方法调用,不会互相影响。

# 4.5.3 继承后:子类构造器的特点

子类继承父类后构造器的特点:

  • 子类中所有的构造器默认都会先访问父类的**无参构造器**,再执行自己。

原因:

  • 子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
  • 子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。

怎么调用父类构造器的?

  • 子类构造器的第一行语句默认都是:super(),不写也存在。

# 4.5.4 继承后:子类构造器访问父类有参构造器

super调用父类有参数构造器的作用:

  • 初始化继承自父类的数据。
  • 即,Teacher 类继承了 People 类,在 Teacher 中初始化 People 定义好的 name,age

如果父类中没有无参数构造器,只有有参构造器,会出现什么现象呢?

  • 会报错。因为子类默认是调用父类无参构造器的。

  • 但子类中如果只调用了父类的有参构造器,是不会报错的。

  • 解决:子类构造器中通过 super(…),手动调用父类的有参数构造器

  • 建议:将无参构造器全都写出来

# 4.6 this、super 使用总结

  • this:代表本类对象的引用;

  • super:代表父类存储空间的标识。

  • 示例代码

    /**
    	后台创建对象封装数据的时候如果用户没有输入学校,则默认使用“黑马培训中心”;如果用户输入了学校则使用用户输入的学校信息
    */
    public class Student {        
        private String schoolName;
        private String name;
       
        public Student(String name){
              this(name , “黑马培训中心”);	// 调用两个参数的构造器
        }	
         
        public Student(String name , String schoolName ){
              this.name = name;
            this.schoolName = schoolName;
        }	
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

this(...) 和 super(...) 使用注意点

  • 子类通过 this (...)去调用本类的其他构造器(如有参构造器),本类其他构造器会通过 super 去手动调用父类的构造器,最终还是会调用父类构造器的。

  • this(…) super(…) 都只能放在构造器的第一行,所以二者不能共存在同一个构造器中。

    • 用 this() 构造器时,可能会调用兄弟构造器,兄弟构造器也会调用他爸 super(),若自己也调用super(),会造成调用两次 super()
编辑 (opens new window)
上次更新: 2023/03/15, 14:34:12
常用API(String、ArrayList)
面向对象进阶2(包、权限修饰符、抽象类、接口)

← 常用API(String、ArrayList) 面向对象进阶2(包、权限修饰符、抽象类、接口)→

最近更新
01
YAML、Pod、Job、CronJob、ConfigMap、Secret
06-06
02
Kubernetes 的安装与基本架构
06-04
03
初识容器
05-30
更多文章>
Theme by Vdoing | Copyright © 2022-2023 Nrich | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式