目录
- 一. 对象简介
- 1. 概念
- 2. 行为和特征
- 3. 对象的创建方式(重点)
- 3.1 显式创建
- 3.2 隐式创建
- 二. 显式创建
- 1. 使用new关键字创建对象
- 1.1 new简介
- 1.2 new作用
- 1.3 基本语法
- 1.4 代码案例
- 2. newlnstance()方法
- 3. clone()方法
- 4. readObject()方法
- 5. 小结
- 三. 隐式创建
- 1. 给String字符串变量赋值
- 2. “+”号拼接多个字符串
- 3. JVM虚拟机加载类时隐式创建类对象
- 4. 小结
- 四. 对象的使用
- 1. 简介
- 2. 使用
- 五. 创建对象的内存分析(重点)
- 1. 内存简介
- 2. 内存分析
- 六. 结语
一. 对象简介
1. 概念
在之前的文章中,其实已经给大家解释过对象的概念了。在面向对象的编程规范中,“一切皆对象”,对象就是面向对象编程的核心。现实世界中的每一个物体都是对象!当然,这个“对象”不是你怀里撒娇的男/女朋友,而是指某个“类”的一个实例。
比如,“人”是一个“整体”的概念,它不是“个体”的概念,所以“人”是“类别(模板)”,而不是对象。但是在人这个“类别”中,有法外狂徒张三这个个体,张三就是一个具体的“对象”。每个对象都有自己的状态和行为,比如张三的状态有:身高、体重、爱好;行为有:吃、喝、piao、du、抽,坑、蒙、拐、骗、偷等。
2. 行为和特征
一个对象主要包括行为和特征(状态、属性)两个核心要素。
在这段简单的代码中,Person p1=new Person();
,前半部分Person p1
,表示是在内存中分配一个Person类型的变量p1。后半部分new Person();
,表示利用new关键字和构造方法来创建一个Person类型的对象,Person()
是构造方法的名字。当构造方法执行完,这个Person类型的对象就建造出来了,也就为其分配了对应的内存。
虽然p1是引用类型的变量,但p1自身会存储在栈内存中。而使用new关键字创造出来的对象,则被存储在堆内存(heap)中。且在new关键字真正创建出引用类型的对象之后,会把这个对象在内存中的地址返回给p1变量进行引用,这样我们就可以通过调用p1找到对内存中的Person对象。
所以Person p1=new Person();
这行代码的作用就是,把Person对象在内存中的地址 赋值 给变量p1。在Java中,我们可以把p1变量叫做引用变量,或者简称为引用。又因为p1变量中存放的是引用类型的内存地址值,我们也可以把p1的值叫做引用地址。
2. newlnstance()方法
除了使用new关键字创建对象之外,我们还可以使用newInstance()方法创建对象。该方法是java.lang.Class 或 java.lang.reflect.Constuctor类中的实例方法,其语法格式如下:
java.lang.Class 类对象名称 = java.lang.Class.forName(要实例化的类全称);
类名 对象名 = (类名)Class类对象名称.newInstance();
我们在调用java.lang.Class类中的forName()方法时,需要将待实例化类的全限定名(如 com.yyg.Person)作为参数传递进来,然后再调用 java.lang.Class类对象的newInstance()方法创建对象。
/** * @author */ public class Demo01 { public static void main(String[] args) { //newInstance()方法创建对象 try { Class<?> clazz = Class.forName("com.yyg.Person"); Person p2 = (Person) clazz.newInstance(); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } }
因为这一块的内容涉及到了反射的知识点,后面会专门讲解反射,这一块的内容大家先有所了解即可。
3. clone()方法
第三种显式创建对象的方法,就是利用实例对象的clone()方法进行对象的复制,也可以创建一个新对象出来。clone()方法创建对象时,不会调用类的构造方法,它会创建一个复制的对象,这个对象和原来的对象具有不同的内存地址,但它们的属性值却相同。
另外clone()方法是Java Object对象中被protected修饰的方法,可供子类调用以实现子类的克隆逻辑,但不能直接调用。并且我们使用该方法创建对象时,还要求待实例化的类必须实现 java.lang.Cloneable接口,否则会抛出 java.lang.CloneNotSupportedException异常,比较麻烦,所以该方法并不常用。 clone()方法创建对象的语法格式如下:
类名 对象名 = (类名)已创建好的类对象名.clone();
4. readObject()方法
我们还可以使用java.io.ObjectlnputStream
对象的readObject()
方法来创建一个新对象。ObjectlnputStream
是IO流中的内容,以后会详细讲解Java中的IO流,大家对此先有一个大致的印象即可。
5. 小结
在上面给大家提到了4种显式创建对象的方式,在此我们进行一个小小的总结:
- new关键字是最常用的创建对象方式;
- 使用 new关键字 或 Class对象的newInstance()方法 创建对象时,都会调用类的构造方法;
- 使用 Class类的newInstance()方法 创建对象时,会调用类的默认构造方法,即无参构造方法;
- 使用 Object类的clone()方法 创建对象时,不会调用此类的构造方法,只会创建一个复制出的对象,该对象和原来的对象具有不同的内存地址,但它们的属性值相同;
- 如果类没有实现Cloneable接口却调用它的clone()方法,会抛出 java.lang.CloneNotSupportedException异常。
三. 隐式创建
在Java中,除了可以显式地创建对象之外,还可以隐式地创建对象。隐式创建对象主要是通过以下几种方式:
- 给String字符串变量赋值;
- “+”号拼接多个字符串;
- JVM虚拟机加载类时隐式创建类对象。
1. 给String字符串变量赋值
我们经常给一个字符串变量赋值,如下所示:
String str = "Hello,壹哥";
上面的str变量,其实就是一个String对象,该对象会由JVM虚拟机隐式地创建出来。
2. “+”号拼接多个字符串
我们知道,“+”运算符有两个基本作用:加法运算和字符串拼接。当进行字符串拼接时,其运算的结果是一个新的 String对象,示例如下:
String str1 = "Hello"; String str2 = "一一哥"; String str3 = str1+str2; // str3引用一个新的String对象
通过上面的语句,JVM会隐式地创建出第3个String对象。
3. JVM虚拟机加载类时隐式创建类对象
当Java的JVM虚拟机加载一个类时,会隐式地创建出一个可以描述这个类的Class实例对象。类的加载是指把类的.class字节码文件读到内存中,把它存放到运行时数据区的方法区中,然后会在堆区创建一个 java.lang.Class对象,用来封装类在方法区内的数据结构。
4. 小结
无论我们是釆用显式或是隐式方式创建对象,Java虚拟机都会在创建对象时包含以下步骤:
- 给对象分配内存;
- 将对象的实例变量自动初始化成该变量类型的默认值;
- 初始化对象,给实例变量赋予正确的初始值。
四. 对象的使用
1. 简介
我们在通过一系列方式创建好对象之后,那么这个对象该怎么用呢?我们知道对象中有状态和行为两个核心要素,那怎么来调用一个对象的行为和状态呢?Java中,一个普通的实例对象调用它的行为和状态,基本的语法格式如下:
//调用状态
对象.属性名;
//调用方法
对象.方法名();
接下来我们通过一个小案例给大家展示一下。
2. 使用
/** * @author */ public class Demo02 { public static void main(String[] args) { // 对象的使用 //先创建一个对象 Person p1=new Person(); //调用对象的属性(该属性不能是私有的,否则别的类中无法看到)--状态. String name = p1.name; //调用对象的方法 p1.eat(); p1.speak("一一哥", "男", 18); } }
大家要注意,当我们在A类中调用B类对象时,B类对象的属性或方法不能是私有的,否则在A类中会无法看到该属性或方法。
五. 创建对象的内存分析(重点)
1. 内存简介
Java程序在运行时,操作系统会给程序分配三块主要的内存空间:栈内存、堆内存、方法区。
- 栈内存: 栈内存也叫做虚拟机栈,可以存放基本类型的数据和引用类型的地址,主要是局部变量。 这些 局部变量存放了编译期可知长度的各种基本数据类型和对象引用,方法执行完就会自动释放。栈内存具有先进后出、占用空间比较小、存取速度相对较快的特点。
- 堆内存: 堆内存中可以存放各对象实例(所有new出来的对象都是)和数组都在堆中存放,即堆中存放的是引用类型的实际数据。 堆内存占用的空间比较大,存取速度相对较慢。
- 方法区: 方法区主要用于存储类信息、常量、静态变量,静态代码块、即时编译器(JIT Compiler)编译后的代码数据等 , 简单言之就是存储类的结构信息。类的结构信息包括类的名称、方法信息、字段信息等。在方法区中,有一块空间叫做常量池(串池),用来存放字符串常量。还有一块空间叫做静态区,用来存储静态变量等静态数据。但在JDK 7之后,常量池、静态区作为堆中的一个子部分,方法区的概念被弱化。
2. 内存分析
/** * @author */ public class Demo02 { public static void main(String[] args) { // 对象的使用 //先创建一个Person对象 Person p1=new Person(); p1.name="一一哥"; p1.age=18; p1.sex="男"; //创建p2对象 Person p2=new Person(); } }
在上述代码中,我们定义的Person类型p1变量实际上是一个引用,它会被存放在栈内存中,而存放实际数据的Person对象则存放在堆内存中。如下图所示:
根据上面的内存分析图可知:
- Person类中包含了name、age、sex三个属性,它们分别是String、int、String类型。我们知道,它们的默认值分别是null、0、null。
- 我们创建一个p1对象之后,p1会被存放在栈区,然后p1会通过堆中Person对象的内存地址,去找到该对象被实例化的内容(即name、age、sex等)。
- Java代码中刚创建出来的对象属性都是采用默认值(null、0、null),等我们通过"对象.属性"的方式对属性进行重新赋值后,堆内存中存储的才是赋值后的内容。
- 我们创建的另一个对象p2,p2依然在栈区,但并没有对p2进行初始化,所以p2的属性在堆内存中都是默认值。
- 堆内存中的每一个对象,都有一个独立的内存空间。
六. 结语
至此,就给大家把Java的对象介绍完毕了,现在你知道几种创建对象的方式呢?最后再跟大家说一下,不管我们用哪种方式创建出来的对象,每个对象都是相互独立的。这些不同的对象在内存中会占有独立的内存地址,且每个对象都具有自己的生命周期。当一个对象的生命周期结束时,对象就变成了垃圾,这些垃圾对象会被JVM虚拟机自带的GC垃圾回收机制来处理。
以上就是Java创建对象之显示创建与隐式创建的详细内容,更多关于Java创建对象的资料请关注其它相关文章!
原文地址:https://juejin.cn/post/7207078219216322618