【DeeplabV3+ MIoU】DeeplabV3+计算评价指标

2023-05-16

文章目录

  • 1 分割常用评价指标
    • 1.1 什么是MIoU
    • 1.2 什么是PA
    • 1.2 什么是MPA
  • 2 根据混淆矩阵计算评价指标
    • 2.1 计算MIoU
    • 2.2 计算PA
    • 2.3 计算MPA
  • 3 计算 MIoU 总体代码
  • 4 compute_mIoU()函数 代码解析
  • 5 输出展示实例
  • 6 感谢链接

1 分割常用评价指标

1.1 什么是MIoU

MIoU全称为Mean Intersection over Union,平均交并比。计算两个集合之间交集和并集的比例。
M I o U = 1 k ∑ i = 1 k P ∩ G P ∪ G MIoU=\frac{1}{k} \sum_{i=1}^{k} \frac{P \cap G}{P \cup G} MIoU=k1i=1kPGPG
或者说:在每个类上计算IoU,然后平均。
M I o U = 1 k + 1 ∑ i = 0 k p i i ∑ j = 0 k p i j + ∑ j = 0 k p j i − p i i MIoU=\frac{1}{k+1} \sum_{i=0}^{k} \frac{p_{ii}}{\sum_{j=0}^{k}p_{ij}+\sum_{j=0}^{k}p_{ji}-p_{ii}} MIoU=k+11i=0kj=0kpij+j=0kpjipiipii
i 表示真实值,j 表示预测值 , p i j p_{ij} pij表示将 i 预测成 j 。

1.2 什么是PA

像素精度(Aixel Accuracy, PA),表示被正确分类的像素个数占总像素数的比例(指混淆矩阵中),也就是被正确分类的正例占所有正例的比例,结合2.2节的代码看。

P A = ∑ i = 0 k p i i ∑ i = 0 k ∑ j = 0 k p i j PA=\frac{\sum_{i=0}^{k}p_{ii}}{\sum_{i=0}^{k}\sum_{j=0}^{k}p_{ij}} PA=i=0kj=0kpiji=0kpii

1.2 什么是MPA

均像素精度(Mean Pixel Accuracy, MPA),在PA基础上进行调整,为每个类别内像素正确分类概率的平均值。
M P A = 1 k + 1 ∑ i = 0 k p i i ∑ j = 0 k p i j MPA=\frac{1}{k+1} \sum_{i=0}^{k} \frac{p_{ii}}{\sum_{j=0}^{k}p_{ij}} MPA=k+11i=0kj=0kpijpii

2 根据混淆矩阵计算评价指标

混淆矩阵:Confusion Matrix,用于直观展示每个类别的预测情况,能从中计算准确率(Accuracy)、精度(Precision)、召回率(Recall)、交并比(IoU)。

混淆矩阵是n*n的矩阵(n是类别),对角线上的是正确预测的数量。

每一行之和是该类的真实样本数量,每一列之和是预测为该类的样本数量。
混淆矩阵
混淆矩阵的生成见详我的另一篇博客 np.bincount()用在分割领域生成混淆矩阵 。
结果类似于下图:

混淆矩阵

2.1 计算MIoU

假设求dog的IoU,如下图:
dog的IoU

true_dog = (7+2+28+111+18+801+13+17+0+3) 		# 上图绿框
predict_dog = (1+0+8+48+13+801+4+17+1+0) 		# 上图黄框
# 因为分母的801加了两次,因此要减一次
iou_dog = 801 / (true_dog + predict_dog - 801)

按照dog求IoU的方法,对每个类别进行求值,再求平均,就是语义分割模型的MIoU值。
理论上说,MIoU值越大(越接近1),模型效果越好。

代码:

def Mean_Intersection_over_Union(confusion_matrix):
    Per_class_IoU = np.diag(confusion_matrix) / (
                np.sum(confusion_matrix, axis=1) + np.sum(confusion_matrix, axis=0) -
                np.diag(confusion_matrix))
    MIoU = np.nanmean(Per_class_IoU) # 跳过0值求mean
    return MIoU
 
Mean_Intersection_over_Union(matrix)

其中函数解读:

np.diag(v, k=0): 提取对角线元素或构造对角线数组。
	k: 对角线的位置参数,0为默认主对角线,1为主对角线偏上一个单位,-1为主对角线偏下一个单位,以此类推。
