实例化,构造方法,this指针及其static关键字
目录
前面我们简单了解了类和对象的相关知识,今天我们看如何通过类来构造出实例,也就是对象。
一. 什么是实例化?
定义了一个类,就相当于在计算机中定义了一种新的类型,与int,double类似,只不过int和double是 java语言自带的内置类型,而类是用户自定义了一个新的类型,比如之前的:Cat类。有了这些自定义的类之后,就可以使用这些类来定义实例(或者称之为对象)。
用类类型创建对象的过程,称为类的实例化(对象初始化),在java中才用new关键字,配合类名来实例化对象。
// 人类
class Person {
// 属性
public int age;// 年龄
public String name;// 姓名
public String sex;// 性别
// 方法
public void eat() {// 吃饭的方法
System.out.println("吃饭!");
}
public void sleep() {// 睡觉的方法
System.out.println("睡觉!");
}
}
public class Main{
public static void main(String[] args) {
// 通过new实例化对象
Person person = new Person();
// 成员方法调用需要通过对象的引用调用
person.eat();
person.sleep();
}
}
上面这段代码中 Person person = new Person(); 这一句就是实例化出对象,通过关键字 new 。这句代码执行之后发生了什么?接下来我们来详细讲讲。
二. 实例化(对象初始化)的三种方法
1. 字段(又称为属性)就地初始化。
例如下面的代码:
class Cat {
public String name = "咪咪";
public String gander = "公猫";
}
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
System.out.println(cat.name);
System.out.println(cat.gander);
}
}
当属性定义的时候后面就跟上内容,则在实例化时将会根据这个跟随的内容初始化属性。
上面代码的运行结果为
如果属性在定义的时候没有跟上内容,则会将属性赋值为默认值。
例如下面的代码:
class T {
public int i;
public long l;
public double d;
public float f;
public boolean b;
public String s;
public char c;
public int[] is;
}
public class Test1 {
public static void main(String[] args) {
T t = new T();
System.out.println(t.i);
System.out.println(t.l);
System.out.println(t.d);
System.out.println(t.f);
System.out.println(t.b);
System.out.println(t.s);
System.out.println(t.c);
System.out.println(t.is);
}
}
运行结果为:
可以看出不同的类型对应的默认值是不同的,下面我们简单总结一下:
类型 | 默认值 |
short | 0 |
byte | 0 |
int | 0 |
long | 0 |
double | 0.0 |
float | 0.0 |
char | /u000 (Unicode码 本质上还是0) |
boolean | false |
引用类型 | null |
规律是:数字为 0 或者 0.0 ,boolean 类型为 false ,引用类型为 null 。
2. 代码块初始化
我们给出一个例子更直观的感受代码块初始化的意思:
class Cat {
public String name;
public String gander;
// 代码块初始化
{
name = "花花";
gander = "母猫";
}
{
name = "瓜瓜";
gander = "公猫";
}
}
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
System.out.println(cat.name);
System.out.println(cat.gander);
}
}
一个大括号为一个代码块,可以在这个代码块中初始化字段的值(也可以是部分字段)。一个类中可以存在多个代码块,执行顺序为根据代码块的顺序,从上往下依次执行。
上面代码执行后的结果为:
如果代码中有初始化代码块,那么每次构造实例这个代码块就执行一次。
3. 构造方法
构造方法是一种特殊方法, 使用关键字 new 实例化新对象时会被自动调用, 用于完成初始化操作。
构造方法的格式为:
public 类名 (参数列表) {
......
}
构造方法有如下几个特点:
1. 方法名和类名一致
2. 不需要返回值类型,内部不用写 return 语句
3. 每一个类中至少存在一个构造方法,如果没有定义,系统会自动生成一个无参构造(系统生成你看不见,而且该方法的方法体中没有任何东西)
而且需要注意如果你自己写了构造方法,那么编译器将不会生成默认无参构造。
在字段就地初始化和代码块就地初始化的代码中,我们都没有写构造方法,编译器为我们自动生成了一个无参构造。
4. 构造方法支持重载
现在我们可以回答上面的问题了,new 的执行过程完成了两件事,为对象分配内存空间,调用对象的构造方法。
下面我们给出一个具体的例子:
class Cat {
public String name;
public String gander;
// 无参构造,如果 new 对象时不传入参数将调用该方法
public Cat() {
name = "咪咪";
gander = "公猫";
}
// 含有两个参数的构造方法
public Cat(String n, String g) {
name = n;
gander = g;
}
}
public class Test {
public static void main(String[] args) {
Cat cat1 = new Cat();
System.out.println(cat1.name);
System.out.println(cat1.gander);
Cat cat2 = new Cat("花花","母猫");
System.out.println(cat2.name);
System.out.println(cat2.gander);
}
}
这里的两个构造方法就构成了重载,根据括号内传入的参数不同调用不同的构造方法。
在 IDEA 中可以通过 ALT + INSERT 快捷键生成构造方法。
this 指针
在 IDEA 快捷键生成的含参构造方法中,可以见到 this 。
this 表示当前对象引用(注意不是当前对象)。 可以借助 this 来访问对象的字段和方法(普通方法和构造方法)。
下面我们给出一个例子:
class Person {
private String name;
private int age;
private String sex;
//默认构造函数
public Person() {
this.name = "曹操";
this.age = 30;
this.sex = "男";
}
//带有3个参数的构造函数 注意此时形参的命名和属性的命名一样
public Person(String name,int age,String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public void eat() {
System.out.println(this.name+" 正在吃饭!");
}
public void show(){
System.out.println("name: "+this.name+" age: "+this.age+" sex: "+this.sex);
//在show方法当中,通过this引用来调用本类的eat方法
this.eat();
}
}
public class Main{
public static void main(String[] args) {
Person p1 = new Person();//调用不带参数的构造函数 如果程序没有提供会调用不带参数的构造函数
p1.show();
Person p2 = new Person("张飞",20,"男");//调用带有3个参数的构造函数
p2.show();
}
}
我们在三个参数的构造方法中,由于形参和属性名称一样,编译器认不出来,所以通过 this.属性 引用表示该对象的属性。
编译器可以认出来时 this 可以省略。
通过引用调用方法的时候,编译器会默认传入 this 来代表这个对象的引用,所以编译器才可以找到这个对象的方法。
上面代码的运行结果为:
还可以通过 this 调用自身的构造方法。
下面给出例子:
//默认构造函数
public Person() {
// 通过 this 调用自身的构造方法
// 必须放在该方法体的第一行
this("关羽", 25, "男");
}
需要注意的是:通过 this 调用构造方法,只能在构造方法中使用,不能在普通方法中使用,且必须放在方法体的第一行,只能调用一次。
还需要注意的是:this 的指向不能修改,this 也不能是 null 。
三. 三种实例化的先后顺序
实例化的三种方法在执行过程中的先后顺序为:就地初始化和代码块初始化优先级相同,构造方法最后执行。
就地初始化和代码块初始化优先级相同,都是最先执行的,它们的具体顺序是根据代码编写的先后关系,从上而下依次执行。然后才是构造方法的执行。
下面我们给出例子:
public class T1 {
public int a = retInt();
{
a = 1;
b = 1;
System.out.println("代码块初始化");
}
public int b = retInt();
private int retInt() {
System.out.println("就地初始化");
return 1;
}
public T1() {
a = 1;
b = 1;
System.out.println("构造方法");
}
public static void main(String[] args) {
T1 t1 = new T1();
}
}
运行的结果为:
可见初始化的先后顺序正如我们所说。
四. static 关键字
我们之前写代码一直见到 static 关键字,main 方法有,有些属性和方法前也有 static ,static 的字面意思是静态的,但是的实际意义和静不静态一点关系都没有,这是一个历史遗留问题。
static 最常见的方式有两个,一种是修饰属性,另一种是修饰方法。
1. static 修饰属性和方法
由 static 修饰的属性和方法是属于类的,而不是属于某一个对象的。
例如我们 Cat 类,需要有一个属性来表示猫咪的总数,所以这个属性就可以用 static 修饰。
需要注意的是:
a)普通的方法和属性在调用的时候是用 引用. 的方式,而静态方法和属性是既可以用 类名. 也可以用 引用. 的方式调用,但是不建议使用 引用. 调用,因为毕竟这个属性是属于这个类整体的。
b)静态属性是该类的所有对象共有的,如果通过一个对象改变这个属性,那么这个静态属性就会改变。
c)静态方法是脱离实例而存在的,所以静态方法中不能有 this(指向对象自己的引用) 和 super(指向该对象的父类对象的) 引用。( super 引用的知识后面会详细讲)
b)静态方法不能直接使用非静态数据成员或者调用非静态方法。(非静态数据成员和方法都是和实例相关的)
下面给出具体例子:
class Cat {
public String name;// 名称
public String gander;// 公母
// 静态属性
public static int number = 100;// 猫咪的数量为一百
// 静态方法
public static void showNumber() {// 显示猫咪的数量
System.out.println("此时猫咪的总数为 " + number);
}
// 含有两个参数的构造方法
public Cat(String n, String g) {
name = n;
gander = g;
}
}
public class Test {
public static void main(String[] args) {
// 通过 类名. 的方式调用静态方法或者属性
Cat.showNumber();
// 新建一直猫
Cat cat = new Cat("花花","母猫");
System.out.println("新增一只猫咪 花花 ");
// 通过 引用. 的方式调用静态方法或者属性(不推荐)
// 猫咪总数+1
cat.number++;
// 显示猫咪总数
Cat.showNumber();
}
}
结果为:
2. static 修饰代码块
static 修饰的代码块叫静态代码块。
格式为:
static {
......
}
和代码块类似,也是用来初始化的,不过它是用来初始化静态成员属性。
静态代码块只在类加载时执行一次,所以它是先于普通代码块执行的,如果代码中有关于这个类的信息就会执行,且只执行一次。
给出例子:
class Cat {
public String name;// 名称
public String gander;// 公母
// 静态属性
public static int number ;// 猫咪的数量
// 静态方法
public static void showNumber() {// 显示猫咪的数量
System.out.println("此时猫咪的总数为 " + number);
}
// 静态代码块
static {
number = 100;// 猫咪的数量为100
System.out.println("静态代码块");
}
// 含有两个参数的构造方法
public Cat(String n, String g) {
name = n;
gander = g;
}
}
public class Test {
public static void main(String[] args) {
// 调用静态方法或者属性,注意这里没有实例化对象
Cat.showNumber();
Cat.showNumber();
}
}
注意,我们在 main 方法中并没有实例化对象,而是直接通过类名调用了两次静态方法。
结果为:,从这里可以看出,静态代码块优先执行,且只执行一次。
3. static 修饰类
Java 中的 static 一般是不可以用来修饰类的,但有一类特殊的情况,用来修饰内部类,叫做静态内部类。
这里先不做赘述,将来总结内部类时在补充。
今天的分享就到这里,希望大家多多评论,共同提高。