面对对象第六天


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

  (一)Static关键字

补充知识:

实例化:

在java里面一般把函数称为方法.

在面向对象的编程中,通常把用类创建对象的过程称为实例化,其格式如下:类名 对象名 = new 类名(参数1,参数2...参数n)(这里,“类名”这个类名实例化成了“对象名”这个对象)如 Date date=new Date();就是用日期类创建了一个日期的对象,就叫对象的实例化。多数语言中,实例化一个对象 其实就是在内存中开放一个空间 用于存储新的产物,即对象。例如一个类

class A{  .....  }  

A a=new A();


小结:当我们有了一个写好的类之后,要使用这个类,就要创建这个类的对象,就是要让对象实例化,格式就是:

类名 对象名=new 类名();

要给对象中某个属性赋值或改变其值,格式是:

对象名.变量名=具体的某个值;

要让对象使用方法,格式是:

对象名.函数名 ();

比如:

class Person
{
    String name;
    String country="CN";
    public void show()
    {
        System.out.println("name:"+name+","+"country:"+country);
    }
}
class StaticDemo
{
    public static void main(String[] args)
    {
        Person p=new Person();//
        p.name="zhangsan";
        p.show();
    }
}


概念:

static是静态修饰符,用于修饰成员变量和成员函数,在程序中的任何变量或者代码都是在编译的时候由系统自动分配内存来存储的,有些变量是临时存在的,但被静态修饰符修饰之后的变量或者函数只要程序运行,就会在内存中一直存在,直到程序退出.被静态修饰的代码存在于方法区中,而不是存在堆内存中的,而且被所有对象共享.

例如:

class Person
{
    String name;//当Person类加载之后,内存中没有name存在,只有当对象创建以后才在堆内存中存在.这样的变量就是实例变量.对象消失,此变量消失.
    static String country="CN";//country在堆内存外单独存在,任何对象都能访问.这个叫静态变量也叫类变量.当这个变量存在的时候,对象还没有.
    public void show()
    {
        System.out.println("name:"+name+","+"country:"+country);
    }
}
class StaticDemo
{
    public static void main(String[] args)
    {
        Person p=new Person();//
        p.name="zhangsan";
        p.show();
    }
}

当成员被静态修饰之后,除了能被对象共享,还能直接被类名调用.格式是类名.静态函数名或者静态变量名.比如

在上面的定义完Person类之后,给country加上了static,那么可以这样调用:Systm.out.println(Person.country);


静态变量产生的原因:

当对象被创建以后,才会在堆内存中开辟空间来存储实例变量,但是每创建一个新的对象,就会随之开辟新的空间来存储实例变量,如果这个实例变量是每个对象都有的,那每一个对象都会为这个实例变量开辟空间,这样就很浪费内存,所以需要给这些共用的变量加上静态修饰符,这样每个对象不必再为这样的变量开辟空间,以节约内存.但是那些对象特有的,不需要共享的变量,就不要加静态修饰符.


静态的两个作用:

1.被对象共享

2.能直接被类名调用,格式就是类名. 静态元素名;


概念:方法区,共享区,数据区,这是内存中的一个区域,都是同一概念,这个区是类中的方法和类中的共享数据,


特点:

1.静态随着类的加载而加载,类消失,静态就消失,说明静态的生命周期最长.

2.优先于对象存在.静态是先存在的,对象后存在的.因为先加载类才有对象.

3.被所有对象共享

4.可以直接被类名调用


实例变量和静态变量的区别:

java类的成员变量有两种:一种是被static关键字修饰的变量,叫类变量或者静态变量;另一种没有static修饰,为实例变量。

1.语法格式不同:

静态变量前面需要加上static

实例变量前面不加static

2.程序运行不同:

静态变量不属于某个实例对象,而是属于类,所以也叫类变量,只要程序加载了类的字节码,就比如我有一个Demo类,编译之后生成的class文件就是字节码文件,只要我在dos里面执行了java Demo,这个Demo类就被加载了,此时,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。

