java类的创建与使用_创建和使用类 | 关于Java入门基础教程的第8部分内容 | 天码营 - 实战开发技术学习服务平台...

创建对象

我们此前已经多次创建对象了,现在我们来进一步深入创建对象的知识。

假设我们已经如下定义了Car和Engine类:

Car.java

package com.tianmaying;

public class Car{

private int color;

private int speed;

private Engine engine;

public Car(){

}

public Car(int color, int speed){

this.color = color;

this.speed = speed;

}

public Car(int color, int speed, Engine engine){

this.color = color;

this.speed = speed;

this.engine = engine;

}

void startup(){

System.out.println("启动!");

}

void run(){

startup();

System.out.println("前进,速度为:" + speed);

}

}

Engine.java

package com.tianmaying;

public class Engine{

public double power;

public Engine(double power){

this.power = power;

}

}

重新审视我们曾经编写的创建对象的代码:

Car myCar = new Car();

这一行代码包含了三个部分:

Car myCar表示声明了一个Car类型的变量myCar,即myCar是一个引用类型变量

new关键字表示创建一个对象

Car()则是构造器的调用,对创建的对象进行初始化

每次用new创建一个对象,就会在堆中分配新的内存来保存新的对象信息,而myCar这个引用变量本身则存储在栈中。

堆和栈的区别

堆和栈都是Java中常用的存储结构,都是内存中存放数据的地方:

在方法中定义的基本类型变量和引用类型变量,其内存分配在栈上,变量出了作用域(即定义变量的代码块)就会自动释放

堆内存主要作用是存放运行时通过new操作创建的对象

下面这张图展示了Car myCar = new Car();这行代码运行时的内存状态:

4597838151f130ee04ddcd15e308fd68.png

图中0x6E34是我们假设的内存地址。myCar作为一个引用类型变量保存在栈中,你可以直观地认为myCar变量保存的就是所创建对象在堆中的地址0x6E34,即myCar引用了一个对象,这正是引用类型变量这个叫法的原因;而堆中则保存着的对象本身,包含了其成员变量,如speed、color和engine。

如果成员变量没有在构造器中初始化,则会是默认值。speed和color是int基本类型,默认值为0;engine为引用类型,默认值为null,即不引用任何对象。

你可以创建多个对象,每个对象都会在堆中拥有自己单独的内存空间,例如:

Car myCar = new Car();

Car herCar = new Car();

此时内存状态如下:

7680e7ecbb74efe299ac4a081ac0a4a2.png

一个对象的成员变量,如果是引用类型的变量的话,比如engine,则该成员变量可以引用到堆中的其它对象。

如下代码:

Engine engine = new Engine(180);

Car myCar = new Car(0xffffff, 100, engine);

此时内存状态如下:

a3935aece60b34eea065f9e1e602a4c3.png

堆中的对象如果没有任何变量引用它们时,Java就会适时地通过垃圾回收机制释放这些对象占据的内存。你可以认为没有任何引用的对象(即没有任何引用类型的变量指向它),这个对象就成为"垃圾",Java虚拟机就会清理它们,为将来要创建的对象腾出空间。

引用类型与基本类型的区别

了解了堆和栈的区别,理解引用类型和基本类型的区别就很容易了。比如我们定义如下代码:

int color = 0;

int speed = 100;

Car myCar = new Car(color, speed);

则内存状态如下:

01b254b6269e059f0d5d777530876da2.png

与引用类型myCar不同,基本类型变量的值就是存储在栈中,作用域结束(比如main方法执行结束)则这些变量占据的栈内存会自动释放。

访问对象属性

在类的内部可以访问自身的属性。例如:

void run(){

startup();

System.out.println("前进,速度为:" + speed);

}

在Car的方法run()中,访问了speed属性。

也可以这样写:

void run(){

startup();

System.out.println("前进,速度为:" + this.speed);

}

即在类的内部可以通过this来访问自身的属性。

在外部(即其它类中)也可以访问一个类的非private属性,通过对象名.属性名的方式进行访问。

例如将Car的color属性设置为public:

public class Car{

public int color;

// ...

}

如果我们定义一个Driver类,可以这样访问color属性:

public class Driver{

public static void main(String[] args){

Car car = new Car();

car.color = 0xffffff; // 修改color属性的值

int color = car.color; // 访问color属性的值,将其赋给其他变量

System.out.println(car.color); // 将color作为参数,打印

}

}

访问对象方法

方法可以在一个类内部进行调用,例如:

void run(){

startup();

System.out.println("前进,速度为:" + speed);

}

在Car的方法run()中,访问了start()方法。

也可以这样写:

void run(){

this.startup();

System.out.println("前进,速度为:" + speed);

}

即在类的内部可以通过this来访问自身的方法。

