【Java基础教程】(十八)包及访问权限篇 · 下:Java编程中的权限控制修饰符、单例设计模式 (Singleton)和多例设计模式的综合探析~

news2024/10/7 10:20:27

Java基础教程之包及访问权限 · 下

  • 本节学习目标
  • 1️⃣ 访问控制权限
  • 2️⃣ 命名规范
  • 3️⃣ 单例设计模式 (Singleton)
  • 4️⃣ 多例设计模式

在这里插入图片描述

本节学习目标

  • 掌握Java 中的4种访问权限;
  • 掌握Java 语言的命名规范;
  • 掌握单例设计模式与多例设计模式的定义结构;

1️⃣ 访问控制权限

对于封装性,实际上之前只详细讲解了 private, 而封装性如果要想讲解完整,必须结合全部4种访问权限来看,这4种访问权限的定义如下表所示。

范围privatedefaultprotectedpublic
同包的同类中
同包的不同类
不同包的子类
不同包的非子类

对于上表可以简单理解为: private 只能在本类中访问;default 只能在同一个包中访问;protected 可以在不同包的子类中访问;public 为所有类都可以访问。

前面文章中对于 privatedefaultpublic 都已经有所了解,所以本节主要讲解 protected

//	范例 1: 定义 com.xiaoshan.demoa.A 类
package com.xiaoshan.demoa; 

public class A{
	protected String info="Hello";//使用 protected权限定义
}
//	范例 2: 定义 com.xiaoshan.demob.B 类,此类继承A 类
package com.xiaoshan.demob;

import com.xiaoshan.demoa.A;

public class B extends A{    // B是A 不同包的子类
	public void print(){              //直接访问父类中的protected属性
		System.out.println("A 类的 info = "+ super.info);
	}
}

由于B类是A 的子类,所以在B 类中可以直接访问父类中的 protected 权限属性。

//	范例 3: 代码测试
package com.xiaoshan.test;

import com.xiaoshan.demob.B;

public class Test {
	public static void main(String args[]){
		new B().print();
	}
}

程序执行结果:

A 类的 info = Hello

此程序直接导入了B类 , 而后实例化对象调用 print() 方法,而在 print()方法中利用“super.info” 直接访问了父类中的 protected 权限属性。
而如果要在 com.xiaoshan.test 包中直接利用 Test 主类访问A 中的属性,由于它们不在同一个包下,也不存在继承关系,所以将无法访问。

//	范例 4: 错误的访问
package com.xiaoshan.test;

import com.xiaoshan.demoa.A;

public class Test {
	public static void main(String args[]){
		A a = new A();
		System.out.println(a.info);		//错误:无法访问
	}
}

此程序在进行编译时会直接提示用户,infoprotected 权限,所以无法被直接访问。

实际上在给出的4种权限中,有3种权限 ( privatedefaultprotected) 都是对封装的描述,也就是说面向对象的封装性现在才算是真正讲解完整。从实际的开发使用来讲,几乎不会使用到 default 权限,所以真正会使用到的封装概念只有两个权限 privateprotected

对于访问权限,初学者要把握以下两个基本使用原则即可。

  • 属性声明主要使用private权限;
  • 方法声明主要使用public权限。

2️⃣ 命名规范

命名规范的主要特点就是保证程序中类名称或方法等名称的标记明显一些,可是对于 Java 而言,有如下一些固定的命名规范还是需要遵守的。

  • 类名称:每一个单词的开头首字母大写,例如:TestDemo;
  • 变量名称:第一个单词的首字母小写,之后每个单词的首字母大写,例如: studentName;
  • 方法名称:第一个单词的首字母小写,之后每个单词的首字母大写,例如: printInfo();
  • 常量名称:每个字母大写,例如: FLAG;
  • 包名称:所有字母小写,例如:com.xiaoshanjava.util

需要注意以上所给出的5种命名规范,是所有开发人员都应该遵守的,而不同的开发团队也可能会有属于自己的命名规范,对于这些命名规范,在日后从事软件开发的过程中,都应该仔细遵守。

3️⃣ 单例设计模式 (Singleton)

