1. 前言

在《还不清楚怎样面向对象?》和《面向对象再探究》两篇文章中,都介绍了关于面向对象程序设计的概念和特点。其中也涉及到了许多代码,比如:

Dog dog = new Dog();

这篇文章就主要来谈谈创建对象时的具体操作。

2. 引入例子

下面是一个Dog类:

/**
* @author Xing Xiaoguan (xingrenguanxue)
*/ public class Dog {
private String name;
private int age;
private String address; public void say() {
System.out.println("我叫" + name + ",今年" + age + "岁了,家住" + address + ",汪汪汪...");
} //getters 和 setters
}

下面是一个Test类,创建了一个Dog对象,然后进行相关操作:

public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.setName("哮天犬");
dog.setAge(1);
dog.setAddress("光明小区")
dog.say();
}
}

输出:

我叫小黑,今年1岁了,家住光明小区,汪汪汪...

3. 对象和对象变量

对象是根据类创造出来的,我们使用的是具体的对象,而不是类。要想使用对象,那对象必须得先被创建出来才行。下面一行代码是创建对象的语句:

Dog dog = new Dog();

我们将其分成三部分来看:

(1)new :如果你想创建一个对象,那么必须使用new操作符。

(2)Dog():这是一个构造器,构造器是一个特殊的方法。通过调用该构造器,我们可以创建并初始化一个对象出来。

new Dog()连起来就能正确地创建出一个对象了。但是我们创造出来的对象并不只会使用一次,而会使用许多次,所以我们需要给该对象 “取一个名字”,保证它“随叫随到”。这就需要第三部分了:

(3)Dog dog:声明一个Dog类型的、名为dog的变量。它就类似于int number,声明一个int类型的number变量。

然后我们使用=进行赋值(引用),便给创建出的对象 “取一个名字” 叫dog,以后可以称它为dog对象。

这里可能会出现一个误区,认为:Dog dog部分便能创建出一个dog对象,这是错误的。应当明确:dog从头到尾都只是一个变量而已。这个变量和使用int number、String str等方式声明的变量,除了类型不同之外并无差别。

真正创建出对象的是new Dog()部分。

下面解释一下 “取一个名字” 是什么意思。

在Java中,对象变量中存储的并不是对象,真正的变量在内存的某个地方躺着呢。该变量记录的是对象在内存中的位置,我们有了对象变量,就有了对象的位置,有了位置,就能找到真正的对象。

看下面的代码:

public class Test {
public static void main(String[] args) {
Dog dog = new Dog();//创建出一个dog对象
System.out.println(dog);//打印dog
}
}
打印结果:basic.Dog@1b6d3586

1b6d3586便是dog对象在内存中的地址。

4. 构造器

前面提到,创建一个对象的关键在于使用new操作符和构造器。构造器是一个特殊的方法,通过调用构造器,我们可以创建并初始化一个对象出来。

构造器的特点:

  1. 有一个访问修饰符
  2. 构造器的名字和类名相同
  3. 构造器没有返回值
  4. 构造器要和new操作符一起使用
  5. 构造器可以有0个、1个或多个构造器
  6. 一个类中可以有多个构造器

4.1. 无参构造器

即没有参数的构造器,如Dog()。无参构造器是Java默认的构造器,如果你在编写类时没有写构造器,那么Java会在类中默认提供一个无参构造器。

在Dog类中并没有写构造器,Dog类默认拥有下面的无参构造器:

public Dog() {

}

4.2. 有参构造器

有参构造器,即有参数的构造器。例如:

public Dog(String n, int a, String addr) {
name = n;
age = a;
address = addr;
}

这个有参构造器非常简单,分别给n、a参数传值,然后给对象的属性赋值。美中不足的是参数的变量名取得不够“见名知意”,所以我们通常这样写:

public Dog(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}

参数的名字和对象的成员变量名一样,这样,参数的意义就很清晰了。在赋值的时候,使用this关键字区分二者,因为this代表我们所创建的对象,this.name即对象的成员变量。

有了有参构造器,我们就可以在创建对象时初始化对象的属性,比如:

public static void main(String[] args) {
Dog dog = new Dog("小狗有参", 1, "地球");
dog.say();
}

输出:

我叫小狗有参,今年1岁了,家住地球,汪汪汪...

4.3. 多个构造器的使用

先了解一个概念——重载(overloading)。重载是指在一个类中,有几个方法的方法名字相同,而参数不同。返回值类型可以相同也可以不相同。注意:每个重载的方法的参数列表必须独一无二。

注意:参数列表的独一无二是指参数列表的类型的独一无二。

千万不要以为func(String name)和func(String address)这两个方法的参数列表是不同的。它俩应该这样看:func(String)和func(String),所以这俩方法是相同的。

下图是String类中的部分方法的重载情况,可从中体会参数列表的独一无二:

为什么要求参数列表必须独一无二呢?

这就得介绍另一个概念——方法的签名。方法的签名是指要完整地描述一个方法,需要指出方法名和参数类型。注意:方法的返回类型不是签名的一部分。

