黑马程序员--面向对象(二)static关键字、静态代码块、对象的初始化过程

static (静态)关键字

静态:static

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

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

语法:类名.静态成员

例:

 1 class Person 
 2 {
 3     String name;//成员变量,也叫实例变量
 4     /*静态的成员变量,也叫类变量
 5     使用static关键字,使之成员变量被共享*/
 6     static String country="CN";
 7     //定义静态方法,但是要注意静态只能访问静态
 8     public static  void show(){
 9         System.out.println("::"+country);
10     }
11 }
12 class StaticDemo
13 {
14     public static void main(String[] args) 
15     {
16         Person p=new Person();
17         p.name="zhangsan";
18         //p.show();
19         //直接被类名调用、
20         Person.show();
21         System.out.println(Person.country);//使用静态调用,类名.成员
22     }
23 }

static内存特点

  1. 随着类的加载而加载

也就是说:静态会随着类的消失而消失,说明它的生命周期最长

  1. 优先于对象存在

明确一点:静态是先存在,对象是后存在的

  1. 被所有对象所共享
  2. 可以直接被类名所调用

实例变量和类变量的区别

  1. 存放位置

类变量随着类的加载而存放在于方法区中

实例变量随着对象的建立存在于堆内存中

  1. 生命周期

类变量生命周期最长:随着类的消失而消失

实例变量生命周期随着对象的消失而消失

静态的使用注意事项

  1. 静态方法只能访问静态成员

非静态方法既可以访问静态也可以访问非静态

类优先于对象出现在内存中,先出现的不可以访问后出现的的,(静态出现时示例的还没有出现,所以无法访问)后出现的可以访问先出现的

  1. 静态方法中不可以定义this ,super关键字

因为静态优先于对象存在,所以静态方法中不可以出现this,super

  1. 主函数是静态的

静态有利有弊

利处:1.对对象的共享数据进行单独空间的存储,节省空间,没有必要每一个对象中都存储一份

   可以直接被类名调用

弊处:生命周期长,

      访问出现局限性(只能访问静态)

 

 

main主函数

public static void main(){String []args}

主函数:是一个特殊的函数,作为程序的入口,可以被JVM调用

主函数的定义:

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

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

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

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

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

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

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

 

可以看一下JVM在调用主函数时,往里传入的是什么

代码:

 1 class MainDemo 
 2 {
 3     public static void main(String[] args) 
 4     {
 5         //看看JVM在调用主函数时传的什么参数
 6         System.out.println(args);
 7         //数组args的长度
 8         System.out.println(args.length);
 9     }
10 }

结果:

 

往主函数中传值:

编译后启动虚拟机,在执行一个类的同时传入参数

向参数中传了3个字符串:hahe hehe heihei

这时数组的长度为3了

 

主函数也能调用主函数:

代码:

 1 class MainDemo 
 2 {
 3     public static void main(String[] args) 
 4     {
 5         String[] arr={"haha","hehe","heihei","xixi"};
 6         MainText.main(arr);
 7     }
 8 }
 9 //不同类的两个主函数
10 class MainText
11 {
12     public static void main(String []args){
13         for(int i=0;i<args.length;i++){
14             System.out.println(args[i]);
15         }
16     }
17 }

结果:

 

当什么时候使用static

要从两个方面下手

因为静态修饰的内容有成员变量和函数

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

    当对象中出现共享数据时,该数据被静态所修饰,对象中的特有数据要定义称非静态存在于堆内存中

什么时候定义静态函数

    当功能内部没有访问到静态数据或对象特有的数据,那么该功能可以定义成静态的

例:

 1 class Person 
 2 {
 3     String name;
 4     public static void show(){//此方法没有使用到对象中的特有数据
 5         System.out.println("haha");
 6     }
 7     
 8 }
 9 class PersonDemo6
10 {
11     public static void main(String[] args) 
12     {
13         //Person p=new Person();
14         /*把数据封装成类,使用对象来调用,是为了更
15         方便的调用它的属性与方法,而这个show()方法并没有使用
16         到类特有的数据,这样创建对象并没有意义,所以这种情况
17         可以将show()写成static的*/
18         //p.show();
19         Person.show();
20     }
21 }

静态的应用:

每一个应用程序中都有共性的功能

可以将这些功能进行抽取,独立封装,以便复用

 

虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作

发现问题:

  1. 对象是用来封装数据的,可是ArrayTool对象并未封装特有数据
  2. 操作数组的每一个方法都没有用到ArrayTool对象中的特有数据

这时就考虑,让程序更严谨,是不需要对象的

可以将ArrayTool中的方法都定义成静态的,直接通过类名调用即可

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

 