实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。

3.存在位置不同:

静态变量随着类的加载而存在于方法区中,被所有的对象共享.

实例变量随着对象的建立而存在于堆内存中,每创建一个实例,JVM就会为实例变量分配一次内存.

2.生命周期不同,

静态变量可以由类名直接访问,是随着类的加载而加载,类存在,静态变量就存在,否则静态变量就不存在.所以静态变量生命周期最长.

实例变量随着对象消失而消失,对象在,实例变量就在,否则就不在.


静态的使用注意事项:
1.静态的方法只能访问静态成员.

但是非静态的方法既可以访问静态的又可以访问非静态的.

2.静态方法中不可以定义this,super关键字,因为this,super是对象创建之后才会出现,而静态优先于对象存在,对象都没有加了this毫无意义,所以静态方法中不能出现this,

3.主函数一定是静态的.

4.static只能修饰成员.

比如:

class Person
{
    String name;
    static String country="CN";//注意country加了static.
    public void show()//注意show这个方法没有加static.
    {
        System.out.println("country:"+country);//注意看此处没有变量name.
    }
}
class StaticDemo
{
    public static void main(String[] args)
    {
        Person.show();
    }
}

报错,原因是show()不是静态,不能被类直接调用.如果给show()加上静态,也就是public static void show(),就能被Person直接调用了,但是show()中语句的函数也必须是静态的,如果这样写,

public static void show()
    {
        System.out.println("name:"+name+","+"country:"+country);//注意多了name.
    }

还是会报错,虽然show是静态的,但是里面的name函数不是.

又比如在方法中访问方法,上面的例子是在方法中访问成员变量,在show()中访问country和name.

public static void show()
    {
        System.out.println("name:"+","+"country:"+country);
        haha();

    }

public void haha(){}//注意haha没有加static.

结果还是会报错,因为haha不是静态.不能被静态的show访问.


静态的利弊:

好处:

1.能有单独空间存储对象的共享数据,节省内存.

2.可以直接被类名调用.如果想调用类中的成员,又没有变量,就可以加静态.

坏处:

1.生命周期过长.

2.访问有局限性,因为静态的方法只能访问静态成员.


  (二)main主函数

主函数是程序的入口能被jvm调用.


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


注意事项:

1.由于虚拟机只认这个public static void main(String[] args)固定写法作为主函数,所以下面的写法都是不对的.但是变量名可以更改,也就是args可以改动,其他不能改.

2.jvm在调用主函数时,传入的是new string[0],也就是相当于定义了一个长度为0的数组,string[] args=new string[0];注意长度为0的数组表示里面没有元素,所以打印角标为0的元素会提示越界.

比如:

class MainDemo
{
    public static void main(String[] args)
    {
        System.out.println("hello world");
    }
    public static void main(int x){}//注意这里是函数的重载.
}

或者

class MainDemo
{
    public static void main(String[] args,int x)
    {
        System.out.println("hello world");
    }
}

3.虽然主函数的数组里面没有元素,但是可以在运行虚拟机的时候给要运行的那个类中的主函数添加元素.比如:

class MainDemo
{
    public static void main(String[] args)
    {
        System.out.println(args[0]);//打印args数组0角标的元素.
    }
}

在dos中输入javac编译完之后,再输入java MainDemo 1 2 3 4,这个表示给MainDemo这个类中的主函数中的数组添加了{'1','2','3','4'}这四个元素,打印0角标的自然结果是1.

给主函数添加元素不仅可以在dos中添加,还可以直接在代码中添加,

class MainDemo
{
    public static void main(String[] args)
    {
        String[] arr={"1","2","3","4"};//注意数据类型是字符串,需要加双引号,而直接在dos中添加元素的时候是没有加双引号的,直接写元素即可.
        MainTest.main(arr);
    }
}
class MainTest
{
    public static void main(String[] args)
    {
        for(int x=0;x<args.length;x++)
            System.out.print(args[x]);
    }
}


  (三)静态什么时候用

