黑马程序员——Java基础:static关键字、单例设计模式

时间:2021-02-23 00:39:23

——- android培训java培训、期待与您交流!  ———-

一、static静态关键字

用法:是一个修饰符,用于修饰成员(成员变量,成员函数)

当成员被静态修饰后,就多了一个调用方式,除了可以被对象调用外,还可以直接被类名调用:类名.静态成员

特点:1.随着类的加载而加载,随着类的消失而消失,说明它的生命周期最长。

            2.优先于对象存在,静态是先存在的,对象是后存在的。

            3.被所有对象共享(数据是对象共有的)

            4.可以直接被类名所调用。

实例变量和类变量的区别?

1.别名不同

        成员变量又称实例变量

        静态变量又称类变量

2.存放位置不同

         类变量随着类的加载而存在于方法区(共享数据区)的静态区中,所以也叫对象的共享数据。

         实例变量随着对象的建立而存在于堆内存中,也叫做对象的特有数据。

3.调用方式不同

        类变量可以被对象调用,还可以被类名调用

        实例变量只能被对象调用

4.生命周期不同(类变量>对象>实例变量)

        类变量生命周期最长,随着类的加载而存在,随着类的消失而消失。

        实例变量生命周期,随着对象的创建而存在,随着对象的回收而释放。

静态使用的注意事项:

       1.静态方法只能访问静态成员(方法和变量),非静态方法既可以访问静态,也可以访问非静态。

       2.静态方法中不可以定义this,super关键字,因为静态优先于对象存在,所以静态方法中不可以出现this。

       3.主函数是静态的。

静态的好处:对对象的共享数据进行单独空间的存储,节省空间,没有必要每一个对象都存储一份,可以直接被类调用。

静态的弊端:生命周期过长,访问出现了局限性(静态虽好,但只能访问静态)。

示例:

class Person{
private String name;//成员变量/实例变量
static String country="CN";//静态的成员变量/类变量
public static void show(){
System.out.println("country2="+country);
}
}
public class Test4 {

public static void main(String[] args) {
//类名调用
System.out.println("country="+Person.country);
Person.show();
}
}

运行结果为:

黑马程序员——Java基础:static关键字、单例设计模式

注:public static void main(String[] args)是一个特殊的函数,作为程序的入口,可以被JVM(java虚拟机)调用。

        public:代表着该函数的访问权限最大

        static:代表主函数随着类的加载就已近存在了

        void:主函数没有具体的返回值

        main:不是关键字,但是是一个特殊的单词,可以被JVM识别

       (String[] args):函数的参数,参数类型是一个数组,该数组中的元素是字符串,是字符串类型的数组

主函数是固定格式的,被JVM识别。

JVM在调用主函数时,传入的是new String[0];

示例:

class Test6
{
public static void main(String[] args)
{
for(int i=0;i<args.length;i++)
{
System.out.println(args[i]);
}
}
}
<span style="font-size:14px;">
</span>

运行结果为:

黑马程序员——Java基础:static关键字、单例设计模式
注:java  类名  参数,将值传入。就相当于String[ ] args="haha,kaka";

什么时候使用静态?

从修饰静态成员变量和函数入手。

  1).什么时候定义静态变量(类变量)呢?

        当对象中出现共享数据时,该数据被静态所修饰。

        当对象中有特有数据要定义成非静态存在于堆内存中。

  2).什么时候定义静态函数呢?

       当功能内部没有访问到非静态数据(对象的特有数据)时,该功能可以定义成静态的。

    简单点说,从源代码看,该功能是否需要访问非静态的成员变量,如果需要,该功能就是非静态的。
    如果不需要,就可以将该功能定义成静态的。当然,也可以定义成非静态,但是非静态需要被对象调用。
    如果没有访问特有数据的方法,该对象的创建是没有意义。

静态的应用:

每一个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装成类,以便复用。

示例:定义一个类ArrayTool,在该类中可以对整数数组进行查询其中最大值,最小值,排序,打印等功能。