np.sum(v, axis): 如果axis为None,则数组所有元素求和,得到一个值。如果指定axis,则按指定轴求和,例如二维数组,
	axis=0: 列和,返回数组
	axis=1 or -1: 行和,返回数组
np.nanmean(): array中nan取值为0,且取均值时忽略它

2.2 计算PA

代码:

def Pixel_Accuracy(confusion_matrix):
    Acc = np.diag(confusion_matrix).sum() / confusion_matrix.sum()
    return Acc
    
Pixel_Accuracy(matrix)

2.3 计算MPA

代码:

 def Pixel_Accuracy_Class(confusion_matrix):
 	# -----------------------------------------#
 	#	np.diag(confusion_matrix):一维数组
 	#	confusion_matrix.sum(axis=1):一维数组
 	# 	per_class_Acc:一维数组
 	# -----------------------------------------#
    per_class_Acc = np.diag(confusion_matrix) / confusion_matrix.sum(axis=1)
    Acc = np.nanmean(per_class_Acc)		# 返回一个数值
    return Acc
       
Pixel_Accuracy_Class(matrix)

3 计算 MIoU 总体代码

get_miou.py代码中,给出了下列代码,完成计算miou的全部过程。
在往下看之前,需要先看DeeplabV3+获取数据集预测结果灰度图,或者你知道运行下方代码操作前,要先有数据集分割预测结果(8位灰度图png)存放在本地文件夹中即可。

import os

from PIL import Image
from tqdm import tqdm

# ----------------------------------------------------------#
#	DeeplabV3表示分割网络结构,其代码在deeplab.py中
#	得到数据集分割预测结果(8位灰度图png),本篇文章不用
# ----------------------------------------------------------#
from deeplab import DeeplabV3   
# ---------------------------------------------------------------------#
#	compute_mIoU和show_results,其代码在utils/utils_metrics.py中,解读见下节
# ---------------------------------------------------------------------#
from utils.utils_metrics import compute_mIoU, show_results


