黑马程序员——>第六天<面向对象(静态-帮助文档-单例设计模式)>


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

 

01面向对象(Static关键字)
 static关键字: 用于修饰成员(成员变量和成员函数)
 被修饰后的成员具备以下特点:
   随着类的加载而加载   随着类的消失而消失,说明它的生命周期最长。
   优先于对象先存在   明确一点:静态是先存在,对象是后存在的。
   被所有对象所共享
   可以直接被类名调用(当成员被静态修饰后,就多了一个调用方式,除了可以被对象调用外,还可以直接被类名调用。 类名.静态成员)

实例变量和类变量的区别:
 存放位置

   类变量随着类的加载而存在于方法区中
   实例变量随着对象的建立而存在于堆内存中
 生命周期
   类变量生命周期最长,随着类的消失而消失
   实例变量生命周期随着对象的消失而消失

   区别:类一进内存,可以给这个类产生n多对象,想什么时候建立对象就什么时候建立对象,可是,对象用完会消失,这个对象一消失,对象中的name也跟着消失(实例变量也跟着消失),而当类消失后,就无法创建对象了,所以,生命周期最长的是类,其次是对象,实例变量是跟着对象走的。


 使用注意:
   静态方法只能访问静态成员(静态先在,非静态还没在内存中,当然不能访问了)
     非静态方法既可以访问静态也可以访问非静态
   静态方法中不可以写this,super关键字
      因为静态优先于对象存在,所以静态方法中不可以出现this
   主函数是静态的

被static关键字修饰后就不在堆内存当中了,会被单独提取出来

特有内容随着对象存储(堆内存当中),共性内容(方法区,共享区,数据区)

class Person
{
   String name;//成员变量,实例变量
   static String country = "CN";//静态的成员变量,类变量   这个变量存在时没有对象,所以使用它只能用类名。所以多一种调用方式:类名调用。
   public void show()
   {
     System.out.println(name+":::::"+country);
   }
}



为什么不把所有成员都定义成静态的,什么数据是对象特有的,什么数据是对象共有的要区分的出来,因为这样它符合现实生活中的描述,还有一点,在开发中,如果你将所有的东西都变成静态,这些数据的生命周期会特别长:对象用完后,成员变量还在,这样会对内存的消耗很大。所以不建议定义过多的静态变量,什么时候定义:要区分的出来是不是被多个对象所共享,是就静态,不是就千万别静态。

本来对象就是用来访问非静态的。

静态有利有弊:
利处:
对对象的共享数据进行单独空间的存储,节省空间,没有必要每一个对象中都存储一份。
   可以直接被类名调用(因为静态在时只有类,没有对象,调用者就俩:一个类名,一个对象。没对象只能用类名来调用)   如果想使用一个类中的成员,一般情况下:建立那个类的对象,调用它里面的方法;那么这个类中没对象时,就得用类名调用。用类名调用,就得在成员里边定义几个静态成员

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


02面向对象(main函数)
public static void main(String[] args)
主函数:是一个特殊的函数,作为程序的入口,可以被jvm调用


  主函数是被虚拟机调用,调用函数时都要往里边传与之对应的参数(实际参数)
    虚拟机调用main时传啥?
       jvm在调用主函数时,传入的是new String[0];

主函数的定义:
 public:代表着该函数访问权限是最大的。
 static:代表主函数随着类的加载就已经存在了。
 void:主函数没有具体的返回值。
 main:不是关键字,但是是一个特殊的单词,可以被jvm识别。
(String[] args):函数的参数,参数类型是一个数组,该数组中的元素是字符串,字符串类型的数组

字符串是最方便的数据

主函数是固定格式的:jvm识别。  只有args(arguments参数)这个可以改,这个是变量名,怎么起都行

public static void main(int x)   这叫做重载,不是主函数


03面向对象(静态什么时候使用)

什么时候使用静态?
  要从两方面下手:
    因为静态修饰的内容有成员变量和函数。
什么时候定义静态变量(类变量)呢?
  当对象中出现共享数据时,该数据被静态所修饰。
  对象中的特有数据要定义成非静态存在于对内存中。

  为了增加程序的严谨性,为了更符合我们描述事物的本质,更贴切,就需要  用静态来修饰一下
什么时候定义静态函数呢(在设计时也能用)一定要背下来
  当功能内部没有访问到非静态数据(对象的特有数据),那么该功能可以定义成静态的。
举例:

class Person
{
   String name;
  public void show()    //没有访问到对象中的特有数据 name  就加static

   {
      System.out.println("haha");
   }
}


 

class Person
{
   String name;
  public void show()    //  绝对不能加static,因为下面那句输出语句中有访问到对象中的特有数据

   {
      System.out.println(name+"haha");
   }
}


04面向对象(静态的应用—工具类)
静态的应用:


 

