【java】Static解读和public static void main(String[] args)详细分析
首先问一个问题:publicstatic void main(String[] args)代码代表了什么含义?以及思考为什么java程序一开始就会进入main()函数,其实c、c++也是如此?后面一个问题有static的基础就非常容易。(后一个问题的答案提前公布:用static修饰的代码块表示静态代码块,当Java虚拟机(JVM)加载类时,就会执行该代码块) 现在回到第一个问题:
首先问一个问题:
public static void main(String[] args)代码代表了什么含义?以及思考为什么java程序一开始就会进入main()函数,其实c、c++也是如此?后面一个问题有static的基础就非常容易。(后一个问题的答案提前公布:用static修饰的代码块表示静态代码块,当Java虚拟机(JVM)加载类时,就会执行该代码块)
现在回到第一个问题:
下面分别解释这些关键字的作用:
(1)public关键字,这个好理解,声明主函数为public就是告诉其他的类可以访问这个函数。
(2)static关键字,告知编译器main函数是一个静态函数。也就是说main函数中的代码是存储在静态存储区的,即当定义了类以后这段代码就已经存在了。如果main()方法没有使用static修饰符,那么编译不会出错,但是如果你试图执行该程序将会报错,提示main()方法不存在。因为包含main()的类并没有实例化(即没有这个类的对象),所以其main()方法也不会存。而使用static修饰符则表示该方法是静态的,不需要实例化即可使用。
(3)void关键字表明main()的返回值是无类型。
(4)参数String[] args
public class Testargs {
public static void main(String[] args) {
String str = new String();
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
str += args[i];
}
System.out.println(str);
}
}
如果不使用编译器,使用命令行进行编译。你可以在准备编译class的时候传入abc,那么上面程序运行的结果就是abc,也就是参数args的主要作用是为程序使用者在命令行状态下与程序交互提供了一种手段。
另外一种作用就是像正常函数方法一样调用其函数,但是需要注意的一点就是static声明的变量或者方法,是类方法或者说是类变量,不需要实例化就可以直接使用
具体实现步骤:
首先声明一个类A:
public classA {
public static void main(String[] args)
{
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
}
接着再写一个B类:
public classB {
public static void main(String[] args)
{
//A c = new A();
String[] b = {"111","222","333"};
//c.main(b); //输出111 222 333
//因为main()函数是静态函数,因此不需要实例化就可以使用
A.main(b);//输出结果和上面的是一样的
}
}
这个没有很常用,只是作为了解就可以了
static 修饰符能够与变量、方法一起使用,表示是“静态”的。
静态变量和静态方法能够通过类名来访问,不需要创建一个类的对象来访问该类的静态成员,所以static修饰的成员又称作类变量和类方法。静态变量与实例变量不同,实例变量总是通过对象来访问,因为它们的值在对象和对象之间有所不同。
请看下面的例子:
public class Demo {
static int i = 10;
int j;
Demo() {
this.j = 20;
}
public static void main(String[] args) {
System.out.println("类变量 i=" + Demo.i);
Demo obj = new Demo();
System.out.println("实例变量 j=" + obj.j);
}
}
运行结果:
类变量 i=10
实例变量 j=20
static 的内存分配
静态变量属于类,不属于任何独立的对象,所以无需创建类的实例就可以访问静态变量。之所以会产生这样的结果,是因为编译器只为整个类创建了一个静态变量的副本,也就是只分配一个内存空间,虽然有多个实例,但这些实例共享该内存。实例变量则不同,每创建一个对象,都会分配一次内存空间,不同变量的内存相互独立,互不影响,改变 a 对象的实例变量不会影响 b 对象。请看下面的代码:
- public class Demo {
- static int i;
- int j;
- public static void main(String[] args) {
- Demo obj1 = new Demo();
- obj1.i = 10;
- obj1.j = 20;
-
- Demo obj2 = new Demo();
-
- System.out.println("obj1.i=" + obj1.i + ", obj1.j=" + obj1.j);
- System.out.println("obj2.i=" + obj2.i + ", obj2.j=" + obj2.j);
- }
- }
obj1.i=10, obj1.j=20
obj2.i=10, obj2.j=0
注意:静态变量虽然也可以通过对象来访问,但是不被提倡,编译器也会产生警告。
上面的代码中,i 是静态变量,通过 obj1 改变 i 的值,会影响到 obj2;j 是实例变量,通过 obj1 改变 j 的值,不会影响到 obj2。这是因为 obj1.i 和 obj2.i 指向同一个内存空间,而 obj1.j 和 obj2.j 指向不同的内存空间,请看下图:
图1 静态变量内存分配
注意:static 的变量是在类装载的时候就会被初始化。也就是说,只要类被装载,不管你是否使用了这个static 变量,它都会被初始化。
小结:类变量(class variables)用关键字 static 修饰,在类加载的时候,分配类变量的内存,以后再生成类的实例对象时,将共享这块内存(类变量),任何一个对象对类变量的修改,都会影响其它对象。外部有两种访问方式:通过对象来访问或通过类名来访问。
静态方法
静态方法是一种不能向对象实施操作的方法。例如,Math 类的 pow() 方法就是一个静态方法,语法为 Math.pow(x, a),用来计算 x 的 a 次幂,在使用时无需创建任何 Math 对象。因为静态方法不能操作对象,所以不能在静态方法中访问实例变量,只能访问自身类的静态变量。
以下情形可以使用静态方法:
- 一个方法不需要访问对象状态,其所需参数都是通过显式参数提供(例如 Math.pow())。
- 一个方法只需要访问类的静态变量。
读者肯定注意到,main() 也是一个静态方法,不对任何对象进行操作。实际上,在程序启动时还没有任何对象,main() 方法是程序的入口,将被执行并创建程序所需的对象。
关于静态变量和静态方法的总结:
- 一个类的静态方法只能访问静态变量;
- 一个类的静态方法不能够直接调用非静态方法;
- 如访问控制权限允许,静态变量和静态方法也可以通过对象来访问,但是不被推荐;
- 静态方法中不存在当前对象,因而不能使用 this,当然也不能使用 super;
- 静态方法不能被非静态方法覆盖;
- 构造方法不允许声明为 static 的;
- 局部变量不能使用static修饰。
静态方法举例:
- public class Demo {
- static int sum(int x, int y){
- return x + y;
- }
- public static void main(String[] args) {
- int sum = Demo.sum(10, 10);
- System.out.println("10+10=" + sum);
- }
- }
10+10=20
static 方法不需它所属的类的任何实例就会被调用,因此没有 this 值,不能访问实例变量,否则会引起编译错误。
注意:实例变量只能通过对象来访问,不能通过类访问。
关于在静态方法中间接调用非静态方法示例:
package com.imooc.testargs;
public class StaticInvokeNonStaticMethod {
private String name;
public StaticInvokeNonStaticMethod(String name) {
this.name = name;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
StaticInvokeNonStaticMethod s = new StaticInvokeNonStaticMethod(
"zhangsan");
StaticInvokeNonStaticMethod s2 = new StaticInvokeNonStaticMethod(
"wangwu");
StaticInvokeNonStaticMethod.ask(s);
StaticInvokeNonStaticMethod.ask(s2);
//s.ask(s2); // 对象调用静态方法的方式不推荐使用
}
/**
* 通过传入一个实例对象的方法引用,达到静态方法内部调用非静态方法
* */
public static void ask(StaticInvokeNonStaticMethod s) {
System.out.println("静态方法内部调用非静态方法");
//System.out.println(s); //这是在类中访问类对象,不是实例变量
//Cannot make a static reference to the non-static field name, 说明不能在静态方法中访问实例变量
//System.out.println(name);
s.say();
}
public void say() {
System.out.println("hello my name is " + name);
}
}
静态初始器(静态块)
块是由大括号包围的一段代码。静态初始器(Static Initializer)是一个存在于类中、方法外面的静态块。静态初始器仅仅在类装载的时候(第一次使用类的时候)执行一次,往往用来初始化静态变量。示例代码:
- public class Demo {
- public static int i;
- static{
- i = 10;
- System.out.println("Now in static block.");
- }
- public void test() {
- System.out.println("test method: i=" + i);
- }
- public static void main(String[] args) {
- System.out.println("Demo.i=" + Demo.i);
- new Demo().test();
- }
- }
Now in static block.
Demo.i=10
test method: i=10
静态导入
静态导入是 Java 5 的新增特性,用来导入类的静态变量和静态方法。一般我们导入类都这样写:
- import packageName.className; // 导入某个特定的类
- import packageName.*; // 导入包中的所有类
而静态导入可以这样写:
- import static packageName.className.methonName; // 导入某个特定的静态方法
- import static packageName.className.*; // 导入类中的所有静态成员
导入后,可以在当前类中直接用方法名调用静态方法,不必再用 className.methodName 来访问。
对于使用频繁的静态变量和静态方法,可以将其静态导入。静态导入的好处是可以简化一些操作,例如输出语句 System.out.println(); 中的 out 就是 System 类的静态变量,可以通过 import static java.lang.System.*; 将其导入,下次直接调用 out.println() 就可以了。
请看下面的代码:
- import static java.lang.System.*;
- import static java.lang.Math.random;
- public class Demo {
- public static void main(String[] args) {
- out.println("产生的一个随机数:" + random());
- }
- }
产生的一个随机数:0.05800891549018705
更多推荐
所有评论(0)