"""
进行指标评估需要注意:
该文件生成的图为灰度图,因为值比较小,按照PNG形式的图看是没有显示效果的,所以看到近似全黑的图是正常的。
"""
if __name__ == "__main__":
    #---------------------------------------------------------------------------#
    #   miou_mode用于指定该文件运行时计算的内容
    #   miou_mode为0代表整个miou计算流程,包括获得预测结果、计算miou。
    #   miou_mode为1代表仅仅获得预测结果。其解读见:https://blog.csdn.net/weixin_45377629/article/details/124159784?spm=1001.2014.3001.5501
    #		本篇不介绍该过程
    #   miou_mode为2代表仅仅计算miou。	讲这个部分
    #---------------------------------------------------------------------------#
    miou_mode       = 2
    #------------------------------------#
    #   分类个数+1、如2+1
    #   VOC数据集,所需要区分的类的个数+1
    #------------------------------------#
    num_classes     = 21
    #--------------------------------------------#
    #   区分的种类,和json_to_dataset里面的一样
    #   种类名称,此例为VOC
    #--------------------------------------------#
    name_classes    = ["background","aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"]
    # name_classes    = ["_background_","cat","dog"]
    #-------------------------------------------------------------------#
    #   指向VOC数据集所在的文件夹
    #   默认指向根目录下的VOC数据集
    #   链接:https://pan.baidu.com/s/1OZfxoyVUKlESsyqs1nuuuw 提取码:wlna
    #-------------------------------------------------------------------#
    VOCdevkit_path  = '../VOCdevkit'

    #--------------------------------------------#
    #   image_ids:['图片名1', '图片名2',...]
    #--------------------------------------------#
    image_ids       = open(os.path.join(VOCdevkit_path, "VOC2007/ImageSets/Segmentation/val.txt"),'r').read().splitlines() 
    gt_dir          = os.path.join(VOCdevkit_path, "VOC2007/SegmentationClass/")
    miou_out_path   = "miou_out"
    #-------------------------------------------------#
    #   pred_dir预测结果png图片路径,只有8位深度,灰度图
    #   正常jpg,RGB三通道,24位深度
    #   彩色png,RGBA四通道,32位深度
    #-------------------------------------------------#
    pred_dir        = os.path.join(miou_out_path, 'detection-results')  

    #-------------------------------------------------#
    #   获得预测结果,输出为8位深度的灰度图
    #	解读见:https://blog.csdn.net/weixin_45377629/article/details/124159784?spm=1001.2014.3001.5501
    #-------------------------------------------------#
    if miou_mode == 0 or miou_mode == 1:
        if not os.path.exists(pred_dir):
            os.makedirs(pred_dir)
            
        #-----------------------------------------------------------------------------------#
        #   详细解读见:https://blog.csdn.net/weixin_45377629/article/details/124124238
        #-----------------------------------------------------------------------------------#
        print("Load model.")
        deeplab = DeeplabV3()
        print("Load model done.")

        print("Get predict result.")
        for image_id in tqdm(image_ids):
            image_path  = os.path.join(VOCdevkit_path, "VOC2007/JPEGImages/"+image_id+".jpg")
            image       = Image.open(image_path)
            # ------------------------------------#
            #   image是png图片,8位深度,灰度图
            #   deeplab.get_miou_png(image)见下方解读
            #   # image size:(原图宽, 原图高)
            # ------------------------------------#
            image       = deeplab.get_miou_png(image)   
            image.save(os.path.join(pred_dir, image_id + ".png"))
        print("Get predict result done.")

    #-----------------------------------------------------------------------------#
    #   计算miou,怎么计算的,见下面分析
    #   gt_dir:VOC2007/SegmentationClass/,里面放着分割标注png图片
    #   pred_dir:预测结果输出路径,此时里面按道理已经有预测输出的8位png图片了
    #   image_ids:val.txt中的每一行图片名称,image_ids:['图片名1', '图片名2',...]
    #   num_classes:分类个数+1,故VOC为21
    #   name_classes:种类名称,例如这样:["_background_","cat","dog",...]
    #	compute_mIoU()见下一节分析
    #-----------------------------------------------------------------------------#
    if miou_mode == 0 or miou_mode == 2:
        print("Get miou.")
        # --------------------------------------------------------#
        #   hist:验证集的混淆矩阵,shape:(21,21)
        #   IoUs:每个类别的IoU,shape:(21,)
        #   PA_Recall:每个类别的像素精度召回率(行和),shape:(21,)
        #   Precision:每个类别的像素精度(列和),shape:(21,)
        # --------------------------------------------------------# 
        hist, IoUs, PA_Recall, Precision = compute_mIoU(gt_dir, pred_dir, image_ids, num_classes, name_classes)  # 执行计算mIoU的函数
        print("Get miou done.")

4 compute_mIoU()函数 代码解析

针对一张图片,针对10张图片,针对验证集所有图片,函数理解要有深度。

import csv
import os
from os.path import join

import matplotlib.pyplot as plt
import numpy as np
import torch
import torch.nn.functional as F
from PIL import Image

# ---------------------------------------------------#
#   生成混淆矩阵,设标签宽W,长H
#   a是转化成一维数组的预测结果,形状(H×W,);
#   b是转化成一维数组的标签,形状(H×W,);
#   n是类别数,21
# ---------------------------------------------------#
def fast_hist(a, b, n):
    # ---------------------------------------------------#
    #   确保a在0~n的范围内,因为b为标签,默认是在0~n范围的
    #   k是(HxW,)的True和False一维数列
    # ---------------------------------------------------#
    k = (a >= 0) & (a < n)
    #--------------------------------------------------------------------------------#
    #   np.bincount计算了从0到n**2-1这n**2个数中每个数出现的次数,返回是一个一维数组
    #       minlength:限制返回列表的最小长度
    #   array.astype(int):主要是用来保证array里面元素确实为int
    #   array.reshape(n, n):返回值形状为(n, n)
    #   返回中,斜对角线上的为分类正确的像素点
    #   更详细介绍参考:https://blog.csdn.net/weixin_45377629/article/details/124237272
    #--------------------------------------------------------------------------------#
    return np.bincount(n * a[k].astype(int) + b[k], minlength=n ** 2).reshape(n, n)  

# --------------------------------------------------------------------#
#   每个类别的IoU,返回一维数组
#   np.maximum(X, Y) 用于逐元素比较两个array的大小,返回大的数,结果为一维数组
#       这样做为了防止出现除以0的情况
# --------------------------------------------------------------------#
def per_class_iu(hist):
    return np.diag(hist) / np.maximum((hist.sum(1) + hist.sum(0) - np.diag(hist)), 1) 