在外部(即其它类中)也可以访问一个类的非private方法,通过对象名.方法名的方式进行访问。

如果我们定义一个Driver类,可以这样访问run()方法:

public class Driver{

public static void main(String[] args){

Car car = new Car();

car.run(); // 访问car对象的run()方法

}

}

方法的返回和参数

具有某个返回类型的方法,其返回结果可以用赋值操作赋给该类型的变量。

也可以赋给该类型的子类型的变量,可以在学习到继承和接口之后再做了解

例如,我们定义一个Calculator类:

class Calculator{

public int add(int a, int b){

return a + b;

}

}

可以用如下方式调用add()方法:

class CalculatorTest{

public static void main(String args){

Calculator calculator = new Calculator();

int c = calculator.add(1, 3);

int d = 9;

int e = calculator.add(c, c * d);

System.out.println(c); // 输出 4

System.out.println(e); // 输出 40

}

}形参是方法定义中出现的参数,用于接收外部传入的变量:如public int add(int a, int b)中,a和b是形参

实参即方法调用时传入的实际参数:

int c = calculator.add(1, 3); 表示将字面常量1和3作为实参,将方法计算结果赋值给变量c。

int e = calculator.add(c, c * d); 表示将变量c和表达式c * d作为实参,将方法计算结果赋给变量e。

可见调用方法时,传入的实参可以为字面量、变量或者表达式。

方法的调用过程

以int e = calculator.add(c, c * d);为例,当程序执行到这一行代码时,会跳转到add()方法的内部执行:

a和b就类似于在add()方法内定义的两个局部变量,它们是main()方法中c和c * d的值的拷贝;

add()执行完之后,a和b两个形参专用的内存就会被释放掉,同时会返回main()方法继续执行其接下来的代码。

1ab0b4a042b7bf93b1519f9af76c4d66.png

基本类型参数

传参即是实参的值赋给形参。对于基本类型的形参,在方法内部对形参的修改只会局限在方法内部,不会影响实参。

比如,给Calculator增加一个increase(int)方法:

class Calculator{

public int add(int a, int b){

return a + b;

}

public int increase(int a){

return ++a;

}

public static void main(String args[]){

Calculator calculator = new Calculator();

int x = 10;

int y = calculator.increase(x);

System.out.println(x);

}

}

increase(int a)方法定义了一个int形参a,将x作为实参传入,虽然方法内部做了自增操作,但是并不会改变x的值。因此,打印出来的x的值是10而不是11。

引用类型参数

引用类型的实参传入方法中时,是将对象的引用传入,而非对象本身。因此,在方法执行时,实参和形参会引用到同一个对象。

在方法结束时,形参占据的内存虽然会被释放,但是通过形参对对象进行的修改则不会丢失,因为对象依然保存在堆中。

例如,Car的构造器中如果对engine进行修改:

public Car(int color, int speed, Engine engine){

this.color = color;

this.speed = speed;

engine.power = 200; // 这里讲engine的power赋值为200

this.engine = engine;

}

则在main方法中执行如下代码

Engine myEngine = new Engine(180);

Car myCar = new Car(0xffffff, 100, myEngine);

System.out.println(myEngine.power);

在Car myCar = new Car(0xffffff, 100, myEngine);这行代码中,我们将myEngine作为实参传递给了Car的构造器,由于构造器中的engine形参此时和myEngine指向同一个对象,因此执行完构造器后,myEngine的power值会从180变成200。

4c5582b323e7edec068a96bb62828274.png

再来考虑另外一种情况:

public Car(int color, int speed, Engine engine){

this.color = color;

this.speed = speed;

engine = null; // 这里将engine设置为null

}

myEngine传入到这个构造器中执行后,myEngine是否会变为null呢? 答案是否定的。虽然实参指向的对象可以在方法调用时被修改,但是实参本身的值(你可以认为是实参引用的对象地址)不会发生改变。

147251acfa61307a5145379655ff6474.png

初始化成员变量

成员变量直接赋值

初始化成员变量,一般通过构造器完成。也可以直接给类的属性进行赋值,例如:

class Post{

private String title = "默认标题";

private String content = "默认内容";

}

通过final方法赋值

可以通过调用final修饰的方法来进行赋值,例如:

class Post{

private String title = "默认标题";

private String content = initContent();

private final String initContent(){

return "默认内容";

}

}

通过构造块初始化

还有一种方式是通过初始化构造块来,编译器会将初始化构造块的代码会自动插入到在每个构造器中。例如:

class Post{

private long id;

private String title;

private String content;

{

title = "默认标题";

content = "默认内容";

}

public Post(){

}

public Post(long id){

this.id = id;

}

}

这种情况下Post的两个构造器内部虽然没有初始化title和content,但是由于构造块的存在,这两个成员变量会进行赋值。构造块可用于多个构造器复用代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值