/*java的说明书通过文档注释来完成*/
/**
这是一个数组操作类,在该类中可以对整型数组进行
查询其中最大值,最小值,排序,打印等功能。
@author huan
@version v1.0
*/
public class ArrayTool77//只有public protected才可以创建文档
{
private ArrayTool77(){}//强制让该类不能创建对象,外界想使用该对象只能类名调用
/**
获取数组中的最大值
@param arr 接收一个整型类型的数组
@return max 返回数组中的最大值
*/
public static int getMax(int[] arr)
{
int max=arr[0];
for(int i=0;i<arr.length;i++)
{
if(max<arr[i])
max=arr[i];
}
return max;
}
/**
获取数组中的最小值
@param arr 接收一个整型类型的数组
@return min 返回数组中的最小值
*/
public static int getMin(int[] arr)
{
int min=arr[0];
for(int i=0;i<arr.length;i++)
{
if(min>arr[i])
min=arr[i];
}
return min;
}
/**
将数组从小到大进行选择排序
@param arr 接收一个整型类型的数组
*/
public static void xuZeSort(int[] arr)
{
for(int x=0;x<arr.length-1;x++)
{
for(int y=x+1;y<arr.length;y++)
{
if(arr[x]>arr[y])
{
swap(arr,x,y);
/*
int temp;
temp=arr[x];
arr[x]=arr[y];
arr[y]=temp;*/
}

}
}
}
/**
将数组从小到大进行冒泡排序
@param arr 接收一个整型类型的数组
*/
public static void maoPaoSort(int[] arr)
{
for(int x=0;x<arr.length-1;x++)
{
for(int y=0;y<arr.length-x-1;y++)
{
if(arr[y]>arr[y+1])
{
swap(arr,y,y+1);
/*
int temp;
temp=arr[y];
arr[y]=arr[y+1];
arr[y+1]=temp;
*/
}
}
}
}
/**
给数组中的元素进行位置置换
@param arr 接收一个整型类型的数组
@param a 要置换的位置
@param b 要置换的位置
*/
private static void swap(int[] arr,int a,int b)
{
int temp;
temp=arr[a];
arr[a]=arr[b];
arr[b]=temp;
}
/**
打印数组中的元素
@param arr 接收一个整型类型的数组
*/
public static void print(int[] arr)
{
System.out.print("{");
for(int i=0;i<arr.length;i++)
{
if(i!=arr.length-1)
System.out.print(arr[i]+",");
else
System.out.println(arr[i]+"}");
}
}
}
<span style="font-size:12px;">
</span>

在另一个文件中调用该类,使用其中方法:

class StaticDemo77 
{
public static void main(String[] args)
{
int[] arr=new int[]{3,1,5,7,4,8};

int max=ArrayTool77.getMax(arr);
System.out.println("max="+max);

int min=ArrayTool77.getMin(arr);
System.out.println("min="+min);

ArrayTool77.xuZeSort(arr);
ArrayTool77.print(arr);
ArrayTool77.maoPaoSort(arr);
ArrayTool77.print(arr);
}
}
<span style="font-size:12px;">
</span>

运行结果为:

黑马程序员——Java基础:static关键字、单例设计模式

如果将ArrayTool77.class文件发送给其他人,其他人只要将该文件设置到classpath路径下,即可使用该工具类。但是,该类中到底定义了多少个方法,对方却不清楚,因为该类并没有使用说明书。

那么如何制作说明书呢?Java的说明书是通过文档注释(/**  注释内容  */)来完成的。可以将ArrayTool77类制作成说明书。方法如下:

黑马程序员——Java基础:static关键字、单例设计模式

注:这里的ArrayTool77类必须是public或者protected权限的。

如果出现如上的情况,说明生成成功,会生成MyHelp文件夹,文件夹中的内容如下:

黑马程序员——Java基础:static关键字、单例设计模式

打开index,既是打开ArrayTool77类的说明书:

黑马程序员——Java基础:static关键字、单例设计模式

静态代码块:

        格式:static { 静态代码块中的执行语句 }

        特点:随着类的加载而执行,只执行一次,并且优先于主函数。

        作用:给类进行初始化,用到类中的内容就加载了。

 示例1:

class StaticCode
{
static//静态代码块
{
System.out.println("A");
}
}
class StaticDemo
{
static
{
System.out.println("B");
}
public static void main(String[] args)
{
new StaticCode();
new StaticCode();
System.out.println("C");
}
static
{
System.out.println("D");
}
}

运行结果为:

 黑马程序员——Java基础:static关键字、单例设计模式

注:由代码显示可得知:静态代码块可以定义多次,且每一个只执行一次,且优先于主函数。

示例2:

class StaticCode
{
int num=9;

StaticCode()//构造函数
{
System.out.println("B");
}

static//静态代码块
{
System.out.println("A");
}

{//构造代码块
System.out.println("C"+this.num);
}

StaticCode(int x)
{
System.out.println("D");
}
}
class StaticDemo
{
static
{
System.out.println("F");
}
public static void main(String[] args)
{
new StaticCode();
System.out.println("===============");
new StaticCode(4);
}
}

运行结果为:

黑马程序员——Java基础:static关键字、单例设计模式

注:这里的num不可以放在静态代码块中,因为无法从静态上下文中引用非静态变量

二、单例设计模式

设计模式:解决某一类问题最行之有效的方式。

单例设计模式:解决一个类在内存中只存在一个对象的问题,保证对象的唯一性。例如:A、B两个人只对同一个文件进行操作。

如何保证对象唯一性?

       1.为了避免其他程序过多的建立该类对象,先禁止其他程序建立该类对象

       2.为了其他程序可访问到该类对象,只好在本类中自定义一个对象

       3.为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式

这三步的代码体现:

       1.将构造函数私有化

       2.在本类中创建一个本类对象

       3.提供一个方法可以获取该类对象

示例(饿汉式):先初始化对象。

class Single1
{
//private:不能够创建对象,只能类名调用,所以getInstance()要是静态的
//因为静态只能访问静态,所以s也要是静态的,因为s是该类中的特有数据,所以是private
private Single1(){}
private static Single1 s=new Single1();
public static Single1 getSingle1()
{
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single1 s1=Single1.getSingle1();
Single1 s2=Single1.getSingle1();
System.out.println(s1==s2);
}
}

运行结果为:true

注:Single1类一进内存,就创建好了对象。之所以不用Single.s;的方式获取Single对象,而采用getInstance获取是因为在getInstance方法中我们可以做一些判断来决定是否返回Single的对象,也就是实现了对单例对象的可控。所以,给Single的构造方法加上了private限制,禁止使用者直接采用Single.s;的方式获取。

示例(懒汉式):对象方法被调用时才初始化,也叫做对象的延时加载。

class Single2
{
private Single2(){}
private static Single2 s=null;
public static Single2 getSingle2()
{
if(s==null)
s=new Single2();
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single2 s1=Single2.getSingle2();
Single2 s2=Single2.getSingle2();
System.out.println(s1==s2);
}
}

运行结果为:true

注:Single2类进入内存,对象还没有建立,只有调用了getSingle2方法时,才建立了对象。当多线程访问时,会出现安全问题,可以加同步代码块或同步函数方式实现。但比较低效,可使用双重判断的形式来解决效率问题。加同步的时候,使用的锁是该类所属的字节码文件对象。

示例:

class Single2
{
private Single2(){}
private static Single2 s=null;
public static Single2 getSingle2()
{
if(s==null)
{
synchronized(Single2.class)
{
if(s==null)
s=new Single2();
}
}
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single2 s1=Single2.getSingle2();
Single2 s2=Single2.getSingle2();
System.out.println(s1==s2);
}
}

运行结果为:

黑马程序员——Java基础:static关键字、单例设计模式

——- android培训java培训、期待与您交流!  ———-