# ----------------------------------------------#
#   array.sum(1):每一行的和,返回一个一维数组
# ----------------------------------------------#
def per_class_PA_Recall(hist):
    return np.diag(hist) / np.maximum(hist.sum(1), 1) 

# ----------------------------------------------#
#   array.sum(0):每一列的和,返回一个一维数组
# ----------------------------------------------#
def per_class_Precision(hist):
    return np.diag(hist) / np.maximum(hist.sum(0), 1) 

# ----------------------------#
#   像素精度PA
# ----------------------------#
def per_Accuracy(hist):
    return np.sum(np.diag(hist)) / np.maximum(np.sum(hist), 1) 

#-----------------------------------------------------------------------------#
#   计算miou
#   gt_dir:VOC2007/SegmentationClass/,里面放着分割标注png图片
#   pred_dir:预测结果输出路径,,此时里面已经有预测输出的8位png图片了
#   png_name_list==image_ids:val.txt中的每一行图片名称,['图片名1', '图片名2',...]
#   num_classes:分类个数+1,故VOC为21
#   name_classes:种类名称,例如这样:["_background_","cat","dog",...]
#-----------------------------------------------------------------------------#
def compute_mIoU(gt_dir, pred_dir, png_name_list, num_classes, name_classes):  
    print('Num classes', num_classes)  
    #-----------------------------------------#
    #   创建一个全是0的矩阵,是一个混淆矩阵
    #   shape: 21x21
    #-----------------------------------------#
    hist = np.zeros((num_classes, num_classes))  
    
    #------------------------------------------------#
    #   gt_imgs: 获得验证集标签路径列表,方便直接读取
    #   pred_imgs: 获得验证集分割预测结果路径列表,方便直接读取
    #------------------------------------------------#
    gt_imgs     = [join(gt_dir, x + ".png") for x in png_name_list]  
    pred_imgs   = [join(pred_dir, x + ".png") for x in png_name_list]  

    #------------------------------------------------#
    #   读取每一个(图片-标签)对
    #------------------------------------------------#
    for ind in range(len(gt_imgs)): 
        #------------------------------------------------#
        #   读取一张图像分割结果,转化成numpy数组
        #------------------------------------------------#
        pred = np.array(Image.open(pred_imgs[ind]))  
        #------------------------------------------------#
        #   读取一张对应的标签,转化成numpy数组
        #------------------------------------------------#
        label = np.array(Image.open(gt_imgs[ind]))  

        #------------------------------------------------#
        #   如果图像分割结果与标签的大小不一样,这张图片就不计算
        #   ndarray.flatten(): 返回一个折叠成一维的数组
        #------------------------------------------------#
        if len(label.flatten()) != len(pred.flatten()):  
            print(
                'Skipping: len(gt) = {:d}, len(pred) = {:d}, {:s}, {:s}'.format(
                    len(label.flatten()), len(pred.flatten()), gt_imgs[ind],
                    pred_imgs[ind]))
            continue

        #------------------------------------------------------------------------------------------#
        #   对一张图片计算21×21的hist矩阵(混淆矩阵)
        #   并累加(毕竟是求miou,针对多张图片)
        #   这儿第一个参数和第二个参数不可以交换,有点迷,结合链接
        #   https://blog.csdn.net/weixin_45377629/article/details/124237272?spm=1001.2014.3001.5501
        #------------------------------------------------------------------------------------------#
        hist += fast_hist(label.flatten(), pred.flatten(), num_classes)  
        #-----------------------------------------------------------#
        #   每10张就输出一下 目前已计算的图片 中 所有类别平均的mIoU值
        #   np.nanmean(): array中nan取值为0,且取均值时忽略它
        #-----------------------------------------------------------#
        if ind > 0 and ind % 10 == 0:  
            print('{:d} / {:d}: mIou-{:0.2f}%; mPA-{:0.2f}%; Accuracy-{:0.2f}%'.format(
                    ind, 
                    len(gt_imgs),
                    100 * np.nanmean(per_class_iu(hist)),       # 100* 是为了显示百分数
                    100 * np.nanmean(per_class_PA_Recall(hist)),
                    100 * per_Accuracy(hist)
                )
            )
    #-----------------------------------------------------#
    #   在上面for循环完成后,hist已经是针对所有验证集的混淆矩阵了
    #   计算所有验证集图片的逐类别mIoU值
    #   IoUs:一维数组,每个类别的IoU
    #-----------------------------------------------------#
    IoUs        = per_class_iu(hist)            # shape: (21,)
    PA_Recall   = per_class_PA_Recall(hist)     # shape: (21,)
    Precision   = per_class_Precision(hist)     # shape: (21,)
    #------------------------------------------------#
    #   逐类别输出一下mIoU值
    #------------------------------------------------#
    for ind_class in range(num_classes):
        print('===>' + name_classes[ind_class] + ':\tIou-' + str(round(IoUs[ind_class] * 100, 2)) \
            + '; Recall (equal to the PA)-' + str(round(PA_Recall[ind_class] * 100, 2))+ '; Precision-' + str(round(Precision[ind_class] * 100, 2)))

    #-----------------------------------------------------------------#
    #   在所有验证集图像上求所有类别平均的mIoU值,计算时忽略NaN值
    #-----------------------------------------------------------------#
    print('===> mIoU: ' + str(round(np.nanmean(IoUs) * 100, 2)) + '; mPA: ' + str(round(np.nanmean(PA_Recall) * 100, 2)) + '; Accuracy: ' + str(round(per_Accuracy(hist) * 100, 2)))
    # --------------------------------------------------------#
    #   np.array(hist, np.int),hist:验证集的混淆矩阵,shape:(21,21)
    #   IoUs:每个类别的IoU,shape:(21,)
    #   PA_Recall:每个类别的像素精度召回率(行和),shape:(21,)
    #   Precision:每个类别的像素精度(列和),shape:(21,)
    # --------------------------------------------------------#   
    return np.array(hist, np.int), IoUs, PA_Recall, Precision