在之前大部分的属性定义时都使用了 private 进行声明,而对于构造方法其实也可以使用 private 声明,则此时的构造方法就被私有化。而构造方法私有化之后会带来哪些问题,以及有什么作用呢?下面就来进行简单的分析。

首先在讲解私有化构造方法操作之前,来观察如下的程序。

//	范例 5: 构造方法非私有化
class Singleton {                 	//定义一个类,此类默认提供无参构造方法
	public void print(){
		System.out.println("Hello World.");
	}
}

public class TestDemo{
	public static void main(String args[])(
		Singleton inst = null;                          //声明对象
		inst = new Singleton(;                       //实例化对象
		inst.print();                                       //调用方法
	}
}

程序运行结果:

Hello World.

在本程序中, Singleton 类里面存在构造方法 ( 因为如果一个类中没有明确地定义一个构造方法,则会自动生成一个无参的、什么都不做的构造方法) , 所以可以先直接实例化对象,再调用类中提供的 print()方法。下面将构造方法改变一下,即使用 private封装。

//	范例 5: 私有化构造方法
class Singleton{                                       //定义一个类
	private  Singleton(){                                 //构造方法私有化
	}
	public void print(){
		System.out.println("Hello World.");
	}
}

public class TestDemo {
	public static void main(String args[]){
		Singleton inst = null;               //声明对象
		inst = hew Singletono:           //错误:The constructor Singleton() is not visible
		inst.print();                      //调用方法
	}
}

此程序在实例化 Singleton 类对象时,程序出现了编译错误,因为构造方法被私有化了,无法在外部调用,即无法在外部实例化 Singleton类的对象。

那么现在在保证 Singleton 类中的构造方法不修改不增加,以及 print()方法不修改的情况下,如何操作才可以让类的外部通过实例化对象去调用 print()方法呢?

思考一:使用 private 访问权限定义的操作只能被本类所访问,外部无法调用,现在既然构造方法被私有化,就证明这个类的构造方法只能被本类所调用,即只能在本类中产生本类实例化对象。

//	范例 6: 第一步思考
class Singleton {	//定义一个类
	Singleton instance = new Singleton();	//在内部实例化本类对象
	
	private Singleton(){	//构造方法私有化
	}
	
	public void print(){
		System.out.println("Hello World.");
	}
}

思考二: 对于一个类中的普通属性,默认情况下一定要在本类存在实例化对象后才可以进行调用,可是此程序在 Singleton 类的外部无法产生实例化对象,就必须想一个办法,让 Singleton 类中的 instance 属性可以在没有 Singleton 类实例化对象时来进行调用。因此可以使用 static 完成,static 定义的属性特点是由类名称直接调用,并且在没有实例化对象时候可以调用。

//	范例 7: 第二步思考
class Singleton {	//定义一个类

	static Singleton instance = new Singleton();	//可以由类名称直接访问  

	private Singleton(){	//构造方法私有化
	}
	
	public void print(){
		System.out.println("Hello World.");
	}
}
	
public class TestDemo {
	public static void main(String args[]){ 
		Singleton inst = null; //声明对象
		inst = Singleton.instance; //利用“类.static属性”方式取得实例化对象
		inst.print();	//调用方法 
	}
}

程序运行结果:

Hello World.

思考三: 类中的全部属性都应该封装,所以上边范例中的 instance 属性也应该进行封装,而封装之后要想取得属性,则要编写 getter方法,只不过这时的 getter 方法应该也由类名称直接调用,定义为 static 型。

//	范例 8: 第三步思考
class Singleton {                	//定义一个类
	private static Singleton instance = new Singleton();
	
	private Singleton(){         	//构造方法私有化
	}
	
	public void print(){
		System.out.println("Hello World.");
	}
	
