Java基础(四)面向对象(二)

  1. static关键字
  • 用于修饰成员(成员变量和成员函数)。

  • 被修饰的成员具备一下特点:

    • 随着类的加载而加载,随着类的消失而消失,说明他的生命周期最长
    • 优先于对象存在
      明确一点:静态先存在,对象后存在
    • 被所有对象共享
    • 可以直接被类名所调用(也可以被对象调用)
  • 使用注意:

    • 静态方法只能访问静态成员(静态成员函数和静态成员变量)
    • 静态方法中不可以写this,super关键字(静态优先对象存在)
    • 主函数时静态的(作为程序入口,可以被JVM调用)
    • 非静态可以访问静态,也可以访问非静态。

    成员变量,实例变量(不同的属性和行为)
    静态的成员变量,类变量(共有属性和行为)

    实例变量和类变量的区别:

    1. 存放位置:
      类变量随着类的加载而存在于方法区;
      实例变量随着对象的建立而存在与堆内存中。
    2. 生命周期
      类变量的生命周期最长,随着类的消失而消失;
      实例变量生命周期随着对象的消失而消失。

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

  1. Java主函数解析:

    public static void main(String[] args):
    主函数:是一个特殊的函数,作为程序的入口,可以被JVM调用
    public:代表该函数访问权限最大的。
    static:代表主函数随着类的加载就已经存在了。
    void:主函数没有具体的返回值。
    main:不是关键字,但是是一个特殊的单词,可以被JVM识别。
    (String[] args):函数的参数,参数类型是一个数组,该数组中的元素是字符串,字符串类型的数组。args可以改。
    主函数是固定格式的:JVM识别。
    JVM在调用主函数时,传入的时new String[0];
    命令行传值:java Demo(类名) hahah hehe heiehi1(三个字符串)

public class MainDemo {
    public static void main(String[] args) {
        String[] arr = {"haha", "heihei", "xixi"};
        MainTest.main(arr);
    }
}

class MainTest
{
    public static void main(String[] args) {
        for(int i = 0; i < args.length; i++)
            System.out.println(args[i]);
    }
}

什么时候使用静态?
要从两个方面下手:
因为静态修饰的内容有成员变量和函数。

什么时候定义静态变量(类变量)呢?
当对象中出现共享数据时,该数据被静态所修饰。
对象中的特有数据要定义成非静态存在与堆内存中。

什么时候定义静态成员函数呢?
当功能内部没有到非静态成员变量(对象的特有数据),
那么该功能可以定义为静态的。
  1. 静态的应用
    每个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装,以便复用。
public class ArrayTool {
    public int getMax(int[] arr) //获取最大值
    {
        int max = 0;
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > arr[max])
                max = i;
        }
        return arr[max];
    }

    public int getMin(int[] arr) //获取最小值
    {
        int min = 0;
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] < arr[min])
                min = i;
        }
        return arr[min];
    }

    public void selectionSort(int[] arr)  //原始选择排序,效率不高,易于记忆
    {
        for (int i = 0; i < arr.length; i++) {
            for (int j = i; j < arr.length; j++)
                if (arr[i] > arr[j])
                    swap(arr, i, j);
        }
    }

    public void bubbleSort(int[] arr) //原始冒泡排序,效率不高,易于记忆
    {
        for (int i = 0; i < arr.length; i++)
            for (int j = 0; j < arr.length - i - 1; j++)
                if (arr[j] > arr[j + 1])
                    swap(arr, j, j + 1);
    }

    public void swap(int[] arr, int a, int b)  //交换值
    {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

    public void printArray(int[] arr)   //打印数组
    {
        System.out.print("[");
        for (int i = 0; i < arr.length; i++) {
            if (i != arr.length - 1)
                System.out.print(arr[i] + ",");
            else
                System.out.println(arr[i] + "]");
        }
    }

    public int halfSearch(int[] arr, int key) {
        int min = 0;
        int max = arr.length - 1;
        int mid;
        while (min <= max) {
            mid = (min + max) >> 1;
            if (arr[mid] < key)
                min = mid + 1;
            else if (arr[mid] > key)
                max = mid - 1;
            else
                return mid;
        }
        return -1;
    }
}
public class ArrayToolTest {
    public static void main(String[] args) {
        int[] arr = {12,311,45,3,4,63,23,453,54,443,234};
        ArrayTool tool = new ArrayTool();
        int max = tool.getMax(arr);
        System.out.println("max=" + max);
        int min = tool.getMin(arr);
        System.out.println("min=" + min);

        tool.printArray(arr);
        tool.selectionSort(arr);
        tool.printArray(arr);

        int[] arr1 = {12,311,45,3,4,63,23,453,54,443,234};
        tool.printArray(arr1);
        tool.bubbleSort(arr1);
        tool.printArray(arr1);
        
        System.out.println(tool.halfSearch(arr,4));
        System.out.println(tool.halfSearch(arr,43));
    }
}

在这里插入图片描述

注意:虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作,发现了问题:

  1. 对象是用于封装数据的,可是ArrayTool对象并未封装特有数据(成员变量)
  2. 操作数组的每一个方法都没有用到ArrayTool对象中的特有数据(成员变量)。

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

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