5 输出展示实例

以mobilenetv2为backbone的deeplabv3+模型在voc上的评价指标输出演示:

Load model.
model_data/deeplab_mobilenetv2.pth model, and classes loaded.
Load model done.
Get predict result.
100%|████████████████████████████████████████| 1449/1449 [15:38<00:00,  1.54it/s] 
Get predict result done.
Get miou.
Num classes 21
10 / 1449: mIou-42.30%; mPA-46.51%; Accuracy-95.15%
20 / 1449: mIou-48.72%; mPA-53.71%; Accuracy-94.98%
30 / 1449: mIou-54.87%; mPA-61.61%; Accuracy-94.93%
...中间删掉了
1430 / 1449: mIou-72.43%; mPA-82.89%; Accuracy-93.59%
1440 / 1449: mIou-72.46%; mPA-82.90%; Accuracy-93.60%
===>background: Iou-93.07; Recall (equal to the PA)-97.0; Precision-95.83
===>aeroplane:  Iou-82.29; Recall (equal to the PA)-92.21; Precision-88.44
===>bicycle:    Iou-41.94; Recall (equal to the PA)-87.43; Precision-44.63
===>bird:       Iou-83.24; Recall (equal to the PA)-92.82; Precision-88.97
===>boat:       Iou-62.78; Recall (equal to the PA)-78.5; Precision-75.81
===>bottle:     Iou-70.33; Recall (equal to the PA)-87.74; Precision-78.0
===>bus:        Iou-93.43; Recall (equal to the PA)-95.99; Precision-97.22
===>car:        Iou-85.4; Recall (equal to the PA)-90.35; Precision-93.97
===>cat:        Iou-88.13; Recall (equal to the PA)-94.02; Precision-93.36
===>chair:      Iou-35.82; Recall (equal to the PA)-56.13; Precision-49.75
===>cow:        Iou-79.71; Recall (equal to the PA)-86.17; Precision-91.4
===>diningtable:        Iou-50.65; Recall (equal to the PA)-54.37; Precision-88.11
===>dog:        Iou-80.32; Recall (equal to the PA)-90.76; Precision-87.47
===>horse:      Iou-78.72; Recall (equal to the PA)-88.03; Precision-88.15
===>motorbike:  Iou-82.17; Recall (equal to the PA)-91.93; Precision-88.56
===>person:     Iou-80.3; Recall (equal to the PA)-86.76; Precision-91.51
===>pottedplant:        Iou-57.04; Recall (equal to the PA)-70.01; Precision-75.49
===>sheep:      Iou-82.15; Recall (equal to the PA)-89.21; Precision-91.22
===>sofa:       Iou-46.13; Recall (equal to the PA)-51.58; Precision-81.36
===>train:      Iou-84.59; Recall (equal to the PA)-89.52; Precision-93.89
===>tvmonitor:  Iou-66.49; Recall (equal to the PA)-72.73; Precision-88.58
===> mIoU: 72.6; mPA: 83.01; Accuracy: 93.59
Get miou done.

