您的位置:首页 > 职场人生

黑马程序员_Java基础Day06_面向对象静态相关内容(Done)

2013-05-24 17:29 519 查看
-------
android培训、java培训、期待与您交流! ----------

static——静态
用法:
static是一个修饰符,用于修饰成员(成员变量和成员函数)。静态修饰的内容被对象所共享。

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

写法格式:类名.静态成员

静态的特点:
①随着类的加载而加载,随着类的消失而消失(说明它的生命周期最长);
②优先于对象存在:静态是先存在的,对象是后存在的;
③被所有随想共享;
④可以直接被类名调用。

特有数据随着对象存储,存放于堆内存中。共享数据使用static修饰,存放于方法区中。

方法区(也叫共享区、数据区):类中的共享数据、类中的方法都存储在方法区中

实例变量(一般成员变量)和类变量(被static修饰的变量)的区别:
1.存放位置:类变量随着类的加载而存在于方法区(共享区、数据区)中。实例变量随着对象的建立而存在于堆内存中。
2.生命周期:类变量生命周期最长,随着类的消失而消失。实例变量生命周期随着对象的消失而消失。

静态有利有弊:
利:
对对象的共享数据进行单独空间的存储,节省空间。没有必要每一个对象中都存储一份。
可以直接被类名调用。

弊:

生命周期过长。

访问出现局限性。(静态虽好,只能访问静态)
class Student//创建学生类,有姓名和所属学校
{
String name;
static String school = "itheima";//itheima的学生,其学校肯定是itheima,所以加static修饰共有数据。
public void show()//用于展示学校和姓名
{
System.out.println("学校是:"+school+" 姓名是:"+name);
}
}

class StaticDemo0
{
public static void main(String[] args)
{
Student s = new Student();//创建学生类的对象
s.name = "张三";//给对象赋值
s.show();//调用对象方法
System.out.println(Student.school);//由于school变量在Student类中是被static修饰的,所以可以直接由类名调用而不需要创建对象。
}
}
运行结果:



静态使用注意事项:

1.静态方法只能访问静态成员(方法、变量)。非静态方法既可以访问静态也可以访问非静态。

2.静态方法中不可以定义this、super关键字。因为静态优先于对象存在。所以静态方法中不可以出现this。

3.主函数是静态的。

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

主函数是固定格式的,jvm识别。主函数是特殊的函数,由函数的定义,可知jvm在调用主函数时,传入的是new String[0]。

什么时候使用静态????
由于静态修饰的内容包括成员变量和成员函数,所以从这两方面考虑。
当对象中出现共享数据时,该数据被静态所修饰;而对象中的特有数据要定义成非静态,存在于堆内存中。
当功能内部没有访问到非静态数据(即,没有访问到对象的特有数据)时,该功能可以被定义成静态的。

每个应用程序都有共性的功能。于是可以考虑,将这些功能进行抽取,独立封装,以便复用。
如:将在学习数组时的一些操作数组的方法抽取出来,独立封装在同一个类中,即ArrayTool工具类。

在创建工具类过程中发现,虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作。

发现了问题:

1,对象是用于封装数据的,可是ArrayTool对象并未封装特有数据。

2,操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。

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

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

将方法都静态后,可以方便于使用,但是该类还是可以被其他程序建立对象的。

为了更为严谨,强制让该类不能建立对象。可以通过将构造函数私有化完成。

工具类(ArrayTool)代码如下:
class ArrayTool//创建一个对数组进行一系列操作的工具类。
{
private ArrayTool(){}//工具类仅向外提供方法,没有特有数据,没必要创建工具类对象。所以使用private修饰该类构造函数,强制禁止创建该类的对象。
public static int getMax(int[] arr)//没有使用工具类的特有数据,所以使用static修饰
{
int max = 0;
for (int x=1;x<arr.length ;x++ )
{
if (arr[max]<arr[x])
max = x;
}
return arr[max];
}
public static int getMin(int[] arr)
{
int min = 0;
for (int x=1;x<arr.length ;x++ )
{
if (arr[min]>arr[x])
min = x;
}
return arr[min];
}
private static void swap(int[] arr,int a,int b)//此方法仅仅用于类中排序方法调用,不需要对外提供,所以使用private修饰
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
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);
}
}
}
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);
}
}
}
public static void printArray(int[] arr)
{
System.out.print("[");
for (int x=0;x<arr.length
4000
;x++ )
{
if(x!=arr.length-1)
System.out.print(arr[x]+",");
else
System.out.println(arr[x]+"]");
}
}
}
将此类的.class文件给别人,别人将该文件设置到classpath路径下后,就也可以使用这个工具类了。
但是,工具里中定义了什么,如何使用,对方是不知道的。因此,需要制作一个对应该类的“说明书”(通过文档注释完成此过程)。
这个“说明书”又叫做API(Application Programming Interface)——应用程序接口。

文档注释格式:
/**
@author 作者
@version 版本号
@param 参数
*/
文档注释时,使用到的是%JAVA_HOME%\bin\目录下提供的javadoc.exe工具。
命令行格式为:javadoc -d 想要保存到的文件夹名(路径) -author -version 类名.java
想要生成帮助文档的类必须被public或者protected修饰

小插曲:
一个类中,总会有一个默认的构造函数(空参数构造函数),这个默认的构造函数的权限是与所属类一致的。默认构造函数的权限是随着类的变化而变化的。即,类是什么权限,类中的默认构造函数的权限就是什么样的。

