• 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文件不在同一个目录下)

     
  • 相关阅读:
    多线程交替打印示例
    单列集合框架体系Collection
    同域名下,两个网站通过cookie共享登录注册功能大概思路。
    CSS 隐藏滚动条
    Vue3--组件间传值
    TypeScript--类(class)
    TypeScript--泛型(generic)
    理解LDAP与LDAP注入
    CRLF injection 简单总结
    pigctf期末测评
  • 原文地址:https://www.cnblogs.com/yiqiu2324/p/2868718.html
Copyright © 2020-2023  润新知