6 感谢链接

https://blog.csdn.net/weixin_44791964/article/details/107687970
https://blog.csdn.net/weixin_44791964/article/details/120113686
https://www.jianshu.com/p/42939bf83b8a
https://www.bilibili.com/video/BV173411q7xF?p=15
https://blog.csdn.net/qq_41375318/article/details/108380694
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【DeeplabV3+ MIoU】DeeplabV3+计算评价指标 的相关文章

  • 解压码

    BN00001 22kke BN00002 88cde BN00003 00ike BN00004 76cdb BN00005 09dbm BN00006 0mndc BN00007 cd78d BN00008 bdmf8 BN00009
  • 保险项目业务流程

    1 整个项目分为四分模块 xff1a 信息采集模块 信息验证 审批 生成合同 xff08 开单 xff09 信息采集模块 xff1a 包括购买保险产品 xff0c 客户个人信息 1 纸质文档给客户填写 xff0c 在回来录入系统 2 客户直
  • IDEA使用maven自定义archetype

    标题自定义archetype 在pom文件中添加archetype plugin span class token generics span class token punctuation lt span plugin span clas
  • 自定义Perperties文件内容读取

    新建properties文件放在resources目录下 properties文件内容 url span class token operator 61 span jdbc span class token operator span my
  • 使用CSS中的Hover控制显示子元素或者兄弟元素

    lt DOCTYPE html gt lt html lang 61 34 en 34 gt lt head gt lt meta charset 61 34 UTF 8 34 gt lt meta name 61 34 viewport
  • iphone表情显示问号_如何在iPhone上搜索特定的表情符号

    iphone表情显示问号 Most of us use emoji on our iPhone but until recently finding the right one has been tricky Luckily startin
  • maven项目中的jdbc连接步骤

    在maven项目pom xml中到入驱动包 xff08 以下是驱动包代码 xff09 lt dependencies gt lt https mvnrepository com artifact mysql mysql connector
  • executeUpdate()与executeQuery()的使用

    增 删 改 用executeUpdate xff08 xff09 返回值为int型 xff0c 表示被影响的行数 例子 查用executeQuery 返回的是一个集合 next xff08 xff09 表示 指针先下一行 xff0c 还有f
  • Access denied for user ''@'localhost' (using password: YES)错误解决方法

    远程登录被拒绝 xff0c 要改一个表数据的属性让他可以远程登录 解决方法如下 xff0c 执行命令 xff1a mysql gt use mysql mysql gt select host user from user 查看结果是不是r
  • leetcode部分数据库+sqlzoo练习题

    175 组合两个表 SQL架构 表1 Person 43 43 43 列名 类型 43 43 43 PersonId int FirstName varchar LastName varchar 43 43 43 PersonId 是上表主
  • ubuntu下手动安装gnome插件

    ubuntu下手动安装gnome插件 span class token comment 下载环境 span sudo apt span class token operator span span class token keyword g
  • 类和对象的理解

    类和对象的关系 是java中两个重要的概念 xff0c 简单一句话将就是 xff1a 类是对象的模板 xff0c 对象是类的实例 比如 xff1a 设计车的图纸是类 xff0c 然后比亚迪 本田 奔驰这些车 xff08 对象 xff09 都
  • java设计模式的几种体现方式

    1 单例模型 有时候在我的设计中 xff0c 所有的类只共享一个实例 xff0c 那么这时候就需要设计一个单实例的类 思路是将这个类构造器私有化 xff0c 这样外部就无法直接创建对象 xff0c 然后提供公有的静态方法 xff0c 让外部
  • springIOC使用xml装配JavaBean对象

    在一个maven工程下 xff0c 在pom xml中导入spring依赖和相关的配置 lt xml version 61 34 1 0 34 encoding 61 34 UTF 8 34 gt lt project xmlns 61 3

随机推荐