要从两方面入手思考:

因为静态修饰的内容有成员变量和方法,所以这两方面就是:

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

当对象中出现共享数据时,该数据被静态修饰.而对象中的特有数据定义成实例变量存在于堆内存中.

比如:人人都有姓名,但姓名值不同,也就是数据不同,所以姓名要定义成实例变量,但是人人都有国籍,全中国人的国籍都是中国,也就是国籍值都一样,所以国籍应该定义成静态变量.

2.什么时候定以静态方法?

就看这个静态方法有没有访问到静态变量了,如果没有,就定义成实例方法,如果访问到了静态变量,那就要定义成静态方法了.

注意:要运行程序必要要有主方法,比如:

class Person
{
    String name;
    public void show()
    {
        System.out.println(name);
    }
}

在编译的时候没有语法错误,不会报错,但是运行的时候就会提示找不到主方法,就是缺少main主函数,所以应该定义主函数:

class Person
{
    String name;
    public static void main(String[] args)
    {
        System.out.println(name);
    }
}

但在编译的时候会报错,说无法从静态上下文中引用非静态变量name,因为主函数是静态函数,所以在访问到变量的时候,这个变量也应该是静态的,所以应该这样写:

class Person
{
    static String name;
    public static void main(String[] args)
    {
        System.out.println(name);
    }
}

由于name没有定义初始化,所以结果是null.


  (四)静态的应用-工具类

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

小知识:当编译java文件的时候,如果这个文件调用了其他java文件的类,jvm会找指定目录(也就是set classpath的路径)或者当前目录下有没有调用的那个类的class文件,如果没有class文件,jvm就会在当前目录下再找一次有没有对应的java文件,如果有,jvm就自动先编译调用的那个类的java文件,再编译自己.比如:

class  ArrayTool
{
    public void 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.java.

class  ArrayToolDemo
{
    public static void main(String[] args)
    {
        int[] arr={3,5,7,8,9,14};
        ArrayTool tool=new ArrayTool();
        int max=tool.getMax(arr);
        System.out.println("max="+max);
    }
}

保存为ArrayToolDemo.java.

可以直接编译ArrayToolDemo.java,因为jvm会先在指定路径和当前目录找有没有ArrayToolDemo.java所调用的ArrayTool类的class文件,没有,会再次在当前目录找有没有ArrayTool类的java文件,有,则先编译ArrayTool,再编译ArrayToolDemo.



下面是具体的工具应用举例:先定义一个类,包含了所有对数组操作的工具,再定义一个类,把对象实例化,让对象调用工具.

class ArrayTool
{
    public 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];
    }
    public int getMin(int[] arr)//求最小值
    {
        int min=0;
        for(int x=1;x<arr.length;x++)
        {
            if(arr[x]<arr[min])
            min=x;
        }
        return arr[min];
    }
    
    public void selectSort(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);
                }
            }
        }
        printArray(arr);
    }
    public 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);
                }
            }
        }
        printArray(arr);
    }
    public void swap(int[] arr,int x,int y)//交换数组中两个元素位置.
    {
        arr[x]=arr[x]^arr[y];
        arr[y]=arr[x]^arr[y];
        arr[x]=arr[x]^arr[y];
    }
    public 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]+"]");
        }
        System.out.println();
    }
}
class ArrayToolDemo
{
    public static void main(String[] args)
    {
        int[] arr={3,7,2,1,0,8};
        ArrayTool tool=new ArrayTool();
        System.out.println("max="+tool.getMax(arr));
        System.out.println("min="+tool.getMin(arr));
        tool.selectSort(arr);
    }
}   
注意:发现上面的代码中ArrayTool里面并没有对象特有的数据,而且操作的数组的每一个方法也没有对象的特有数据,也就是说当对象被创建之后,对象里面并没有属性,所以不需要建立对象,应该把ArrayTool里面的函数静态,那么ArrayToolDemo里面也就是用户就可以直接调用ArrayTool这个类了.