所以在Java中,方法名和参数列表能确定一个方法。不存在方法名和参数列表相同,而返回类型不同的方法们。

而重载要求的是方法名相同,参数列表不同。从方法的签名角度来看,重载方法之间本就是不同的方法。

由于重载的存在,我们可以在一个类中编写多个参数列表不同的构造器,使该类具有多种创建对象的形式。比如:

public class Dog {
private String name;
private int age;
private String address; public Dog() {//无参构造器,有其他构造器存在,系统不会默认提供 } public Dog(String name, int age) {
this.name = name;
this.age = age;
} public Dog(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
} public void say() {
System.out.println("我叫" + name + ",今年" + age + "岁了,家住" + address + ",汪汪汪...");
} //getters 和 setters
}

写了三种构造器,便有三种创建对象的方式:

Dog dog = new Dog("小狗", 1, "太阳系");
Dog dog1 = new Dog("小狗1", 2);
Dog dog2 = new Dog();

编译器会根据我们提供的参数匹配到合适的构造器。

注意:无参构造器只有在我们没有编写任何构造器时,系统才会默认提供。所以当一个类中有其他构造器时,如果我们需要无参构造器,那么必须手动编写出来,系统不会默认提供。

4.4. 构造器之间的关系

在类中,一个构造器可以调用另一个构造器。如下例:

/**
* @author Xing Xiaoguan (xingrenguanxue)
*/ public class Dog {
private String name;
private int age;
private String address; public Dog(String name, int age) {
this(name, age, "银河系");//调用另一个构造器
System.out.println("两个参数的构造器");
} public Dog(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
System.out.println("三个参数的构造器");
} public void say() {
System.out.println("我叫" + name + ",今年" + age + "岁了,家住" + address + ",汪汪汪...");
} //getters and setters...
}

使用Dog(String, int)创建对象:

public class Test {
public static void main(String[] args) {
Dog dog = new Dog("小狗有参", 1);
dog.say();
}
}

输出:

三个参数的构造器
两个参数的构造器
我叫小狗有参,今年1岁了,家住银河系,汪汪汪...

我们在构造器的第一行语句中使用this(...)来调用另一个构造器。注意:一定要是第一行语句。

5. 对象属性的初始化

所谓初始化,就是我们在创建对象同时设置对象的属性值。

5.1. 默认的属性值

当我们创建对象时如果不显式地设置属性值,对象的属性值会被初始化为默认值。

数值的默认值为0,布尔值的默认值为false,对象引用的默认值为null。

如下例中的Dog类的属性值:

public class Dog {
private String name;
private int age;
private String address; public void say() {
System.out.println("我叫" + name + ",今年" + age + "岁了,家住" + address + ",汪汪汪...");
}
}

创建对象:

public static void main(String[] args) {
Dog dog = new Dog();
dog.say();
}

输出:

我叫null,今年0岁了,家住null,汪汪汪...

5.2. 直接设置属性值

我们可以在类中直接设置类的属性值,这样一来,根据该类创建的对象的属性值就确定了。

如下例中Dog类的属性值:

public class Dog {
private String name = "小黑";
private int age = 2;
private String address = "太阳系"; public void say() {
System.out.println("我叫" + name + ",今年" + age + "岁了,家住" + address + ",汪汪汪...");
}
}

这时再创建对象:

public static void main(String[] args) {
Dog dog = new Dog();
dog.say();
}

输出:

我叫小黑,今年2岁了,家住太阳系,汪汪汪...

5.3. 使用构造器初始化

(一) 当构造器中没有显式地设置对象的属性值时,这些属性值会被初始化为默认值。如下面的无参构造器:

public Dog() {

}

创建对象:

public static void main(String[] args) {
Dog dog = new Dog();
dog.say();
}

输出:

我叫null,今年0岁了,家住null,汪汪汪...

(二)可以在无参构造器的方法体中初始化属性值:

public Dog() {
name = "小黑";
age = 2;
address = "宇宙";
}

创建对象:

public static void main(String[] args) {
Dog dog = new Dog();
dog.say();
}

输出:

我叫小黑,今年2岁了,家住宇宙,汪汪汪...

(三)也可以使用有参构造器,对象会按照我们传入的变量初始化属性值:

public Dog(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}

创建对象:

public static void main(String[] args) {
Dog dog = new Dog("小黑", 3, "南极");
dog.say();
}

输出:

我叫小黑,今年3岁了,家住南极,汪汪汪...

如有错误,还请指正。

