Java编程思想学习笔记二:一切都是对象

用引用操作对象

在Java中一切都是对象,但操纵标识符的是对象的一个引用。(生活中的例子:可以将这一情形想象是电视机与遥控器的关系。电视机是一个对象,遥控器是对象的引用,我们在房间里拿着遥控器不管在哪里都可以操控电视机对象。此外,脱离电视机对象,遥控器引用也是可以独立存在的。)

1
String s; //这里只是创建了一个引用,并没有创建对象,如果此时向s发送消息的话,会报出运行时异常的错误,因为我们并没有给这个引用指定对象,前文我们说到消息的传递都是基于对象的。

我们在声明引用的时候要进行初始化:

1
String s = "abc"; //字符串可以使用带引号的文本进行初始化

必须由开发者创建所有的对象

  • 我们创建了一个操作对象的引用,通常希望它可以与一个对象进行关联。
  • Java中更通用的做法是使用new关键字创建一个对象。
  • new关键字的意思是“给我一个新的对象”。
1
String s = new String("abc"); //表示给我一个String类型的对象,并且初始化值为“abc”

计算机如何分配内存

当我们创建好对象时,他们是怎么样进行内存分配的呢?程序在计算机中,有如下五个地方可以存储数据:

1. 寄存器

这是最快的存储区,他与其他存储区不同的地方在于他在处理器的内部。同时他的数量也是非常有限的,因此他也是根据需求进行分配,你基本不能自己控制他进行内存分配。

2. 堆栈

位于通用RAM(随机存储器)中,通过堆栈指针可以从处理器得到支持,指针向下移动分配新内存,指针向上移动回收内存。这是一种快速有效的分配内存方式,仅次于寄存器。创建程序时,Java系统必须知道处于堆栈中的所有项的确切声明周期,以便堆栈指针的移动。这一操作限制了程序的灵活性,虽然Java中某些数据存储在堆栈中,比如对象的引用,但是Java对象并不存在堆栈中。

3. 堆

一种同样位于RAM中的内存池,用于存放所有的Java对象,堆不同于堆栈的好处在于编译器不需要提前知道对象的确切声明周期,因此灵活性很高。当需要一个对象的时候,只需要new一段代码,会自动的分配所需要的内存。当然这种分配的效率要比使用堆栈的低。

4. 常量存储

常量值通常存在程序代码内部,这样做使得他们永远不会被改变。在部分嵌入式系统中,常量本身会与其他部分分离,这种情况通常将常量存储在ROM(只读存储器)中。

5. 非RAM存储

数据可以完全存储在程序之外,那么他可以完全不受程序的控制,即便程序没有启动也可以存在。比如文件流对象和持久化对象。文件流通常是被发送给另一台机器,持久化对象通常存储在硬盘中。

基本类型

通常情况下,new将对象存储在堆里,当创建一个简单的小的对象时,显得不是很有效,因此Java采用跟C或者C++相同的方式,不用new创建变量,而创建一个并非是引用的自动变量,这个变量直接存储值,并将其存储在堆栈中。与其他语言不同的是,Java中所有的基本类型的大小在各个平台都相同,这使得Java程序有更好的跨平台移植性。

  • 所有的数值类型都有正负号,所以Java中不存在无符号类型。

java-basic-type

包装器类

基本类型具有包装器类,使得可以在堆中创建一个非基本对象,用来表示对应的基本类型。

1
2
3
4
5
6
7
8
char c = 'x'
Character ch = new Character(c);
// 也可以这样用:
Character ch = new Character('x');
// Java SE5的自动包装功能可以自动的将基本类型转换为包装器类型:
Character ch = 'x'
// 也可以反向转换:
char c = ch;

Java中还提供了两个用于高精度计算的类:BigIntegerBigDecimal,他们大体属于包装器类,但是他们没有对应的基本类型,不过可以通过方法调用的方式与int 和 float进行交互操作。BigInteger支持任意精度的整数,BigDecimal支持任意精度的浮点数。

Java与其他语言一样都提供了数组的功能,不同的是,Java语言保证了数组的安全性,会确保数组被初始化才使用,不会出现其他语言中的访问没有被初始化的内存区域。当创建了一个数组对象时,实际上就创建了一个引用数组,并且每个引用会自动初始化一个特定的值,该值拥有自己的关键字null,一旦Java看到了null,就知道这个引用还有指向对象,也就是没有被初始化。这个过程称为下标检查。

永远不需要销毁对象

作用域

Java语言与其他语言都有作用域的概念,作用域决定了在其内部定义的变量名的可见性和生命周期。作用域为对应的花括号区间内部。