class ArrayTool
{
    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];
    }
    public static int getMin(int[] arr)//求最小值
    {
        int min=0;
        for(int x=1;x<arr.length;x++)
        {
            if(arr[x]<arr[min])
            min=x;
        }
        return arr[min];
    }
    
    public static void selectSort(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);
                }
            }
        }
        printArray(arr);
    }
    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);
                }
            }
        }
        printArray(arr);
    }
    public static void swap(int[] arr,int x,int y)//交换数组中两个元素位置.
    {
        arr[x]=arr[x]^arr[y];
        arr[y]=arr[x]^arr[y];
        arr[x]=arr[x]^arr[y];
    }
    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]+"]");
        }
        System.out.println();
    }
}
class ArrayToolDemo
{
    public static void main(String[] args)
    {
        int[] arr={3,7,2,1,0,8};
        int max=ArrayTool.getMax(arr);
        System.out.println("max="+max);
        int min=ArrayTool.getMin(arr);
        System.out.println("min="+min);
        ArrayTool.printArray(arr);
        ArrayTool.selectSort(arr);
    }
}

小结:这就是工具类,一般来说,工具类里面定义的一般都是静态方法,在ArrayToolDemo类中,还是可以建立对象并且使对象初始化的,因为在ArrayTool类中没有构造函数,所以系统会默认建立一个空参数的构造函数,那么对象是可以建立成功并初始化的,所以说,将方法都静态后,为了更为严谨,应该强制该类不能建立对象,可以通过将构造函数私有化完成.

所以可以在ArrayTool的第一行就加入这段代码:

class ArrayTool
{
    private ArrayTool(){};

}

这样就能禁止用户创建ArrayTool()的对象,那么用户在ArrayToolDemo中创建对象的话ArrayTool tool=new ArrayTool()就会报错.这样就防止了用户创建一些冗余的对象占用内存.

另外ArrayTool中的排序方法swap没有机会被对象直接调用,它是在ArrayTool里面被其他方法调用,那么也可以私有化,private static void swap(int[] arr).


  (五)帮助文档的制作java.doc

小知识:当在设置class文件的路径的时候,为了防止多个class文件不在同一目录下,比如有的在指定目录,有的在当前目录,那么在设置的时候最好在前面加上.表示先搜索当前目录,set classpath=.;d:\abc;e:\abc


什么时候制作帮助文档呢?

加入别人发给我一个class文件,但我并不知道这个文件里面有哪些方法,怎么用,所以这时就需要制作帮助文档.java的说明书通过文档注释来完成.

比如在前面的ArrayTool类加入文档注释,除了在开始要写功能,作者和版本号等信息,里面的每一个方法也要加上注释加以说明.

/**
这是一个可以对数组进行操作的工具类,该类中提供了获取最值,排序,打印等功能.
@author 李明
@version V1.7
*/
public class ArrayTool
{
    /**
    空参数构造函数.
    */
    private ArrayTool(){};
    /**
    获取一个整型数组中的最大值.
    @param arr 接收一个int类型的数组. param.是parameter的缩写,意思是参数,变量.
    @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])
            min=x;
        }
        return arr[min];
    }
    /**
    给一个整型数组进行选择排序.
    @param arr 接收一个int类型的数组.     
    */
    public static void selectSort(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);
                }
            }
        }
        printArray(arr);
    }
    /**
    给一个整型数组进行冒泡排序.
    @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);
                }
            }
        }
        printArray(arr);
    }
    /**
    置换一个整型数组中的任意两个元素.
    @param arr 接收一个int类型的数组.
    @param x 要置换的位置
    @param b 要置换的位置
    */
    private static void swap(int[] arr,int x,int y)
    {
        arr[x]=arr[x]^arr[y];
        arr[y]=arr[x]^arr[y];
        arr[x]=arr[x]^arr[y];
    }
    /**
    用于打印一个整型数组,打印形式是:[element1,element2,...]
    @param arr 接收一个int类型的数组.
    */
    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]+"]");
        }
        System.out.println();
    }
}

