day06_静态(static)


 

static关键字:

static是一个修饰符,用于修饰成员(成员变量,成员函数), 不能修饰局部变量(局部变量生命周期随着作用域结束而结束).

当成员被static修饰后,除了可以被对象调用,还可以用 类名.静态成员

/*
方法区:存放类中的方法(函数包括构造函数)和共享数据(多个对象共享)
static特点:
①随着类的加载而加载,随着类的消失而消失.
  当person类被使用,加载到内存
  country="cn"已经在开辟好了空间
   String name;还没有在内存中,因为还没有创建对象


②根据以上
  静态成员 先于 对象存在


③被该类所有对象共享
④可以直接被类名调用
 */
class Person
{
   String name;//成员变量/实例变量
   static String country="CN";//静态成员变量/类变量
   public static void showCountry()
   {
       //System.out.println(name);//根据①,此时可能没有任何对象,
				      //而通过类名直接调用showCountry()
				      //那么name属于哪个对象?说白了,对象
				      //都还没有,哪来的name.
       System.out.println(country);
   }
}
class StaticDemo
{
	public static void main(String[] args)
	{
	  Person p=new Person();
	  p.showCountry();//CN
	  Person.showCountry();//CN
	}
}
/*
(理解为主!)
类变量和实例变量的区别:
1.存放位置
  类变量 随着类的加载 而存在于 方法区 中
  实例变量 随着对象的建立 而存在于堆内存中
2.生命周期
  
  类变量>实例变量(当对象消失,对象中的实例变量也跟着消失,但类依然还在
                    类变量也还存在)
  
  类变量生命周期最长,随着类消失而消失
  实例变量随着对象的消失而消失.

*/



/*
  静态使用注意事项:
Ⅰ.静态方法  只能访问  静态成员
   非静态方法 既可以访问 静态 也可以访问 非静态
    (静态先在,非静态后再)


Ⅱ.静态方法中 不可以 定义 this,super 关键字
    因为静态先于对象存在,所以静态方法中不可以有this

Ⅲ.主函数是静态的
*/
静态简单示例

static优点与缺点:(理解)

优点:对 对象共享的数据单独进行存储,有利于节约空间(不用每个对象都存储一份)

         可以通过类名直接调用.

缺点:生命周期过长,只能访问静态.

 

什么时候用静态呢?

/*
一.什么时候定义静态变量(类变量)?
   当对象中出现共享数据时,存放该数据的变量定义成静态
   而对象中特有的数据定义成非静态存放在堆内存中.

二.什么时候定义静态方法?
    当该方法中没有访问到对象中特有的数据时
*/
class Person
{
  String name;
  //public void show()
  public static void show()
  {
    System.out.println("show()");
  
  }

}
class StaticF 
{
	public static void main(String[] args) 
	{
		//Person p=new Person();
		//p.show();
        Person.show();//由于show()中没有用到非静态变量(对象特有数据)
		              //完全可以把show()用static修饰 通过类名访问
	}
}

        静态代码块(StaticOfCode):

    示例:

/*
静态代码块:
格式:
static
{
  静态代码块中的执行语句
}
特点:
①随着类的加载而执行,只执行一次(当第二次用到该类时,将不再执行类中的静态代码块),并优先于主函数执行.(主函数被JVM调用才执行)
②用于给类进行初始化
类什么时候加载?
 只有用到 类中的内容才加载
*/
class StaticCode
{
    static
	{
	  System.out.println("a");
	
	}
	
}
class StaticCodeDemo
{
  static//首先加载StaticCodeDemo类 该静态代码块执行
  {
    System.out.println("b");
  
  }
  public static void main(String[] args)//主函数需要JVM调用才执行
  {					   //JVM调用主函数
         new StaticCode();//加载StaticCode类 执行其静态代码块
	 new StaticCode();//StaticCode已经加载,由于静态代码块只执行一次
	 		   //因此不再执行静态代码块
	 
        //StaticCode s=null;//将不再加载该类,s没有指向任何对象
	 //s=new StaticCode();//用到了类中的默认构造函数
	 
	 System.out.println("over");

  
  }
  static//执行该静态代码块
  {
    System.out.println("c");
   }


}

静态代码块演示

与构造代码块示例:

class StaticCode
{
    int num=10;
	StaticCode(int num)
	{ 
	  System.out.println("StaticCode的构造函数");
	  this.num=num;
	 
	}
	static
	{
	  System.out.println("StatiCode的静态代码块");
	
	}
	{
	  System.out.println("StaticCode的构造代码块"+""+num);
	
	}
	
}
class StaticCodeDemo
{
  static
  {
    System.out.println("StaticCodeDemo的静态代码块 1");
  
  }