	public static Singleton getInstance(){                 //取得本类对象
		return instance;
	}
}

public class TestDemo(
	public static void main(String args[]){ 
		Singleton inst = null;			//声明对象
		inst = Singleton.getInstance();	//利用“类.static方法()”取得实例化对象
		inst.print();			//调用方法
	}
}

程序运行结果:

Hello World.

思考四: 这样做的目的是什么?程序中的 instance 属性属于 static 定义,就表示所有 Singleton 类的对象不管有多少个对象声明,其本质都会共同拥有同一个 instance 属性引用,那么既然是同一个,又有什么意义呢?

如果要控制一个类中实例化对象的产生个数,首先要锁定的就是类中的构造方法(使用 private 定义构造方法), 因为在实例化任何新对象时都要使用构造方法,如果构造方法被锁,就自然就无法产生新的实例化对象。

如果要调用类中定义的操作,那么很显然需要一个实例化对象,这时就可以在类的内部使用 static 方式来定义一个公共的对象,并且每一次通过 static 方法返回唯一的一个对象,这样外部不管有多少次调用,最终一个类只能够产生唯一的一个对象,这样的设计就属于单例设计模式 (Singleton)。

不过本程序依然有一个问题,那就是以下代码也可以使用。

//	范例 9: 程序出现的问题
class Singleton {       //定义一个类
	private static Singleton instance = new Singleton();
	
	private Singleton(){  	//构造方法私有化
	}
	
	public void print(){
		System.out.println("Hello World.");
	}
	
	public static Singleton getInstance(){	// 取得本类对象   
		instance = new Singleton();	//重新实例化对象
		return instance;
	}
}

此程序中操作语法没有错误,也不需要考虑是否有意义,现在的代码也允许这样做,而这样做会发现之前表示唯一一个实例化对象的所有努力就白费了。因此,必须想办法废除这种做法,于是需要在定义 instance 的时候增加一个 final 关键字。

//	范例 10: 一个完整的单例模式的程序 
class Singleton {                 	//定义一个类
	private final static Singleton instance = new Singleton();
	
	private Singleton(){        	//构造方法私有化
	}
	
	public void print(){
		System.out.println("Hello World.");
	}
	
	public static Singleton getInstance(){	//取得本类对象
		return instance;
	}
}

public class TestDemo{
	public static void main(String args[]){ 
		Singleton inst = null;	//声明对象
		inst =  Singleton.getInstance(); 	//利用“类.static方法()”取得实例化对象
		inst.print();		//调用方法
	}
}

程序运行结果:

Hello World.

在使用 Singleton 类时,不管代码如何操作,也永远只会存在唯一的一个 Singleton 类的实例化对象,而这样的代码,在设计模式上就称为单例设计模式(Singleton)。

4️⃣ 多例设计模式

单例设计模式只留下一个类的一个实例化对象,而多例设计模式,会定义出多个对象。

例如:定义一个表示星期几的操作类,这个类的对象只能有7个实例化对象(星期一 ~星期日);定义一个表示性别的类,只能有2个实例化对象(男、女);定义一个表示颜色基色的操作类,只能有3个实例化对象(红、绿、蓝)。

这些情况下,这样的类就不应该由用户无限制地去创造实例化对象,应该只使用有限的几个,这个就属于多例设计。不管是单例设计还是多例设计,有一个核心不可动摇,即构造方法私有化。

//	范例 11: 定义一个表示性别的类
package com.xiaoshan.demo;

class Sex{
	private String title;
	private static final Sex MALE = new Sex("男");
	private static final Sex FEMALE = new Sex("女");
	
	private Sex(String title){		//构造私有化
		this.title = title;
	}
	public String toString(){
		return this.title;
	}
	
	public static Sex getInstance(int ch){	//返回实例化对象
		switch (ch){
			case 1:
				return MALE;
			case 2:
				return FEMALE;
			default:
				return;
		}
	}
}

public class TestDemo  {
	public static void main(String args[]){
		Sex sex = Sex.getInstance(2);
		System.out.println(sex);
	}
}

程序执行结果:

此程序首先定义了一个描述性别的多例程序类,并且将其构造方法封装,然后利用 getInstance() 方法,接收指定编号后返回一个实例化好的Sex 类对象。

范例11的代码利用数字编号来取得了一个 Sex 类的对象,有朋友可能觉得这样做表示的概念不明确,那么为了更加明确要取得对象类型,可以引入一个接口进行说明。

//	范例 12: 利用接口标记对象内容
interface Choose {
	public int MAN = 1;		//描述数字
	public int WOMAN = 2;		//描述数字
}

public class TestDemo  {
	public static void main(String args[]){	//利用接口标记内容取得对象
		Sex sex = Sex.getInstance(Choose.MAM); 
		System.out.println(sex);
	}
}

此程序如果要取得指定的 Sex 类对象,可以利用接口中定义的全局常量(实际上也可以在Sex 类中定义一些全局常量)来进行判断。这样的做法是一种标准做法,但是这样做有一些复杂,所以利用字符串直接判断会更加简单一些。

在 JDK 1.7 之前, switch 只能支持对 intchar 类型进行判断,正因为如果纯粹是数字或字符意义不明确,所以增加了String 的支持。

//	范例 13: 对取得Sex 类对象进行修改
package com.xiaoshan.demo;

class Sex{
	private String title;
	private static final Sex MALE = new Sex("男");
	private static final Sex FEMALE = new Sex("女");
	