写完注释之后,回到dos,输入javadoc -d myhelp -author -version ArrayTool.java,-d表示生成路径,如果是-d myhelp,那么就在ArrayTool所在目录下生成一个myhelp的文件夹,帮助文档就生成在这个文件夹里面.如果要指定其他的路径,则这样输入-de:\myhelp,则是在e盘下的myhelp文件夹下生成文档.

注意:如果要把一个类生成帮助文档,这个类必须要用public修饰.也就是要在class ArrayTool前面加上public.但是在java中,公有类必须在于类名同名的java文件中定义,也就是说保存的java文件名也必须是ArrayTool,不然不能生成文档.

生成之后,首先我们查看下index.html这个索引文件,发现里面的方法概要没有swap这个方法,因为swap加了私有private,所以不会在文档中出现,只有public和protected才能出现在文档中,不会被隐藏.

小结:

一个类中默认会有一个空参数的构造函数,这个默认的构造函数的权限和所属类一致,一般来说如果类加了public,这个空的构造函数也要加public,但有时为了防止用户建立冗余的对象,需要把这个构造函数私有化,这时就不要加public了.如果类前面没有加public,这个构造函数就不需要写出来了,因为默认就是类名(){},不用再到第一行写一个类名(){},这样就不是默认的了,而是自定义的构造函数,也就是说空参数的构造函数不等于默认的构造函数.

生成的index.html里面的方法概要和方法详细资料叫做API帮助文档,对暴露出来的方法进行说明,API是Application Programming Interface,即应用程序编程接口.

java里面也有JDK API帮助文档,里面包含了各种类,每个类里面包含了各种方法和以及如何使用这些方法.


  (六)静态代码块

格式:

static
{
    静态代码块中的执行语句.
}

特点:

1.随着类的加载而执行,只执行一次.并优先于主函数,因为主函数是有函数名的,要被调用才会执行,这个静态代码块没函数名,直接就被执行了.

2.静态代码块用于给类进行初始化.

3.静态代码块也是静态的,所以也只能访问静态成员.

举例:

class StaticCode
{
    static
    {
        System.out.println('x');
    }

}

class StaticCodeDemo
{
    static
    {
        System.out.println('y');
    }
    public static void main(String[] args)
    {
        new StaticCode();
        new StaticCode();
        System.out.println('z');
    }
    static
    {
        System.out.println('a');
    }
}

因为有两个类,我们编译StaticCodeDemo这个类,结果显示:

y

a

x

z

分析下StaticCodeDemo这个类的执行顺序,当在dos中开始运行StaticCodeDemo这个类的时候,就在内存中开始加载这个类,当然先打印y,由于静态代码块优先于主函数,所以接着打印a,再运行主函数,第一个对象调用了StaticCode类,此时往内存中加载了StaticCode这个类,里面有一个静态代码块,打印x,因为此时StaticCode这个类已经加载进来了,就不会再执行下面的那个new StaticCode(),接着就打印z.

注意:内存中要加载类的话,必须要用到类中的内容,比如StaticCode s=null这个表示定义一个StaticCode类型的变量,但是这个变量里面没有任何实体指向,如果为了这么一个空的变量而要加载整个StaticCode类的话就太废内存了,所以这个命令无效StaticCode是不会加载的,但是s=new StaticCode,这个是会加载的,因为在StaticCode中有默认的空构造函数SaticCode(){}.

对于类是否加载的问题的举例:

class StaticCode
{
    StaticCode()//没有对应的对象来调用.
    {
        System.out.println('a');
    }
    static//静态代码块,给类初始化,是首先执行的
    {
        System.out.println('b');
    }
    {
        System.out.println('c');//构造代码块,给所有的对象进行初始化,优先于构造函数,是第二个执行的
    }
    StsticCode(int x)//带了一个整型参数x的构造函数,和上面的StaticCode()是重载,下面有对应的对象,new StaticCode(4),是第三个执行的
    {
        System.out.println('d');
    }
    public static void show()//不会执行
    {
        System.out.println('e');
    }
}
class StaticCodeDemo
{
    public static void main(String[] args)
    {
        new StaticCode(4);//创建了一个匿名对象,向里面传了值为4,4是整数,所以会使用StaticCode(int x)这个函数.
    }
}