  public static void main(String[] args)
  {
	 System.out.println("主函数");
	 StaticCode s=new StaticCode(4);
  }
  static
  {
    System.out.println("StaticCodeDemo的静态代码块 2");
   }


}

Static_Construct

为了更清楚以上过程,首先来看一下对象初始化过程.

示例:

class Person 
{
  private int age;
  private String name;
  private static String country="cn"; 
  
  Person(String name)
  {
   this.name=name;
  }
  Person(int age,String name)
  {
  this.age=age;
  this.name=name;
  }
  
  
  public void setName(String name)
  {
	this.name=name;
  }
  
  public void speak()
  {
    System.out.println(this.name+"..."+this.age);
  
  }
  public static void country()
  {
     System.out.println("country="+country);   
    
  }
}
class Demo
{
  public static void main(String[] args)
  {
   Person p=new Person("lisi");
  
  }
}
/*
 Person p=new Person("lisi");
 在内存中(注意顺序):
 ①new Person("lisi")用到Person.class文件,JVM查找并加载Person.class
 
 ②执行静态代码块,如果有,给Person类初始化
 
 ③在堆内存中开辟空间,分配内存地址  
 
 ④在堆内存中建立对象特有(非static)属性,进行默认初始化(null,0...)
 
 ⑤对属性进行显式初始化(private int age=10;)
 
 ⑥对 对象 进行  构造代码块  初始化
 
 ⑦对 对象 进行对应 构造函数 初始化

⑧将 堆内存中的地址 赋给 栈内存中 的对象引用变量p ⑤和⑥的执行顺序与代码的书写顺序一致 如下流程图: */

创建对象内存加载过程

 

在上面的主函数中加上:

Person p2=new  Person("zhangsan",20);

p2.setName("lisi");

内存图大致为:
MemoryDemo 

单例设计模式:

 
/*
设计模式:解决某一问题行之有效的方法
java中23中设计模式

单例设计模式:解决一个类在内存中只存在一个对象

要求:A和B程序 操作的同一对象

①将构造函数私有化
②在类内部创建一个本类对象
③对外提供一个方法获取这个对象
*/
//饿汉式(安全简单)-->开发采用
//Single类一进内存就创建了对象
class Single
{
  private int num;
  private Single(){} //禁止类外初始化(建立)对象(根据需要的构造函数)
  private static Single s=new Single();    
  public static Single getInstance() //由于要求外部不能创建对象
  {                                 //要想访问该方法必须通过类名访问
    return s;                       //因此加上static修饰
  }

