静态
静态域
带有static标识符,这部分变量具有独立的存储空间,与对象无关,而是与整个类相关,类的所有实例共享静态域,不属于任何独立的对象。
我们可以对Student类增加一个静态域来记录下一个学生的id或记录学生的数量
class{
private static int nextId = 1;
}
静态常量
静态常量量要比静态变量使用得多,在数学中最常使用的静态常量是Math.PI
public class Math{
piublic static final double PI = 3.1415826535897932846;
}
静态方法
与静态域相同,这部分代码块仍然属于类不属于对象,同时静态方法不能使用this调用,只能使用对象名或类名调用。(通常默认都使用类名调用!!!)
静态方法不能操作对象,即类中的静态方法不能访问实例域,但是静态方法可以访问自身类中的静态域。
使用静态方法的两种情况:
- 一个方法不需要访问对象,其所需参数都是通过现式参数提供(Math.pow())
- 一个方法只需要访问类中的静态域(Student.getNextId())
方法参数
首先搞清楚两个概念~
- 按值调用(call by value)表示方法接收的是调用者提供的值
- 按引用调用(call by name)表示方法接收的是调用者提供的变量地址
java语言总是采用按值调用
也就是说,方法参数得到的是参数值的拷贝,方法不能修改传递给他的任何参数变量的内容
方法参数分为基本数据类型(数字、布尔值) 和 对象引用
下面看两个例子
传递基本数据类型
public static void tripleValue(double x) {
x=3*x;
System.out.println("End of method x="+x );//30.0
}
double percent=10;
System.out.println("Before:percent="+percent);//10.0
tripleValue(percent);
System.out.println("After:percent="+percent);//10.0
由于java按值调用的属性,在tripleValue方法参数中传递的是值是x的值即10,x所指向的内存空间和percent的内存空间并不是同一个,仅仅是值拷贝。在方法执行完x的内存空间就被释放掉,对于percent不产生任何影响
传递对象引用
class Employee
{
private String name;
private double salary;
public String getName()
{return name; }
public void setName(String name)
{ this.name=name;}
public double getSalary()
{return salary;}
public void raiseSalary(double byPercent)
{double raise = salary * byPercent / 100;
salary += raise;}
}
public static void tripleSalary (Employee x) {
x.raiseSalary(200);
System.out.println("End of method : salary"+x.getSalary());
}
public static void swap(Employee a,Employee b) {
Employee tempEmployee=a;
a=b;
b=tempEmployee;
System.out.println("a="+a.getName());//Bob
System.out.println("b="+b.getName());//Alice
}
System.out.println("Testing tripleSalary");
Employee harry=new Employee("Harry",50000);
System.out.println("Before:salary="+harry.getSalary());//50000.0
tripleSalary(harry);//150000.0
System.out.println("After:salary="+harry.getSalary());//150000.0
System.out.println("Testing swap");
Employee x=new Employee("Alice",70000);
Employee y=new Employee("Bob",60000);
System.out.println("Before:x="+x.getName());//Alice
System.out.println("Before:y="+y.getName());//Bob
swap(x, y);//Bob Alice
System.out.println("After:x="+x.getName());//Alice
System.out.println("After:y="+y.getName());//Bob
tripleSalary方法中传递的是Employ对象,即为x开辟一块内存空间指向传入的参数harry指向的内存空间,由于二者指向相同,在x指向中所做的修改,等同于对harry进行修改,所以harry的工资在调用方法后发生了改变。
但在swap方法中,实质是a、b分别指向了Alice和Bob的内存空间,方法中交换了a、b的指向,方法执行后,a、b分贝的内存空间被回收,最终对Alice和Bob没有任何影响。
对象构造
重载
多个方法有相同的方法名、不同的参数类型即为重载
顺便想下重写,重写是只子类重写父类的方法,子类方法名和参数名都和父类相同
完整描述一个方法需要有:方法名、参数类型(也叫做方法的签名)
返回类型不是方法签名的一部分
不能有两个名字相同、参数类型相同但返回值类型不同的方法~
初始化块
调用构造器的具体步骤:
- 所有数据域被初始化为默认值(0、false、null)
- 按照在累的声明中出现的次序,依次进行所有域初始化语句和初始化块
- 如果构造器第一行调用了第二构造器,则执行第二个构造器主体
- 执行这个构造器主体
如果在类中包含有静态的初始化块,这部分代码会在类第一次加载的过程中进行初始化
所有的静态初始化语句以及静态初始化块都会依照类定义的顺序执行
执行顺序
这里仍然以employee类为例,放入多个静态初始化块和初始化块,探寻这些代码块或变量的执行顺序
public class Employee {
private String name = "lisi";
{
System.out.println("初始化1");
name = "wangwu";
age = 20;
}
private int age = 30;
/**
* 静态初始化块
*/
static {
System.out.println("静态初始化1");
System.out.println("id0:" + Employee.id);
id = 40;
System.out.println("id1:" + Employee.id);
System.out.println("x 开始");
Employee x = new Employee("liuliu", 50);
System.out.println(x);
System.out.println("liuliu id:" + x.getId());
System.out.println("liuliu结束");
}
private static int id = 30;
{
System.out.println("初始化2");
name = "zhaosi";
age = 40;
}
static {
System.out.println("id2:" + Employee.id);
System.out.println("静态初始化2");
id = 80;
System.out.println("id3:" + Employee.id);
}
public static int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Employee(String name, int age) {
super();
System.out.println("有参构造器");
this.name = name;
this.age = age;
}
public Employee() {
super();
System.out.println("无参构造器");
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Employee [name=" + name + ", age=" + age + "]";
}
}
public class Test {
public static void main(String[] args) {
System.out.println("y开始");
Employee y = new Employee("y", 20);
System.out.println(y);
System.out.println(y.getId());
System.out.println("y结束");
System.out.println("z开始");
Employee z = new Employee("z", 19);
System.out.println(z);
System.out.println(z.getId());
System.out.println("z结束");
}
}
最终的执行结果下
根据最终结果可以看出:
第一步——静态块(包含“静态块调用构造对象”)
最先输出的是“静态初始化1”,即静态块最先执行,因为在类的第一次加载(第一次)的时候,就会进行静态域的初始化。之后分配内存空间并设置为默认值(0 、null、false),在分配后在执行到语句执行顺序按照定义语句时的先后顺序来执行
第二步——初始化块
在“静态初始化1”块里面,我们还定义了一个对象实例,但是,后续显示的却是“初始化1”、“初始化2”、“有参构造器”等,直到该name = “x”;的实例结束; 因为此时为第二次装载这个类,不需要静态块或静态域的执行,直接执行初始化块的内容,再继续执行有参构造器。需要注意的是初始化块中不能调用静态变量~
第三步——构造函数
所有的静态块和初始化都执行完成后,才开始执行类对象的初始化,即第一个 Employee y = new Employee(“y”, 20); ,调用的有参构造器 public Employee(String name, int age);当一个对象调用有参构造器时,就不能会调用无参构造器,这一点需要注意。初始化块会随着类每一次的调用全部执行~