Java对象与其他的基本类型不同,他可以存活于作用域之外。

1
2
3
{
String s = new String("x");
}

引用s在对应的作用域终点就消失了,然而,s指向的String对象仍然占据内存空间,我们无法在作用域之外访问这个对象,因为它的唯一的引用已经超出了作用域的范围,后面会讲述程序的执行过程中,如何传递和复制对象的引用。

垃圾回收机制是否对象占用的内存

Java中,只要你需要,对象会一直存在,Java自带了垃圾回收器,用来监视new创建的对象,并辨别那些不会再引用的对象,从而释放内存供其他对象使用。所以在Java中不必担心忘了释放对象的内存。

创建新的数据类型:类

同一种对象的集合我们可以理解为类,这里类表示由他实例化的对象都具有相同的基本属性。
Java中使用class关键字创建一个新的类型。

1
2
3
class Students{} //定义了一种新的类型 Students,在这个类型中,没有说明属性,也没有指定方法,所以他暂时还不能处理消息,但是我们可以用它来创建新的对象,实例化一个类的对象如下

Students student = new Student();

类的数据类型

  1. 字段(类的一些属性) – 数据
  2. 方法(类与其它类之间交互消息的方式) – 行为

类基础数据类型的初始化

Java中会为类当中(不是类中的不算哦)的基本类型成员提供一个默认值,以确保这个基本数据类型可以有效的进行初始化,防止程序出现错误,当然这个初始化的值可能不符合程序需求,需要自己明确的进行初始化。如果不是在类中的基本类型变量,那么它初始化的值是随机的。

java-basic-type-init

方法、参数和返回值

方法的定义

Java的方法决定了对象能够做哪些事情,可以接收处理哪些信息。方法的基本组成包括:名称、参数、返回值和方法体。

1
2
3
ReturnType methodName(Arg arg){
/*Method Body*/
}

  • ReturnType是调用了该方法之后,返回给调用方的值的类型
  • methodName是该方法的名字,括号里的是参数列表
  • Arg是参数类型,
  • arg是参数的名称
  • Method Body是函数方法的执行内容。

方法的调用

Java中方法的调用是通过对象进行调用,且这个对象必须能够执行这个方法,否则在编译过程中会报错。

1
2
3
objectName.methodName(arg,arg1,arg2);
//有返回值的调用
ReturnType x = obejctName.methodName(arg,arg1,arg2);

构建一个Java程序

java的包

C++中对类的名字有命名空间的概念,Java中避免重复的命名采用了包的形式,将相同类型,或者相同作用相同业务场景的类放在一个包中,不同包中可以出现相同名称的类。

导包

如果需要使用另一个包中的类,Java中通过import关键字导入对应类所属的包,这样就可以在当前类调用其他类了。

静态 static

通常情况下,我们必须创建一个对象,并用这个对象访问数据或者方法,而有一种特殊的情况,我们可以用static修饰域或者方法,这意味着这个域或者方法不会与包含它的那个类的任何对象实例关联在一起。所以即使从未创建某个类的任何对象,也可以调用其static方法访问static域。

只需将static放在要修饰的定义之前,就可以将字段或者方法设定为static,如下生成了一个static字段,并进行初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class TestStatic(){
static int i = 4;
}
//现在即使创建了两个`TestStatic`对象,`i`仍然只有一个存储空间,**两个对象的值共享一个变量**。
TestStatic ts1 = new TestStatic();
TestStatic ts2 = new TestStatic();

// 如上ts1.i和ts2.i指向同一个内存空间,因此他们值相同都是4
// 直接通过类名.变量的方式,如TestStatic.i ,而这对于非static变量是不允许的。被static修饰的变量称为静态变量,他的作用是所有对象共享同一个数据,且声明周期贯穿整个程序执行过程,共享一个数据的应用可以作为一个计数器。
// 类似的逻辑还可以应用于静态方法,既可以像非静态方法一样使用对象进行调用,也可以使用类名直接调用。定义静态方法与定义静态变量相似

class TestStatic(){
static void staticMethod();
}

静态方法和变量存在的意义:

  • static变量或者方法的应用重要意义就是可以不需要创建对象就可以调用它。

第一个java程序

1
2
3
4
5
6
7
import java.util.*
public class HelloWorld{
public static void main(String[] args) {
System.out.println("Hello World");
}
}
// import是导入java.util工具包下的所有类,HelloWorld是类名,类名需要与文件名相同,main方法是程序的入口,虽然这里没有用到参数列表,但是对于main函数来说这是必须的。使用开发工具如Eclipse运行程序即可看到控制台打印“HelloWorld”