  public void setNum(int num)
  {
   this.num=num;
  }
  public int getNum()
  {
    return num;
  }
}
 
 
//懒汉式
//调用方法时才创建对象,叫做对象的延迟加载—>懒汉式
//Single类进内存还没有创建对象,调用getInstance时才创建对象
class Single()
{
  private static Singel s=null;
  private Single(){}
  public static Single getInstance()//在Single 前加 synchronized 同步 学到多线程技术深入理解
    {
       if(s==null) 
      {
        /*
-->A A执行到此处,cpu切换到B
-->B s==null依然满足
-->A A创建对象返回
-->B此时B又创建对象返回
*/
s=new Single(); return s } //改进写法: //if(s==null) //{ // synchronized(Single.class) // { // if(s==null) // s = new Single(); // } } } } class SingleDemo { public static void main(String[] args) { Single s1=Single.getInstance(); Single s2=Single.getInstance(); s1.setNum(30); System.out.println(s2.getNum()); } }

SingleDemo

 

解析下主函数:

public static void main(String[] args)
{
}
①为什么要加public修饰符?
这是因为JVM需要调用类的main()方法,这时把main方法暴露出去,权限足够大,所以方法的权限必须是public
②为什么要加static修饰符?
这是因为JVM在执行main()方法是不必创建对象,所以方法必须是static
 
void:JVM调用的main()方法要求返回值为void
 
main:不是关键字,但是能够被JVM识别
 
Sting[] args:形参为一个 字符串数组的引用变量 args
 
为什么写args?
arguments(参数)逐渐演变而来,当然可以用别的引用变量
JVM在调用main()方法时传入的为new String[0];(这个可以再main()中验证)
帮助文档制作:
简单示例:
//ArrayTool.java
/**
 这是一个可以对数组进行操作的工具类,该类中提供,排序,求最值等功能
 @author 张三
 @version V1.1
 */

public class  ArrayTool
{  


	 private ArrayTool(){}//不加private,javadoc.exe也不会提取,更改访问权限为public
	   
    /**
	这是交换数组中的两个元素
        @param b 接收一个int[] 型的数组引用变量
	@param i 要交换的值
	@param j 要交换的值
   */
   //交换
   private static void swap(int[] b,int i,int j)
	{
      int temp=b[i];
      b[i]=b[j];
      b[j]=temp;
    }
	/**
	这是对数组中的元素从小到大排序
	@param a 接收一个int[] 型的数组引用变量
	
	*/
   //冒泡排序
   public static void bubbleSort(int[] a)
   {
     for(int i=0;i<a.length-1;++i)//控制趟数
      for(int j=0;j<a.length-i-1;++j)//控制每趟比较的次数
	    if(a[j]>a[j+1])
	      swap(a,j,j+1);
   }
   /**
	这是对数组中的元素从小到大排序
	@param b 接收一个int[] 型的数组引用变量
	
	*/
   //选择排序
    public static void selectSort(int[] b)
	{
	  for(int i=0;i<b.length-1;++i)//控制趟数
	     for(int j=i+1;j<b.length;++j)//共比较(arr.length-1)-(i+1)+1
	        if(b[i]>b[j])                //即arr.length-i-1
	          swap(b,i,j);
	}
   //选择排序第二种写法:
    /**
	这是对数组中的元素从小到大排序
	@param c 接收一个int[] 型的数组引用变量
	
	*/
    public static void selectSort_2(int[] c)
    {
	   int k;
	   for(int i=0;i<c.length-1;++i)
		{
	      k=i;
	      for(int j=i+1;j<c.length;++j)
			 if(c[k]>c[j])
	            k=j; 
		  if(k!=i)
		   swap(c,k,i);
	    }
    }
	/**
	 打印数组中的元素.
	 打印格式:[element1,element2,....]
	 @param arr 接收一个int[] 型的数组引用变量
	 
	*/
  
   //打印数组
    public static void showArray(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]+"]");
    }
   /**
  获取一个整形数组中的最大值.
  @param arr 接收一个int[] 型的数组引用变量
  @return 会返回一个数组中的最大值
   */
	//求最大值
   public static int getMax(int[] arr)
   {
     int max;
	 max=arr[0];
     for(int i=1;i<arr.length;++i)
       if(arr[i]>max)
	       max=arr[i];
	   return max;
   }
   //求最小值
    /**
  获取一个整形数组中的最小值.
  @param arr 接收一个int[] 型的数组引用变量
  @return 会返回一个数组中的最小值
   */
   public static int getMin(int[] arr)
   {
     int min;
	 min=arr[0];
     for(int i=1;i<arr.length;++i)
       if(arr[i]<min)
	     min=arr[i];
	   return min;
   }

} 
/*
一个类默认会有一个空参数的构造函数
这个默认的构造函数的权限和所属类一致
即默认构造函数的权限随着类的变化而变化
注意:
class Demo
{
  Demo(){}//这个不是默认构造函数,是自定义构造函数

}
*/
用javadoc.exe提取文档注释:
MyHelp
 
当两个类在不同的Java文件,并且一个类用到另一个:
例如:(用到上面的ArrayTool.java)
//ArrayToolTest.java
class  ArrayToolShow
{
	public static void main(String[] args) 
	{
		int[] arr={3,7,1,2,5};
		//ArrayTool tool=new ArrayTool();
		//tool.bubbleSort(arr);
		//tool.showArray(arr);
		ArrayTool.bubbleSort(arr);//把ArrayTool中的bubbleSort定义成静态,通过类名
		ArrayTool.showArray(arr);
	}
}

用到ArrayToolTest.java中的类.
注意:
当使用 Javac ArrayToolTest.java(其中用到类ArrayTool) 时,JVM会在指定路径(classpath)找有没有ArrayTool.class文件,若有则编译成功没有则JVM 会在classpath路径下再找一次看有没有ArrayTool.java(名称必须和类名相同) 有则先编译ArrayTool.java,之后再编译ArrayToolTest.java.
ArrayTest
 
也可以set classpath=.;类所在的目录 //先找当前目录再找指定目录(当两个java文件不在同一个目录下)

 
posted @ 2013-01-20 17:24  伊秋  阅读(412)  评论(0编辑  收藏  举报