工具类代码:

 

 1 //封装工具类
 2 class ArrayTool
 3 {
 4 
 5     //将构造函数私有化,为了防止实例化对象
 6     private ArrayTool(){}
 7     //获取数组中最小的值
 8     public static int getMax(int[] arr){
 9         int max=0;
10         for(int x=0;x<arr.length;x++){
11             if(arr[x]>arr[max]){
12                     max=x;
13             }
14         }
15     return arr[max];
16     }
17 //获取数组中最小的值
18     public static int getMin(int[] arr){
19         int min=0;
20         for(int x=0;x<arr.length;x++){
21             if(arr[x]<arr[min]){
22                 min=x;
23             }
24         }
25         return arr[min];
26     }
27 //冒泡排序
28     public static void selectSort(int[] arr){
29         
30         for(int x=0;x<arr.length-1;x++){
31             for(int y=0;y<arr.length-x-1;y++){
32                 if(arr[y]>arr[y+1]){
33                     swap(arr,y,y+1);
34                 }
35             }
36         }
37     }
38 //选择排序
39     public static void bubbleSort(int[] arr){
40         for(int x=0;x<arr.length-1;x++){
41             for(int y=x+1;y<arr.length;y++){
42                 if(arr[x]>arr[y]){
43                     swap(arr,x,y);
44                 }
45             }
46         }
47     }
48     //两个数交换,这个方法内部使用,不需要提供出去,所以私有化
49     private static void swap(int[]arr,int a,int b){
50         int temp=arr[a];
51         arr[a]=arr[b];
52         arr[b]=temp;
53     }
54     //遍历数组
55     public static void showArray(int[] arr){
56         for(int x=0;x<arr.length;x++){
57             System.out.print(arr[x]+" ");
58         }
59         System.out.println();
60     }
61 }
62 
63 调用工具类的方法
64 class  ArrayToolDemo
65 {
66     public static void main(String[] args) 
67     {
68         int[] arr={3,4,1,8,5,3};
69         //获取最大值
70         int max=ArrayTool.getMax(arr);
71         System.out.println("max="+max);
72         //获取最小值
73         int min=ArrayTool.getMin(arr);
74         System.out.println("min="+min);
75         //排序
76         ArrayTool.showArray(arr);
77         ArrayTool.selectSort(arr);
78         ArrayTool.showArray(arr);
79         
80     }
81 }

制作程序说明书

将ArrayTool.class文件发送给其他人,其他人只要将该文件设置到classpath路径下,就可以使用该工具类

但是,很遗憾,该类中到底定义了多少个方法,对方却不清楚,因为该类并没有使用说明书

制作程序的说明书

1.给方法加注释使用文档注释符/** */

在dos命令行输入 javadoc –d myhelp –author –versinon ArrayTool.java

 Myhelp:要保存的目录

-autor -version:(选择属性)

ArrayTool.java:文件名

静态代码块

格式:

static{

静态代码块中的执行语句

}

静态代码块的特点:

随着类的加载而执行,只执行一次,用于给类进行初始化,并优先于主函数

 

练习:

 1 class StaticCode 
 2 { 
 3     static{
 4         System.out.println("a");
 5     }
 6 }
 7 class StaticCodeDemo
 8 {
 9     static{
10         System.out.println("b");
11     }
12     public static void main(String []args){
13         new StaticCode();//当用到StaticCode类时才会加载
14         new StaticCode();
15         System.out.println("over");
16     }
17     static{
18         System.out.println("c");
19     }
20 }

结果:

b c a over

结论:静态代码块优先于主函数,并且静态代码块只有第一次new的之后只执行一次

 

 

练习2

 1 class StaticCode 
 2 { 
 3     int num=9;
 4     StaticCode(){
 5         System.out.println("b");
 6     }
 7 //静态代码块,给类初始化的
 8     static{
 9         System.out.println("a");
10     }
11 //构造代码块,给对象初始化的
12     {
13         System.out.println("c"+this.num);
14     }
15 //构造函数,给对应对象初始化的
16     StaticCode(int x){
17         System.out.println("d");
18     }
19     public static void show(){
20         System.out.println("show run");
21     }
22 }
23 class StaticCodeDemo
24 {
25     
26     public static void main(String []args){
27         new StaticCode(4);
28         
29     }
30 }

结果:a c9 d

结论:静态代码块优先于类,构造代码块优先于构造函数

 

 

对象的初始化过程

 1 class Person 
 2 {
 3     private String name;
 4     private int age;
 5     private static String country="cn";
 6     Person(String name,int age){
 7         this.name=name;
 8         this.age=age;
 9     }
10     public void setName(String name){
11         this.name=name;
12     }
13     public void speak(){
14         System.out.println(this.name+"..."+this.age);
15     }
16     public static void showCountry(){
17         System.out.println("country="+country);
18     }
19     
20 }
21 class PersonDemo7
22 {
23     public static void main(String[] args) 
24     {
25         Person p=new Person("zhangsan",20);
26     }
27 }

Person p=new Person(“zhangsan”,20);

该句话都做了什么事情?

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

 

 

 

posted @ 2013-12-27 01:37  h19891117  阅读(260)  评论(0编辑  收藏  举报