所以结果是

b
c
d

注意:上面出现了构造函数代码块,构造函数代码块是用来给对象初始化的,假如我没创建对象,那么这个构造函数代码块就不会执行的,比如:

class Person
{
    static
    {
        System.out.println("a");
    }
    {
        System.out.println("b");
    }
    public static void show()
    {
        System.out.println("x");
    }
}
class PersonDemo
{
    public static void main(String[] args)
    {
        Person.show();//我没创建对象,而是直接用类名调用的show方法,所以那个打印b的构造函数代码块是不会执行的.
    }
}

所以结果是

a
x


  (七)对象的初始化过程

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="+this.name+","+"age="+this.age);
    }
    public void setName(String name)
    {
        this.name=name;
    }
    public void speak()
    {
        System.out.println("name="+this.name+","+"age="+this.age);
    }
    public static void showCountry()
    {
        System.out.println("country="+country);
    }
}
class PersonDemo
{
    public static void main(String[] args)
    {
        Person p=new Person("张三",20);
    }    
}

步骤分析:

1.编译之后会生成两个class文件:Person.class和PersonDemo.class,当在dos中运行 java PersonDemo之后,就会通过jvm的加载器把Person.class和PersonDemo.class加载进内存,并开辟堆内存,栈内存和方法区,栈内存中有主函数和p变量,方法区中开辟两片区域,一片静态区,有静态变量country和静态方法showCountry(),一片非静态区,有成员方法setName和speak,还有构造函数Person().

2.如果PersonDemo类有静态代码块,则执行静态代码块,给PersonDemo类进行初始化,接着执行PersonDemo类中的主函数,因为这个主函数用到了Person类,所以,如果Person类也有静态代码块,那么接着就是执行Person类的静态代码块.

3.在堆内存中开辟空间,创建对象new Person(),并把一个十六进制地址值分配给这个空间.

4.在堆内存中建立对象特有属性name和age,对于这两个属性的值首先是默认初始化值,name是null,age是0

5.对属性进行显示初始化,例子中的name已经有初始化值,那么显示初始化的结果就是李四和0

6.接着是构造函数代码块初始化,就是接着执行构造函数代码块

7.接着是对对象进行与之对应的构造函数初始化,例子中p对应的构造函数是Person(String name,int age),传进去的值是"张三"和20.

8.将对象的内存地址赋给栈内存中的p变量.


  (八)对象调用成员过程

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="+this.name+","+"age="+this.age);
    }
    public void setName(String name)
    {
        this.name=name;
    }
    public void speak()
    {
        System.out.println("name="+this.name+","+"age="+this.age);
    }
    public static void showCountry()
    {
        System.out.println("country="+country);
    }
}
class PersonDemo
{
    public static void main(String[] args)
    {
        Person p=new Person("张三",20);
        p.setName("王五");
    }    
}
对象调用setName的步骤分析:

在栈内存中开辟空间,有setName(),name和this.name,非静态方法因为要被指定对象调用,所以在栈内存中开辟空间,对象开始调用非静态方法之后,这个非静态方法里面就已经有了一个指向对应对象的this.所以当对象调用的时候,这个this就有一个值,这个值就是p的十六进制值.所以在对象调用setName的时候,"王五"传给局部变量,再赋值给this.name,于是p对象的name属性就相应变成了"王五".

注意: 静态成员是被类名直接调用的,跟对象没关系,所以通常静态成员前面省略的是类名点,而非静态成员前面省略的是this点.因为成员要被调用才会有用,所以在写法上,调用成员的时候,成员前面都有是谁在调用,再加上一个点,但有的情况是省略了没写而已.


  (九)单例设计部分

