1. 万物皆对象
Java是面对对象的设计语言,在Java程序中,一切皆为对象
1.1. 面对对象的设计方式
1.1.1 抽象成为对象
对象具有状态、行为和标识
对象可以用户有内部数据(该对象的状态)和方法(对象的行为),并且每一个对象都能与其他对象区分;对象之间可以发送消息(调用请求)完成协作;程序是由对象(及其方法)组成的;对象具有类型,相同类型的对象能接收相同类型的消息
1.1.2 类与接口
类描述了一系列具有相同特征(数据元素)与行为(方法)的对象集合,可以把他看作是一类对象的模板,对象按照类被创造出来。
接口确定了一个类能接收或发出的请求,在程序中每个对象都能提供服务(特定的功能),它们依靠内部的方法与数据恰好完成一件任务并且不尝试做更多的事,称之为实现。类中每一个接口对应一个方法,他们能接收特定的请求,当向对象发送请求时,与之相关联的方法将会被调用。
1.1.3 类的复用
继承
继承可以实现这样的效果:以现有的类为基础,通过扩展原有类的副本创建一个新的类,但他拥有比基础的类更多的功能。当源类(父类、基类)发生修改时,继承自它的类(导出类、子类)也会自动修改。一个基础类包含其所有导出类所共享的特性和行为;子类也包含其父类的所有特征与行为。
当继承一个类的时候,实际上是创建了一个的类,这个类复制了其父类的全部成员,可以通过两种方法使子类与其父类产生差异:
- 在子类中添加新的方法,扩充父类的功能
- 在子类中覆盖父类的方法实现,改变子类的行为
所有的类都继承于Object类。
单根继承
但根继承是指所有的类都只能拥有一个父类。
- 单根继承保证所有的对象都具备某些最基础的功能
- 单根继承使得类能够很容易的在堆上创建,同时简化参数的传递
- 单根继承使得垃圾回收器的实现变得容易
接口
接口可以理解为某种协议。接口内可以定义成员变量(需要初始化)以及成员方法,但是接口中非静态的方法都不能提供方法体。所有实现某个接口的类都拥有方法定义的特征以及方法,并且必须实现接口定义的方法。
2. 操纵对象
通过对操作对象,完成特定的功能。想要操作对象必须首先拥有一个对象并且使用一个相同类型的引用去持有它
2.1 对象的引用
你不能直接操纵对象,必须借助于“引用”来间接地槽中对象。
创建引用时必须声明他的引用类型,在这之后这个引用只能操作相同类型的对象。
-
声明一个应用,并让它指向一个对象
通过这种方式创建一个String类型的引用同时对它进行初始化,这样这个引用就能操作这个对象了。使用new关键字来创建新的对象。
Object obj = new Object();
-
声明一个不指具体对象的引用
也可以声明一个引用但不对它进行初始化,这个引用可以在将来用到的时候指向一个具体的对象。
Object obj; Object obj2 = null; // 初始化为null
java中所有的变量都需要被初始化之后才能使用。
2.2 创建对象
想要使用对象提供的功能,你必须先拥有它。
通常使用new关键字以及类的构造器生成某类的实例对象。但是对于特殊的额String类型的对象,可以直接使用双引号生成对象。
String str = new String();
String s = "Object"; // 声明一个匿名对象
2.2.1 对象的存储
名称 | 具体位置 | 特点及描述 |
---|---|---|
寄存器 | 处理器内部 | 最快的存储区,但不能直接控制 |
堆栈 | RAM | 存储引用以及基本类型的值,通过堆栈指针操作 |
堆 | RAM | 存放引用类型的对象 |
常量 | ROM | 常量值直接存放在程序内部 |
非RAM存储 | 磁盘或网络中 | 这类数据完全存活于程序之外,如流对象; |
流对象:可以序列化成字节流被发往其他机器需要时也能恢复成常规对象
2.2.2 对象的类型
引用类型
引用类型程序中最常见的类型,程序中大部分的类的类型都是引用类型。
- 引用类型的对象存放在堆内,每个对象占据的内存空间是不确定的,引用类型对象内可以组合其他类型的对象。
- 引用类型的对象使用new关键字以及类的构造器创建。但String是个特例,它可以通过双引号进行创建。
基本类型
基本类型是在便赐额程序过程中最常用的类。因为基本类型对象一般都是一些“小的”、简单的对象,这类型的对象使用new对象往堆里创建对象往往不是很有效。
-
基本类型的变量拥有固定的大小,这使得java具有多平台移植的特性。
-
基本类型的变量不使用new关键字创建对象,而是创建一个**”并非是引用“**的变量,这个变量直接存储在堆栈中。
-
基本类型对象子啊创建的时候会赋予一个默认初始值。
包装类
基本类型都有一个对应的包装类,包装类是引用类型。Java5开始引入自动装箱拆箱机制,使得基本类型与对应的包装器类型能够自动转换。
2.2.3 数组
数组其实也符合 引用-对象
模型,实际上在未对数据进行初始化的时候,我们拥有的只是数组的引用,因此数组其实也是一种对象,可以使用new关键字创建。
在执行 int[] i;
时编译器只是为数组的引用分配了在堆中的存储空间,而需要通过堆数组对象的初始化来为数组分配对应的存储空间。
- 数组是java中最快的容器,但是它的元素只能是同一种对象且它的长度是固定的。
- 数组确保创建时它的元素被正确的初始化,对于引用类型的元素自动初始化为null,对于基本类型的元素自动初始化为对应的默认初始值。
- 对于基本数据类型,数组其实持有的是元素值;对与运用类型,数组持有对对象的引用。
数组的初始化
数组保证在创建时其内部的元素被正确初始化,但是我们使用数组是总是希望它能保存我们提供给它的元素,因此这就产生了两种初始化数组的方式:
-
静态初始化
静态初始化即是在数组初次创建的时候就对其进行初始化
public static void main(String[] args) { int[] i_Array = {1,2,3}; for (int i : i_Array) { System.out.println(i); } }
-
动态初始化
数组在动态初始化中实际上被初始化了两次,第一次是编译器创建数组并初始化数组的存储空间,第二次是我们手动将元素放入数组的某个位置中。
public static void main(String[] args) { int[] i_Array = new int[3]; for (int i = 0; i < i_Array.length; i++) { i_Array[i] = i; } System.out.println(i_Array); }
数组的复制
对于数组的复制,在java中有浅复制与深复制两种区别:
-
浅复制
浅复制只是两个相同类型的数组引用共享同一个对象,并没有产生新的数组对象,在通过其中一个引用修改对象的时候能通过另一个引用访问修改后的数组。
int[] i1 = {1,2,3,4,5}; int[] i2 = i1; // 浅复制 i1[0] = 99; System.out.println(i2[0]); // 99
-
深复制
深复制时真正地产生了新的数组对象,两个引用分别持有两个单独的数组对象。用过
array.clone()
实现数组的深复制int[] i1 = {1,2,3,4,5}; int[] i2 = i1.clone(); // 深复制 i1[0] = 99; System.out.println(i2[0]); // 1
2.2.4 对象的作用域
作用域决定了在其内定义的变量名的可见性与声明周期,在作用域内定义的变量只能作用于作用域结束之前。
但是在作用域结束之后,在作用域内创建的对象仍然占有这空间,虽然我们以及无法访问到这个对象(通过new创建的对象,只要你需要都会一直存在下去);但是不要紧,java的垃圾回收机制会在适宜的时候将不在占用的对象清理掉。
2.3 类
类是对象的模板,它定义了对象的特征和行为。使用class关键字来定义一个新的类型,在类型被定义之后就能通过new关键字来生成该类型的实例对象。
所有的对象都应该在类的域内。
// 声明一种新的类型
class People{
private String name;
public static void main(String args[]){
// 在声明对象完成之后就能通过new关键字创建该类型的实例对象。
People p = new People();
}
}
2.3.1 类成员
在类的内部可以声明一系列的成员,包括成员变量与成员方法,分别表示该类的实例对象的属性以及行为。
成员变量(字段)
字段有时候又称为数据成员,字段可以为任意类型:
-
如果字段是引用类型,那么在使用前必须初始化该引用。
你可以在声明成员变量的时候让引用指向一个具体的对象。
class Test{ Private People = new People("Leric"); }
也可以在后续听过调用成员方法的方式(通常通过调用构造函数实现)使引用持有一个具体的对象
class Test{ public static void main(String args[]){ People p; p = new People("Leric"); System.out.println(p.getName()); } }
-
如果字段是基本类型,那么在声明类的成员变量的时候编译器会自动根据类型赋予一个默认初始值
成员方法
成员方法决定了一个对象能够接收什么样的消息以及对象能执行什么样的行为。
方法的基本组成部分:名称 参数列表 返回值 方法体,当然它还会有其他的修饰符。方法名和参数列表组合称为方法签名,唯一的标识一个方法。
参数列表
参数列表指定要传递给方法的参数,传递给方法的参数类型:
- 引用类型:传递的是对象的引用
- 基本类型:传递的是值
可变参数列表
使用可变参数列表的方法可以接收不定长的参数(可以为0个),但是这些参数必须是相同类型的。
-
可变参数列表使用
Param... param
表示 -
可变参数列表实际上将所有接收的参数都存放在一个数组内,可变参数列表也可以接收数组。
-
当方法还需要接收其他类型的参数是,可变参数必须放在最后,也意味着一个方法只能接收一个可变参数列表
public static void paramList(String s,int... i){
System.out.println(s);
for (int j = 0; j < i.length; j++) {
System.out.print(j+"\t");
}
System.out.println(" ");
}
public static void main(String[] args) {
paramList("可变参数列表",1,2,3,4,5);
int[] i = {6,7,8,9,0};
paramList("数组",i);
}
重载与覆盖
重载与覆盖都是针对方法而言的,目的都是为了复用方法。
-
重载:重载是两个参数列表的数量,顺序不同但是方法名相同的两个方法之间的关系
// 不构成重载 /*public void overrideTest(){ System.out.println("t1"); } */ // 下面三个方法构成重载 public String overrideTest(){ return ""; } public String overrideTest(String s,int i){ return ""; } public String overrideTest(int i,String s){ return ""; }
-
覆盖:覆盖发生在继承关系中,子类重新定义了父类方法的执行逻辑,覆盖可以通过
@Override
注解检查
返回值
由于作用域的存在,在方法内部产生的对象或值需要通过return语句返回出去。
return语句代表两个意思
- 返回方法产生的值
- 结束方法
若方法需要返回一个对象,必须在声明方法的时候写上对应的返回值类型,若不反悔任何对象则使用void,同时方法体的return语句后不能跟着任何对象。
public static void test(){
System.out.println("test");
System.out.println("test");
return;
}
调用
在获得持有具体对象的引用之后就能通过引用.成员
的方式调用对象的成员(务必注意成员的访问权限以及作用域)
p.name; // 访问成员变量
p.getName();// 访问成员方法
Java方法只能作为类的一部分创建,除静态方法外,方法只能通过对象进行调用。调用方法的行为通常被称为发送消息给对象。下面的例子中,消息是f(),对象是a。
int x = a.f();
调用的结果可以使用一个相同类型的引用持有:
- 引用类型:实际上传的是返回对象的地址
- 基本类型:实际上传的是返回的值