class Demo
{
  public static void main(String[] args)
  {
     int[] arr = {3,4,1,8};
     int max = 0;
     for(int x=1;x<arr.length;x++)  //数组最大值的获取
      {
         if(arr[x]>arr[max])
            max = x;
      }
    System.out.println("max="+arr[max]);
  }
}



————————————————————————

class Demo
{
  public static void main(String[] args)
  {
     int[] arr = {3,4,1,8};
     int max = getMax(arr);
     
    System.out.println("max="+max);
  }
  public static int getMax(int[] arr)  //通过函数来封装代码,提高了下面代码的复用性
    {
       int max = 0;
       for(int x=1;x<arr.length;x++)  
      {
         if(arr[x]>arr[max])
            max = x;
      }
      return arr[max];
    }
}



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

 


虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作
发现了问题:
1,对象是用于封装数据的,可是ArrayTool对象并未封装特有数据。
2,操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。

这时就考虑,让程序更严谨,是不需要对象的。
可以将ArrayTool中的方法都定义成static的。直接通过类名调用即可。

将方法都静态后,可以方便于使用,但是该类还是可以被其他程序建立对象的。
为了更为严谨,强制让该类不能建立对象。
可以通过将构造函数私有化完成。


 


05面向对象(帮助文档的制作 javadoc)

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

开始制作程序的说明书。java的说明书通过文档注释来完成。

里面通常定义类的描述信息

/**    
这是一个可以对数组进行操作的工具类,该类中提供了获取最值,排序等功能。
@author  张三
@version v1.1
*/

public class ArrayTool //如果一个类要想变成帮助文档,必须是public来修饰

{
   /**
    空参数构造函数
   */ 
   private ArrayTool(){};  // 如果前面什么修饰都没有,也不能提取,权限不够大

  /**
    获取一个整数数组中的最大值。
    @param arr 接收一个int类型的数组。
    @return 会返回一个该数组中的最大值
  */
   public static int getMax(int[] arr)
   {
      int max = 0;
      for(int x=1;x<arr.length;x++)  
      {
         if(arr[x]>arr[max])
            max = x;
      }
      return arr[max];
   }
    /**
    获取一个整数数组中的最小值。
    @param arr 接收一个int类型的数组。
    @return 会返回一个该数组中的最小值
  */
    public static int getMin(int[] arr)
    {
     int min = 0;
     for(int x=1;x<arr.length;x++)  
      {
         if(arr[x]<arr[min])
            max = x;
      }
      return arr[min];
    }
   /**
     给int数组进行选择排序
     @param arr 接收一个int类型的数组。
   */
    
     public static void selectSort(int[] arr)
     {
        for (int x=0; x<arr.length; x++)
        {
           for(int y=x+1; y<arr.length; y++)
           {
              if(arr[x]>arr[y])
              {
                   swap(arr,x,y);
              }
           }
        }
     }

     /**
     给int数组进行冒泡排序
     @param arr 接收一个int类型的数组。
   */
     public static void bubbleSort(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);
                      }
               }
        }
     }

    /**
    给数组中的元素进行位置的置换。
   @param arr 接收一个int类型的数组。
   @param a 要置换的位置。
   @param b 要置换的位置。
   */
     private static void swap(int[] arr,int a,int b)
     {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
     }
   /**
   用于打印数组中的元素,打印形式是:[element1,element2,...]
   */
     public static void printArray(int[] arr)
     {
        System.out.print("[");
        for(int x=0; x<arr.length; x++)
        {
           if(x!=arr.length-1)
               System.out.print(arr[x]+",");
           else
               System.out.print(arr[x]+"]");
        }
     }
}

class ArryToolDemo
{
   public static void main(String[] args)
   {
     int[] arr = {3,1,87,32,8};
    /*
     ArrayTool tool = new ArrayTool();                                       
     int max = tool.getMax(arr);
     System.out.println("max="+max);

     tool.printArray(arr);     排序前
     tool.selectSort(arr);
     tool.printArray(arr);     排序后
    */
    
      int max = ArrayTool.getMax(arr);
      System.out.println("max="+max);   
    
   }
}



 这个文档当中要体现的是你准备对外暴露的内容,那么,私有的是不会体现的,即使写了文档注释,javadoc也不提取,它会判断权限,只有两种权限才会被提取到文档当中去,一个叫做public,一个叫protected 被保护权限

一个类中默认会有一个空参数的构造函数,
这个默认的构造函数的权限和所属类一致。
如果类被public修饰,那么默认的构造函数也带public修饰符。
如果类没有被public修饰,那么默认的构造函数,也没有public修饰。

默认构造函数的权限是随着类的变化而变化的。
api帮助文档 应用程序接口


06面向对象(静态代码块)
格式:
static
{
  静态代码块中的执行语句
}
特点:随着类的加载而执行,只执行一次。并优先于主函数。
      用于给类进行初始化的。
   (静态的都是随着类的加载而加载,而其他内容都有名字,可以被调用才执行,因为它是没有名字的,所以它随着类的加载完,里面的代码紧跟着就执行完了)

