:curry: 如何初始化对象
我们知道再 Java 方法内部定义一个 局部变量 的时候,必须要初始化,否则就会编译失败
要让这串代码通过编译,很简单,只需要在正式使用a之前,给a设置一个初始值就好
那么对于创造好的对象来说,我们也要进行相对应的初始化
我们先写一个Mydate的类
public class MyDate {
public int year;
public int month;
public int day;
/**
* 设置日期:
*/
public void setDate(int year,int month,int day) {
this.year = year;
this.month = month ;
this.day = day;
}
public void printDate() {
System.out.println("年:"+this.year+"月:"+this.month+"日:"+this.day);
}
public static void main(String[] args) {
// this.printDate();
MyDate myDate = new MyDate();
myDate.setDate(,3,25);
myDate.printDate();
MyDate myDate = new MyDate();
myDate.setDate(2022,3,28);
myDate.printDate();
}
}
我们可以看到,在Mydate类中,要在调用之前实现写好setDate才能将具体的日期设置到对象当中
我们通过这个例子就可以发现两个问题:
- 每次对象创建好之后调用setDate方法设置具体时期,比较麻烦,那么对象该如何初始化呢?
- 局部变量必须要初始化之后才能使用,那么为什么字段声明之后没有给初值,它依旧可以使用呢?
答案:
- 我们可以运用 构造函数 来进行初始化
- 因为这里和 main函数 中定义的局部变量不同,编译器会自动为你的字段声明的局部变量赋初始零值。
:bento: 构造方法
当我们实例化一个对象的时候:必须会有这两步,但并不是一定只有这两步
- 为对象分配内存
- 调用合适的构造方法
构造方法(也称为构造器)是一个特殊的成员方法, 名字必须与类名相同,在创建对象的时候,由编译器自动调用,并且在整个对象的 生命周期 内只调用一次
class Person {
public String name;
public int age;
//构造方法:
//名字与类名相同,且没有返回值类型,void也不行
//一般情况下使用public修饰
//在创建对象的时候由编译器自动调用,并且在对象的声明周期内只调用一次
public Person(String name,int age){
this.name = name;
this.age = age;
System.out.println("构造方法被调用了")
}
public void eat() {
System.out.println("吃饭!");
}
public void sleep() {
System.out.println("睡觉!");
}
public static void main(String[] args){
//在此处创建了一个Date类型的对象,并没有显式调用构造函数
Person p = new Person("xiaohong",);
p.eat();
}
}
:warning::构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间
:pizza: 特性
- 名字必须和类名相同
- 没有返回值类型,void也不行
- 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生)
- 构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)
class Student {
//属性:成员变量-》类的内部 方法的外部的
public String name;
public int age;
public double score ;
public String sex;
public Student() {
//调用本类中 带有个参数的构造方法,第一个参数的类型是 String ,第2个参数的类型是int
this.age =;
System.out.println("这个是不带参数的构造方法!");
}
public Student(String name,int age) {
//this();
this.age = age;
this.name = name;
System.out.println("这个是带个参数的构造方法!");
}
public Student(String name, int age, double score, String sex) {
this.name = name;
this.age = age;
this.score = score;
this.sex = sex;
System.out.println("带个参数的构造方法!");
}
public void doClass() {
System.out.println(name+"正在上课!");
//this.doHomeWork();
}
public void doHomeWork(){
System.out.println(name+"正在写作业");
}
public void show() {
System.out.println("姓名:"+name+" 年龄:"+age+" 学分:"+score+" 性别:"+sex);
}
}
//重载的时候
上述方法中:名字相同,参数列表不同,因此构成了 方法重载
如果用户没有显式定义,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的。
class MyGirlFired {
public String name;
public int age;
public String eyes;
public void eat() {
System.out.println("吃火锅!");
}
}
在上面的代码中,没有定义任何构造方法,编译器就会默认为我们生成一个不带参数的构造方法
:warning:: 一旦用户定义了,编译器则不再生成任何构造函数
class MyGirlFired {
public String name;
public int age;
public String eyes;
public MyGirlFired(String name,int age){
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("吃火锅!");
}
public static void main(String[] args) {
//如果编译器会生成,则生成的构造方法一定是无参数的
//则此处创建对象是可以通过编译的
//但实际上会报错
MyGirlFired xHong = new MyGirlFired();
}
}
在构造方法中,可以通过this调用其他构造方法来简化代码
class Student {
//属性:成员变量-》类的内部 方法的外部的
public String name;
public int age;
public double score ;
public String sex;
public Student() {
//调用本类中 带有个参数的构造方法,第一个参数的类型是String,第2个参数的类型是int
this("yumi",);
//this("bit",99,98.9,"女");
System.out.println("这个是不带参数的构造方法!");
}
public Student(String name,int age) {
//this();
this.age = age;
this.name = name;
System.out.println("这个是带个参数的构造方法!");
}
public Student(String name, int age, double score, String sex) {
this.name = name;
this.age = age;
this.score = score;
this.sex = sex;
System.out.println("带个参数的构造方法!");
}
public void doClass() {
System.out.println(name+"正在上课!");
//this.doHomeWork();
}
public void doHomeWork(){
System.out.println(name+"正在写作业");
}
public void show() {
System.out.println("姓名:"+name+" 年龄:"+age+" 学分:"+score+" 性别:"+sex);
}
}
:warning: 注意:
- this()必须是构造方法中的第一条语句,且只能放在构造函数中
- 不能形成“环”
例如
绝大多数情况我们都用public来修饰,特殊场景下会被 private 修饰
默认初始化
上面我们提到了一个问题:为什么局部变量在使用时必须要用初始化,而成员变量可以不用呢?
public class Date {
public int year;
public int month;
public int day;
public Date(int year,int month,int day){
//成员变量在定义之前,并没有给出初始值,那为什么就可以使用呢?
System.out.println(this.year);
System.out.println(this.month);
System.out.println(this.day);
}
public static void main(String[] args) {
//此处a并没有初始化,编译器报错;
//Error:(,28)Java:可能尚未初始化变量a
//int a;
//System.out.println(a);
Date d = new Date(,3,29);
}
}
要搞清楚这个过程,我们需要知道new关键字背后所发生的一些事情
Date d = new Date(,6,9);
在程序层面只是简单的一条语句,而在 JVM 层面则需要做好多事情
- 检测对象对应的类是否加载了,如果没有加载则加载
- 为对象分配内存空间
- 处理并发安全问题
:point_right:比如:多个 线程 同时申请对象,JVM要保证给对象分配的空间不冲突
- 初始化所分配的空间
即:对象空间被申请好了之后,对象中包含的成员已经设置好了初始值
数据类型 |
默认值 |
byte |
0 |
char |
‘u0000’ |
short |
0 |
int |
0 |
long |
0L |
boolean |
false |
float |
0.0f |
double |
0.0 |
reference |
null |
- 设置对象头信息(关于对象内存模型后面再说)
- 调用构造方法,是给对象中的各个成员赋值
:fried_shrimp:就地初始化
在声明成员变量时,就直接给出了初始值
public class Date {
public int year;
public int month;
public int day;
public Date(){
System.out.println(this.year);
System.out.println(this.month);
System.out.println(this.day);
}
public Date(int year,int month,int day){
//成员变量在定义之前,并没有给出初始值,那为什么就可以使用呢?
System.out.println(this.year);
System.out.println(this.month);
System.out.println(this.day);
}
public static void main(String[] args) {
//此处a并没有初始化,编译器报错;
//Error:(,28)Java:可能尚未初始化变量a
//int a;
//System.out.println(a);
Date d = new Date(2022,3,29);
Date d = new Date();
}
}
//运行结果:
//
//
//
//
//
//
:warning: 注意 :代码编译完成之后,编译器会将所有成员初始化的这些语句添加到各个构造函数中
5.一个类至少会有1个构造函数,就算你没有写!
6.构造方法 本质 就是来实例化对象的时候调用
(1)分配内存
(2)调用合适的构造方法
7.this可以用来调用本类中的其他构造方法【构造方法当中使用】
且必须放在第一行!所以,只能在当前构造方法当中,调用一次
8.this的用法:
- this.data 访问属性
- this.func(); 访问方法
- this(); //用来调用本类中的其他构造方法
9.this不能形成环。