详解Java的对象创建的更多相关文章

  1. 详解Java中对象的软、弱和虚引用的区别

    对于大部分的对象而言,程序里会有一个引用变量来引用该对象,这是最常见的引用方法.除此之外,java.lang.ref包下还提供了3个类:SoftReference.WeakReference和Phan ...

  2. JVM系列之:详解java object对象在heap中的结构

    目录 简介 对象和其隐藏的秘密 Object对象头 数组对象头 整个对象的结构 简介 在之前的文章中,我们介绍了使用JOL这一神器来解析java类或者java实例在内存中占用的空间地址. 今天,我们会 ...

  3. Java精通并发-自旋对于synchronized关键字的底层意义与价值分析以及互斥锁属性详解与Monitor对象特性解说【纯理论】

    自旋对于synchronized关键字的底层意义与价值分析: 对于synchronized关键字的底层意义和价值分析,下面用纯理论的方式来对它进行阐述,自旋这个概念就会应运而生,还是很重要的,下面阐述 ...

  4. 图文详解 IntelliJ IDEA 15 创建 Maven 构建的 Java Web 项目(使用 Jetty 容器)

    图文详解 IntelliJ IDEA 15 创建 maven 的 Web 项目 搭建 maven 项目结构 1.使用 IntelliJ IDEA 15 新建一个项目.  2.设置 GAV 坐标  3. ...

  5. 详解Java GC的工作原理+Minor GC、FullGC

    详解Java GC的工作原理+Minor GC.FullGC 引用地址:http://www.blogjava.net/ldwblog/archive/2013/07/24/401919.html J ...

  6. 详解Java中的clone方法

    详解Java中的clone方法 参考:http://blog.csdn.net/zhangjg_blog/article/details/18369201/ 所谓的复制对象,首先要分配一个和源对象同样 ...

  7. 详解java动态代理机制以及使用场景

    详解java动态代理机制以及使用场景 https://blog.csdn.net/u011784767/article/details/78281384 深入理解java动态代理的实现机制 https ...

  8. 「跬步千里」详解 Java 内存模型与原子性、可见性、有序性

    文题 "跬步千里" 主要是为了凸显这篇文章的基础性与重要性(狗头),并发编程这块的知识也确实主要围绕着 JMM 和三大性质来展开. 全文脉络如下: 1)为什么要学习并发编程? 2) ...

  9. 实例详解 Java 死锁与破解死锁

    锁和被保护资源之间的关系 我们把一段需要互斥执行的代码称为临界区.线程在进入临界区之前,首先尝试加锁 lock(),如果成功,则进入临界区,此时我们称这个线程持有锁:否则呢就等待,直到持有锁的线程解锁 ...

随机推荐

  1. P2629 【好消息,坏消息】

    其实刚开始看到这道题,应该很多都会想到区间DP中的合并石子,开一个2倍的空间(严格来说的话应该是2n-1),将本来的环变成一个链式的结构.然后对于得到的消息,可以预处理一个前缀和,这样就可以很方便的知 ...

  2. 原生开发、H5开发、混合移动开发的优缺点

    一.原生开发(Native App开发) 原生开发,是在Android.IOS等移动平台上利用官方提供的开发语言.开发类库.开发工具进行App开发.比如Android是利用Java.Eclipse.A ...

  3. CF1292C Xenon's Attack on the Gangs 题解

    传送门 题目描述 输入格式 输出格式 题意翻译 给n个结点,n-1条无向边.即一棵树.我们需要给这n-1条边赋上0~ n-2不重复的值.mex(u,v)表示从结点u到结点v经过的边权值中没有出现的最小 ...

  4. css3 文本行的斑马线

    背景知识 CSS 渐变, background-size ,“条纹背景”,“灵活的背景定位 难题 几年前,在刚刚获得 :nth-child() / :nth-of-type() 伪类之后,我们最常用其 ...

  5. Maven [ERROR] 不再支持源选项 5,请使用 7 或更高版本的解决办法

    刚刚学Maven,当我点击test时 就出现了这两个错误: [ERROR] 不再支持源选项 5.请使用 7 或更高版本.[ERROR] 不再支持目标选项 5.请使用 7 或更高版本. 后来在看到这篇文 ...

  6. 06 drf源码剖析之权限

    06 drf源码剖析之权限 目录 06 drf源码剖析之权限 1. 权限简述 2. 权限使用 3.源码剖析 4. 总结 1. 权限简述 权限与身份验证和限制一起,决定了是否应授予请求访问权限. 权限检 ...

  7. scrapy 源码解析 (三):启动流程源码分析(三) ExecutionEngine执行引擎

    ExecutionEngine执行引擎 上一篇分析了CrawlerProcess和Crawler对象的建立过程,在最终调用CrawlerProcess.start()之前,会首先建立Execution ...

  8. 网络编程-HTTPS

    明文: 对称加密: 非对称:(公钥:pk 私钥:sk) 对称+非对称: 先用非对称方式发送num1给server,server用私钥得出key(由num1算出来),自此,约定C.S以此key(num1 ...

  9. 【JS代码压缩】使用YUI Compressor对js文件进行压缩处理

    概述 在使用html5开发Hybird APP的时候,可能会引入大量的js包,另外对于一些核心的js文件,进行一些特殊的处理, 如压缩和加密就显得很重要了,YUI Compressor就是这样一个用于 ...

  10. 记一次开发CefSharp做浏览器时Facebook广告页支付方式绑定不上Paypal问题

    问题:用CefSharp做浏览器开发.在做Facebook广告页面绑定Paypal支付方式时出现了绑定不上的问题. 让我们来还原问题的步骤: 第一步登录Facebook. 第二步进入广告绑卡页面选择P ...