Java基础(面向对象二——static关键字、主函数定义和单例设计模式)

时间:2021-10-24 20:47:24


一、static关键字

理解:关于static我们可能最熟悉的就是main函数了,因为main函数就是被static所修饰,而我们也知道当我们想在main所在的类中定义功能并在main方法中调用这个功能的时候,这个功能必须被static所修饰,否则就会编译报错。那时候大家应该都会很疑惑吧,为什么呢?那是因为被static修饰的成员,我们通常称之为:静态成员或类成员,被static修饰的成员有一个很大的特点:静态成员优先于非静态成员存在于内存中,因为是静态成员先进入的内存,所以说当我们在main方法中调用非静态方法时才会出现编译报错,下面就详细说说static吧

static用法

static是一个关键字,用于修饰类中的成员(成员变量和成员函数),被static修饰的成员我们称之为静态成员(也就是类成员)。

被静态修饰的成员具备的特点:

1、随着类的加载而加载

2、优先于对象存在

3、被所用对象所共享

4、可以直接使用类名调用

注:静态方法只能访问静态成员

      静态方法中不可以使用this、super关键字

      主函数是静态的

那么静态变量和成员变量有什么区别呢?

1、存放位置

静态变量随着类的加载就存在与内存中的方法区中了,成员变量是随着对象的建立才存在于堆内存中,如果对象不存在,则成员变量就不存在。而静态变量则不同,静态变量可以使用类名.变量名访问

2、生命周期

静态变量生命周期最长,是随着类的消失而消失,而成员变量生命周期是随着对象的消失而消失

静态使用注意事项:

1,静态方法只能访问静态成员。
    非静态方法既可以访问静态也可以访问非静态。
2,静态方法中不可以定义this,super关键字。
    因为静态优先于对象存在。所以静态方法中不可以出现this。
3,主函数是静态的。

静态有利有弊

利处:对对象的共享数据进行单独空间的存储,节省空间。没有必要每一个对象中都存储一份。
    可以直接被类名调用。
弊端:生命周期过长。
      访问出现局限性。(静态虽好,只能访问静态。)

什么时候使用静态变量呢?

当对象中出现了共享数据时(共享数据不是属性,而是属性值 如:人都有共同的属性:姓名,但是姓名却不相同,有的叫张三,有的叫李四,但是不管是张三还是李四都是中国人  这个国籍就可以作为共享数据存在),而对象特有的数据(属性 如姓名)要定义成非静态,存在于堆内存中

什么时候使用静态方法呢?

当功能内部没有访问到非静态数据(或对象的特有数据时)可以把该函数定义成静态的

二、API帮助文档的生成

//定义一个工具类,实现对数组的操作
//文档注释提示作者和版本号,作者用@ author声明 版本号用@version声明
/**
这是一个可以对数组进行操作的类,可使用该类对已有数组进行排序、遍历、查找等操作
@author 马凯
@version v.1
*/
public class Tool
{

/*因为类中没有用到成员变量,却都是静态方法,让使用者创建对象也毫无
意义而言,所以直接控制该类不可以实例化,只能通过类名.方法名调用相应功能
*/
private Tool(){}
//开始文档注释
/**
该方法是对提供的数组进行遍历,输出格式为[参数1,参数2....]
@param array 形式参数,接收一个int类型的数组
*/
public static void printArray(int [] array){
System.out.print("[");
for(int i=0;i<array.length;i++){
if(i==array.length-1)
System.out.print(array[i]+"]");
else
System.out.print(array[i]+",");
}
}
/**
该方法是查找已知数组中是否包含要查找的元素
@param key 接收一个int类型的数值
@return 会返回该数值所在数组中的下标
*/
public static int getFind(int [] arr,int key){
for(int i=0;i<arr.length;i++){
if(key==arr[i])
return i;
}
return -1;
}
public static void getSort(int [] arr){
for(int i=0;i<arr.length-1;i++){
for(int j=i+1;j<arr.length;j++){
if(arr[i]>arr[j]){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = arr[i];
}
}
}
}
}
注:1、要生成文档注释的类必须被public所修饰

       2、生成API文档必须使用文档注释

       3、@author(提取作者内容)

       4、@version(提取版本信息)

       5、@param 参数名称//形式参数的变量名称

       6、@return 函数运行完返回的数据

最总要的一点:如果你的文档注释中声明了author和version这两个参数,那么在生成API文档时,也要指定这两个参数    那么用DOS命令行生成API文档格式为:  javadoc -d myclss -author -version Tool.java

格式定义:       Javadoc用来生成API文档的工具

                      -d:是指当前目录

                     myclass:指生成的API文档存放的文件夹

                     -author和-version:如果文档注释中声明了这两个参数 那么在生成API文档时必须要写这两个参数

                    Tool.java:指要生成的Java文件

