- static关键字
- 用于修饰成员(成员变量和成员函数)。
- 被修饰的成员具备一下特点:
- 随着类的加载而加载,随着类的消失而消失,说明他的生命周期最长
- 优先于对象存在
明确一点:静态先存在,对象后存在 - 被所有对象共享
- 可以直接被类名所调用(也可以被对象调用)
- 被所有类所共享
- 使用注意:
- 静态方法只能访问静态成员(静态成员函数和静态成员变量)
- 静态方法中不可以写this,super关键字(静态优先对象存在)
- 主函数时静态的(作为程序入口,可以被JVM调用)
- 非静态可以访问静态,也可以访问非静态。
静态的方法中是否有this这个对象? 没有
工具类Math,构造函数私有,math类中的函数方法全部时静态,可以直接调用
Math类下常用
PI
abs 返回绝对值
ceil 向上取整
floor 向下取整
round 四舍五入
max 取两个数的最大值
min 取两个数的最小值
random 返回一个随机数,大于0且小于1
成员变量,实例变量(不同的属性和行为)
静态的成员变量,类变量(共有属性和行为)
实例变量和类变量的区别:
1. 存放位置:
类变量随着类的加载而存在于方法区;
实例变量随着对象的建立而存在与堆内存中。
2. 生命周期
类变量的生命周期最长,随着类的消失而消失;
实例变量生命周期随着对象的消失而消失。
静态有利有弊:
利:对对象的共享数据进行单独空间的存储,节省空间,没有必要每个对象都存储一份;可以直接被类名调用。
弊:生命周期过长,访问出现局限(静态虽好,只能访问静态)。
- Java主函数解析:主函数方法可以放在任意类中
public abstract class Demo {
public static void main(String[] args) {
System.out.println("hello");
}
}
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]);
}
}
什么时候使用静态?
要从两个方面下手:
因为静态修饰的内容有成员变量和函数。
什么时候定义静态变量(类变量)呢?
当对象中出现共享数据时,该数据被静态所修饰。
对象中的特有数据要定义成非静态存在与堆内存中。
什么时候定义静态成员函数呢?
当功能内部没有到非静态成员变量(对象的特有数据),
那么该功能可以定义为静态的。
- 静态的应用
每个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装,以便复用。
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的对象使用这些工具方法,对数组进行操作,发现了问题:
- 对象是用于封装数据的,可是ArrayTool对象并未封装特有数据(成员变量)
- 操作数组的每一个方法都没有用到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
-
一个类中的默认(没有写)会有一个空参数的构造函数,这个默认构造函数的权限和所属类一致。
如果类被public所修饰,那么默认的构造函数也带public所修饰。
如果类没有被public所修饰,那么默认的构造函数没有public所修饰。 -
静态代码块初始化。
随着类的加载而加载,只加载一次,加载类时需要做的一些初始化,比如加载驱动
格式:
static
{
//代码
}
- 构造代码块初始化
构造带马块:提取构造方法中的共性,每次创建对象都会执行,并且在构造方法执行之前执行
{
}
特点:随着类的加载而执行,只执行一次,用于给类进行初始化的。
用于给类进行初始化的。光引用不加载类,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
执行顺序: 没有对象的创建。(构造代码块不执行,显示初始化也不执行)
静态代码块初始化
对应的构造函数执行一次
静态属于类,
非静态属于对象(类的实例)
创建对象时:
静态代码块初始化 ->默认初始化->显式初始化->构造代码块初始化-> 构造函数->调用(对象)函数(即非静态函数)
不创建对象时:
静态代码块初始化—>调用静态函数
- 执行过程
Person p = new Person("zhangsan", 20);
该句话都做了什么事情?
1. 因为new用到了Person.class。所以会先找到Person.class文件并加载到内存中。
注意:只创建引用不会加载类,只有调用类方法(静态函数)或者创建对象时才会加载类。
2. 执行该类中的 static代码块 ,如果有的话,给Person.class类进行初始化。
3. 在堆内存中开辟空间,分配内存地址。
4. 在堆内存中建立对象的特有属性,并进行默认初始化。
5. 对属性进行显示初始化。
6. 对对象进行构造代码块初始化。 //构造代码块就是非静态初始化代码块
7. 对对象进行对应的构造函数初始化
8. 将内存地址赋给栈内存中的p变量。
静态代码块初始化——>开辟空间,分配内存——>默认初始化(堆内存中)——>显示初始化——>构造代码块初始化(非静态代码块)——>构造函数 ——>内存地址赋给内存中的变量
代码执行顺序:
Coder静态代码块执行-------Coder构造代码块执行--------Coder无参空构造执行
BlockerTest静态代码块执行------BlockTest的主函数执行-------Coder静态代码执行--------Coder构造代码块执行--------Coder无参空构造执行
Coder构造代码块执行-------Coder无参空构造执行
public class CoderTest {
static{
System.out.println("CoderTest静态代码块");
}
{
System.out.println("CoderTest构造代码块");
}
CoderTest()
{
System.out.println("CoderTest无参构造函数");
}
public static void main(String[] args) {
System.out.println("CoderTest主函数执行了");
System.out.println("第一次执行构造函数");
Coder c1 = new Coder();
System.out.println("第二次执行构造函数");
Coder c2 = new Coder();
}
}
class Coder{
static{
System.out.println("Coder静态代码块");
}
{
System.out.println("Coder构造代码块");
}
public Coder(){
System.out.println("Coder无参构造函数");
}
}
注意:创建类的引用,不会加载类(加载类是在方法区)
- 栈,堆和方法区
栈:临时变量,函数的形参(局部变量),类的引用变量
堆:对象的成员变量
方法区:静态区:某个类的静态变量,静态函数。 非静态区: 对象的函数(非静态)
函数内部调用:静态函数省略的是类名,非静态函数省略的是this**(本类的引用)**。