Java构造函数及关键字

构造函数

  • 简单说,构造函数就是构建创造对象时调用的函数
  • 创建对象都必须要通过构造函数初始化,一个类如果没有定义过构造过程,会有一个默认空参构造函数。若定义了指定的构造函数,那么类中默认构造函数就会消失。
特点
  • 函数名与类类名相同
  • 不能返回值类型
  • 没有具体返回值
区别
  • 构造函数
    • 对象创建时,会掉用与之相应的构造函数,对对象进行初始化
    • 对象创建时,会调用且仅调用一次
  • 一般函数
    • 对象创建后,需要使用函数功能时才调用
    • 对象创建后,可调用多次
重载
  • 当一个类中出现多个构造函数时,必须以重载形式体现
内存图解
  • 接下来用下面这段代码,来演示内存变化过程
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public 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
    8
    class 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
    10
    class 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
    13
    class 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
    19
    public 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
    3
    public boolean compare(Person p){
    return this.age == p.age ;
    }
  • 主类中如何使用?

    1
    2
    3
    Person 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
    24
    public 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
    4
    public static void main(String[] args){
    System.out.println(args);
    System.out.println(args.length);
    }
  • 结果:[Ljava.lang.String;@1540e19d 0