构造函数
- 简单说,构造函数就是构建创造对象时调用的函数
- 创建对象都必须要通过构造函数初始化,一个类如果没有定义过构造过程,会有一个默认空参构造函数。若定义了指定的构造函数,那么类中默认构造函数就会消失。
特点
- 函数名与类类名相同
- 不能返回值类型
- 没有具体返回值
区别
- 构造函数
- 对象创建时,会掉用与之相应的构造函数,对对象进行初始化
- 对象创建时,会调用且仅调用一次
- 一般函数
- 对象创建后,需要使用函数功能时才调用
- 对象创建后,可调用多次
重载
- 当一个类中出现多个构造函数时,必须以重载形式体现
内存图解
- 接下来用下面这段代码,来演示内存变化过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class Person {
private String name;
private int age;
Person(){};
Person(String name,int age){
this.name = name;
this.age = age;
}
public void speak(){
System.out.println(name+":"+age);
}
public static void main(String[] args){
Person person = new Person("jack",20);
person.speak();
}
}
- 左面是栈内存,右面是堆内存。首先在栈内存运行main函数,创建名字为p的对象,然后栈内存调用了有参构造函数。在堆内存中开辟一片区域,给予其地址,将构造函数中的值寻址赋给堆中变量的值,然后将构造函数弹栈。将堆内存中的地址赋给p。
- 然后在栈中入栈speak方法,运行后寻址输出变量值
this关键字
- 当成员变量和局部变量重名,可用关键字this区分
- this代表对象,一般当前对象,this代表其所在函数所属对象的引用
1
2
3
4
5
6
7
8class Person {
Person(String name){
this.name = name;
}
public static void main(){
Person p = new Person(name);
}
}
功能
- this也可用于在构造函数中调用其他构造函数
注:只能定义在构造函数第一行,因为初始化动作要先执行
1
2
3
4
5
6
7
8
9
10class Person {
Person(){}
Person(String name){
this.name = name;
}
Person(String name,int age) {
this(name);
this.age = age;
}
}当执行主函数中的语句时,调用有参构造函数,此时this.name就是代指对象p
- 简单来说,哪个对象调用this所在的函数,this就代表哪个对象
注意事项
- 运行下面的代码失败,死循环
1
2
3
4
5
6
7
8
9
10
11
12
13class Person {
Person(){
this(jack);
}
Person(String name){
this();
this.name = name;
}
Person(String name,int age) {
this(name);
this.age = age;
}
}
内存图解
- 接下来通过下面的代码来演示更加完整的过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public class Person {
private String name;
private int age;
Person(String name){
this.name = name;
}
public void speak(){
System.out.println(name+":"+age);
}
}
public class test {
public static void main(String[] args){
Person p = new Person("rose");
p.speak();
Person p1 = new Person("dell");
p1.speak();
}
}
- 首先在栈内存运行main函数,创建名字为p的对象,然后栈内存调用了有参构造函数。在堆内存中开辟一片区域,给予其地址后初始化数值,构造函数中的this起到了作用,指向p对象的地址,将 name=”jack” 传到堆中更改值,然后将构造函数弹栈。将堆内存中的地址赋给p。speak函数入栈也是通过this寻址堆中赋值。
小练习(双人年龄比较)
思路:在Person类中写一个boolean类型方法,巧妙地利用this就可以compare方法中只带一个参数。
1
2
3public boolean compare(Person p){
return this.age == p.age ;
}主类中如何使用?
1
2
3Person p1 = new Person();
Person p2 = new Person();
p1.compare(p2);
static关键字
特点
- static是一个修饰符,用来修饰成员
- static修饰成员被所有对象共享
- static优先于对象存在(static随类的加载就已经存在了)
- static修饰的成员多了一种调用格式,可以直接被类名调用。如:类名.静态成员
- static修饰的是共享数据 对象中存储的是特有数据
成员变量、静态变量区别
- 区别:
- 生命周期不同
- 调用方式不同
- 别名不同
- 数据存储位置不同
- 成员变量:
- 随对象创建存在,随其回收而释放
- 只能被对象调用
- 实例变量
- 存在堆内存的对象中,也叫对象的特有数据
- 静态变量:
- 随着类加载而存在,随着类消失而消失
- 可以对象调用,也可以类调用
- 类变量
- 存放在方法区(也叫共享数据区)的静态区中,也叫对象的共享数据
静态使用注意
- 静态方法只能调用静态成员(非静态都可以访问)
- 静态方法不可使用this 或者 super关键字
- 主函数静态的
内存图解
- 内存划分
- 寄存器
- 本地方法区
- 方法区
- 栈内存
- 堆内存
- 接下来用一段代码来演示内存变化过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class Person {
private String name;
private int age;
static String country = "CN";
Person(){};
Person(String name,int age){
this.name = name;
this.age = age ;
}
public void show(){
System.out.println(Person.country+":"+this.name+":"+this.age);
}
public static void method(){
System.out.println(Person.country);
}
}
public class test {
public static void main(String[] args){
Person.method();
Person p = new Person("jack",20);
p.show();
}
}
- 运行程序的时候,读取StaticDemo类,在方法区存放他的名字,注意还有他的自带的构造函数,然后在他的静态区粘贴上类的名字和代码,栈内存中压栈main,根据上面代码需要调用Person类,将Person类的构造函数show方法粘在方法区,在静态区添加入静态变量和静态方法。栈内存中压栈method,弹栈。继续执行主函数的语句,创建对象在堆内存开辟空间,压栈Person的构造函数,用this指向堆中的地址,赋值给堆内存。Person构造函数弹栈,将堆中地址赋值给p对象,show方法压栈,this指向堆中的地址,弹栈
静态什么时候用
静态变量
- 当分析对象中所具备的成员变量的值都是相同的,这是这个成员就可以被静态修饰。只要数据在对象中都不同的,就是对象的特有数据,必须存在对象中。非静态的若是有相同数据,且对象不需做修改只需要使用,,那么就不需要存储在对象中,定义成静态的就可以了
静态函数
- 参考一点,该函数功能是否有访问到对象中的特有数据
- 简单来说,从源代码看该函数功能是否需要访问非静态的成员变量,如果需要,该方法就是非静态的,如果不需要,该方法就可以定义成静态的
- 非静态需要被对象调用,而仅创建调用非静态但没有访问特有数据的方法,那么该对象的创建是没有意义的。
静态代码块
- 随类的加载而执行,且只执行一次
- 作用:给类初始化
- 构造代码块:给所有对象初始化
- 构造函数:给对应对象针对性初始化
- 执行顺序,谁先执行
- 有对象:静态代码块 > 构造代码块 > 构造函数
- 无对象:静态代码块 > 构造函数 > 构造代码块
主函数特殊处
- 格式固定
- 被JVM所识别,所调用
- 分析:
- public 权限最大
- static 不需要对象,直接用主函数所属类名调用
- void 没有具体返回值
- main 不是关键字,只是被JVM所识别固定的名字
- String[] args 主函数参数列表,一个数组类型参数,元素都是字符串
接下来我们输出代码测试一下
1
2
3
4public static void main(String[] args){
System.out.println(args);
System.out.println(args.length);
}结果:[Ljava.lang.String;@1540e19d 0