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

——- 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();
	}
}

运行结果为:

注: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  类名  参数,将值传入。就相当于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>

运行结果为:

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

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

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

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

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

静态代码块:

        格式: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");
	}
}

运行结果为:

 

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

示例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);
	}
}

运行结果为:

注:这里的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);
	}
}

运行结果为:

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值