public class ArrayTool {
    private ArrayTool(){}   //防止别人创建对象。
    public static int getMax(int[] arr) //获取最大值
    {
        int max = 0;
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > arr[max])
                max = i;
        }
        return arr[max];
    }

    public static int getMin(int[] arr) //获取最小值
    {
        int min = 0;
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] < arr[min])
                min = i;
        }
        return arr[min];
    }

    public static void selectionSort(int[] arr)  //原始选择排序,效率不高,易于记忆
    {
        for (int i = 0; i < arr.length; i++) {
            for (int j = i; j < arr.length; j++)
                if (arr[i] > arr[j])
                    swap(arr, i, j);
        }
    }

    public static void bubbleSort(int[] arr) //原始冒泡排序,效率不高,易于记忆
    {
        for (int i = 0; i < arr.length; i++)
            for (int j = 0; j < arr.length - i - 1; j++)
                if (arr[j] > arr[j + 1])
                    swap(arr, j, j + 1);
    }

    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 printArray(int[] arr)   //打印数组
    {
        System.out.print("[");
        for (int i = 0; i < arr.length; i++) {
            if (i != arr.length - 1)
                System.out.print(arr[i] + ",");
            else
                System.out.println(arr[i] + "]");
        }
    }

    public static int halfSearch(int[] arr, int key) {
        int min = 0;
        int max = arr.length - 1;
        int mid;
        while (min <= max) {
            mid = (min + max) >> 1;
            if (arr[mid] < key)
                min = mid + 1;
            else if (arr[mid] > key)
                max = mid - 1;
            else
                return mid;
        }
        return -1;
    }
}
public class ArrayToolTest {
    public static void main(String[] args) {
        int[] arr = {12,311,45,3,4,63,23,453,54,443,234};

        int max = ArrayTool.getMax(arr); //静态成员函数通过类名直接调用
        System.out.println("max=" + max);
        int min = ArrayTool.getMin(arr);
        System.out.println("min=" + min);

        ArrayTool.printArray(arr);
        ArrayTool.selectionSort(arr);
        ArrayTool.printArray(arr);

        int[] arr1 = {12,311,45,3,4,63,23,453,54,443,234};
        ArrayTool.printArray(arr1);
        ArrayTool.bubbleSort(arr1);
        ArrayTool.printArray(arr1);

        System.out.println(ArrayTool.halfSearch(arr,4));
        System.out.println(ArrayTool.halfSearch(arr,43));
    }
}

·set classpath=.;路径目录; //调用别人写的class文件
接下来,将ArrayTool.class文件发送给其他人,其他人只要将该文件设置到classpath路径下,就可以使用该工具类。
但是,很遗憾,该类中到底定义了多少个方法,对方也不清楚,因为该类并没有使用说明书。
开始制作程序的说明书,java的说明书通过文档注释来完成
/**
*/
javadoc -d C:\myhelp -encoding UTF-8 -author - version ArrayTool.java

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

  2. 静态代码块初始化。
    格式:

static
{
	//代码
}
  1. 构造代码块初始化
{

}
特点:随着类的加载而执行,只执行一次,用于给类进行初始化的。
用于给类进行初始化的。光引用不加载类,StaticCode s = null;
调用类方法(静态方法)或者创建类的对象时都会加载类。
public class StaticCode {
    static{
        System.out.println("a");
    }
}

class StaticDemo
{
    static
    {
        System.out.println("b");
    }
    public static void main(String[] args) {
        new StaticCode();
        new StaticCode();
        System.out.println("over");
    }
    static {
        System.out.println("c");
    }
}
输出:
	b
	c
	a

执行顺序:
主函数的静态代码块初始化
调用类的静态代码块初始化
调用类的静态成员函数

public class StaticCode {
    {
        System.out.println("a");
    }
    static
    {
        System.out.println("b");
    }
    StaticCode()
    {
        System.out.println("c");
    }
    StaticCode(int x)
    {
        System.out.println("d");
    }


}

class StaticDemo
{
    public static void main(String[] args) {
        new StaticCode(4);

    }

}

输出
b
a
d

执行顺序:有创建对象
先执行静态代码块初始化
在执行构造代码块初始化(创建对象时才会执行默认初始化)
构造函数(只会执行对应的构造函数一次)
调用对象的成员函数

public class StaticCode {
    {
        System.out.println("a");
    }
    static
    {
        System.out.println("b");
    }
    StaticCode()
    {
        System.out.println("c");
    }
    StaticCode(int x)
    {
        System.out.println("d");
    }
    public static void show()
    {
        System.out.println("e");
    }


}

class StaticDemo
{
    public static void main(String[] args) {
        StaticCode.show();

    }
}

输出:
b
e

执行顺序: 没有对象的创建。(构造代码块不执行,显示初始化也不执行)
静态代码块初始化
对应的构造函数执行一次

静态属于类,
非静态属于对象(类的实例)

创建对象时:
静态代码块初始化 ->默认初始化->显式初始化->构造代码块初始化-> 构造函数->调用(对象)函数(即非静态函数)
不创建对象时:
静态代码块初始化—>调用静态函数

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

静态代码块初始化——>开辟空间,分配内存——>默认初始化(堆内存中)——>显示初始化——>构造代码块初始化(非静态代码块)——>构造函数 ——>内存地址赋给内存中的变量

  1. 栈,堆和方法区
    栈:临时变量,函数的形参(局部变量),类的引用变量
    堆:对象的成员变量
    方法区:静态区:某个类的静态变量,静态函数。 非静态区: 对象的函数(非静态)
    函数内部调用:静态函数省略的是类名,非静态函数省略的是this**(本类的引用)**。

猜你喜欢

转载自blog.csdn.net/Dcwjh/article/details/89049154