本文内容参考自Java8标准
前面的博文已经有很详细说明,Java中总共分两大类型:
⑴、基本类型: 基本类型总共有8种(byte,short,char,int,long,float,double,boolean)。
重点 | 说明 |
---|---|
实际存储位置 | 堆栈 |
有无确定生命周期 | 有(生命周期与程序运行结束有关) |
声明形式 | 类型关键字 名称 = 值 (int a = 5) |
传递方式 | 值传递 |
重点:在以上举例的表格中,声明了一个int类型的基本类型,它的名称是’a’,值为5,也就是说,这个a是直接存储在了堆栈中,也可以说值5是直接存储在了堆栈中,因为堆栈的特性是随着程序的运行不断地创建和销毁它里面的内容,程序运行结束,堆栈一定为空,所以存储在堆栈上的内容一定是有生命周期的
⑵、引用类型: 其中,引用类型内又分成了四类:
①、类类型
②、接口类型
③、数组
④、null
重点 | 说明 |
---|---|
实际存储位置 | 堆(名称引用是存储在堆栈中 ) |
有无确定生命周期 | 无(生命周期和程序运行结束无关) |
声明形式 | 类型关键字 名称 = new 构造方法 (Car c = new Car()) |
传递方式 | 引用传递 |
重点:在以上举例的表格中,声明了一个Car的类类型(引用类型),它的名称是’c’,值为new Car();实际上,这个new Car();代表的是堆中的一个空间,以及这个空间里面的所有东西(字段、方法、其他内容)。所以,引用类型的存储分两步:它的名称’c’是存储在堆栈中的,而它本身,也就是new Car();它是存储在堆中的,堆中的内容和程序运行结束是没有任何关系的,可以一直存储在里面,因为堆本身就是一个单纯的存储空间。Java中的机制就是,在程序运行的时候通过名称’c’来控制new Car();里面的所有东西,当程序运行结束了,要么将new Car();传递给堆栈中另外一个Car类型的名称(也叫引用),要么就让new Car();脱离引用而存在(因为程序结束后,堆栈中的名称’c’被销毁了),它或许一直存储在堆中,或许被垃圾回收器回收。
接下来博文的内容,都是以上面类型的分类框架为基础的。
尽管Java是基于C++的,但是相比之下,Java是一种更纯粹的面向对象语言,Java和C++都是混合/杂合型的语言,但是Java的设计者认为这种杂合性并不像在C++中这么重要,杂合型语言允许多种编程风格,C++之所以成为杂合型语言主要是因为它支持与C语言的向后兼容,因为C++是C的一个超集,所以势必包括许多C语言不具有的特性,这些特性使C++在某些方面显得过于复杂。
Java语言假设我们只进行面向对象程序设计,也就是说,在用Java进行设计之前,必须将思想转换到面向对象上来,它学起来更简单,也比其他的OOP语言易用,在Java中,几乎一切都是对象!
我们知道,在Java中,几乎一切皆为对象,但是在平时的程序设计中,有8种类型需要特殊对待,可以称他们为"基本"类型,之所以特殊对待,是因为"new"出来的对象都是存储在堆中,但是小的、简单的变量如果用new创建往往效率较低,不是很有效,也不适合存储在堆中,因此,这些类型(8种基本类型),Java采取了与C、C++中相同的方法,也就是说,不通过new来创建他们,而是创建并非是"引用"的自动变量,这些变量直接存储值,并且存储于堆栈中,以达到高效的目的。
Java要确定每种基本类型所占存储空间的大小,它们的大小并不像其他大多数语言那样随着机器硬件结构的变化而变化,这种储存空间大小不变的特性是Java程序可移植的原因之一。
类型 | 存储空间大小 | 最小值 | 最大值 | 对象类型 |
---|---|---|---|---|
boolean | - | - | - | Boolean |
char | 16bite | Unicode 0 | Unicode216-1 | Character |
byte | 8bite | -128 | 127 | Byte |
short | 16bite | -215 | 215-1 | Short |
int | 32bite | -231 | 231-1 | Integer |
long | 64bite | -263 | 263-1 | Long |
float | 32bite | IEEE 754 | IEEE 754 | Float |
double | 64bite | IEEE 754 | IEEE 754 | Double |
float和double的最大值和最小值需要去研究IEEE 754标准,虽然同是32bite和64bite,但是它的取值范围和int以及long有本质区别,因为内存中存储的并不是浮点数的真值,存储的是表示浮点数的三个构造部分的内容(指数、尾数、偏移值),真值还需要通过对应的规则进一步转化得到。
所有的数值类型都是有正负号的,所以不要去寻找无符号的数值类型。
boolean类型所占存储空间的大小没有明确指定,仅定义为能够取字面值true或false。
8种基本类型也有对应的对象类型,也就是说,之前你操作基本类型的时候是直接用基本类型的数值与运算符号直接运算,但是转换到对象类型后,不再直接使用运算符号了,而是使用对应的方法。
下面通过int类型举例:
int类型直接运算:
Integer类型间接运算(通过方法运算):
后来,Java设计者认为Integer a = new Integer(5);这种方式使用起来太复杂。没有必要。所以就增加了与int a = 5类似的形式:
Integer a = 5;-----你唯一需要区别的是:这个"5"存储在堆中,它是一个对象。
Integer a = 5;这种形式表示了对象类型和基本类型之间的自动转换,它是Java SE5推出的自动装箱方式(将数值5装箱成一个Integer对象5),实际上,最开始基本类型的对象类型是为了包装基本类型使用的。例如下面的代码:
char c = ‘a’;
Character x = new Character©;-----将之前声明的基本类型’c’当成参数进行包装。所以又称为包装器类。
后期逐渐演变成了:
Character c = ‘a’;-----直接使用包装器类型声明,然后赋值使用基本类型。
其他的基本类型都可以这么声明:
Byte b = 2;
Short b = 2;
Integer b = 2;
Long b = 2l;-----注意这个’l’不能少!
Float b = 0.0f;-----注意这个’f’不能少!
Double b = 0.0;
Boolean b = true;
实际示例:
在前面的博文中,我曾经提到了一个比较严重的问题,就是在计算机中进行高精度的计算是会出现问题的,比如在计算机中无法精确地表示0.1(至于为什么,可以自行去了解,或者翻阅我之前的博文),那么直接用数值计算0.1*0,1得出的结果自然就是有问题的:
这种问题如何解决?Java提供了两个高精度计算的类型:BigInteger和BigDecimal。
BigInteger-----专门用于任何整数的运算:
再举一例:
BigDecimal-----专门用于任何浮点数的计算:
BigInteger和BigDecimal大体上属于包装类的范畴,但是它们没有对应的基本类型。
它们必须通过调用方法来代替运算符,由于更复杂,所以是用速度代替了精度。
BigInteger支持任意精度的整数,也就是说,在运算中,可以准确表示任意大小的整数,而不会丢失任何的精度。
BigDecimal支持任何精度的定点数,可以用它进行精确的货币计算。
至于BigInteger和BigDecimal是如何实现的,有兴趣的读者可以自行去研究源码。这两个类中其他的方法,可以参阅JDK的API文档,因本博文参照的是JDK8,建议参阅JDK8及以上版本的API文档。
Java SE基础知识(二)–整数的运算符
Java SE基础知识(三)–浮点数(小数)的运算符
Java SE基础知识(四)–布尔值的运算符
以上三篇博文叙述的很详细,此处不再赘述。有兴趣的读者可自行翻阅。
如果Java中的一切都是对象,那么,是什么决定了对象的外观和行为呢?换句话说,是什么决定了对象的类型?你可能期望有一个"type"关键字,当然,它还需有一个类型的名称标识符,然而,从历史发展角度来看,大多数的面向对象程序设计语言都习惯使用"class"关键字来表示新的类。在class关键字之后紧跟新类型的名称标识符。
class aTypeName {在这里添加新类型的内容}
比如:
// 新的数据类型
class Car{
//一个表示汽车名称的变量,它的标识符为carName;
String carName;
//一个表示汽车行为:启动 的方法,它的标识符为start;
public void start(){}
}
以上就是一个新的类型了,因此,你可以使用new来创建一个它的对象:
aTypeName name = new aTypeName();
例如:
// 创建一个类的对象
Car c = new Car();
//向它的方法发送一个消息:表示启动。
c.start();
//以上内容就是:创建了一辆具体的小车(对应一个小车的对象),
//并发送消息让它启动。
一旦定义了一个类(实际上,在Java中你做的所有工作就是定义类,产生这些类的对象,以及发送消息给这些对象),就可以在类中设置两种类型的元素:字段(有时被称为数据成员),方法(有时被称作成员函数)。
字段可以是任何类型的对象,可以通过引用与其通信,也可以是基本类型种的一种,如果字段是对某个对象的引用,必须初始化该引用,以便其与一个实际对象关联(使用new创建的对象):
// 初始化对象引用
String s;---不建议
String s = new String("abc");---建议
每个对象都有用来存储其字段的空间,普通字段不能在对象间共享(如果要使字段能在所有的对象间进行共享,则需要通过static关键字),也就是说,普通字段在所有的对象中都会创建一遍,所有对象间的字段是相互独立的。
下面是一个有一些普通字段的类:
// 拥有普通字段的类
class DataOnly{
int i;
double d;
boolean b;
}
以上类里除了字段,再无其他,所以它创建出来的对象除了存储数据,再不能做更多。尽管如此,你还是可以通过new创建它的一个对象:
DataOnly data = new DataOnly();
可以给字段赋值,但是首先必须知道如何引用一个对象的成员。具体的做法是:在对象的引用后面加上一个".",再紧跟对象内部成员的名称标识符:
例如:
data.i;
data.d;
data.b;
如果想修改的数据不在当前的类型种,而在它的某一个字段的类型种,也就是说
字段不是基本类型,而是引用类型,这时,想访问这个引用类型里面的数据,那么,就需要通过多层的".":
// 字段是引用类型
//新增一个PinPai类型
class PinPai{
String PinPaiName;
}
//将PinPai加入Car类型种作为一个字段
class Car{
PinPai p;
String carName;
}
//现在需要在Car里面访问PaiPai的字段PinPaiName
Car c = new Car();
c.p.PinPaiName;
//就是通过多层的"."逐层访问
如果字段类型是引用类型,你没有进行初始化,那么Java会自动将它赋值为:“null”。
如果字段类型是基本类型,你没有进行初始化,那么Java会确保它获得一个默认的初始值。
如表:
类型 | 初始值 |
---|---|
byte | (byte)0 |
char | '\u0000’或者null |
short | (short)0 |
int | 0 |
long | 0l |
float | 0.0f |
double | 0.0d |
boolean | false |
值前面的()仅说明了内存空间的大小,比如(byte)0,仅说明了这个0在内存中占8位,其他的类似。
实际示例:
这些默认值的知识很重要,需要强记!
通过观察上面示例的内容,我们可以很明确看到,举例的这些变量全部都是类变量,它们都直接属于类。
如图:
Java中只有类变量会被默认初始化!局部变量是不会被初始化的!
除了类变量还有一种变量叫局部变量,局部变量指的是方法变量或者是代码块中的变量:
局部变量示例:
代码块示例:
从上面的示例中可以看出,局部变量在声明的时候就一定要进行初始化!
你可以在上面示例的基础上,对变量进行初始化,看一看结果!
许多程序设计语言用"函数"这个术语来描述、命名子程序,而Java里却用"方法",Java里的"方法"表示做某些事情的方式。实际上,继续把它当成是函数也无妨,只是用词上的差别。
Java的方法决定了一个对象能够接收什么样的消息,方法的基本组成包括:方法名称,参数,返回值,方法体,下面是它最基本的形式:
ReturnType MethodName (参数列表)
{方法体的具体内容}
方法的基本组成说明:
名称 | 是否一定要有 | 是否能有多个 |
---|---|---|
方法名称 | 是 | 否 |
参数 | 否 | 是 |
返回值 | 否 | 否 |
方法体 | 是 | 否 |
方法示例:
// 最简方法示例
public void start(){
}
//以上就是最简单的方法了
//它没有返回值,没有参数。
//最复杂方法体示例
public int start(int i,int j){
System.out.println(i);
}
//以上方法具有返回值和多个参数。
//方法的具体说明可以继续关注后续博文的更新!
返回类型描述的是调用了方法之后从方法返回的值,参数说明了要传递给方法的信息的类型和名称。
方法名称参数列表(它们合起来被称为"方法签名"),可以唯一地标识出某个方法。
后面涉及到的方法重载就是方法名称完全一样,但是参数列表不同。
Java中的方法只能作为类的一部分来创建,大部分方法只有通过对象才能调用(少部分带static关键字的方法用类名直接调用),且这个对象能执行这个方法调用,如果试图在某个对象上调用它并不具备的方法,那么程序在编译的时候就会报错。调用方法的时候,首先是对象的名称,接着是".",然后是方法名称,最后是参数列表。
例如:
ObjectName.MethodName(args1,args2,args3…);
假设有一个方法f(),不带任何参数,返回类型是int,如果有一个名为a的对象,可以通过它调用f(),那么就可以这么写:
int x = a.f();-----用一个int类型的变量去接收f()的返回值
这种调用方法的行为通常称为"发送消息给对象",在上面的例子中,消息是f(),对象是a。
面向对象程序设计通常简单地归纳为"向对象发送消息"。
方法的参数列表指定要传递给方法什么样的信息。这些信息采用的都是对象的形式,因此,参数列表中必须指定每个所传递对象的类型及名字(与Java中任何传递对象的场合一样,这里传递的实际上也是引用),例如参数列表中指定传递String类型,那么在调用方法的时候,你必须传递一个String类型,如果传递其它类型,编译器会报错。
示例:
// 方法参数示例
//它必须置于某个类的定义内才能被正确编译
int toString(String s){
return s.length()*2;
}
//此方法的参数类型是String,参数名是s,一旦将s传递给此方法,就可以将它
//当做对象使用,也可以给它发送消息,上面的示例中给s发送了消息length(),
//length()是String类型中的方法,返回字符串包含的字符数。
上面这个示例方法的作用是计算传入的String对象所需要的存储字节数。
上面的例子中涉及到了return关键字。这里略提一下它的用法。
①、代表已经结束了(注意,如果在方法的执行中途即得到了想要的结果,你可以在方法的任何位置使用return,表示立即结束),离开此方法。
②、如果此方法产生了一个值,这个值要放在return的后面,表示返回的意思。
你可以定义方法返回任意想要的值,如果不需要返回任何值,可以指示此方法返回void,下面是一些示例:
// 方法的示例
boolean flag(){
return true;
}
double naturalLogBase(){
return 2.718;
}
void nothing(){
return;
}
void nothing2(){
}
//如果返回类型是void,那么return的作用只是用来退出方法,因此没有必要等到
//方法结束了才离开,如果返回类型不是void,那么无论在何处返回,
//编译器都会强制返回一个正确类型的返回值。
每种编程语言都有自己操纵内存的方式(在Java中涉及到最多的两种内存就是堆栈和堆),程序员必须要注意,将要处理的数据是什么类型(是基本类型还是引用类型,是引用类型的话,又具体到是哪一种),如果是基本类型,那就是堆栈,堆栈是可以直接操纵元素的,如果是引用类型,那就是堆,堆中元素(对象)是不能直接操纵的,需要一种基于特殊语法的间接表示来操纵(C和C++中的指针就是一个具体的例子)。
因为C和C++中的指针过于复杂,使用不方便,又因为Java中一切皆对象,可以使用单一固定的语法,所以,一切在Java中得到了简化,在前面创建对象的代码中,我们发现,在创建对象时,Java中全都是通过一个标识符"引用"一个对象:
比如:
Car c = new Car();
在这里,是通过标识符’c’引用了对象’new Car();’,要操纵**new Car();**以及它里面的内容(字段,方法,其他内容),都需要使用’c’。
下面通过实际具体的代码来示例:
修改对象中字段的值:
通过以上代码示例,我们发现,确实可以通过标识符’c’去引用一个对象(new Car();),并且在接下来的代码中,你还可以继续使用这个标识符去操纵这个对象以及它包括的所有东西。
因为在创建对象的时候,是使用标识符’c’去引用对象new Car();的,所以我们将标识符’c’称为对象new Car();的引用!
以上内容还可以类比为遥控器(引用)和电视机(对象)的关系,只要有这个遥控器,就可以保持与电视机的连接,当你想改变频道和调节音量时,都是通过遥控器(引用)去操纵电视机(对象),也就是说,你实际使用的是这个遥控器,你并没有直接去操纵电视机,如果你想在房间里面的任何地方都能操纵电视机(对象),你只需要携带遥控器(引用)就行,而不是电视机(对象)。
这里引申出一个很重要的问题,接着上面类比的内容,如果没有电视机(对象),遥控器(引用)也可以单独存在,也就是说,你拥有一个引用,并不一定需要有一个对象与它关联。
比如,你想操纵一个词或者句子,你可以创建一个String类型的引用:
String s;
但是这里创建的仅仅是一个引用,并没有具体的对象与它关联,如果此时你向s发送一个消息,程序会立即报错:
所以,一种安全的做法是,在声明一个引用时,立即创建一个对象与之关联:
在声明一个引用的时候立即与一个对象进行关联的操作称为"初始化"。
在以后的编程中都需要注意,最安全的做法就是,在声明一个引用的时候立即初始化。否则程序可能会报错。
一旦声明了一个引用,就希望有一个对象与它关联,在Java中,通过"new"关键字来创建对象,“new"关键字的意思是:给我一个新对象。
所以你在前面看到了:
String s = new String(“asdfgh”);这种形式的代码。
s是引用,new String(“asdfgh”);是对象。
这里通过提供一个初始字符串"asdfgh”,给出了怎样产生一个String对象的信息。
除了String以外,Java还提供了其他大量过剩的现成类型,最重要的是,你可以自行创建类型,事实上,这是Java程序设计中的一种基本行为。在以后的博文中,你会有更深刻的体会!
在大多数的程序设计语言中,如何控制变量的生命周期占据了程序设计工作的大部分时间,变量应该存活多长时间,该什么时刻销毁对象,如果变量的生命周期混乱,会造成大量的BUG,因为提前销毁,后期如果要用,没有了就会报错,如果一直不销毁,内存会爆满,Java中完美解决了这个问题,大大减轻了编程工作。
在这里强调一个前提:变量是存活在堆栈中!堆栈里面的东西必须有确切的生命周期。脑子有了这个概念,再继续往下看
大多数的过程型语言都有作用域的概念,作用域决定了在其内定义的变量的可见性和生命周期,在C和C++以及Java中,作用域是由花括号的位置决定的,例如:
// 花括号的位置决定了变量的可见性和作用域
{
int x = 12;
//在这里仅仅x是可见的
{
//在这里,x和q都是可见的
int q = 96;
}
//在这里,仅仅x是可用的
}
在作用域里定义的变量只可以作用于作用域结束之前。
这里仅仅说明Java的作用域,C和C++的作用域容易造成混乱。有兴趣的读者可以自行了解。
在这里强调一个前提:对象是存储在堆中的,堆中的东西与程序的结束无关,可以一直存在。脑子有了这个概念,再继续往下看
Java的对象不具备和基本类型一样的生命周期,当用new创建一个对象的时候,它是可以存活于作用域之外的。比如:
// 对象的作用域
{
String s = new String(" a String");
}
在这里,分两步介绍,其一:引用’s’在花括号之外不可见以及不存在了,它是和基本类型一致的,在作用域的终点消失了。
其二:引用’s’指向的对象new String(“a String”);在作用域之外还仍然占据着内存空间,在以上这一小段代码中,我们是无法在作用域之外访问这个对象了(因为它没有在引用’s’消失之前传递出去,这里就解释了为什么对象是引用传递),因为,它的唯一的一个引用已经在作用域之外消失了(这里就暗示了,一个对象可以有多个引用,在实际编程中确实如此,一旦通过引用传递,一个对象确实会存在多个引用,任何一个引用都可以操纵这歌对象,在后续的博文中,我会详细解释如何传递和复制引用。)。
事实证明,由new创建的对象,只要你需要,它就能一直存在下去,这样,在C++中存在的问题在Java中就消失了。在C++中,你不仅需要保证所有对象的存活时间与你需要它们的时间一样长,并且,在你不需要它们的那一刻就必须进行销毁。
那么问题就来了,如果这些对象一直存在,靠什么来防止它们填满你的内存空间呢?这正是C++中存在的问题。这也是Java的神奇所在。Java有一个垃圾回收器,用来监视new创建的对象,并辨别那些不再被引用的对象,随后,释放这些对象的内存空间,以便供其他的新对象使用。也就是说,你根本不用担心内存回收的问题,你只需要创建对象,一旦不再需要了,它会自动消失,这样即消除了这类编程问题(即内存泄漏–这是由于程序员忘记释放内存而引起的)。
几乎所有的编程语言都支持数组,在C和C++中使用数组是很危险的,因为C和C++中的数组就是内存块,如果一个程序要访问其自身内存块之外的数组,或者在数组初始化前使用内存(程序中常见的错误),都会产生难以预料的后果。
Java的主要目标之一是安全性,所以很多在C和C++里困扰程序员的问题在Java中都不会出现。 Java中确保数组会被初始化,而且不能在它的范围之外被访问,这种范围检查,是以每个数组上少量的内存开销及运行时的下标检查为代价的,但以此换来的是安全性和效率的提高,因此,付出的代价是值得的(有时Java还能优化这些操作)。
当创建一个数组对象时,实际上就是创建了一个引用数组(这里的引用数组的意思是,数组里面只能存储引用!)。并且每个引用都会被自动初始化为一个特定值,这个值有自己的关键字"null",一旦Java看到"null",就能立即识别这个引用还没有关联到一个具体的对象,如果试图使用一个值还是"null"的引用,程序会立即报错,因此,常犯 的错误在Java中就可以避免。
还可以创建用来存放基本类型的数组。同样的,编译器也能确保这种数组被初始化(根据类型来初始化,初始化的值就是这种类型对应的初始化值,比如int是0,float是0.0f,boolean是false等。),因为它会将这个数组所占的内存全部置0。
有关数组的信息,可以持续关注后续博文的更新。
1、创建新的数据:类
Java中一切皆对象,利用Java编程大部分时间都是在创建新的数据类型。
类中包括字段和方法:
字段: 八种基本类型和引用类型,Java会默认初始化。
类变量在声明的时候如果没有初始化,在编译的时候会被编译器默认初始化,但是局部变量在声明的时候就一定要进行初始化,否则编译器会报错!
方法: 方法名称,参数,返回值,方法体
2、利用类创建对象:
利用new关键字可以创建一个对象。
调用方法就是给对象发送消息。
3、操纵对象的方式:引用:
一个对象可以有多个引用。
引用可以进行传递。
4、对象销毁:
Java中的对象存储在堆中,销毁不需要体现在程序代码中,有垃圾回收机制。
在这里,很有必要再重提一下对象的创建与声明周期:
在使用对象时,最关键的问题之一便是他们的生成和销毁方式,你所创建出的每一个对象为了生存都需要资源,尤其是内存,当不再需要一个对象时,它必须被清理掉,使其占有的资源可以被释放和重用,在相对简单的编程情况下,怎么清理对象看起来似乎不是什么挑战:你创建了对象,根据需要使用它,然后它应该被销毁,然而,你可能会遇到相对复杂的情况。
例如:你正在设计一个机场空中交通管理系统,一开始似乎很简单,创建一个容器来管理所有的飞机,然后为每一架进入空中交通控制区域的飞机创建一个新的对象,并将其置于容器中,对于清理工作,只需要在飞机离开此区域时删除相关的飞机对象既可。
但是,可能还有别的系统记录着有关飞机的数据,例如,它可能记录着所有飞离机场的所有小型飞机的飞行计划,因此,你需要有第二个容器来存放小型飞机,无论何时,只要创建的是小型飞机,那么它就应该被置入第二个容器之中,然后,某个后台进程在空闲时对第二个容器内的对象进行操作。
这个时候,问题就变得困难了,怎样才能确定何时销毁这些对象呢?当处理完某个对象的时候,系统某个其他部分可能还在处理它,在其他许多场合中也会遇到同样的问题,在必须明确删除对象的编程系统中(例如C++),此问题会变得十分复杂。
对象的数据存储在何处,怎样控制对象的生命周期?C++认为效率控制是最重要的,所以给程序员提供了选择的权利,为了追求最大的执行速度,对象的存储空间和生命周期可以在编写程序时确定,这样可以将对象至于堆栈或静态存储区域来实现,这种方式将存储空间分配和释放至于优先考虑的位置,某些情况下这样控制非常有价值,但是,也牺牲了灵活性,因为必须在编写程序时知道确切的对象数量、生命周期、类型,如果试图解决更一般化的问题,例如计算机辅助设计,仓库管理或者空间中交通控制,这种方式就显得过于受限了。
第二种方式是在被称为堆的内存池中动态地创建对象。在这种方式中,直到运行时才知道要创建多少个对象,它们的生命周期如何,以及它们是什么类型,只能在程序运行至相关代码时的那一刻才能确定,如果需要一个新对象,可以在需要的时刻直接在堆中创建,因为存储空间是在运行时被动态管理的,所以需要大量的时间在堆中分配空间(注意,这个时间是花费在运行的时间内,而C和C++在运行开始之前就确定了,这就是为什么Java较慢的原因),这可能要远远大于在堆栈中创建存储空间的时间(在堆栈中创建和释放存储空间往往各需要一条指令既可),分别对应将栈顶指针向下移动和栈顶指针向上移动,而创建堆存储空间的时间依赖于存储机制的设计。
动态方式有这样一个一般性的逻辑假设:对像趋向于变得复杂,所以查找和释放存储空间的开销不会对对象的创建造成重大冲击,动态方式所带来的更大的灵活性正是解决一般化编程问题的要点所在(通俗地理解就是:对象一般是很复杂的,所以呢在堆栈和堆中创建时间不会有非常显著的差距,基本可以忽略)。
Java完全采用了动态内存分配方式,每当想要创建新对象时,就要用new关键字来构建此对象的动态实例。
还有一个议题,就是对象的生命周期:对于允许在堆栈上创建对象的语言,编译器可以确定对象存活的时间,并可以自动销毁它,然而,如果在堆上创建对象,编译器就会对它的生命周期一无所知,在像C++这样的语言中,必须通过编程方式来确定何时销毁对象,这可能会因为不能正确处理而导致内存泄露(这是C++程序中常见的问题),Java提供了被称为垃圾回收器的机制,它可以自动发现对象何时不再被使用,并继而销毁它,垃圾回收器非常有用,因为它减少了所必须考虑的议题和必须编写的代码,更重要的是,垃圾回收器提供了更高层的保障,可以避免暗藏的内存泄露问题。
Java的垃圾回收器被设计用来处理内存释放问题,垃圾回收器知道对象何时不再被使用,并自动释放对象占用的内存(主要是因为两个特点:1、单根继承结构,所有的对象都是继承自Object,2、Java中只能以一种方式创建对象-----堆上创建),Java编程比C和C++要简单的多,需要考虑的问题和克服的障碍也少得多。
5、Java中一种最原始的对象:数组:
数组代表的就是内存块,所以操作数组的时候需要特别注意!
6、基本数据:存储长度,初始化默认值:
PS:时间有限,有关Java SE的内容会持续更新!今天就先写这么多,如果有疑问或者有兴趣,可以加QQ:2649160693,并注明CSDN,我会就博文中有疑义的问题做出解答。同时希望博文中不正确的地方各位加以指正!