二、主函数的定义

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

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

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

void:代表主函数没有具体的返回值

main:不是关键字,但是一个特殊的单纯,他可以被虚拟机所识别

String [] args:代表函数的参数是一个字符串类型的数组

注:主函数是一个函数,它也具有重载的特性,只不过虚拟机只识别参数为String类型的数组的main函数

三、静态代码块

格式:

static{
执行语句
}

特点:随着类的加载就已经存在,而且只执行一次,并且优先于主函数

作用:给类进行初始化

类的加载:只有使用到类中的内容时,类才会被加载

举例:

Person p = null;    不会加载类,因为没有使用到类中的内容

Person p = new Person();     //会加载类,因为使用到了Person类中无参的构造函数

四、创建一个对象,计算机的执行流程

如:Person p = new Person();

这句话到底都做了什么事情呢?

1、因为new用到了Person.class文件,所以会先找到Person.class文件并加载到内存中

2、执行该类中的静态代码块,如果有的话,给Person类进行初始化

3、在栈内存中开辟空间p,在堆内存中开辟new Person()

4、在堆内存中建立对象的特有属性并进行默认初始化

5、对属性进行显示初始化

6、对对象进行构造代码块初始化

7、对对象进行与之对应的构造函数初始化

8、将内存地址赋值给栈内存中的变量p

五、设计模式

什么是设计模式

设计模式就是一种思想,用来解决某一类问题最之有效的方法

Java*有23中设计模式

设计模式中的一种:单例设计模式

什么是单例设计模式?

单例设计模式就是用来保证一个类在内存中只存在一个对象

想要保证对象唯一,要实现的步骤:

1、为了避免其他程序过多的建立该类对象,应先禁止其他程序建立该类对象

2、为了让其他程序可以访问到该类对象,只好在本来中自定义一个对象

3、为了方便其他程序对自己对象的访问,可以在本类中提供一个访问方式

单例设计模式实现方式一:

饿汉式

class Demo
{
private String name;
private int age;
//第二步,在本类内部自定义一个对象
private Demo demo = new Demo();
//第一步,把构造函数是私有化,禁止该类实例化(就是建对象)
private Demo(){}
/*第三步,对外提供访问对象的方式,因为本类禁止实例化,
所以只能使用类名.方法名访问类内部的功能,所以对外提供访问对象的
方法应该是静态方法*/
public static Demo getDemo(){
return demo;
}
}

单例设计模式实现方式二、

懒汉式

class Demo1
{
    private String name;
    private int age;
    //第二步,在本类内部定义类类型变量,初始化为null
    private Demo demo = null;
    //第一步,把构造函数是私有化,禁止该类实例化(就是建对象)
    private Demo(){}
    /*第三步,对外提供访问对象的方式,因为本类禁止实例化,
    所以只能使用类名.方法名访问类内部的功能,所以对外提供访问对象的
    方法应该是静态方法*/
    public static Demo getDemo(){
        if(demo==null){
            demo = new Demo();
        }
        return demo;
    }
}
        /*但是发现上述代码并不一定能够保证对象的唯一,
        因为CPU是在多个程序中来回切换的,所以有可能会在执行“demo = new Deno()
        ”前回跳到另一个程序继续执行(这个进程我们用“A”表示)
        在cpu执行另一个程序的同时,可能会有新的程序(“B”)也调用
        了getDemo方法,因为“A”还没有建立对象就去执行下一个程序了,
        所以demo此时还是为“null”,所以这时“B”是满足“demo==null”这个
        条件的,这时“B”就有可能会创建了对象,而“A”因为在之前也是
        满足“demo==null”的,所以这时“A”也是可以执行“demo=new Demo()”的,
        当执行到这里,我们发现此时对象已经不唯一了,
        所以应该怎么办呢?
        解决方法一:
        在函数上声明锁:
            public static synchronized Demo getDemo(){
                            if(demo==null)
                                demo = new Demo();
            }
            //synchronized  是同步的意思   表示在调用该函数后,
            在“A”没有执行完该函数以前,“B”是进不来的,当“A”执行完后,
            “B”在进来时demo就已经不为null了,条件不成立,所以“B”是
            创建不了对象的,这样就保证了对象的唯一性,但是这样写会严重
            影响程序的效率,有没有其他效率比价高的方法呢?

        解决方法二、
            在函数内部声明锁:
            public static Demo getDemo(){
                if(demo==null){
                    synchronized(Demo1.class){  //类名.class
                        if(demo==null){
                            demo = new Demo();
                        }
                    }
                }
            }
        }*/