概念:设计模式,解决某一类问题最行之有效的方式,在java中有23种设计模式.说白了就是一些模板,别人做好了可以拿来用,也可以把多种模式混合,优化,就成了更强的模式.

现在只学单例设计模式:解决一个类在内存中只存在一个对象.比如两个或多个程序都使用同一个对象.

举例:

/*
思路:
1.为了避免其他程序过多建立该类对象,先要禁止其他程序建立该类对象.
2.为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象.
3.为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式.
步骤:
1.将默认构造函数私有化.
2.在类中创建一个本类对象
3.提供方法可以获取到该对象,可以用get函数来获取对象的地址值

4.在其他类中要访问这个对象,可以用类名直接调用那个get函数,把返回的对象地址值赋给其他变量,这样多个变量指向同一对象,让后就可以像对象调用成员一样让这些变量来调用成员了.

*/
class Single
{
    private int num;
    private Single(){}
    //虽然构造函数初始化被私有了,但可以在本类中创建对象,下面的静态方法访问到了变量s,所以s前面要加静态,另外这个s是成员变量,由setInstance来调用,所以最好加上私有.
    private static Single s=new Single();
    //由于不能在其他类创建对象了,所以要调用这个方法只能是类名调用了,所以前面加静态.
    public static Single getInstance()
    {
        return s;
    }
    public void setNum(int num)
    {
        this.num=num;
    }
    public int getNum()
    {
        return num;
    }
}
class SingleDemo
{
    public static void main(String[] args)
    {
        //不管有几个人用,都始终只是在用这一个对象,因为在栈内存中的变量x,y都和s一样指向堆内存中的new Single()               

       //下面的赋值动作就是把s的地址值赋给了x和y.那么无论操作x还是y都同样是在操作s
        Single x=Single.getInstance();//由于getInstance是静态,所以可以类名调用。
        Single y=Single.getInstance();
        x.setNum(10);//x就相当于s,所以可以直接调用成员.
        System.out.println(y.getNum());
    }
}


  (十)单例设计模式方式二

单例设计模式的方式一思路是:

class Single
{
    private Single(){}
    private static Single s=new Single();
    public static Single getInstance()
    {
        return s;
    }
}

这个方式叫饿汉式,是先初始化对象.

方式二的思路是:

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

这个方式叫懒汉式,是方法被调用的时候对象才初始化,也叫对象的延时加载.

区别:

饿汉式:Single类一进内存,就已经创建好了对象,s指向对象地址值.

懒汉式:Single类进内存,s没有指向,也没有加载对象,只有调用getInstance方法的时候才加载对象.

小知识:

1.cpu一个核心在同一个时刻只能运行一个程序,当处理一会这个程序,大概是几千分之一毫秒,马上就切换处理另外一个程序,由于切换速度非常快,所以给人感觉好像是在同时处理多个程序.所以多核更快.

2.由于getInstance总要执行,因为总要把对象的地址值赋给其他变量,所以一般开发用饿汉式,简单安全.

3.由于cpu的程序切换,所以懒汉式存在一定安全隐患,比如当A程序调用getInstance,执行到if的时候,判断s是否等于空,满足,然后挂着,cpu切换处理B程序,同样调用了getInstance方法,也满足,然后创建了对象,这时切换A程序,创建了对象,这就创建了两个对象,显然不行,所以应该在getInstance方法加上synchronized ['sɪŋkrənaɪzd] ,表示当A程序调用这个方法的时候会检查是否有其他程序在调用,如果有,就等其他程序用完了自己再用.具体写法是:public static synchronized Single getInstance(),但是这样写的话就降低效率了,每次都得先判断有没有其他程序在调用这个方法.所以最终的写法是:

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

双重判断,减少了每次都要判断是否有程序在调用的次数.但是这段代码繁琐,最终还是用饿汉式.



---------------------- android培训java培训、期待与您交流! ----------------------详细请查看:http://edu.csdn.net/heima

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值