	private Sex(String title){		//构造私有化
		this.title = title;
	}
	public String toString(){
		return this.title;
	}
	public static Sex getInstance(String ch){
		switch (ch){               	//利用字符串判断
			case "man":
				return MALE;
			case "woman":
				return FEMALE;
			default:
				return null;
		}
	}
}

public class TestDemo {
	public static void main(String args[]){
		Sex sex = Sex.getInstance("man");
		System.out.println(sex);
	}
}

程序执行结果:

此程序直接使用 String 作为 switch 的判断条件,这样在取得实例化对象时就可以利用字符串来描述对象名字,这一点要比直接使用数字更加方便。


温习回顾上一篇(点击跳转)《【Java基础教程】(十七)包及访问权限篇 · 上:包的定义及导入、常用系统包概览,javac、java和jar命令的作用,package和 import关键字的应用~》

继续阅读下一篇(点击跳转)《【Java基础教程】(十九)异常处理篇 · 上:~》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/767675.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【HISI IC萌新虚拟项目】cpu_if的接口cpu_agent utils搭建

关于整个虚拟项目,请参考: 【HISI IC萌新虚拟项目】Package Process Unit项目全流程目录_尼德兰的喵的博客-CSDN博客 前言 spt_agent utils已经完成了(虽然之后可能还会有微调),接下来完成配置通路cpu interface对应的utils。这个通路比较特殊,一是带反馈的接口,二是时…

Android 通过插桩来代理线程池

前言 在日常开发App的过程中,难免需要依赖第三方Sdk,这样就无形中增加了我们自己App的线程数,从而会导致App出现内存溢出、Cpu消耗增加等等负面影响。如果依赖的Sdk提供了线程池代理的接口还好,那样直接设置我们自定义的线程池。但…

密码学学习笔记(十五):ECDSA - 椭圆曲线数字签名算法

椭圆曲线数字签名算法是DSA的一种椭圆曲线变体,它发明的初衷只是避免使用Schnorr签名的专利。椭圆曲线数字签名算法依赖于验证器中的私钥和主机用于验证验证器的公钥。它的缺点和DSA一样,它也没有提供安全性证明。 椭圆曲线算法 DSS(数字签…

工作空间内各文件夹解析

src文件夹用来存放源代码 launch文件夹用来存放一个或多个ros节点启动文件 msg文件夹包含用户定制化消息的定义 srv文件夹包含各种服务的定义 action文件夹包含动作文件 package.xml是软件包清单文件 Cmakelists.txt文件包含编译软件包的各类指令

面试官: 说一下你做过哪些性能优化?

前言 如果你已经有 2 - 3 年以上开发经验还不懂的怎么去优化自己的项目,那就有点说不过去了,下面是我自己总结的一套通用级别的 Android 性能优化。 如果你正在找工作, 那么你需要一份 Android 高级开发面试宝典 1、 你对 APP 的启动有过研究吗? 有做过…

Spring解决数据乱码问题-spring21

乱码数据实际情况: 都出现了?????? 怎么解决: 第一步,找到web.xml配置文件。 改成name控制台就不出来了 如何解决地址方法与Controller业务参数名称不一致的情况&#xf…

Linux 之 基本工具(一)

一、粘滞位 1.背景 在现实生活中,有很多人会在同一台云服务器上共同工作,会出现这些人的某些文件需要保存但又不想保存在各自的家目录下且这些文件需要共给其他一同工作的同事共享(可查阅)的情况,则需要将这些文件保…

Mysql表锁与行锁

Mysql锁实战 前言:什么是锁一:全局锁1.1 概念1.2 作用1.3 使用1.4 特点 二:表级锁2.1 概念2.2 分类2.2.1 表锁2.2.2 元数据锁 MDL2.2.3 意向锁 三:行级锁3.1 行锁(Record Lock)3.2 间隙锁(Gap Lock)3.3 临键锁(Next-Key Lock): 四…

redis乐观锁概念

乐观锁(又名乐观并发控制,Optimistic Concurrency Control,缩写“OCC”),是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。…

Ubuntu下安装、配置及重装CUDA教程

安装CUDA 前往Nvidia CUDA Tools官网选择对应的架构和版本下载CUDA 以如下架构和版本为例: 查看显卡驱动 nvidia-smi如果显卡驱动已经装了,那么在CUDA安装过程中不用再勾选安装driver 下载并安装CUDA wget https://developer.download.nvidia.co…

如何设计一个完美的复杂业务系统架构?

1 什么是复杂系统 我们经常提到复杂系统,那么到底什么是复杂系统。我们看下维基的定义:复杂系统(英语:complex system),又称复合系统,是指由许多可能相互作用的组成成分所组成的系统。强调了两…

字幕切分视频

Whisper 仓库地址: https://github.com/openai/whisper 可用模型信息: 测试视频:18段,总共447S视频(11段前:有11段开头有停顿的视频) Tiny: 跑完:142S ,11段前&#xf…

微服务sleuth+zipkin——链路追踪

一、链路追踪🍉 1.什么是链路追踪?🥝 在大型系统的微服务化构建中,一个系统被拆分成了许多模块。这些模块负责不同的功能,组合成系统,最终可以提供丰富的功能。在这种架构中,一次请求往往需要…

深入理解Java虚拟机(二)Java内存区域与内存溢出异常

一、前言 对于Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码,不容易出现内存泄漏和内存溢出问题,看起来由虚拟机管理内存一切都很美好。不过,也正是因为Java程序…

IEC 103/104

IEC101 串口通信 串口远动协议 控制区和数据区,(信息体地址,装置地址等) IEC102 电量采集 IEC103 保护设备的通信 串口 实时监测 各种故障 103协议就是用来保护装置和后台监控进行通信的。 IEC104 调度自动化系统、厂站之间的通讯 网络 帧里面多了…

Ansible Playbook剧本配置文件

一、执行文件 Playbook配置文件使用YAML语法,具有简 洁明了,结构清晰等特点。Playbook配置文件类似于shell脚本,是一个YAML格式的文件,用于保存针对特定需求的任务列表,前面介绍的ansible命令虽然可以完成各种任务&a…

学习记录——SAM、SPM

Segment Anything Model(SAM) 能分割一切的模型 2023 SAM是一个提示型模型,其在1100万张图像上训练了超过10亿个掩码,实现了强大的零样本泛化。许多研究人员认为「这是 CV 的 GPT-3 时刻,因为 SAM 已经学会了物体是什…

EFLFK——ELK日志分析系统+kafka+filebeat架构(有乌云的天反而是最美的)

文章目录 1.修改filebeat配置文件filebeat.yml收集日志转发(生产)给kafka2.修改logstash配置从kafka中消费日志,并输出到kibana前端展示 在ELFK的基础上,添加kafka做数据缓冲 附kafka消息队列 nginx服务器配置filebeat收集日志&am…

appium自动化测试中控件坐标及控件属性获取

在appium有时会遇到content-desc中存在内容的情况,有时需要校验它的值,那么它是怎么获取的呢? driver.find_element_by_XX().get_attribute("contentDescription") 使用上边的方式可以获取到content-desc中的值。 像上图中我们其…

《3.linux应用编程和网络编程-第9部分-3.9.linux网络编程实践》3.9.6_7.soekct实践编程1_2

概念:端口号,实质就是一个数字编号,用来在我们一台主机中(主机的操作系统中)唯一的标识一个能上网的进程。端口号和IP地址一起会被打包到当前进程发出或者接收到的每一个数据包中。每一个数据包将来在网络上传递的时候…