class StaticCode
{
   static
   {
        System.out.println("a");  // 5.执行   7.不执行a了,因为静态代码块只执行一次
   }
}

class StaticCodeDemo
{
   static
  {
      System.out.println("b");   // 1.类一加载就执行静态代码块中的内容
  }
   public static void main(String[] args) // 3. 开始读主函数
   {
       new StaticCode();   //4.会将StaticCode这个class文件加载进内存
       new StaticCode();   //6.又new一个
       System.out.println("over"); // 8.
   }
    static
  {
     System.out.println("c");  //  2. 
  }
}



执行结果为  b  c  a  over

class StaticCode
{
   static
   {
        System.out.println("a");   
   }
   public static void show()
   {
       System.out.println("show run");
   }
}

class StaticCodeDemo
{
   static
  {
      //System.out.println("b");    
  }
   public static void main(String[] args)  
   {
      StaticCode s = null;  // 问:a是否有执行 这个类类型变量没有任何实体指向,这个变量存在就没有意义,所以这                                 //个时候还没加载,
     s = new StaticCode();  //这个时候才加载
     StaticCode.show();  // 这样也加载了
   }//在用到这个类中的内容后才会被加载,
    static
  {
     //System.out.println("c");    
  }
}


 
07面向对象(对象的初始化过程)
演示这些代码在内存中的哪一部分进行存储

class Person
{
  private String name=“”;
  private int age;
  private static String country = "cn";
  person(String name,int age)
  {
    this.name = name;
    this.age = age;
  }
  {
  System.out.println(name+".."+age);
  }
  public void setName(String name)
  {
    this.name = name;
  }
  public void speak()
  {
    System.out.println(this.name+"..."+this.age);
  }
  public static void showCountry()
  {
    System.out.println("country="+country);
    method();//省略了person.
  }
  public static void method()
  {
    System.out.println("method run")
  }
}
class
{
  public static void main(String[] args)
  {
    Person p = new Person("zhangsan",20);//new时会将Person.class文件从硬盘当中通过java虚拟机将这个person.class文件加载进内存并开辟堆内存空间
    P.setName("lisi");
  }
}


1.先将类加载进内存。
2.静态代码块被执行。(静态优先于对象)
3.开辟空间。
4.初始化  属性初始化:1.默认初始化。2.显示初始化3.构造代码块初始化。4.构造函数(有针对性,是所有对象)初始  化。2优先级比3高


Person p = new Person("zhangsan",20);
该句话都做了什么事情?
1.因为new用到了Person.class。所以会先找到Person.class文件并加载到内存中。
2.执行给类中的static代码块,如果有的话,给Person.class类进行初始化。
3.在堆内存中开辟空间,分配内存地址。
4.在堆内存中建立对象的特有属性,并进行默认初始化。
5.对属性进行显示初始化。
6.对对象进行构造代码块初始化。
7.对对象进行对应的构造函数初始化。
8.将内存地址赋给栈内存中的p变量。


08面向对象(单例设计模式)
设计模式:
解决某一类问题最行之有效的方法。
java中有23种设计模式
单例设计模式:解决一个类在内存只存在一个对象。

想要保证对象唯一:
  1.为了避免其他程序过多的建立该类对象,先禁止其他程序建立该类对象
  2.还为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象。
  3.为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。
这三部怎么用代码体现呢?
  1.将构造函数私有化。
  2.在类中创建一个本类对象
  3.提供一个方法可以获取到该对象

对于事物该怎么描述,还怎么描述(描述不会变)。
当需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可。

这个是先初始化对象。
称为:饿汉式
Single类一进内存,就已经创建好了对象。
开发时一般用饿汉式。因为它安全简单:

class Single
{

  private Single(){}
  private static Single s = new Single();   // 静态方法访问静态成员 所以要加静态  类变量要私有化
  
  public static Single getInstance() // 获取实例  因为要用类名调用,所以要静态
  {
     return s;
  }
}
class SingleDemo
{
  public static void main(String[] args)
  {
    Single ss = Single.getInstance();
  }
}



09面向对象(单例设计模式方式二)
对象是方法被调用时,才初始化,也叫做对象的延时加载。
称为:懒汉式
Single类进内存,对象还没有存在,只有调用了getInstance方法时,才建立对象。

class Single
{
  private static Single s = null;
  private Single(){}
  public static synchronized Single getInstance()   //上一个锁,但是程序效率会变低
  {                  //  因为想要new对象时都要先判断一下是否为null
    if(s==null)
      -->A
      -->B
      s = new Single();
    return s;
  }
}


 

一个先初始化,一个后初始化

记住原则:定义单例,建议使用饿汉式
最终解决方案:

class Single
{
  private static Single s = null;
  private Single(){}
  public static synchronized Single getInstance()  
  {  
   if(s==null)
   {
       synchronized(Single.class) 
       {           
              if(s==null)
                  s = new Single();
       }
   }
    return s;
  }
}



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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值