静态代码块
格式是:
static{
静态代码块中的执行语句
}
静态代码快的特点:随着类的加载而执行,只执行一次,并且优先于主函数执行。
根据静态代码快的特点,可知:静态代码块的作用是用于给类进行初始化。
注意:即便没有对象,静态代码块也是能够执行的。如下:
class Static
{
static
{
System.out.println("我是Static类的静态代码块,我为自己代言");
}
static void show()
{
System.out.println("show run");
}
}
class  StaticCodeDemo
{
public static void main(String[] args)
{
Static.show();
}
}
由于Static类中定义的show()方法被静态修饰,所以可以被类名直接调用。可以看到,在主函数中,并没有建立Static类的对象,而直接通过类名调用了show()方法。而想要使用show()方法,就必须先加载Static类(注意,是加载类,不是建立对象)。由于静态代码块随着类的加载而执行,所以这时Static类中的静态代码块也是能够执行到的。结果如下:



截止到目前,已经学习了三种具有初始化动作的概念:构造函数,构造代码块和静态代码块。三者的执行顺序是:静态代码块===>构造代码块===>构造函数
其中,静态代码块是给类进行初始化,只要使用到了类中的东西,类加载进内存,就执行;构造代码块是给对象进行初始化,只要创建了该类的对象,这部分就执行;构造函数是给对应的对象进行初始化,只要创建了该类对应的对象,就执行这部分代码。

那么考虑如下代码,在main中执行new操作时,程序在内存中是按照什么顺序执行的呢?
class Person
{
private String name = "Java";
private int age;
private static String country = "CN";
Person(String name,int age)
{
this.name = name;
this.age = 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);
}
}
class Order
{
public static void main(String[] args)
{
Person p = new Person("zhangsan",20);
}
}
1.因为new操作使用到了Person.class文件,所以会先找到Person.class文件并加载到内存中;
2.执行该类中的静态代码块,给Person.class类进行初始化;
3.在堆内存中开辟空间,分配内存地址;
4.在堆内存中建立对象的特有属性,并进行默认初始化;
5.对属性进行显示初始化;
6.对对象进行构造代码块初始化;
7.对对象进行与之对应的构造函数初始化;
8.将内存地址赋给栈内存中的p变量。

静态成员之间调用省略了"类名.";非静态成员之间调用省略了"this."。

设计模式
设计模式是一种思想,是解决某一类问题最行之有效的方法。Java中共有23种设计模式。多种设计模式相互组合就形成了框架。
单例设计模式:解决一个类在内存中只存在一个对象的情况(对象在内存中的唯一性)。

想要保证对象的唯一性,首先要禁止其他程序建立该类的对象。但是,为了让其他程序可以访问到该类对象,就需要在本类中自定义一个本类的对象,并向外提供访问方法。归纳如下:
1.为了避免其他程序随意的建立该类对象,先禁止其他程序建立该类对象;
2.为了让其他程序能够访问到该类的对象,就在该类中自行建立一个本类对象;
3.为了方便其他程序对自定义对象的访问,考虑对外提供访问方法。

考虑到,之所以能够建立类的对象是因为类中有默认的构造函数,如果将类的构造函数私有化,外界就无法创建该类的对象了。
于是,对应上面三步,提出以下解决办法:
1.将构造函数私有化;
2.在类中创建一个本类对象;
3.对外提供可以获取该对象的方法。
初步代码如下:
class Single
{
private Single(){}//构造函数私有化,外界无法new此类对象
Single s= new Single();//创建本类对象
public Single getInstance()//对外提供获取本类对象的方法
{
return s;
}
}

进一步思考:
1.方法被调用只有两种方式:建立对象调用和类名直接调用。而类的构造函数已经被私有化,不能建立对象,所以这里只能使用类名调用。想要使用类名直接调用,该方法必须被static修饰。所以getInstance(){}方法需要被static修饰;
2.getInstance(){}方法中使用到了Single类中的成员变量s,而getInstance(){}需要被static修饰,静态的访问限制要求静态方法只能访问静态成员,所以。成员变量s也必须被static修饰才可以;
3.s是成员变量,需要被private修饰私有化,并使用getInstance(){}向外界提供访问方式。
于是,上面代码改进为:
class Single
{
private Single(){}//构造函数私有化,外界无法new此类对象
private static Single s= new Single();//创建本类对象
public static Single getInstance()//对外提供获取本类对象的方法
{
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single s = Single.getInstance();//类名直接调用的方式访问Single类的唯一对象
}
}

单例模式是思想,单独的存在是没有意义上的。对于事物该怎么描述就怎么描述。当需要保证该事物的对象在内存中唯一时,就加上以上三部构成单例模式即可。

单例设计模式有两种格式,分别称为“懒汉式”和“饿汉式”。上面的单例模式写法就是“饿汉式”。
“懒汉式”单例模式基本写法如下:
class Single
{
private Single(){}
private static Single s = null;//s没有引用,值为空
public static Single getInstance()
{
if(s==null)//条件语句判断,延迟加载
s = new Single();
return s;
}
}
比较“懒汉式”和“饿汉式”单例模式的写法可知:在“懒汉式”中,对象是方法被调用的时候才开始进行初始化,也叫做对象的延时加载。Single类进内存后,对象还没有存在,只有调用了getInstance(){}方法时,才建立对象。

开发时,一般使用“饿汉式”,因为“饿汉式”的写法简单并且安全。“饿汉式”在有很多东西同时调用getInstance(){}方法时就会出现问题。
这里涉及到多线程的相关内容,这里仅仅先给出解决方案。
“懒汉式”代码优化如下:
class Single
{
private Single(){}
private static Single s = null;//s没有引用,值为空
public static Single getInstance()
{
if(s==null)//条件语句判断,延迟加载
{
synchronized(Single.class)
{
if(s==null)
s = new Single();
}
}
return s;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Java 毕老师基础