JAVA static 单例模式
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA static 单例模式相关的知识,希望对你有一定的参考价值。
class single
//构造函数私有化
//创建一个本类对象
static single s = new single();
private single()
static single getInstance()
return s;
class singleDemo
public static void main(String[] args)
single s1=single.getInstance();
single s2=single.getInstance();
//我调用2次single.getInstace();
static single s = new single();被调用了是2次么?
为什么只产生一个对象
首先我们分析一下单例的意义,在Java中为一个类只能有一个实例化对象。这显然不是我们平常所写的一个普通类所能够做到的事情。那么为了实现这一个要求该如何去做呢。实例化是一个类的初始化时候的问题,而初始化是构造方法去做的事情(当然这里面jvm也帮我们做了很多的事情)。构造方法,我们要设为私有。这样我们让该类不能被随意的初始化,那该在哪初始化,而它有怎样被调用呢。
一个不能初始化的类,也就是说不能够被实例化的类,我们想要调用。那么无疑让我们想到了static关键字,一个public static 的方法可以实现我们这个要求——即在不实例化类的情况下能够调用其中的方法。于是我们写下了一个public static 的方法,用于得到这个类的唯一实例。而这个实例我们是在类的内部实例,并同样定义为 static 的变量(一直用static 只是为了能够不实例化便可以使用。因为static可以在类初始化的时候便生成了,对于此不理解的同学们可以查看我的前一篇博客。。。)。到这里我们一个单例模式的实现框架其实已经搭好了。 参考技术A 你大概理解错了,你声明的是一个static属性,那么它就会在虚拟机加载这个类的时候就帮你实例化好,以后不再执行了,这个single对象会在虚拟机运行时一直存在于内存之中,所以static single s = new single();这句话只会被调用一次,所以自然就只有这一个对象啦追问
NB 谢了啊 帮我确认了假设
追答不客气
本回答被提问者采纳 参考技术B 代码写的很规范,但是不符合单例模式,所谓单例模式,就是不管你调用几次,都只返回一个实例对象。你的代码问题在于,single类每次都会调用static single s = new single();所以有问题。
这段代码应该修改为:
static single s = null;
private single()
static single getInstance()
if(s == null)
s = new singgle();
return s;
这样才是单例模式,不管调用几次,都是同一个实例对象,你可以试试。
亲,记得采纳哦!追问
亲 单例有两种 一种是延时加载 一种是非延时加载 你这个是延时加载 我写的是非延时加载回复
Java_static关键字,单例设计模式,代码块,final关键字,接口,内部类
Java_static关键字
- 当编写一个类时,是在描述其对象的属性和行为,并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。
- 无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。此时用到
static
Java_类属性、类方法的设计思想
- 类属性作为该类各个对象之间共享的变量。
- 在设计类时,分析哪些属性不因对象的不同而改变,将这些属性设置为类属性。与类属性相应的方法设置为类方法。
- 如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用。
Java_static的使用范围
- 在Java类中,
可用static修饰属性、方法、代码块、内部类
被修饰后的成员具备以下特点:
- 随着类的加载而加载
- 优先于对象存在
- 修饰的成员,被所有对象所共享
- 访问权限允许时,可不创建对象,直接被类调用
class Circle {
private double radius;
public static String name = "这是一个圆";
public static String getName() {
return name;
}
public Circle(double radius) {
this.radius = radius;
}
public double findArea() {
return Math.PI * radius * radius;
}
public void display() {
System.out.println("name:" + name + "radius:" + radius);
}
}
public class StaticTest {
public static void main(String[] args) {
Circle c1 = new Circle(2.0);
Circle c2 = new Circle(3.0);
c1.display();
c2.display();
}
}
Java_类变量(class Variable)
- 类变量(类属性)由该类的所有实例共享
public class Person {
private int id;
public static int total = 0;
public Person() {
total++;
id = total;
}
public static void main(String args[]){
Person Tom=new Person();
Tom.id=0;
total=100; // 不用创建对象就可以访问静态成员
}
}
public class StaticDemo {
public static void main(String args[]) {
Person.total = 100; // 不用创建对象就可以访问静态成员
//访问方式:类名.类属性,类名.类方法
System.out.println(Person.total);
Person c = new Person();
System.out.println(c.total); //输出101
}
}
Java_类方法(class method)
- 没有对象的实例时,可以用
类名.方法名()
的形式访问由static
修饰的类方法。 - 在
static方法
内部只能访问类的static修饰的属性或方法
,不能访问类的非static的结构
。 - static修饰的方法不能被重写
class Person {
private int id;
private static int total = 0;
public static int getTotalPerson() {
//id++; //非法
return total;
}
public Person() {
total++;
id = total;
}
}
public class PersonTest {
public static void main(String[] args) {
System.out.println("Number of total is " + Person.getTotalPerson());
//没有创建对象也可以访问静态方法
Person p1 = new Person();
System.out.println( "Number of total is "+ Person.getTotalPerson());
}
}
注意:因为不需要实例就可以访问static方法,因此static方法内部不能有this和super。
class Person {
private int id;
private static int total = 0;
public static void setTotalPerson(int total){
this.total=total; //非法,在static方法中不能有this,也不能有super
}
public Person() {
total++;
id = total;
}
}
public class PersonTest {
public static void main(String[] args) {
Person.setTotalPerson(3);
}
}
Java_单例(Singleton)设计模式
- 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱。”套路”
- 类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
- 如果我们要让类在一个虚拟机中只能产生一个对象,首先必须将类的构造器的访问权限设置为
private
,这样,就不能用new操作符
在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
单例设计模式-饿汉式样例
class Singleton {
// 1.私有化构造器
private Singleton() {
}
// 2.内部提供一个当前类的实例
// 4.此实例也必须静态化
private static Singleton single = new Singleton();
// 3.提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
return single;
}
}
单例(Singleton)设计模式-懒汉式样例
- 下列样式还存在问题,暂时未修复
class Singleton {
// 1.私有化构造器
private Singleton() {
}
// 2.内部提供一个当前类的实例
// 4.此实例也必须静态化
private static Singleton single;
// 3.提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
if(single == null) {
single = new Singleton();
}
return single;
}
}
Java_单例模式的优点
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
Java_单例设计模式的使用场景
- 网站的计数器,一般也是单例模式实现,否则难以同步
- 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志 文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
- 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置 文件数据,都生成一个对象去读取。
Application
也是单例的典型应用- Windows的
Task Manager
(任务管理器)就是很典型的单例模式 - Windows的
Recycle Bin (回收站)
也是典型的单例应用。在整个系统运行过程
中,回收站一直维护着仅有的一个实例。
Java_main()方法
- 由于Java虚拟机需要调用类的
main()
方法,所以该方法的访问权限必须是public
- 又因为Java虚拟机在执行
main()
方法时不必创建对象,所以该方法必须是static
修饰的 main()方法接收
一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。- 因为
main() 方法
是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
public class CommandPara {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println("args[" + i + "] = " + args[i]);
}
}
}
//运行程序CommandPara.java
通过cmd执行 java CommandPara "Tom" "Jerry" "Shkstart"
args[0] = Tom
args[1] = Jerry
args[2] = Shkstart
Java_代码块
- 代码块(或初始化块)的作用:对Java类或对象进行初始化
- 代码块(或初始化块)的分类:一个类中代码块若有修饰符,则只能被
static
修饰,称为静态代码块(static block)
,没有使用static
修饰的,为非静态代码块。
static代码块通常用于初始化static的属性
class Person {
public static int total;
static {
total = 100;//为total赋初值
}
…… //其它属性或方法声明
}
Java_静态代码块
静态代码块:用static修饰的代码块
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
- 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
- 静态代码块的执行要先于非静态代码块。
- 静态代码块随着类的加载而加载,且只执行一次。
Java_非静态代码块
非静态代码块:没有static修饰的代码块
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 除了调用非静态的结构外,还可以调用静态的变量或方法。
- 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
- 每次创建对象的时候,都会执行一次。且先于构造器执行。
Java_成员变量赋值的执行顺序
- 程序中成员变量赋值的执行顺序
- 显式初始化、多个初始化块依次被执行(同级别下按先后顺序执行)
- 构造器再对成员进行初始化操作
- 通过
”对象.属性”
或”对象.方法
”的方式,可多次给属性赋值
Java_final关键字
- 在Java中声明类、变量和方法时,可使用关键字final来修饰,表示“最终的”。
final
标记的类不能被继承。提高安全性,提高程序的可读性。final
标记的方法不能被子类重写。final
标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。
5.final标记
的成员变量必须在声明时或在每个构造器中或代码块中显式赋值,然后才能使用。例如final double MY_PI = 3.14
final修饰的实例:
- final修饰类
final class A{
}
class B extends A{ //错误,不能被继承。
}
- final 修饰方法
class A {
public final void print() {
System.out.println("A");
}
}
class B extends A {
public void print() { // 错误,不能被重写。
System.out.println("我爱你");
}
}
- final修饰变量,常量
class A {
private final String INFO = "Love"; //声明常量 ,常量名要大写,内容不可修改
public void print() {
//The final field A.INFO cannot be assigned
//INFO = "woaini";
}
}
Java_抽象类和抽象方法
- 类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
- 用
abstract
关键字来修饰一个类,这个类叫做抽象类。 - 用
abstract
来修饰一个方法,该方法叫做抽象方法。 - 抽象方法:只有方法的声明,没有方法的实现。以分号结束
;
,比如:public abstract void talk();
- 含有抽象方法的类必须被声明为抽象类。
- 抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
- 不能用
abstract
修饰变量、代码块、构造器; - 不能用
abstract
修饰私有方法、静态方法、final的方法
、final的类。
abstract class void A {
m1(); 抽象类举例
public void m2() {
System.out.println("A类中定义的m2方法");
}
}
class B extends A {
void m1() {
System.out.println("B类中定义的m1方法");
}
}
public class Test {
public static void main(String args[]) {
A a = new B();
a.m1();
a.m2();
}
}
Java_接口(interface)
- 接口就是规范定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个"是不是"的关系,而接口实现则是 "能不能"的关系。接口的本质是契约,标准,规范
- 接口(interface)是抽象方法和常量值定义的集合。
接口的特点:
- 接口
(interface)
是抽象方法和常量值定义的集合。 - 接口中的所有成员变量都默认是由
public static final
修饰的。 - 接口中的所有抽象方法都默认是由
public abstract
修饰的。 - 接口中没有构造器。
- 接口采用多继承机制。
定义接口的实例
public interface Student {
int ID = 1;
void start();
public void run();
void stop();
}
- 定义Java类的语法格式:先写
extends,后写implements
,实例:class SubClass extends SuperClass implements InterfaceA{ };
表示SubClass
继承自SuperClass
实现了InterfaceA
接口 - 一个类可以实现多个接口,接口也可以继承其它接口
- 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类(不能被实例化)
- 接口的主要用途就是被实现类实现。(面向接口编程),与继承关系类似,接口与实现类之间存在多态性
- 接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。
interface Runner {
public void start();
public void run();
public void stop();
}
class Person implements Runner {
public void start() {
// 准备工作:弯腰、蹬腿、咬牙、瞪眼
// 开跑
}
public void run() {
// 摆动手臂
// 维持直线方向
}
public void stop() {
// 减速直至停止、喝水。
}
}
Java_接口的应用(代理模式Proxy)
代理模式概述
- 代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问,就像古代的分封制度一样
// 接口代理的样例
interface Network {
public void browse();
}
// 被代理类
class RealServer implements Network {
@Override
public void browse() {
System.out.println("真实服务器上
网浏览信息");
}
}
// 代理类
class ProxyServer implements Network {
private Network network;
public ProxyServer(Network network) {
this.network = network;
}
public void check() {
System.out.println("检查网络连接等操作");
}
public void browse() {
check();
network.browse();
}
}
public class ProxyDemo {
public static void main(String[] args) {
Network net = new ProxyServer(new RealServer());
net.browse();
}
}
代理模式的应用场景
- 安全代理:屏蔽对真实角色的直接访问。
- 远程代理:通过代理类处理远程方法调用(RMI)
- 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
代理模式的分类
- 静态代理(静态定义代理类)
- 动态代理(动态生成代理类)-动态代理我不会唉!
Java_接口和抽象类的对比
如果抽象类和接口都可以使用的话,优先使用接口,因为避免单继承的局限
接口:
- 接口主要是抽象方法和全局常量的集合,
- 接口包含常量、抽象方法、(jdk8.0:默认方法、静态方法)
- 接口的子类实现接口(implements)
- 接口不能继承抽象类,但允许继承多个接口
- 接口没有单继承的局限
- 接口是作为一个标准或是表示一种能力(标准和规范)
抽象类
- 抽象类是包含抽象方法的类
- 抽象类由抽象方法,构造方法,普通方法,常量,变量组成
- 子类可以继承抽象类(extends)
- 抽象类可以实现多个接口
- 抽象类可以通过对象的多态性产生实例化对象
- 抽象类有单继承的局限
Java_Java8中关于接口的改动
- Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
- 静态方法:使用
static
关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。 - 默认方法:默认方法使用
default
关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。
Java_接口中的默认方法
- 若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接口时,会出现:接口冲突。解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突。
- 若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守:类优先原则。接口中具有相同名称和参数的默认方法会被忽略。
Java_内部类
- 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类
- 在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。
- 内部类一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。同时内部类的名字不能和外部类类名相同
内部类分为成员内部类和局部内部类
- 成员内部类(
static
成员内部类和非static
成员内部类) - 局部内部类(不谈修饰符)、匿名内部类
成员内部类作为类的成员的角色:
- 成员内部类和外部类不同,内部类还可以声明为
private
或protected;
- 成员内部类可以调用外部类的结构
- 成员内部类可以声明为
static
的,但此时就不能再使用外层类的非static
的成员变量
成员内部类作为类的角色:
- 成员内部类可以在内部定义属性、方法、构造器等结构
- 成员内部类可以声明为abstract类 ,因此可以被其它的内部类继承
- 成员内部类可以声明为abstract类 ,因此可以被其它的内部类继承
- 成员内部类可以声明为final的
- 成员内部类编译以后生成
.class
字节码文件(也适用于局部内部类)
Java_内部类注意
- 非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员
内部类中才可声明static成员。 - 外部类访问成员内部类的成员,需要**“内部类.成员”或“内部类对象.成员”**的方式
- 成员内部类可以直接使用外部类的所有成员,包括私有的数据
- 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态
class Outer {
private int s;
public class Inner {
public void mb() {
s = 100;
System.out.println("在内部类Inner中s=" + s);
}
}
public void ma() {
Inner i = new Inner();
i.mb();
}
}
public class InnerTest {
public static void main(String args[]) {
Outer o = new Outer();
o.ma();
}
}
如何声明局部内部类
class 外部类{
方法(){
class 局部内部类{
}
}
}
如何使用局部内部类
- 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类
- 局部内部类的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类的父类或父接口类型
局部内部类的特点
- 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的
.class
文件,但
是内部类的前面冠以外部类的类名和$符号,以及数字编号
。 - 只能在声明局部内部类的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类。
- 局部内部类可以使用外部类的成员,包括私有的。
- 局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局
部变量的声明周期不同所致。 - 局部内部类和局部变量地位类似,不能使用
public,protected,default,private
- 局部内部类不能使用
static
修饰,因此也不能包含静态成员
Java_匿名内部类
- 匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在
new
的后面,用其隐含实现一个接口或实现一个类。
匿名内部类格式:
new 父类构造器(实参列表)|实现接口(){
//匿名内部类的类体部分
}
匿名内部类的特点:
- 匿名内部类必须继承父类或实现接口
- 匿名内部类只能有一个对象
- 匿名内部类对象只能使用多态形式引用
interface A{
public abstract void fun1();
}
public class Outer{
public static void main(String[] args) {
new Outer().callInner(new A(){
//接口是不能new但此处比较特殊是子类对象实现接口,只不过没有为对象取名
public void fun1() {
System.out.println(“implement for fun1");
}
});// 两步写成一步了
}
public void callInner(A a) {
a.fun1();
}
}
以上是关于JAVA static 单例模式的主要内容,如果未能解决你的问题,请参考以下文章
Java_static关键字,单例设计模式,代码块,final关键字,接口,内部类